From c1a86f74070ccf71114d4c82584e1236de658713 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Mon, 6 Feb 2017 19:31:05 -0500 Subject: [PATCH 01/28] Attempt to fix race condition between reconciler and CA --- .../vscode/languageserver/testharness/LanguageServerHarness.java | 1 + 1 file changed, 1 insertion(+) diff --git a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java index 812ab72cd..dc1e8e8dd 100644 --- a/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java +++ b/vscode-extensions/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/LanguageServerHarness.java @@ -302,6 +302,7 @@ public class LanguageServerHarness { TextDocumentPositionParams params = new TextDocumentPositionParams(); params.setPosition(cursor); params.setTextDocument(doc.getId()); + server.waitForReconcile(); return server.getTextDocumentService().completion(params).get(); } From 9bef1b8a6b82a91bf6252192c9fb167fdfeaf67c Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Tue, 7 Feb 2017 19:45:59 -0500 Subject: [PATCH 02/28] Initial support for Gradle projects --- .gitignore | 1 + .../commons/commons-gradle/.classpath | 36 +++ .../commons/commons-gradle/.project | 23 ++ .../org.eclipse.core.resources.prefs | 6 + .../.settings/org.eclipse.jdt.core.prefs | 5 + .../.settings/org.eclipse.m2e.core.prefs | 4 + .../commons/commons-gradle/pom.xml | 41 +++ .../ide/vscode/commons/gradle/GradleCore.java | 107 ++++++++ .../commons/gradle/GradleException.java | 45 ++++ .../commons/gradle/GradleJavaProject.java | 36 +++ .../gradle/GradleProjectClasspath.java | 207 +++++++++++++++ .../gradle/GradleProjectFinderStrategy.java | 62 +++++ .../commons/gradle/GradleProjectTest.java | 58 ++++ .../resources/empty-gradle-project/.gitignore | 25 ++ .../empty-gradle-project/build.gradle | 33 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53556 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + .../resources/empty-gradle-project/gradlew | 164 ++++++++++++ .../empty-gradle-project/gradlew.bat | 90 +++++++ .../EmptyGradleProjectApplication.java | 12 + .../src/main/resources/application.properties | 0 .../EmptyGradleProjectApplicationTests.java | 16 ++ .../.gradle/3.3/taskArtifacts/fileHashes.bin | Bin 0 -> 18697 bytes .../3.3/taskArtifacts/fileSnapshots.bin | Bin 0 -> 19148 bytes .../3.3/taskArtifacts/taskArtifacts.bin | Bin 0 -> 19773 bytes .../3.3/taskArtifacts/taskArtifacts.lock | Bin 0 -> 17 bytes .../test/resources/test-app-1/build.gradle | 28 ++ .../gradle/wrapper/gradle-wrapper.properties | 6 + .../src/test/resources/test-app-1/gradlew | 172 ++++++++++++ .../src/test/resources/test-app-1/gradlew.bat | 84 ++++++ .../test/resources/test-app-1/settings.gradle | 20 ++ .../test-app-1/src/main/java/Library.java | 8 + .../test-app-1/src/test/java/LibraryTest.java | 12 + .../src/test/resources/test-resource-1.txt | 1 + .../commons/jandex/JandexClasspath.java | 88 ++++++ .../vscode/commons/jandex/JandexIndex.java | 250 +++++++++--------- .../ide/vscode/commons/java/IClasspath.java | 18 ++ .../ide/vscode/commons/java/IJavaProject.java | 29 +- .../ide/vscode/commons/maven/MavenCore.java | 10 +- .../maven/MavenProjectFinderStrategy.java | 10 +- .../commons/maven/java/MavenJavaProject.java | 53 +--- .../maven/java/MavenProjectClasspath.java | 77 +----- .../java/classpathfile/FileClasspath.java | 42 +++ .../JavaProjectWithClasspathFile.java | 42 --- .../vscode/commons/maven/HtmlJavadocTest.java | 32 +-- .../vscode/commons/maven/JavaIndexTest.java | 14 +- .../commons/maven/SourceJavadocTest.java | 18 +- vscode-extensions/commons/pom.xml | 3 +- vscode-extensions/vscode-boot-java/pom.xml | 5 + .../boot/java/BootJavaLanguageServer.java | 6 +- .../project/harness/ProjectsHarness.java | 2 +- .../vscode-boot-properties/pom.xml | 5 + .../boot/BootPropertiesLanguageServer.java | 6 +- .../boot/metadata/ClassReferenceProvider.java | 6 +- .../DefaultSpringPropertyIndexProvider.java | 4 +- .../boot/metadata/LoggerNameProvider.java | 4 +- .../boot/metadata/hints/StsValueHint.java | 2 +- .../vscode/boot/metadata/types/TypeUtil.java | 2 +- .../vscode/boot/metadata/TypeUtilTest.java | 4 +- .../test/ApplicationPropertiesEditorTest.java | 48 ++-- .../boot/test/ApplicationYamlEditorTest.java | 8 +- .../project/harness/ProjectsHarness.java | 2 +- 62 files changed, 1721 insertions(+), 376 deletions(-) create mode 100644 vscode-extensions/commons/commons-gradle/.classpath create mode 100644 vscode-extensions/commons/commons-gradle/.project create mode 100644 vscode-extensions/commons/commons-gradle/.settings/org.eclipse.core.resources.prefs create mode 100644 vscode-extensions/commons/commons-gradle/.settings/org.eclipse.jdt.core.prefs create mode 100644 vscode-extensions/commons/commons-gradle/.settings/org.eclipse.m2e.core.prefs create mode 100644 vscode-extensions/commons/commons-gradle/pom.xml create mode 100644 vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleCore.java create mode 100644 vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleException.java create mode 100644 vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java create mode 100644 vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java create mode 100644 vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinderStrategy.java create mode 100644 vscode-extensions/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/.gitignore create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/build.gradle create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradle/wrapper/gradle-wrapper.jar create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradle/wrapper/gradle-wrapper.properties create mode 100755 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradlew create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradlew.bat create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/main/java/com/example/EmptyGradleProjectApplication.java create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/main/resources/application.properties create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/test/java/com/example/EmptyGradleProjectApplicationTests.java create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/fileHashes.bin create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/fileSnapshots.bin create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/taskArtifacts.bin create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/taskArtifacts.lock create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/build.gradle create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradle/wrapper/gradle-wrapper.properties create mode 100755 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradlew create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradlew.bat create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/settings.gradle create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/main/java/Library.java create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/test/java/LibraryTest.java create mode 100644 vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/test/resources/test-resource-1.txt create mode 100644 vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java diff --git a/.gitignore b/.gitignore index cb58cf4c8..0932b9cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ hs_err_pid* target/ +bin/ *.log # Mac OSX aux files diff --git a/vscode-extensions/commons/commons-gradle/.classpath b/vscode-extensions/commons/commons-gradle/.classpath new file mode 100644 index 000000000..fae1a2b37 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/.classpath @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vscode-extensions/commons/commons-gradle/.project b/vscode-extensions/commons/commons-gradle/.project new file mode 100644 index 000000000..c9d9c8fb0 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/.project @@ -0,0 +1,23 @@ + + + commons-gradle + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.core.resources.prefs b/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..29abf9995 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8 +encoding/=UTF-8 diff --git a/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.jdt.core.prefs b/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..714351aec --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,5 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.m2e.core.prefs b/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/vscode-extensions/commons/commons-gradle/pom.xml b/vscode-extensions/commons/commons-gradle/pom.xml new file mode 100644 index 000000000..5e9a84009 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/pom.xml @@ -0,0 +1,41 @@ + + 4.0.0 + + commons-gradle + commons-gradle + Gradle Utilities + + + org.springframework.ide.vscode + commons-parent + 0.0.1-SNAPSHOT + ../pom.xml + + + + 3.3 + + + + + gradle-repo + Gradle Tooling Repo + https://repo.gradle.org/gradle/libs-releases + + + + + + org.springframework.ide.vscode + commons-java + ${project.version} + + + org.gradle + gradle-tooling-api + ${gradle-tooling.version} + + + + \ No newline at end of file diff --git a/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleCore.java b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleCore.java new file mode 100644 index 000000000..6cbd3c9bd --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleCore.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * 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.gradle; + +import java.io.File; + +import org.gradle.tooling.GradleConnectionException; +import org.gradle.tooling.GradleConnector; +import org.gradle.tooling.ProjectConnection; +import org.gradle.tooling.model.build.BuildEnvironment; +import org.gradle.tooling.model.eclipse.EclipseProject; +import org.springframework.ide.vscode.commons.util.Assert; + +/** + * Gradle API tooling utility + * + * @author Alex Boyko + * + */ +public class GradleCore { + + @FunctionalInterface + public interface GradleConfiguration { + void configure(GradleConnector connector); + } + + public interface GradleCoreProject { + EclipseProject getProject(); + BuildEnvironment getBuildEnvironment(); + } + + static final String GRADLE_BUILD_FILE = "build.gradle"; + + private static GradleCore defaultInstance = null; + + public static GradleCore getDefault() { + if (defaultInstance == null) { + defaultInstance = new GradleCore(); + } + return defaultInstance; + } + + private GradleConfiguration configuration; + + public GradleCore() { + this.configuration = (connector) -> {}; + } + + public GradleCore(GradleConfiguration configuration) { + Assert.isNotNull(configuration); + this.configuration = configuration; + } + + public GradleCoreProject readProject(File projectDir) throws GradleException { + ProjectConnection connection = null; + try { + GradleConnector gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir); + configuration.configure(gradleConnector); + connection = gradleConnector.connect();; + final EclipseProject project = connection.getModel(EclipseProject.class); + final BuildEnvironment build = connection.getModel(BuildEnvironment.class); + return new GradleCoreProject() { + + @Override + public EclipseProject getProject() { + return project; + } + + @Override + public BuildEnvironment getBuildEnvironment() { + return build; + } + }; + } catch (GradleConnectionException e) { + throw new GradleException(e); + } finally { + if (connection != null) { + connection.close(); + } + } + } + + public T getModel(File projectDir, Class modelType) throws GradleException { + ProjectConnection connection = null; + try { + GradleConnector gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir); + configuration.configure(gradleConnector); + connection = gradleConnector.connect(); + return connection.getModel(modelType); + } catch (GradleConnectionException e) { + throw new GradleException(e); + } finally { + if (connection != null) { + connection.close(); + } + } + } + +} diff --git a/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleException.java b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleException.java new file mode 100644 index 000000000..75342b9ca --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleException.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.gradle; + +import java.util.Arrays; +import java.util.stream.Collectors; + +/** + * Gradle wrapper exception + * + * @author Alex Boyko + * + */ +public class GradleException extends Exception { + + private static final long serialVersionUID = 7958309594787399714L; + + private Throwable[] t; + + public GradleException() { + super(); + } + + public GradleException(Throwable... t) { + super(); + this.t = t; + } + + @Override + public String getMessage() { + if (t != null) { + return String.join("\n", Arrays.stream(t).map(t -> t.getMessage()).collect(Collectors.toList())); + } + return super.getMessage(); + } + +} diff --git a/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java new file mode 100644 index 000000000..ae7121144 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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.gradle; + +import java.io.File; + +import org.springframework.ide.vscode.commons.java.IJavaProject; + +/** + * Implementation of Gradle Java project + * + * @author Alex Boyko + * + */ +public class GradleJavaProject implements IJavaProject { + + private GradleProjectClasspath classpath; + + public GradleJavaProject(GradleCore gradle, File projectDir) throws GradleException { + this.classpath = new GradleProjectClasspath(gradle, projectDir); + } + + @Override + public GradleProjectClasspath getClasspath() { + return classpath; + } + +} diff --git a/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java new file mode 100644 index 000000000..c7b2b8379 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectClasspath.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * 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.gradle; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.gradle.tooling.model.build.BuildEnvironment; +import org.gradle.tooling.model.eclipse.EclipseProject; +import org.springframework.ide.vscode.commons.jandex.JandexClasspath; +import org.springframework.ide.vscode.commons.jandex.JandexIndex; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavadocProvider; +import org.springframework.ide.vscode.commons.java.parser.ParserJavadocProvider; +import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider; +import org.springframework.ide.vscode.commons.javadoc.SourceUrlProviderFromSourceContainer; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +/** + * Implementation of {@link IClasspath} for Gradle projects + * + * @author Alex Boyko + * + */ +public class GradleProjectClasspath extends JandexClasspath { + + private static final String JAVA_HOME = "java.home"; + private static final String JAVA_RUNTIME_VERSION = "java.runtime.version"; + private static final String JAVA_BOOT_CLASS_PATH = "sun.boot.class.path"; + + private EclipseProject gradleProject; + private Supplier buildEnvironment; + + public GradleProjectClasspath(GradleCore gradle, File projectDir) throws GradleException { + super(); + this.gradleProject = gradle.getModel(projectDir, EclipseProject.class); + this.buildEnvironment = Suppliers.memoize(() -> { + try { + return gradle.getModel(projectDir, BuildEnvironment.class); + } catch (GradleException e) { + Log.log(e); + return null; + } + }); + } + + @Override + protected JandexIndex[] getBaseIndices() { + return new JandexIndex[] { new JandexIndex(getJreLibs().map(path -> path.toFile()).collect(Collectors.toList()), + jarFile -> findIndexFile(jarFile), (classpathResource) -> { + try { + String javaVersion = getJavaRuntimeMinorVersion(); + if (javaVersion == null) { + javaVersion = "8"; + } + URL javadocUrl = new URL("http://docs.oracle.com/javase/" + javaVersion + "/docs/api/"); + return new HtmlJavadocProvider( + (type) -> SourceUrlProviderFromSourceContainer.JAVADOC_FOLDER_URL_SUPPLIER + .sourceUrl(javadocUrl, type)); + } catch (MalformedURLException e) { + Log.log(e); + return null; + } + }) }; + } + + @Override + public Stream getClasspathEntries() throws Exception { + return Stream.concat(gradleProject.getClasspath().stream().map(dep -> dep.getFile().toPath()), + gradleProject.getProjectDependencies().stream().map(project -> new File(project.getPath()).toPath())); + } + + @Override + public Stream getClasspathResources() { + return gradleProject.getSourceDirectories().stream().map(sourceDirectory -> sourceDirectory.getDirectory()).flatMap(folder -> { + try { + return Files.walk(folder.toPath()) + .filter(path -> Files.isRegularFile(path)) + .map(path -> folder.toPath().relativize(path)) + .map(relativePath -> relativePath.toString()) + .filter(pathString -> !pathString.endsWith(".java") && !pathString.endsWith(".class")); + } catch (IOException e) { + return Stream.empty(); + } + }); + } + + public Path getOutputFolder() { + return gradleProject.getProjectDirectory().toPath().resolve(gradleProject.getOutputLocation().getPath()); + } + + public String getName() { + return gradleProject.getName(); + } + + public boolean exists() { + return gradleProject != null; + } + + @Override + protected IJavadocProvider createParserJavadocProvider(File classpathResource) { + if (classpathResource.isDirectory()) { + Optional classpathFolder = gradleProject.getSourceDirectories().stream() + .map(dir -> dir.getDirectory()) + .filter(dir -> classpathResource.toPath().startsWith(dir.toPath())) + .findFirst(); + if (classpathFolder.isPresent()) { + return new ParserJavadocProvider(type -> { + return SourceUrlProviderFromSourceContainer.SOURCE_FOLDER_URL_SUPPLIER + .sourceUrl(classpathFolder.get().toURI().toURL(), type); + }); + + } + } else { + + } + return null; + } + + @Override + protected IJavadocProvider createHtmlJavdocProvider(File classpathResource) { + return null; + } + + public String getGradleVersion() throws GradleException { + if (buildEnvironment.get() == null) { + throw new GradleException(new Exception("Cannot find Gradle version")); + } else { + return buildEnvironment.get().getGradle().getGradleVersion(); + } + } + + public File getGradleHome() throws GradleException { + if (buildEnvironment.get() == null) { + throw new GradleException(new Exception("Cannot find Gradle home folder")); + } else { + return buildEnvironment.get().getGradle().getGradleUserHome(); + } + } + + public String getJavaRuntimeVersion() { + return System.getProperty(JAVA_RUNTIME_VERSION); + } + + public String getJavaRuntimeMinorVersion() { + String fullVersion = getJavaRuntimeVersion(); + String[] tokenized = fullVersion.split("\\."); + if (tokenized.length > 1) { + return tokenized[1]; + } else { + Log.log("Cannot determine minor version for the Java Runtime Version: " + fullVersion); + return null; + } + } + + private String getJavaHome() { + if (buildEnvironment.get() == null) { + return System.getProperty(JAVA_HOME); + } else { + return buildEnvironment.get().getJava().getJavaHome().toString(); + } + } + + private Stream getJreLibs() { + String s = System.getProperty(JAVA_BOOT_CLASS_PATH); + return Arrays.stream(s.split(File.pathSeparator)) + .map(strPath -> strPath.replace(System.getProperty(JAVA_HOME), getJavaHome())) + .map(File::new) + .filter(f -> f.canRead()) + .map(f -> f.toPath()); + } + + private File findIndexFile(File jarFile) { + String suffix = null; + String javaHome = getJavaHome(); + if (javaHome != null) { + int index = javaHome.lastIndexOf('/'); + if (index != -1) { + javaHome = javaHome.substring(0, index); + } + } + if (jarFile.toString().startsWith(javaHome)) { + suffix = getJavaRuntimeVersion(); + } + return new File(JandexIndex.getIndexFolder().toString(), jarFile.getName() + "-" + suffix + ".jdx"); + } + +} diff --git a/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinderStrategy.java b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinderStrategy.java new file mode 100644 index 000000000..f46142d8f --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectFinderStrategy.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * 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.gradle; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.ExecutionException; + +import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; +import org.springframework.ide.vscode.commons.util.FileUtils; +import org.springframework.ide.vscode.commons.util.StringUtil; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +/** + * Tests whether document belongs to a Gradle project + * + * @author Alex Boyko + * + */ +public class GradleProjectFinderStrategy implements IJavaProjectFinderStrategy { + + private Cache cache = CacheBuilder.newBuilder().build(); + + private GradleCore gradle; + + public GradleProjectFinderStrategy(GradleCore gradle) { + this.gradle = gradle; + } + + @Override + public GradleJavaProject find(IDocument d) throws ExecutionException, URISyntaxException { + String uriStr = d.getUri(); + if (StringUtil.hasText(uriStr)) { + URI uri = new URI(uriStr); + // TODO: This only work with File uri. Should it work with others + // too? + if (uri.getScheme().equalsIgnoreCase("file")) { + File file = new File(uri).getAbsoluteFile(); + File gradlebuild = FileUtils.findFile(file, GradleCore.GRADLE_BUILD_FILE); + if (gradlebuild != null) { + return cache.get(gradlebuild.getParentFile(), () -> { + return new GradleJavaProject(gradle, gradlebuild.getParentFile()); + }); + } + } + } + return null; + } + +} diff --git a/vscode-extensions/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java b/vscode-extensions/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java new file mode 100644 index 000000000..f99c08bb8 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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.gradle; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.Test; + +/** + * Tests covering Gradle project data + * + * @author Alex Boyko + * + */ +public class GradleProjectTest { + + private GradleJavaProject getGradleProject(String projectName) throws Exception { + Path testProjectPath = Paths.get(GradleProjectTest.class.getResource("/" + projectName).toURI()); + return new GradleJavaProject(GradleCore.getDefault(), testProjectPath.toFile()); + } + + @Test + public void testEclipseGradleProject() throws Exception { + GradleJavaProject project = getGradleProject("empty-gradle-project"); + Set calculatedClassPath = project.getClasspath().getClasspathEntries().collect(Collectors.toSet()); + assertEquals(48, calculatedClassPath.size()); + } + + @Test + public void outputFolder() throws Exception { + GradleJavaProject project = getGradleProject("test-app-1"); + assertTrue(project.getClasspath().getOutputFolder().endsWith("bin")); + } + + @Test + public void gradleClasspathResource() throws Exception { + GradleJavaProject project = getGradleProject("test-app-1"); + List resources = project.getClasspath().getClasspathResources().collect(Collectors.toList()); + assertArrayEquals(new String[] {"test-resource-1.txt"}, resources.toArray(new String[resources.size()])); + } + +} diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/.gitignore b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/.gitignore new file mode 100644 index 000000000..c9f701048 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/.gitignore @@ -0,0 +1,25 @@ +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ \ No newline at end of file diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/build.gradle b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/build.gradle new file mode 100644 index 000000000..87b447d99 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/build.gradle @@ -0,0 +1,33 @@ +buildscript { + ext { + springBootVersion = '1.5.1.RELEASE' + } + repositories { + mavenCentral() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + } +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'org.springframework.boot' + +jar { + baseName = 'empty-boot-1.4.0-web-app' + version = '0.0.1-SNAPSHOT' +} + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + + +dependencies { + compile('org.springframework.boot:spring-boot-starter-actuator') + compile('org.springframework.boot:spring-boot-starter-web') + testCompile('org.springframework.boot:spring-boot-starter-test') +} diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradle/wrapper/gradle-wrapper.jar b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ca78035ef0501d802d4fc55381ef2d5c3ce0ec6e GIT binary patch literal 53556 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�H$uJM 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradlew.bat b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradlew.bat new file mode 100644 index 000000000..832fdb607 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/main/java/com/example/EmptyGradleProjectApplication.java b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/main/java/com/example/EmptyGradleProjectApplication.java new file mode 100644 index 000000000..5c892a565 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/main/java/com/example/EmptyGradleProjectApplication.java @@ -0,0 +1,12 @@ +package com.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class EmptyGradleProjectApplication { + + public static void main(String[] args) { + SpringApplication.run(EmptyGradleProjectApplication.class, args); + } +} diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/main/resources/application.properties b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/main/resources/application.properties new file mode 100644 index 000000000..e69de29bb diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/test/java/com/example/EmptyGradleProjectApplicationTests.java b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/test/java/com/example/EmptyGradleProjectApplicationTests.java new file mode 100644 index 000000000..f2ba4bbc4 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/empty-gradle-project/src/test/java/com/example/EmptyGradleProjectApplicationTests.java @@ -0,0 +1,16 @@ +package com.example; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class EmptyGradleProjectApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/fileHashes.bin b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/fileHashes.bin new file mode 100644 index 0000000000000000000000000000000000000000..e80e08a000e4dce607c1236d9b99a8a65981701e GIT binary patch literal 18697 zcmeI%JxBs!9LMpexT3OBs6fF6mmmlUg>nr}sv(ddB5@3QnaTot+XROOL5iku%dM$5 z2s8;A0+%Rouq7~QC<3FF278XXOAg1j_&)I5^XGT`IG)dXst`^`%~+zo`syPS0tg_0 z00IagfB*srAbGD9LDkav#X#{0}*J{wL`SNr9pvEL`EtI5qOCT#@!d$YMteMr`9bC3=HxR z7*Urni15<2D5A()1RV-5or0i5q9_uC@L=z|GlL#I8N_$snR(|o!}82$j;G{?6`fo*m8nW^#=^0tg_000IagfB*srAb6uu6JMK&ikJmJs;VgGbYnEp8MvX_N=PNw2#YD`$7JN zI{kF4E4Sm-vze*Qm%lXXKDKoyOs?8^?)=8QXg}C_Y-?$t{PxG}i5q>R)5lLX+DzIu NttP&W>)00``~~KmFn|C6 literal 0 HcmV?d00001 diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/taskArtifacts.bin b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/taskArtifacts.bin new file mode 100644 index 0000000000000000000000000000000000000000..33f6a081c1b2d33b5ba0698aae012c3844cc6c6c GIT binary patch literal 19773 zcmeI%KWI}y90%}wZA6e(gD6HETpSdA?<&vj=;C0>DoQKjpo5F(y}ms9B8Z(@k?(_-yWHR3eeNzxQiLSp zKbsF(ezq=Wk%IsPAOHafKmY;|fB*y_009U<00Izz00bZa0SG|ge-X&&g=`2Gli1hh zXExOd>FUrKqs!iLp{?`rgP^*vH~jw~Om+F5pbno9fB*y_009U<00Izz00bZa0SG_< z0uX=z1Rwwb2tWV=5P$##AOHafKmY;|fB*y_009U<00Izz00bZa0SNpH0^NkfzUudr z{~9DVnPySc4yC)o_nw6p1GoE6$LAlxy3xkQcMm@t1g(Q$FZ_OnoDup z6-@Dh5jW=3H^(nsd^6iHlKt5yNh6-veq;JY_Wka?6OH(f=L>gRA`K(_EwV^Nu|k!xKvbd>5d4S%cZZT zUWB=y294^L_M2&2jj3ppuQ{_BYU<5;y`}*ypNRuQ__l!c(XZB#rivA!E1IZRVY-b;a?fqYaWwa(~QmLClq$=3Qr) zD*2tlTfUi)Q9(8iu^O|=tXj0v)safH(o-aUe4sbgpU!0l(*wD_(fESQJkrWo8LJa(%<53Po6k6$%I{UYJ6>g!rA|1b33?~=k?9k=OWN+ L*L$pg?!Ny53;65W literal 0 HcmV?d00001 diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/taskArtifacts.lock b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/.gradle/3.3/taskArtifacts/taskArtifacts.lock new file mode 100644 index 0000000000000000000000000000000000000000..ae4ecd12eae090e3b0ca5d0cb74d262998a26f8d GIT binary patch literal 17 TcmZR6>!dGXoaUUt009C3BiI7Y literal 0 HcmV?d00001 diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/build.gradle b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/build.gradle new file mode 100644 index 000000000..fd99219f5 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/build.gradle @@ -0,0 +1,28 @@ +/* + * This build file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java project to get you started. + * For more details take a look at the Java Quickstart chapter in the Gradle + * user guide available at https://docs.gradle.org/3.3/userguide/tutorial_java_projects.html + */ + +// Apply the java plugin to add support for Java +apply plugin: 'java' + +// In this section you declare where to find the dependencies of your project +repositories { + // Use jcenter for resolving your dependencies. + // You can declare any Maven/Ivy/file repository here. + jcenter() +} + +dependencies { + // The production code uses Guava + compile 'com.google.guava:guava:20.0' + compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '1.5.1.RELEASE' + compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.1.RELEASE' + + // Use JUnit test framework + testCompile 'junit:junit:4.12' +} + diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradle/wrapper/gradle-wrapper.properties b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..49bf4f6c9 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Feb 07 11:31:46 EST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradlew b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradlew new file mode 100755 index 000000000..4453ccea3 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradlew.bat b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradlew.bat new file mode 100644 index 000000000..f9553162f --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/settings.gradle b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/settings.gradle new file mode 100644 index 000000000..aa96911ff --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/settings.gradle @@ -0,0 +1,20 @@ +/* + * This settings file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * In a single project build this file can be empty or even removed. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user guide at https://docs.gradle.org/3.3/userguide/multi_project_builds.html + */ + +/* +// To declare projects as part of a multi-project build use the 'include' method +include 'shared' +include 'api' +include 'services:webservice' +*/ + +rootProject.name = 'test-app-1' +//org.gradle.java.home = '/Library/Java/JavaVirtualMachines/jdk1.7.0_60.jdk/Contents/Home' + diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/main/java/Library.java b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/main/java/Library.java new file mode 100644 index 000000000..fa1b41a26 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/main/java/Library.java @@ -0,0 +1,8 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +public class Library { + public boolean someLibraryMethod() { + return true; + } +} diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/test/java/LibraryTest.java b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/test/java/LibraryTest.java new file mode 100644 index 000000000..645740d03 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/test/java/LibraryTest.java @@ -0,0 +1,12 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +import org.junit.Test; +import static org.junit.Assert.*; + +public class LibraryTest { + @Test public void testSomeLibraryMethod() { + Library classUnderTest = new Library(); + assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod()); + } +} diff --git a/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/test/resources/test-resource-1.txt b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/test/resources/test-resource-1.txt new file mode 100644 index 000000000..9dbfeeee5 --- /dev/null +++ b/vscode-extensions/commons/commons-gradle/src/test/resources/test-app-1/src/test/resources/test-resource-1.txt @@ -0,0 +1 @@ +Some text \ No newline at end of file diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java new file mode 100644 index 000000000..c90066026 --- /dev/null +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java @@ -0,0 +1,88 @@ +package org.springframework.ide.vscode.commons.jandex; + +import java.io.File; +import java.nio.file.Path; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavadocProvider; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + +public abstract class JandexClasspath implements IClasspath { + + public static JavadocProviderTypes providerType = JavadocProviderTypes.HTML; + + public enum JavadocProviderTypes { + JAVA_PARSER, + HTML + } + + private Supplier javaIndex; + + public JandexClasspath() { + this.javaIndex = Suppliers.memoize(() -> createIndex()); + } + + protected JandexIndex createIndex() { + Stream classpathEntries = Stream.empty(); + try { + classpathEntries = getClasspathEntries(); + } catch (Exception e) { + Log.log(e); + } + return new JandexIndex(classpathEntries.map(p -> p.toFile()).collect(Collectors.toList()), jarFile -> findIndexFile(jarFile), classpathResource -> { + switch (providerType) { + case JAVA_PARSER: + return createParserJavadocProvider(classpathResource); + default: + return createHtmlJavdocProvider(classpathResource); + } + }, getBaseIndices()); + } + + protected JandexIndex[] getBaseIndices() { + return new JandexIndex[0]; + } + + public IType findType(String fqName) { + return javaIndex.get().findType(fqName); + } + + public Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { + return javaIndex.get().fuzzySearchTypes(searchTerm, typeFilter); + } + + public Flux> fuzzySearchPackages(String searchTerm) { + return javaIndex.get().fuzzySearchPackages(searchTerm); + } + + public Flux allSubtypesOf(IType type) { + return javaIndex.get().allSubtypesOf(type); + } + + private File findIndexFile(File jarFile) { + File indexFolder = getIndexFolder(); + if (indexFolder == null) { + return null; + } + return new File(indexFolder.toString(), jarFile.getName() + "-" + jarFile.lastModified() + ".jdx"); + } + + protected File getIndexFolder() { + return JandexIndex.getIndexFolder(); + } + + abstract protected IJavadocProvider createParserJavadocProvider(File classpathResource); + + abstract protected IJavadocProvider createHtmlJavdocProvider(File classpathResource); + +} diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java index 195baf290..43935a478 100644 --- a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java @@ -54,19 +54,29 @@ import reactor.util.function.Tuple2; import reactor.util.function.Tuples; public class JandexIndex { - + + private static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; + @FunctionalInterface public static interface IndexFileFinder { File findIndexFile(File jarFile); } - + @FunctionalInterface public static interface JavadocProviderFactory { IJavadocProvider createJavadocProvider(File jarContainer); } - + + public static File getIndexFolder() { + File folder = new File(System.getProperty(JAVA_IO_TMPDIR), "jandex"); + if (!folder.isDirectory()) { + folder.mkdirs(); + } + return folder; + } + private static final IJavadocProvider ABSENT_JAVADOC_PROVIDER = new IJavadocProvider() { - + @Override public IJavadoc getJavadoc(IType type) { return null; @@ -86,30 +96,31 @@ public class JandexIndex { public IJavadoc getJavadoc(IAnnotation method) { return null; } - + }; - + private Map>> index; - + private JavadocProviderFactory javadocProviderFactory; - + private Map>>> knownTypes; - + private Map>> knownPackages; - + private Cache javadocProvidersCache = CacheBuilder.newBuilder().build(); private JandexIndex[] baseIndex; - + public void setJvadocProviderFactory(JavadocProviderFactory sourceContainerProvider) { this.javadocProviderFactory = sourceContainerProvider; } - + public JavadocProviderFactory getJavadocProviderFactory() { return javadocProviderFactory; } - - public JandexIndex(Collection classpathEntries, IndexFileFinder indexFileFinder, JavadocProviderFactory javadocProviderFactory, JandexIndex... baseIndex) { + + public JandexIndex(Collection classpathEntries, IndexFileFinder indexFileFinder, + JavadocProviderFactory javadocProviderFactory, JandexIndex... baseIndex) { this.baseIndex = baseIndex; this.index = new ConcurrentHashMap<>(); this.knownTypes = new HashMap<>(); @@ -121,186 +132,185 @@ public class JandexIndex { knownPackages.put(file, Suppliers.memoize(() -> getKnownPackages(file).collect(Collectors.toList()))); }); } - + private Optional createIndex(File file, IndexFileFinder indexFileFinder) { - if (file.isFile() && file.getName().endsWith(".jar")) { + if (file != null && file.isFile() && file.getName().endsWith(".jar")) { return indexJar(file, indexFileFinder); - } else if (file.isDirectory()) { + } else if (file != null && file.isDirectory()) { return indexFolder(file); } else { return Optional.empty(); } } - + private static Optional indexFolder(File folder) { Indexer indexer = new Indexer(); - for (Iterator itr = com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(folder).iterator(); itr.hasNext();) { + for (Iterator itr = com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(folder) + .iterator(); itr.hasNext();) { File file = itr.next(); if (file.isFile() && file.getName().endsWith(".class")) { - try { - final InputStream stream = new FileInputStream(file); - try { - indexer.index(stream); - } finally { - try { - stream.close(); - } catch (Exception ignore) { - } - } - } catch (Exception e) { - Log.log(e); - } + try { + final InputStream stream = new FileInputStream(file); + try { + indexer.index(stream); + } finally { + try { + stream.close(); + } catch (Exception ignore) { + } + } + } catch (Exception e) { + Log.log(e); + } } } return Optional.of(indexer.complete()); } - + private static Optional indexJar(File file, IndexFileFinder indexFileFinder) { - File indexFile = indexFileFinder.findIndexFile(file); - if (indexFile != null) { - try { - if (indexFile.createNewFile()) { - try { - return Optional.of(JarIndexer - .createJarIndex(file, new Indexer(), indexFile, - false, false, false, System.out, System.err) - .getIndex()); - } catch (IOException e) { - Log.log("Failed to index '" + file + "'", e); - } - } else { - try { - return Optional.of(new IndexReader(new FileInputStream(indexFile)).read()); - } catch (IOException e) { - Log.log("Failed to read index file '" + indexFile + "'. Creating new index file.", e); - if (indexFile.delete()) { - return indexJar(file, indexFileFinder); - } else { - Log.log("Failed to read index file '" + indexFile); - } + File indexFile = indexFileFinder.findIndexFile(file); + if (indexFile != null) { + try { + if (indexFile.createNewFile()) { + try { + return Optional.of(JarIndexer.createJarIndex(file, new Indexer(), indexFile, false, false, + false, System.out, System.err).getIndex()); + } catch (IOException e) { + Log.log("Failed to index '" + file + "'", e); + } + } else { + try { + return Optional.of(new IndexReader(new FileInputStream(indexFile)).read()); + } catch (IOException e) { + Log.log("Failed to read index file '" + indexFile + "'. Creating new index file.", e); + if (indexFile.delete()) { + return indexJar(file, indexFileFinder); + } else { + Log.log("Failed to read index file '" + indexFile); } } - } catch (IOException e) { - Log.log("Unable to create index file '" + indexFile +"'"); - } - } else { - try { - return Optional.of(JarIndexer - .createJarIndex(file, new Indexer(), file.canWrite(), file.getParentFile().canWrite(), false) - .getIndex()); - } catch (IOException e) { - Log.log("Failed to index '" + file + "'", e); } + } catch (IOException e) { + Log.log("Unable to create index file '" + indexFile + "'"); } + } else { + try { + return Optional.of(JarIndexer + .createJarIndex(file, new Indexer(), file.canWrite(), file.getParentFile().canWrite(), false) + .getIndex()); + } catch (IOException e) { + Log.log("Failed to index '" + file + "'", e); + } + } return Optional.empty(); } public IType findType(String fqName) { return getClassByName(DotName.createSimple(fqName)); } - + IType getClassByName(DotName fqName) { // First look for type in the base index array return (baseIndex == null ? Stream.empty() - : Arrays.stream(baseIndex) - .filter(jandexIndex -> jandexIndex != null) - .map(jandexIndex -> jandexIndex.getClassByName(fqName))) - .filter(type -> type != null) - .findFirst() - // If not found look at indices owned by this JandexIndex instance - .orElseGet(() -> streamOfIndices() - .map(e -> Tuples.of(e.getT1(), e.getT2().getClassByName(fqName))) - .filter(e -> e.getT2() != null) - .map(e -> createType(e)) - .findFirst() - .orElse(null)); - + : Arrays.stream( + baseIndex) + .filter( + jandexIndex -> jandexIndex != null) + .map(jandexIndex -> jandexIndex.getClassByName(fqName))).filter(type -> type != null) + .findFirst() + // If not found look at indices owned by this + // JandexIndex instance + .orElseGet(() -> streamOfIndices() + .map(e -> Tuples.of(e.getT1(), e.getT2().getClassByName(fqName))) + .filter(e -> e.getT2() != null).map(e -> createType(e)).findFirst() + .orElse(null)); + } - + private IType createType(Tuple2 match) { File classpathResource = match.getT1(); IJavadocProvider javadocProvider = null; try { - javadocProvider = javadocProvidersCache.get(classpathResource, () -> javadocProviderFactory == null ? ABSENT_JAVADOC_PROVIDER : javadocProviderFactory.createJavadocProvider(classpathResource)); + javadocProvider = javadocProvidersCache.get(classpathResource, () -> { + IJavadocProvider provider = null; + if (javadocProviderFactory != null) { + provider = javadocProviderFactory.createJavadocProvider(classpathResource); + } + return provider == null ? ABSENT_JAVADOC_PROVIDER : provider; + }); } catch (ExecutionException e) { Log.log(e); } - return Wrappers.wrap(this, match.getT2(), javadocProvider); + return Wrappers.wrap(this, match.getT2(), javadocProvider); } - + private Stream> streamOfIndices() { - return index.entrySet().parallelStream().map(e -> Tuples.of(e.getKey(), e.getValue().get())).filter(t -> t.getT2().isPresent()).map(t -> Tuples.of(t.getT1(), t.getT2().get())); + return index.entrySet().parallelStream().map(e -> Tuples.of(e.getKey(), e.getValue().get())) + .filter(t -> t.getT2().isPresent()).map(t -> Tuples.of(t.getT1(), t.getT2().get())); } - + private Stream> getKnownTypesStream(File file) { Optional indexView = index.get(file).get(); if (indexView.isPresent()) { - return indexView.get().getKnownClasses().parallelStream().map(info -> Tuples.of(info.name().toString(), createType(Tuples.of(file, info)))); + return indexView.get().getKnownClasses().parallelStream() + .map(info -> Tuples.of(info.name().toString(), createType(Tuples.of(file, info)))); } return Stream.empty(); } - + private Stream getKnownPackages(File file) { Optional indexView = index.get(file).get(); if (indexView.isPresent()) { - return indexView.get() - .getKnownClasses() - .parallelStream() - .map(info -> { - String name = info.name().toString(); - return name.substring(0, name.lastIndexOf('.')); - }) - .distinct(); + return indexView.get().getKnownClasses().parallelStream().map(info -> { + String name = info.name().toString(); + return name.substring(0, name.lastIndexOf('.')); + }).distinct(); } return Stream.empty(); } - + public Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { - Flux> flux = Flux.fromIterable(knownTypes.values()) - .publishOn(Schedulers.parallel()) - .flatMap(s -> Flux.fromIterable(s.get())) - .filter(t -> typeFilter == null || typeFilter.test(t.getT2())) + Flux> flux = Flux.fromIterable(knownTypes.values()).publishOn(Schedulers.parallel()) + .flatMap(s -> Flux.fromIterable(s.get())).filter(t -> typeFilter == null || typeFilter.test(t.getT2())) .map(t -> Tuples.of(t.getT2(), FuzzyMatcher.matchScore(searchTerm, t.getT1()))) .filter(t -> t.getT2() != 0.0); if (baseIndex == null) { return flux; } else { - return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchTypes(searchTerm, typeFilter))); + return Flux.merge(flux, + Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchTypes(searchTerm, typeFilter))); } } - + public Flux> fuzzySearchPackages(String searchTerm) { - Flux> flux = Flux.fromIterable(knownPackages.values()) - .publishOn(Schedulers.parallel()) + Flux> flux = Flux.fromIterable(knownPackages.values()).publishOn(Schedulers.parallel()) .flatMap(s -> Flux.fromIterable(s.get())) - .map(pkg -> Tuples.of(pkg, FuzzyMatcher.matchScore(searchTerm, pkg))) - .filter(t -> t.getT2() != 0.0); + .map(pkg -> Tuples.of(pkg, FuzzyMatcher.matchScore(searchTerm, pkg))).filter(t -> t.getT2() != 0.0); if (baseIndex == null) { return flux; } else { return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchPackages(searchTerm))); } } - + public Flux allSubtypesOf(IType type) { DotName name = DotName.createSimple(type.getFullyQualifiedName()); - Flux flux = Flux.fromIterable(index.keySet()) - .publishOn(Schedulers.parallel()) - .flatMap(file -> { - Optional optional = index.get(file).get(); - if (optional.isPresent()) { - return Flux.fromIterable(type.isInterface() ? optional.get().getAllKnownImplementors(name) : optional.get().getAllKnownSubclasses(name)) - .publishOn(Schedulers.parallel()) - .map(info -> createType(Tuples.of(file, info))); - } else { - return Flux.empty(); - } - }); + Flux flux = Flux.fromIterable(index.keySet()).publishOn(Schedulers.parallel()).flatMap(file -> { + Optional optional = index.get(file).get(); + if (optional.isPresent()) { + return Flux + .fromIterable(type.isInterface() ? optional.get().getAllKnownImplementors(name) + : optional.get().getAllKnownSubclasses(name)) + .publishOn(Schedulers.parallel()).map(info -> createType(Tuples.of(file, info))); + } else { + return Flux.empty(); + } + }); if (baseIndex == null) { return flux; } else { return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.allSubtypesOf(type))); } } - + } diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java index 81ef98d3a..a30663512 100644 --- a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java @@ -11,8 +11,12 @@ package org.springframework.ide.vscode.commons.java; import java.nio.file.Path; +import java.util.function.Predicate; import java.util.stream.Stream; +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + /** * Classpath for a Java artifact * @@ -21,7 +25,21 @@ import java.util.stream.Stream; * */ public interface IClasspath { + + String getName(); + + boolean exists(); + IType findType(String fqName); + + Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter); + + Flux> fuzzySearchPackages(String searchTerm); + + Flux allSubtypesOf(IType type); + + Path getOutputFolder(); + /** * Classpath entries paths * diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java index 0e7740556..1b9631e3e 100644 --- a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java @@ -10,20 +10,25 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; -import java.util.function.Predicate; - -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; +import org.springframework.ide.vscode.commons.javadoc.IJavadoc; public interface IJavaProject extends IJavaElement { - IType findType(String fqName); - - Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter); - - Flux> fuzzySearchPackages(String searchTerm); - - Flux allSubtypesOf(IType type); - IClasspath getClasspath(); + + @Override + default String getElementName() { + return getClasspath().getName(); + } + + @Override + default IJavadoc getJavaDoc() { + return null; + } + + @Override + default boolean exists() { + return getClasspath().exists(); + } + } diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java index 18d5b06b1..c02c145d9 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java @@ -76,7 +76,6 @@ public class MavenCore { private static final String CLASSIFIER_TESTS = "tests"; private static final String CLASSIFIER_TESTSOURCES = "test-sources"; - public static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; private static final String JAVA_HOME = "java.home"; private static final String JAVA_RUNTIME_VERSION = "java.runtime.version"; private static final String JAVA_BOOT_CLASS_PATH = "sun.boot.class.path"; @@ -336,18 +335,11 @@ public class MavenCore { } catch (MavenException e) { Log.log(e); } - return new File(getIndexFolder().toString(), jarFile.getName() + "-" + suffix + ".jdx"); + return new File(JandexIndex.getIndexFolder().toString(), jarFile.getName() + "-" + suffix + ".jdx"); } public JandexIndex getJavaIndexForJreLibs() { return javaCoreIndex.get(); } - public File getIndexFolder() { - File folder = new File(System.getProperty(JAVA_IO_TMPDIR), "jandex"); - if (!folder.isDirectory()) { - folder.mkdirs(); - } - return folder; - } } diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenProjectFinderStrategy.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenProjectFinderStrategy.java index e03d4122d..c925015dd 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenProjectFinderStrategy.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenProjectFinderStrategy.java @@ -32,7 +32,13 @@ import com.google.common.cache.CacheBuilder; */ public class MavenProjectFinderStrategy implements IJavaProjectFinderStrategy { - public Cache cache = CacheBuilder.newBuilder().build(); + private Cache cache = CacheBuilder.newBuilder().build(); + + private MavenCore maven; + + public MavenProjectFinderStrategy(MavenCore maven) { + this.maven = maven; + } @Override public MavenJavaProject find(IDocument d) throws ExecutionException, URISyntaxException { @@ -46,7 +52,7 @@ public class MavenProjectFinderStrategy implements IJavaProjectFinderStrategy { File pomFile = FileUtils.findFile(file, MavenCore.POM_XML); if (pomFile != null) { return cache.get(pomFile, () -> { - return new MavenJavaProject(pomFile); + return new MavenJavaProject(maven, pomFile); }); } } diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java index 6f496f4f8..549312c16 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java @@ -11,18 +11,10 @@ package org.springframework.ide.vscode.commons.maven.java; import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.function.Predicate; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.IType; -import org.springframework.ide.vscode.commons.javadoc.IJavadoc; import org.springframework.ide.vscode.commons.maven.MavenCore; -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; - /** * Wrapper for Maven Core project * @@ -32,46 +24,9 @@ import reactor.util.function.Tuple2; public class MavenJavaProject implements IJavaProject { private MavenProjectClasspath classpath; - private MavenCore maven; - public MavenJavaProject(File pom) { - this.maven = MavenCore.getDefault(); - this.classpath = new MavenProjectClasspath(pom, maven); - } - - @Override - public String getElementName() { - return classpath.getName(); - } - - @Override - public IJavadoc getJavaDoc() { - return null; - } - - @Override - public boolean exists() { - return classpath.exists(); - } - - @Override - public IType findType(String fqName) { - return classpath.findType(fqName); - } - - @Override - public Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { - return classpath.fuzzySearchType(searchTerm, typeFilter); - } - - @Override - public Flux> fuzzySearchPackages(String searchTerm) { - return classpath.fuzzySearchPackages(searchTerm); - } - - @Override - public Flux allSubtypesOf(IType type) { - return classpath.allSubtypesOf(type); + public MavenJavaProject(MavenCore maven, File pom) { + this.classpath = new MavenProjectClasspath(maven, pom); } @Override @@ -79,8 +34,4 @@ public class MavenJavaProject implements IJavaProject { return classpath; } - public Path getOutputFolder() { - return Paths.get(new File(classpath.getOutputFolder()).toURI()); - } - } diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java index 0147bbde2..07400d374 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java @@ -19,17 +19,14 @@ import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.maven.artifact.Artifact; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.DirectoryScanner; +import org.springframework.ide.vscode.commons.jandex.JandexClasspath; import org.springframework.ide.vscode.commons.jandex.JandexIndex; -import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavadocProvider; -import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.java.parser.ParserJavadocProvider; import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider; import org.springframework.ide.vscode.commons.javadoc.SourceUrlProviderFromSourceContainer; @@ -40,58 +37,30 @@ import org.springframework.ide.vscode.commons.util.Log; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; - /** * Classpath for a maven project * * @author Alex Boyko * */ -public class MavenProjectClasspath implements IClasspath { - - public static JavadocProviderTypes providerType = JavadocProviderTypes.HTML; - - public enum JavadocProviderTypes { - JAVA_PARSER, -// ROASTER, - HTML - } +public class MavenProjectClasspath extends JandexClasspath { private MavenCore maven; private File pom; private Supplier projectSupplier; - private Supplier javaIndex; - public MavenProjectClasspath(File pom) { - this(pom, MavenCore.getDefault()); - } - - MavenProjectClasspath(File pom, MavenCore maven) { + MavenProjectClasspath(MavenCore maven, File pom) { + super(); this.maven = maven; this.pom = pom; this.projectSupplier = Suppliers.memoize(() -> createMavenProject()); - this.javaIndex = Suppliers.memoize(() -> { - Stream classpathEntries = Stream.empty(); - try { - classpathEntries = getClasspathEntries(); - } catch (Exception e) { - Log.log(e); - } - return new JandexIndex(classpathEntries.map(p -> p.toFile()).collect(Collectors.toList()), jarFile -> findIndexFile(jarFile), classpathResource -> { - switch (providerType) { - case JAVA_PARSER: - return createParserJavadocProvider(classpathResource); -// case ROASTER: -// return createRoasterJavadocProvider(classpathResource); - default: - return createHtmlJavdocProvider(classpathResource); - } - }, maven.getJavaIndexForJreLibs()); - }); } + @Override + protected JandexIndex[] getBaseIndices() { + return new JandexIndex[] { maven.getJavaIndexForJreLibs() }; + } + private final MavenProject createMavenProject() { try { // Read with resolved dependencies @@ -139,29 +108,9 @@ public class MavenProjectClasspath implements IClasspath { } } - public String getOutputFolder() { + public Path getOutputFolder() { MavenProject project = projectSupplier.get(); - return project == null ? null : project.getBuild().getOutputDirectory(); - } - - public IType findType(String fqName) { - return javaIndex.get().findType(fqName); - } - - public Flux> fuzzySearchType(String searchTerm, Predicate typeFilter) { - return javaIndex.get().fuzzySearchTypes(searchTerm, typeFilter); - } - - public Flux> fuzzySearchPackages(String searchTerm) { - return javaIndex.get().fuzzySearchPackages(searchTerm); - } - - public Flux allSubtypesOf(IType type) { - return javaIndex.get().allSubtypesOf(type); - } - - private File findIndexFile(File jarFile) { - return new File(maven.getIndexFolder().toString(), jarFile.getName() + "-" + jarFile.lastModified() + ".jdx"); + return project == null ? null : new File(project.getBuild().getOutputDirectory()).toPath(); } private Optional getArtifactFromJarFile(File file) throws MavenException { @@ -226,7 +175,7 @@ public class MavenProjectClasspath implements IClasspath { // } // } - private IJavadocProvider createParserJavadocProvider(File classpathResource) { + protected IJavadocProvider createParserJavadocProvider(File classpathResource) { MavenProject project = projectSupplier.get(); if (project == null) { return null; @@ -263,7 +212,7 @@ public class MavenProjectClasspath implements IClasspath { } } - private IJavadocProvider createHtmlJavdocProvider(File classpathResource) { + protected IJavadocProvider createHtmlJavdocProvider(File classpathResource) { MavenProject project = projectSupplier.get(); if (project == null) { return null; diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/FileClasspath.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/FileClasspath.java index c71005ded..2b2157ebe 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/FileClasspath.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/FileClasspath.java @@ -10,12 +10,18 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.maven.java.classpathfile; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.function.Predicate; import java.util.stream.Stream; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.maven.MavenCore; +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + /** * Classpath for a project containing classpath text file * @@ -41,5 +47,41 @@ public class FileClasspath implements IClasspath { public Stream getClasspathResources() { return Stream.empty(); } + + @Override + public String getName() { + return classpathFilePath.toFile().getParentFile().getName(); + } + + @Override + public boolean exists() { + return Files.exists(classpathFilePath); + } + + @Override + public IType findType(String fqName) { + //TODO: implement + return null; + } + + @Override + public Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { + return Flux.empty(); + } + + @Override + public Flux> fuzzySearchPackages(String searchTerm) { + return Flux.empty(); + } + + @Override + public Flux allSubtypesOf(IType type) { + return Flux.empty(); + } + + @Override + public Path getOutputFolder() { + return null; + } } diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java index 90725959d..808b80a9d 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java @@ -12,15 +12,9 @@ package org.springframework.ide.vscode.commons.maven.java.classpathfile; import java.io.File; import java.nio.file.Paths; -import java.util.function.Predicate; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.IType; -import org.springframework.ide.vscode.commons.javadoc.IJavadoc; - -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; /** * Java project that contains classpath text file @@ -38,42 +32,6 @@ public class JavaProjectWithClasspathFile implements IJavaProject { this.classpath = new FileClasspath(Paths.get(cpFile.toURI())); } - @Override - public String getElementName() { - return cpFile.getParentFile().getName(); - } - - @Override - public IJavadoc getJavaDoc() { - return null; - } - - @Override - public boolean exists() { - return cpFile.exists(); - } - - @Override - public IType findType(String fqName) { - //TODO: implement - return null; - } - - @Override - public Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { - return Flux.empty(); - } - - @Override - public Flux> fuzzySearchPackages(String searchTerm) { - return Flux.empty(); - } - - @Override - public Flux allSubtypesOf(IType type) { - return Flux.empty(); - } - @Override public IClasspath getClasspath() { return classpath; diff --git a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java index 5995db438..ffcd2e6ba 100644 --- a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java +++ b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java @@ -21,13 +21,13 @@ import java.util.stream.Stream; import org.junit.Assume; import org.junit.Test; +import org.springframework.ide.vscode.commons.jandex.JandexClasspath; +import org.springframework.ide.vscode.commons.jandex.JandexClasspath.JavadocProviderTypes; import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; -import org.springframework.ide.vscode.commons.maven.java.MavenProjectClasspath; -import org.springframework.ide.vscode.commons.maven.java.MavenProjectClasspath.JavadocProviderTypes; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -37,10 +37,10 @@ public class HtmlJavadocTest { private static Supplier projectSupplier = Suppliers.memoize(() -> { Path testProjectPath; try { - MavenProjectClasspath.providerType = JavadocProviderTypes.HTML; + JandexClasspath.providerType = JavadocProviderTypes.HTML; testProjectPath = Paths.get(HtmlJavadocTest.class.getResource("/gs-rest-service-cors-boot-1.4.1-with-classpath-file").toURI()); MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); - return new MavenJavaProject(testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); } catch (Exception e) { return null; } @@ -52,7 +52,7 @@ public class HtmlJavadocTest { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("java.util.Map"); + IType type = project.getClasspath().findType("java.util.Map"); assertNotNull(type); String expected = String.join("\n", "
An object that maps keys to values. A map cannot contain duplicate keys;", @@ -68,7 +68,7 @@ public class HtmlJavadocTest { Assume.assumeTrue(javaVersionHigherThan(6)); MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("java.util.ArrayList"); + IType type = project.getClasspath().findType("java.util.ArrayList"); assertNotNull(type); IMethod method = type.getMethod("", Stream.empty()); assertNotNull(method); @@ -86,7 +86,7 @@ public class HtmlJavadocTest { public void html_testEmptyJavadocClass() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("hello.Application"); + IType type = project.getClasspath().findType("hello.Application"); assertNotNull(type); assertNull(type.getJavaDoc()); } @@ -95,7 +95,7 @@ public class HtmlJavadocTest { public void html_testFieldAndMethodJavadocForJar() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("org.springframework.boot.SpringApplication"); + IType type = project.getClasspath().findType("org.springframework.boot.SpringApplication"); assertNotNull(type); IField field = type.getField("BANNER_LOCATION_PROPERTY_VALUE"); @@ -135,7 +135,7 @@ public class HtmlJavadocTest { public void html_testInnerClassJavadocForOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("hello.Greeting$TestInnerClass"); + IType type = project.getClasspath().findType("hello.Greeting$TestInnerClass"); assertNotNull(type); IJavadoc javaDoc = type.getJavaDoc(); assertNotNull(javaDoc); @@ -168,7 +168,7 @@ public class HtmlJavadocTest { public void html_testInnerClassLevel2_JavadocForOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("hello.Greeting$TestInnerClass$TestInnerClassLevel2"); + IType type = project.getClasspath().findType("hello.Greeting$TestInnerClass$TestInnerClassLevel2"); assertNotNull(type); IJavadoc javaDoc = type.getJavaDoc(); assertNotNull(javaDoc); @@ -200,7 +200,7 @@ public class HtmlJavadocTest { @Test public void html_testJavadocOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("hello.Greeting"); + IType type = project.getClasspath().findType("hello.Greeting"); assertNotNull(type); String expected = "
Comment for Greeting class
"; @@ -237,7 +237,7 @@ public class HtmlJavadocTest { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("java.util.ArrayList"); + IType type = project.getClasspath().findType("java.util.ArrayList"); assertNotNull(type); IMethod method = type.getMethod("size", Stream.empty()); assertNotNull(method); @@ -258,7 +258,7 @@ public class HtmlJavadocTest { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("java.util.Map$Entry"); + IType type = project.getClasspath().findType("java.util.Map$Entry"); assertNotNull(type); String expected = String.join("\n", "
A map entry (key-value pair). The Map.entrySet method returns", @@ -272,7 +272,7 @@ public class HtmlJavadocTest { public void html_testNoJavadocClass() throws Exception { MavenJavaProject project = projectSupplier.get();; - IType type = project.findType("hello.GreetingController"); + IType type = project.getClasspath().findType("hello.GreetingController"); assertNotNull(type); assertNull(type.getJavaDoc()); } @@ -281,7 +281,7 @@ public class HtmlJavadocTest { public void html_testNoJavadocField() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("hello.GreetingController"); + IType type = project.getClasspath().findType("hello.GreetingController"); assertNotNull(type); IField field = type.getField("template"); assertNotNull(field); @@ -302,7 +302,7 @@ public class HtmlJavadocTest { public void html_testNoJavadocMethod() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("hello.Application"); + IType type = project.getClasspath().findType("hello.Application"); assertNotNull(type); IMethod method = type.getMethod("corsConfigurer", Stream.empty()); assertNotNull(method); diff --git a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java index d443adb4f..0fe710e24 100644 --- a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java +++ b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java @@ -45,7 +45,7 @@ public class JavaIndexTest { public MavenJavaProject load(String projectName) throws Exception { Path testProjectPath = Paths.get(DependencyTreeTest.class.getResource("/" + projectName).toURI()); MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); - return new MavenJavaProject(testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); } }); @@ -83,28 +83,28 @@ public class JavaIndexTest { @Test public void findClassInJar() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); - IType type = project.findType("org.springframework.test.web.client.ExpectedCount"); + IType type = project.getClasspath().findType("org.springframework.test.web.client.ExpectedCount"); assertNotNull(type); } @Test public void findClassInOutputFolder() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); - IType type = project.findType("hello.Greeting"); + IType type = project.getClasspath().findType("hello.Greeting"); assertNotNull(type); } @Test public void classNotFound() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); - IType type = project.findType("hello.NonExistentClass"); + IType type = project.getClasspath().findType("hello.NonExistentClass"); assertNull(type); } @Test public void voidMethodNoParams() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); - IType type = project.findType("java.util.ArrayList"); + IType type = project.getClasspath().findType("java.util.ArrayList"); assertNotNull(type); IMethod m = type.getMethod("clear", Stream.empty()); assertEquals("clear", m.getElementName()); @@ -115,7 +115,7 @@ public class JavaIndexTest { @Test public void voidConstructor() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); - IType type = project.findType("java.util.ArrayList"); + IType type = project.getClasspath().findType("java.util.ArrayList"); assertNotNull(type); IMethod m = type.getMethod("", Stream.empty()); assertEquals(type.getElementName(), m.getElementName()); @@ -126,7 +126,7 @@ public class JavaIndexTest { @Test public void constructorMethodWithParams() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); - IType type = project.findType("java.util.ArrayList"); + IType type = project.getClasspath().findType("java.util.ArrayList"); assertNotNull(type); IMethod m = type.getMethod("", Stream.of(IPrimitiveType.INT)); assertEquals(m.getDeclaringType().getElementName(), m.getElementName()); diff --git a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/SourceJavadocTest.java b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/SourceJavadocTest.java index f196b9d37..cffcb6301 100644 --- a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/SourceJavadocTest.java +++ b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/SourceJavadocTest.java @@ -19,12 +19,12 @@ import java.nio.file.Paths; import java.util.stream.Stream; import org.junit.Test; +import org.springframework.ide.vscode.commons.jandex.JandexClasspath; +import org.springframework.ide.vscode.commons.jandex.JandexClasspath.JavadocProviderTypes; import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; -import org.springframework.ide.vscode.commons.maven.java.MavenProjectClasspath; -import org.springframework.ide.vscode.commons.maven.java.MavenProjectClasspath.JavadocProviderTypes; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -34,9 +34,9 @@ public class SourceJavadocTest { private static Supplier projectSupplier = Suppliers.memoize(() -> { Path testProjectPath; try { - MavenProjectClasspath.providerType = JavadocProviderTypes.JAVA_PARSER; + JandexClasspath.providerType = JavadocProviderTypes.JAVA_PARSER; testProjectPath = Paths.get(SourceJavadocTest.class.getResource("/gs-rest-service-cors-boot-1.4.1-with-classpath-file").toURI()); - return new MavenJavaProject(testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); } catch (Exception e) { return null; } @@ -46,7 +46,7 @@ public class SourceJavadocTest { public void parser_testClassJavadocForJar() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"); + IType type = project.getClasspath().findType("org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"); assertNotNull(type); String expected = String.join("\n", "/**", @@ -54,7 +54,7 @@ public class SourceJavadocTest { ); assertEquals(expected, type.getJavaDoc().raw().trim().substring(0, expected.length())); - type = project.findType("org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener$LiquibasePresent"); + type = project.getClasspath().findType("org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener$LiquibasePresent"); assertNotNull(type); expected = String.join("\n", "/**", @@ -67,7 +67,7 @@ public class SourceJavadocTest { @Test public void parser_testClassJavadocForOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("hello.Greeting"); + IType type = project.getClasspath().findType("hello.Greeting"); assertNotNull(type); String expected = String.join("\n", @@ -100,7 +100,7 @@ public class SourceJavadocTest { public void parser_testFieldAndMethodJavadocForJar() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("org.springframework.boot.SpringApplication"); + IType type = project.getClasspath().findType("org.springframework.boot.SpringApplication"); assertNotNull(type); IField field = type.getField("BANNER_LOCATION_PROPERTY_VALUE"); @@ -124,7 +124,7 @@ public class SourceJavadocTest { @Test public void parser_testInnerClassJavadocForOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.findType("hello.Greeting$TestInnerClass"); + IType type = project.getClasspath().findType("hello.Greeting$TestInnerClass"); assertNotNull(type); assertEquals("/**\n * Comment for inner class\n */", type.getJavaDoc().raw().trim()); diff --git a/vscode-extensions/commons/pom.xml b/vscode-extensions/commons/pom.xml index 43dbed3a4..403e59596 100644 --- a/vscode-extensions/commons/pom.xml +++ b/vscode-extensions/commons/pom.xml @@ -18,8 +18,7 @@ java-properties commons-cf commons-maven - - + commons-gradle diff --git a/vscode-extensions/vscode-boot-java/pom.xml b/vscode-extensions/vscode-boot-java/pom.xml index 4457b0e29..bf2fdca88 100644 --- a/vscode-extensions/vscode-boot-java/pom.xml +++ b/vscode-extensions/vscode-boot-java/pom.xml @@ -38,6 +38,11 @@ commons-maven ${project.version} + + org.springframework.ide.vscode + commons-gradle + ${project.version} + org.springframework.ide.vscode commons-language-server diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index 59e1abd98..79efacdd4 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -15,6 +15,8 @@ import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.TextDocumentSyncKind; import org.springframework.ide.vscode.boot.java.completions.BootJavaCompletionEngine; import org.springframework.ide.vscode.boot.java.completions.BootJavaReconcileEngine; +import org.springframework.ide.vscode.commons.gradle.GradleCore; +import org.springframework.ide.vscode.commons.gradle.GradleProjectFinderStrategy; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaProjectFinder; @@ -24,6 +26,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcil import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.maven.JavaProjectWithClasspathFileFinderStrategy; +import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -35,7 +38,8 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument; public class BootJavaLanguageServer extends SimpleLanguageServer { public static final JavaProjectFinder DEFAULT_PROJECT_FINDER = new DefaultJavaProjectFinder(new IJavaProjectFinderStrategy[] { - new MavenProjectFinderStrategy(), + new MavenProjectFinderStrategy(MavenCore.getDefault()), + new GradleProjectFinderStrategy(GradleCore.getDefault()), new JavaProjectWithClasspathFileFinderStrategy() }); diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java index 7f56c7744..f7f95cf19 100644 --- a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java @@ -51,7 +51,7 @@ public class ProjectsHarness { switch (type) { case MAVEN: MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); - return new MavenJavaProject(testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); case CLASSPATH_TXT: MavenBuilder.newBuilder(testProjectPath).clean().pack().skipTests().execute(); return new JavaProjectWithClasspathFile(testProjectPath.resolve(MavenCore.CLASSPATH_TXT).toFile()); diff --git a/vscode-extensions/vscode-boot-properties/pom.xml b/vscode-extensions/vscode-boot-properties/pom.xml index 5c8aff563..badc4a90a 100644 --- a/vscode-extensions/vscode-boot-properties/pom.xml +++ b/vscode-extensions/vscode-boot-properties/pom.xml @@ -53,6 +53,11 @@ commons-maven ${project.version} + + org.springframework.ide.vscode + commons-gradle + ${project.version} + org.springframework.ide.vscode commons-language-server diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java index dd3132a00..89b3ebe6e 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java @@ -24,6 +24,8 @@ import org.springframework.ide.vscode.boot.properties.hover.PropertiesHoverInfoP import org.springframework.ide.vscode.boot.properties.reconcile.SpringPropertiesReconcileEngine; import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext; import org.springframework.ide.vscode.boot.yaml.reconcile.ApplicationYamlReconcileEngine; +import org.springframework.ide.vscode.commons.gradle.GradleCore; +import org.springframework.ide.vscode.commons.gradle.GradleProjectFinderStrategy; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; import org.springframework.ide.vscode.commons.languageserver.completion.VscodeCompletionEngineAdapter; import org.springframework.ide.vscode.commons.languageserver.hover.HoverInfoProvider; @@ -36,6 +38,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcil import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.maven.JavaProjectWithClasspathFileFinderStrategy; +import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -60,7 +63,8 @@ import com.google.common.collect.ImmutableList; public class BootPropertiesLanguageServer extends SimpleLanguageServer { public static final JavaProjectFinder DEFAULT_PROJECT_FINDER = new DefaultJavaProjectFinder(new IJavaProjectFinderStrategy[] { - new MavenProjectFinderStrategy(), + new MavenProjectFinderStrategy(MavenCore.getDefault()), + new GradleProjectFinderStrategy(GradleCore.getDefault()), new JavaProjectWithClasspathFileFinderStrategy() }); diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java index 7a25628b2..2618837bc 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java @@ -131,11 +131,11 @@ public class ClassReferenceProvider extends CachingValueProvider { @Override protected Flux getValuesAsync(IJavaProject javaProject, String query) { - IType targetType = target == null || target.isEmpty() ? javaProject.findType("java.lang.Object") : javaProject.findType(target); + IType targetType = target == null || target.isEmpty() ? javaProject.getClasspath().findType("java.lang.Object") : javaProject.getClasspath().findType(target); if (targetType == null) { return Flux.empty(); } - Set allSubclasses = javaProject + Set allSubclasses = javaProject.getClasspath() .allSubtypesOf(targetType) .filter(t -> Flags.isPublic(t.getFlags()) && !concrete || !isAbstract(t)) .collect(Collectors.toSet()) @@ -143,7 +143,7 @@ public class ClassReferenceProvider extends CachingValueProvider { if (allSubclasses.isEmpty()) { return Flux.empty(); } else { - return javaProject + return javaProject.getClasspath() .fuzzySearchTypes(query, type -> allSubclasses.contains(type)) .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) .flatMap(l -> Flux.fromIterable(l)) diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java index 6434fb19b..0e379d645 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java @@ -18,6 +18,8 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin import org.springframework.ide.vscode.commons.util.text.IDocument; public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexProvider { + + private static final FuzzyMap EMPTY_INDEX = new SpringPropertyIndex(null, null); private JavaProjectFinder javaProjectFinder; private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault()); @@ -34,7 +36,7 @@ public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexPr if (jp!=null) { return indexManager.get(jp, progressService); } - return null; + return EMPTY_INDEX; } public void setProgressService(ProgressService progressService) { diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java index e3fd3ee6e..2f9886766 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java @@ -37,10 +37,10 @@ public class LoggerNameProvider extends CachingValueProvider { @Override protected Flux getValuesAsync(IJavaProject javaProject, String query) { return Flux.concat( - javaProject + javaProject.getClasspath() .fuzzySearchPackages(query) .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())), - javaProject + javaProject.getClasspath() .fuzzySearchTypes(query, null) .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())) ) diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java index c3f088792..8845e0013 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java @@ -79,7 +79,7 @@ public class StsValueHint { try { IJavaProject jp = typeUtil.getJavaProject(); if (jp!=null) { - IType type = jp.findType(fqName); + IType type = jp.getClasspath().findType(fqName); if (type!=null) { return create(type); } diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java index 0a5377962..07bb5c158 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java @@ -543,7 +543,7 @@ public class TypeUtil { private IType findType(String typeName) { try { if (javaProject!=null) { - return javaProject.findType(typeName); + return javaProject.getClasspath().findType(typeName); } } catch (Exception e) { Log.log(e); diff --git a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/TypeUtilTest.java b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/TypeUtilTest.java index 139e550e6..40f29fe16 100644 --- a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/TypeUtilTest.java +++ b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/TypeUtilTest.java @@ -68,8 +68,8 @@ public class TypeUtilTest { @Test public void testGetProperties() throws Exception { useProject("enums-boot-1.3.2-app"); - assertNotNull(project.findType("demo.Color")); - assertNotNull(project.findType("demo.ColorData")); + assertNotNull(project.getClasspath().findType("demo.Color")); + assertNotNull(project.getClasspath().findType("demo.ColorData")); Type data = TypeParser.parse("demo.ColorData"); diff --git a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java index 5ea78f46f..0c6320e80 100644 --- a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java +++ b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java @@ -214,7 +214,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { @Test public void testPredefinedProject() throws Exception { IJavaProject p = createPredefinedMavenProject("tricky-getters-boot-1.3.1-app"); - IType type = p.findType("demo.DemoApplication"); + IType type = p.getClasspath().findType("demo.DemoApplication"); assertNotNull(type); } @@ -223,7 +223,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { //Check some assumptions about the initial state of the test project (if these checks fail then // the test may be 'vacuous' since the things we are testing for already exist beforehand. - Path metadataFile = p.getOutputFolder().resolve(PropertiesLoader.PROJECT_META_DATA_LOCATIONS[0]); + Path metadataFile = p.getClasspath().getOutputFolder().resolve(PropertiesLoader.PROJECT_META_DATA_LOCATIONS[0]); assertTrue(metadataFile.toFile().isFile()); assertContains("\"name\": \"foo.counter\"", Files.toString(metadataFile.toFile(), Charset.forName("UTF8"))); } @@ -288,7 +288,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("boot-1.2.1-app-properties-list-of-pojo"); useProject(p); - assertNotNull(p.findType("demo.Foo")); + assertNotNull(p.getClasspath().findType("demo.Foo")); Editor editor = newEditor( "token.bad.guy=problem\n"+ @@ -313,7 +313,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("boot-1.2.1-app-properties-list-of-pojo"); useProject(p); - assertNotNull(p.findType("demo.Foo")); + assertNotNull(p.getClasspath().findType("demo.Foo")); assertCompletionsVariations("volder.foo.l<*>", "volder.foo.list[<*>"); assertCompletionsDisplayStringAndDetail("volder.foo.list[0].<*>", @@ -462,7 +462,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.Color")); data("foo.colors", "java.util.List", null, "A foonky list"); @@ -485,7 +485,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.Color")); data("foo.color", "demo.Color", null, "A foonky colour"); @@ -504,7 +504,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.Color")); data("foo.color", "demo.Color", null, "A foonky colour"); Editor editor = newEditor( @@ -527,7 +527,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.Color")); assertCompletionsVariations("foo.nam<*>", "foo.name-colors.<*>", @@ -546,7 +546,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { useProject(p); data("foo.name-colors", "java.util.Map", null, "Map with colors in its values"); - assertNotNull(p.findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.Color")); Editor editor = newEditor( "foo.name-colors.jacket=BLUE\n" + @@ -565,8 +565,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { useProject(p); data("foo.color-names", "java.util.Map", null, "Map with colors in its keys"); data("foo.color-data", "java.util.Map", null, "Map with colors in its keys, and pojo in values"); - assertNotNull(p.findType("demo.Color")); - assertNotNull(p.findType("demo.ColorData")); + assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.ColorData")); //Map Enum -> String: assertCompletionsVariations("foo.colnam<*>", "foo.color-names.<*>"); @@ -605,8 +605,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); - assertNotNull(p.findType("demo.ColorData")); + assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.ColorData")); Editor editor = newEditor( "foo.color-names.RED=Rood\n"+ @@ -625,8 +625,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); - assertNotNull(p.findType("demo.ColorData")); + assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.ColorData")); assertCompletion("foo.dat<*>", "foo.data.<*>"); @@ -660,8 +660,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); - assertNotNull(p.findType("demo.ColorData")); + assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.ColorData")); Editor editor = newEditor( "foo.data.bogus=Something\n" + @@ -695,8 +695,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); - assertNotNull(p.findType("demo.ColorData")); + assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.ColorData")); data("atommap", "java.util.Map", null, "map of atomic data"); data("objectmap", "java.util.Map", null, "map of atomic object (recursive map)"); @@ -735,8 +735,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); - assertNotNull(p.findType("demo.ColorData")); + assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.ColorData")); Editor editor = newEditor( "foo.color-names.BLUE.dot=Blauw\n"+ @@ -761,7 +761,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.ClothingSize")); + assertNotNull(p.getClasspath().findType("demo.ClothingSize")); data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); @@ -805,7 +805,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.ClothingSize")); + assertNotNull(p.getClasspath().findType("demo.ClothingSize")); data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); @@ -1400,7 +1400,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.Color")); data("my.colors", "java.util.List", null, "Ooh! nice colors!"); diff --git a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index 860baf75d..f42afb166 100644 --- a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -545,7 +545,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { @Test public void testReconcileBeanPropName() throws Exception { IJavaProject p = createPredefinedMavenProject("boot-1.2.1-app-properties-list-of-pojo"); useProject(p); - assertNotNull(p.findType("demo.Foo")); + assertNotNull(p.getClasspath().findType("demo.Foo")); data("some-foo", "demo.Foo", null, "some Foo pojo property"); Editor editor = newEditor( "some-foo:\n" + @@ -577,7 +577,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { @Test public void testReconcilePojoArray() throws Exception { IJavaProject p = createPredefinedMavenProject("boot-1.2.1-app-properties-list-of-pojo"); useProject(p); - assertNotNull(p.findType("demo.Foo")); + assertNotNull(p.getClasspath().findType("demo.Foo")); { Editor editor = newEditor( @@ -654,7 +654,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { @Test public void testEnumPropertyReconciling() throws Exception { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.Color")); + assertNotNull(p.getClasspath().findType("demo.Color")); data("foo.color", "demo.Color", null, "A foonky colour"); Editor editor = newEditor( @@ -1846,7 +1846,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { @Ignore @Test public void testEnumsInLowerCaseContentAssist() throws Exception { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.findType("demo.ClothingSize")); + assertNotNull(p.getClasspath().findType("demo.ClothingSize")); data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); diff --git a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java index 56fda5153..bde1c3f47 100644 --- a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java +++ b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java @@ -52,7 +52,7 @@ public class ProjectsHarness { switch (type) { case MAVEN: MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); - return new MavenJavaProject(testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); case CLASSPATH_TXT: MavenBuilder.newBuilder(testProjectPath).clean().pack().skipTests().execute(); return new JavaProjectWithClasspathFile(testProjectPath.resolve(MavenCore.CLASSPATH_TXT).toFile()); From 55f4e897f2d8e8602653b422f656a1873cdeded6 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Tue, 7 Feb 2017 21:49:14 -0500 Subject: [PATCH 03/28] Upgrade slf4j --- vscode-extensions/commons/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode-extensions/commons/pom.xml b/vscode-extensions/commons/pom.xml index 403e59596..4d3b0fe14 100644 --- a/vscode-extensions/commons/pom.xml +++ b/vscode-extensions/commons/pom.xml @@ -72,7 +72,7 @@ 1.17 4.11 3.5.2 - 1.7.21 + 1.7.22 19.0 2.5.0 2.10 From 1356d4a09c39c5facfe066bc06c4af2f1f93a149 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Fri, 10 Feb 2017 10:57:58 +0100 Subject: [PATCH 04/28] do not connect to running LS by default --- vscode-extensions/vscode-boot-java/lib/Main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode-extensions/vscode-boot-java/lib/Main.ts b/vscode-extensions/vscode-boot-java/lib/Main.ts index 62c553daf..944b0ca6d 100644 --- a/vscode-extensions/vscode-boot-java/lib/Main.ts +++ b/vscode-extensions/vscode-boot-java/lib/Main.ts @@ -19,7 +19,7 @@ export function activate(context: VSCode.ExtensionContext) { let options: commons.ActivatorOptions = { DEBUG: false, - CONNECT_TO_LS: true, + CONNECT_TO_LS: false, extensionId: 'vscode-boot-java', fatJarFile: 'target/vscode-boot-java-0.0.1-SNAPSHOT.jar', clientOptions: { From ad7203fc7c1ba7677a03dcff3c61d43299ac061d Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Fri, 10 Feb 2017 11:00:12 +0100 Subject: [PATCH 05/28] basic property completion support for value annotations added --- vscode-extensions/vscode-boot-java/pom.xml | 9 + .../ConfigurationMetadataGroup.java | 76 +++ .../ConfigurationMetadataHint.java | 74 +++ .../ConfigurationMetadataItem.java | 60 ++ .../ConfigurationMetadataProperty.java | 190 ++++++ .../ConfigurationMetadataRepository.java | 47 ++ ...gurationMetadataRepositoryJsonBuilder.java | 231 +++++++ .../ConfigurationMetadataSource.java | 131 ++++ .../configurationmetadata/Deprecation.java | 66 ++ .../DescriptionExtractor.java | 58 ++ .../boot/configurationmetadata/Hints.java | 82 +++ .../configurationmetadata/JsonReader.java | 195 ++++++ .../boot/configurationmetadata/README.txt | 10 + .../RawConfigurationMetadata.java | 106 ++++ ...SimpleConfigurationMetadataRepository.java | 130 ++++ .../boot/configurationmetadata/ValueHint.java | 97 +++ .../configurationmetadata/ValueProvider.java | 66 ++ .../configurationmetadata/package-info.java | 20 + .../boot/java/BootJavaLanguageServer.java | 5 +- .../ide/vscode/boot/java/Main.java | 4 +- .../completions/BootJavaCompletionEngine.java | 9 +- .../completions/ValueCompletionProcessor.java | 158 +++++ .../completions/ValuePropertyKeyProposal.java | 66 ++ .../boot/metadata/CachingValueProvider.java | 148 +++++ .../boot/metadata/ClassReferenceProvider.java | 154 +++++ .../DefaultSpringPropertyIndexProvider.java | 46 ++ .../vscode/boot/metadata/IndexNavigator.java | 133 +++++ .../boot/metadata/LoggerNameProvider.java | 52 ++ .../boot/metadata/MetadataManipulator.java | 230 +++++++ .../boot/metadata/PropertiesLoader.java | 147 +++++ .../vscode/boot/metadata/PropertyInfo.java | 190 ++++++ .../boot/metadata/ResourceHintProvider.java | 71 +++ .../SpringPropertiesIndexManager.java | 74 +++ .../boot/metadata/SpringPropertyIndex.java | 156 +++++ .../metadata/SpringPropertyIndexProvider.java | 20 + .../boot/metadata/ValueProviderRegistry.java | 98 +++ .../boot/metadata/hints/StsValueHint.java | 137 +++++ .../metadata/hints/ValueHintHoverInfo.java | 33 + .../boot/metadata/util/DeprecationUtil.java | 59 ++ .../vscode/boot/metadata/util/FuzzyMap.java | 170 ++++++ .../vscode/boot/metadata/util/Listener.java | 20 + .../boot/metadata/util/ListenerManager.java | 36 ++ .../completions/test/ScopeCompletionTest.java | 10 +- .../completions/test/ValueCompletionTest.java | 248 ++++++++ .../project/harness/ProjectsHarness.java | 90 +-- .../project/harness/PropertyIndexHarness.java | 565 ++++++++++++++++++ .../.gitignore | 0 .../.mvn/wrapper/maven-wrapper.jar | Bin .../.mvn/wrapper/maven-wrapper.properties | 0 .../mvnw | 0 .../mvnw.cmd | 0 .../pom.xml | 0 .../org/test/TestAnnotationsApplication.java} | 4 +- .../java/org/test/TestScopeCompletion.java | 0 .../java/org/test/TestValueCompletion.java | 17 + 55 files changed, 4703 insertions(+), 95 deletions(-) create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataGroup.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataHint.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataItem.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataProperty.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepository.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilder.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataSource.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/DescriptionExtractor.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Hints.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/README.txt create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/RawConfigurationMetadata.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueHint.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/package-info.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValuePropertyKeyProposal.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/CachingValueProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ValueProviderRegistry.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/ValueHintHoverInfo.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/DeprecationUtil.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/Listener.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/ListenerManager.java create mode 100644 vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java create mode 100644 vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java rename vscode-extensions/vscode-boot-java/src/test/resources/test-projects/{test-scope-annotation => test-annotations}/.gitignore (100%) rename vscode-extensions/vscode-boot-java/src/test/resources/test-projects/{test-scope-annotation => test-annotations}/.mvn/wrapper/maven-wrapper.jar (100%) rename vscode-extensions/vscode-boot-java/src/test/resources/test-projects/{test-scope-annotation => test-annotations}/.mvn/wrapper/maven-wrapper.properties (100%) rename vscode-extensions/vscode-boot-java/src/test/resources/test-projects/{test-scope-annotation => test-annotations}/mvnw (100%) rename vscode-extensions/vscode-boot-java/src/test/resources/test-projects/{test-scope-annotation => test-annotations}/mvnw.cmd (100%) rename vscode-extensions/vscode-boot-java/src/test/resources/test-projects/{test-scope-annotation => test-annotations}/pom.xml (100%) rename vscode-extensions/vscode-boot-java/src/test/resources/test-projects/{test-scope-annotation/src/main/java/org/test/TestScopeAnnotationApplication.java => test-annotations/src/main/java/org/test/TestAnnotationsApplication.java} (64%) rename vscode-extensions/vscode-boot-java/src/test/resources/test-projects/{test-scope-annotation => test-annotations}/src/main/java/org/test/TestScopeCompletion.java (100%) create mode 100644 vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestValueCompletion.java diff --git a/vscode-extensions/vscode-boot-java/pom.xml b/vscode-extensions/vscode-boot-java/pom.xml index bf2fdca88..7214d4cb3 100644 --- a/vscode-extensions/vscode-boot-java/pom.xml +++ b/vscode-extensions/vscode-boot-java/pom.xml @@ -22,6 +22,10 @@ true + + project-repo + file://${project.basedir}/repo + @@ -33,6 +37,11 @@ + + org.springframework.ide.eclipse + org.json + 1.0 + org.springframework.ide.vscode commons-maven diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataGroup.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataGroup.java new file mode 100644 index 000000000..ea6428e85 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataGroup.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Gather a collection of {@link ConfigurationMetadataProperty properties} that are + * sharing a {@link #getId() common prefix}. Provide access to all the + * {@link ConfigurationMetadataSource sources} that have contributed properties to the + * group. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +@SuppressWarnings("serial") +public class ConfigurationMetadataGroup implements Serializable { + + private final String id; + + private final Map sources = new HashMap(); + + private final Map properties = new HashMap(); + + public ConfigurationMetadataGroup(String id) { + this.id = id; + } + + /** + * Return the id of the group, used as a common prefix for all properties associated + * to it. + * @return the id of the group + */ + public String getId() { + return this.id; + } + + /** + * Return the {@link ConfigurationMetadataSource sources} defining the properties of + * this group. + * @return the sources of the group + */ + public Map getSources() { + return this.sources; + } + + /** + * Return the {@link ConfigurationMetadataProperty properties} defined in this group. + *

+ * A property may appear more than once for a given source, potentially with + * conflicting type or documentation. This is a "merged" view of the properties of + * this group. + * @return the properties of the group + * @see ConfigurationMetadataSource#getProperties() + */ + public Map getProperties() { + return this.properties; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataHint.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataHint.java new file mode 100644 index 000000000..9f5e1f204 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataHint.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.util.ArrayList; +import java.util.List; + +/** + * A raw view of a hint used for parsing only. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +class ConfigurationMetadataHint { + + private static final String KEY_SUFFIX = ".keys"; + + private static final String VALUE_SUFFIX = ".values"; + + private String id; + + private final List valueHints = new ArrayList(); + + private final List valueProviders = new ArrayList(); + + public boolean isMapKeyHints() { + return (this.id != null && this.id.endsWith(KEY_SUFFIX)); + } + + public boolean isMapValueHints() { + return (this.id != null && this.id.endsWith(VALUE_SUFFIX)); + } + + public String resolveId() { + if (isMapKeyHints()) { + return this.id.substring(0, this.id.length() - KEY_SUFFIX.length()); + } + if (isMapValueHints()) { + return this.id.substring(0, this.id.length() - VALUE_SUFFIX.length()); + } + return this.id; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public List getValueHints() { + return this.valueHints; + } + + public List getValueProviders() { + return this.valueProviders; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataItem.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataItem.java new file mode 100644 index 000000000..4001c4da1 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataItem.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +/** + * An extension of {@link ConfigurationMetadataProperty} that provides a reference to its + * source. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +class ConfigurationMetadataItem extends ConfigurationMetadataProperty { + + private String sourceType; + + private String sourceMethod; + + /** + * The class name of the source that contributed this property. For example, if the + * property was from a class annotated with {@code @ConfigurationProperties} this + * attribute would contain the fully qualified name of that class. + * @return the source type + */ + public String getSourceType() { + return this.sourceType; + } + + public void setSourceType(String sourceType) { + this.sourceType = sourceType; + } + + /** + * The full name of the method (including parenthesis and argument types) that + * contributed this property. For example, the name of a getter in a + * {@code @ConfigurationProperties} annotated class. + * @return the source method + */ + public String getSourceMethod() { + return this.sourceMethod; + } + + public void setSourceMethod(String sourceMethod) { + this.sourceMethod = sourceMethod; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataProperty.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataProperty.java new file mode 100644 index 000000000..997746490 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataProperty.java @@ -0,0 +1,190 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.Serializable; +import java.util.List; + +/** + * Define a configuration property. Each property is fully identified by its + * {@link #getId() id} which is composed of a namespace prefix (the + * {@link ConfigurationMetadataGroup#getId() group id}), if any and the {@link #getName() + * name} of the property. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +@SuppressWarnings("serial") +public class ConfigurationMetadataProperty implements Serializable { + + private String id; + + private String name; + + private String type; + + private String description; + + private String shortDescription; + + private Object defaultValue; + + private final Hints hints = new Hints(); + + private Deprecation deprecation; + + /** + * The full identifier of the property, in lowercase dashed form (e.g. + * my.group.simple-property) + * @return the property id + */ + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + /** + * The name of the property, in lowercase dashed form (e.g. simple-property). If this + * item does not belong to any group, the id is returned. + * @return the property name + */ + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * The class name of the data type of the property. For example, + * {@code java.lang.String}. + *

+ * For consistency, the type of a primitive is specified using its wrapper + * counterpart, i.e. {@code boolean} becomes {@code java.lang.Boolean}. If the type + * holds generic information, these are provided as well, i.e. a {@code HashMap} of + * String to Integer would be defined as {@code java.util.HashMap + * }. + *

+ * Note that this class may be a complex type that gets converted from a String as + * values are bound. + * @return the property type + */ + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + /** + * A description of the property, if any. Can be multi-lines. + * @return the property description + * @see #getShortDescription() + */ + public String getDescription() { + return this.description; + } + + public void setDescription(String description) { + this.description = description; + } + + /** + * A single-line, single-sentence description of this property, if any. + * @return the property short description + * @see #getDescription() + */ + public String getShortDescription() { + return this.shortDescription; + } + + public void setShortDescription(String shortDescription) { + this.shortDescription = shortDescription; + } + + /** + * The default value, if any. + * @return the default value + */ + public Object getDefaultValue() { + return this.defaultValue; + } + + public void setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + } + + /** + * Return the hints of this item. + * @return the hints + */ + public Hints getHints() { + return this.hints; + } + + /** + * The list of well-defined values, if any. If no extra {@link ValueProvider provider} + * is specified, these values are to be considered a closed-set of the available + * values for this item. + * @return the value hints + * @see #getHints() + */ + @Deprecated + public List getValueHints() { + return this.hints.getValueHints(); + } + + /** + * The value providers that are applicable to this item. Only one + * {@link ValueProvider} is enabled for an item: the first in the list that is + * supported should be used. + * @return the value providers + * @see #getHints() + */ + @Deprecated + public List getValueProviders() { + return this.hints.getValueProviders(); + } + + /** + * The {@link Deprecation} for this property, if any. + * @return the deprecation + * @see #isDeprecated() + */ + public Deprecation getDeprecation() { + return this.deprecation; + } + + public void setDeprecation(Deprecation deprecation) { + this.deprecation = deprecation; + } + + /** + * Specify if the property is deprecated. + * @return if the property is deprecated + * @see #getDeprecation() + */ + public boolean isDeprecated() { + return this.deprecation != null; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepository.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepository.java new file mode 100644 index 000000000..95122ac2a --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepository.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.util.Map; + +/** + * A repository of configuration metadata. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +public interface ConfigurationMetadataRepository { + + /** + * Defines the name of the "root" group, that is the group that gathers all the + * properties that aren't attached to a specific group. + */ + String ROOT_GROUP = "_ROOT_GROUP_"; + + /** + * Return the groups, indexed by id. + * @return all configuration meta-data groups + */ + Map getAllGroups(); + + /** + * Return the properties, indexed by id. + * @return all configuration meta-data properties + */ + Map getAllProperties(); + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilder.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilder.java new file mode 100644 index 000000000..b7d096fd8 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilder.java @@ -0,0 +1,231 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.ide.eclipse.org.json.JSONException; + +/** + * Load a {@link ConfigurationMetadataRepository} from the content of arbitrary + * resource(s). + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +public final class ConfigurationMetadataRepositoryJsonBuilder { + + /** + * UTF-8 Charset. + */ + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + private Charset defaultCharset = UTF_8; + + private final JsonReader reader = new JsonReader(); + + private final List rawDatas = new ArrayList<>(); + + private ConfigurationMetadataRepositoryJsonBuilder(Charset defaultCharset) { + this.defaultCharset = defaultCharset; + } + + /** + * Add the content of a {@link ConfigurationMetadataRepository} defined by the + * specified {@link InputStream} json document using the default charset. If this + * metadata repository holds items that were loaded previously, these are ignored. + *

+ * Leaves the stream open when done. + * @param origin optional information object to help identify where the inputstream came from + * @param inputStream the source input stream + * @return this builder + * @throws IOException in case of I/O errors + */ + public ConfigurationMetadataRepositoryJsonBuilder withJsonResource( + Object origin, InputStream inputStream) throws IOException { + return withJsonResource(origin, inputStream, this.defaultCharset); + } + + /** + * Add the content of a {@link ConfigurationMetadataRepository} defined by the + * specified {@link InputStream} json document using the specified {@link Charset}. If + * this metadata repository holds items that were loaded previously, these are + * ignored. + *

+ * Leaves the stream open when done. + * @param origin optional information object to help identify where the inputstream came from + * @param inputStream the source input stream + * @param charset the charset of the input + * @return this builder + * @throws IOException in case of I/O errors + */ + public ConfigurationMetadataRepositoryJsonBuilder withJsonResource( + Object origin, InputStream inputStream, Charset charset) throws IOException { + if (inputStream == null) { + throw new IllegalArgumentException("InputStream must not be null."); + } + this.rawDatas.add(parseRaw(origin, inputStream, charset)); + return this; + } + + /** + * Build a {@link ConfigurationMetadataRepository} with the current state of this + * builder. + * @return this builder + */ + public ConfigurationMetadataRepository build() { + SimpleConfigurationMetadataRepository result = new SimpleConfigurationMetadataRepository(); + result.include(create(rawDatas)); + return result; + } + + private RawConfigurationMetadata parseRaw(Object origin, InputStream in, Charset charset) + throws IOException { + try { + return this.reader.read(origin, in, charset); + } + catch (IOException ex) { + throw new IllegalArgumentException( + "Failed to read configuration " + "metadata", ex); + } + catch (JSONException ex) { + throw new IllegalArgumentException( + "Invalid configuration " + "metadata document", ex); + } + } + + private SimpleConfigurationMetadataRepository create( + Iterable metadatas) { + SimpleConfigurationMetadataRepository repository = new SimpleConfigurationMetadataRepository(); + + for (RawConfigurationMetadata metadata : metadatas) { + repository.add(metadata.getSources()); + } + for (RawConfigurationMetadata metadata : metadatas) { + for (ConfigurationMetadataItem item : metadata.getItems()) { + ConfigurationMetadataSource source = getSource(metadata, item); + repository.add(item, source); + } + } + for (RawConfigurationMetadata metadata : metadatas) { + Map allProperties = repository + .getAllProperties(); + for (ConfigurationMetadataHint hint : metadata.getHints()) { + ConfigurationMetadataProperty property = allProperties.get(hint.getId()); + if (property != null) { + addValueHints(property, hint); + } + else { + String id = hint.resolveId(); + property = allProperties.get(id); + if (property != null) { + if (hint.isMapKeyHints()) { + addMapHints(property, hint); + } + else { + addValueHints(property, hint); + } + } + } + } + } + return repository; + } + + private void addValueHints(ConfigurationMetadataProperty property, + ConfigurationMetadataHint hint) { + addAll(property.getHints().getValueHints(), hint.getValueHints()); + property.getHints().getValueProviders().addAll(hint.getValueProviders()); + } + + private void addMapHints(ConfigurationMetadataProperty property, + ConfigurationMetadataHint hint) { + addAll(property.getHints().getKeyHints(), hint.getValueHints()); + property.getHints().getKeyProviders().addAll(hint.getValueProviders()); + } + + /** + * Add a bunch of hints to a list, but guard against duplicates. + */ + private void addAll(List existing, List toAdd) { + if (existing.isEmpty()) { + existing.addAll(toAdd); + } else if (toAdd.isEmpty()) { + //nothing to add + } else { + Set existingValues = existing + .stream() + .map((hint) -> ""+hint.getValue()) + .collect(Collectors.toSet()); + for (ValueHint hint : toAdd) { + if (!existingValues.contains(""+hint.getValue())) { + existing.add(hint); + } + } + } + } + + private ConfigurationMetadataSource getSource(RawConfigurationMetadata metadata, + ConfigurationMetadataItem item) { + if (item.getSourceType() != null) { + return metadata.getSource(item.getSourceType()); + } + return null; + } + + /** + * Create a new builder instance using {@link #UTF_8} as the default charset and the + * specified json resource. + * @param inputStreams the source input streams + * @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance. + * @throws IOException on error + */ + public static ConfigurationMetadataRepositoryJsonBuilder create( + InputStream... inputStreams) throws IOException { + ConfigurationMetadataRepositoryJsonBuilder builder = create(); + for (InputStream inputStream : inputStreams) { + builder = builder.withJsonResource(null, inputStream); + } + return builder; + } + + /** + * Create a new builder instance using {@link #UTF_8} as the default charset. + * @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance. + */ + public static ConfigurationMetadataRepositoryJsonBuilder create() { + return create(UTF_8); + } + + /** + * Create a new builder instance using the specified default {@link Charset}. + * @param defaultCharset the default charset to use + * @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance. + */ + public static ConfigurationMetadataRepositoryJsonBuilder create( + Charset defaultCharset) { + return new ConfigurationMetadataRepositoryJsonBuilder(defaultCharset); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataSource.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataSource.java new file mode 100644 index 000000000..9c1dad953 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataSource.java @@ -0,0 +1,131 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * A source of configuration metadata. Also defines where the source is declared, for + * instance if it is defined as a {@code @Bean}. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +@SuppressWarnings("serial") +public class ConfigurationMetadataSource implements Serializable { + + private String groupId; + + private String type; + + private String description; + + private String shortDescription; + + private String sourceType; + + private String sourceMethod; + + private final Map properties = new HashMap(); + + /** + * The identifier of the group to which this source is associated. + * @return the group id + */ + public String getGroupId() { + return this.groupId; + } + + void setGroupId(String groupId) { + this.groupId = groupId; + } + + /** + * The type of the source. Usually this is the fully qualified name of a class that + * defines configuration items. This class may or may not be available at runtime. + * @return the type + */ + public String getType() { + return this.type; + } + + void setType(String type) { + this.type = type; + } + + /** + * A description of this source, if any. Can be multi-lines. + * @return the description + * @see #getShortDescription() + */ + public String getDescription() { + return this.description; + } + + void setDescription(String description) { + this.description = description; + } + + /** + * A single-line, single-sentence description of this source, if any. + * @return the short description + * @see #getDescription() + */ + public String getShortDescription() { + return this.shortDescription; + } + + public void setShortDescription(String shortDescription) { + this.shortDescription = shortDescription; + } + + /** + * The type where this source is defined. This can be identical to the + * {@link #getType() type} if the source is self-defined. + * @return the source type + */ + public String getSourceType() { + return this.sourceType; + } + + void setSourceType(String sourceType) { + this.sourceType = sourceType; + } + + /** + * The method name that defines this source, if any. + * @return the source method + */ + public String getSourceMethod() { + return this.sourceMethod; + } + + void setSourceMethod(String sourceMethod) { + this.sourceMethod = sourceMethod; + } + + /** + * Return the properties defined by this source. + * @return the properties + */ + public Map getProperties() { + return this.properties; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java new file mode 100644 index 000000000..8261a2a85 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.Serializable; + +/** + * Indicate that a property is deprecated. Provide additional information about the + * deprecation. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +@SuppressWarnings("serial") +public class Deprecation implements Serializable { + + private String reason; + + private String replacement; + + /** + * A reason why the related property is deprecated, if any. Can be multi-lines. + * @return the deprecation reason + */ + public String getReason() { + return this.reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + /** + * The full name of the property that replaces the related deprecated property, if + * any. + * @return the replacement property name + */ + public String getReplacement() { + return this.replacement; + } + + public void setReplacement(String replacement) { + this.replacement = replacement; + } + + @Override + public String toString() { + return "Deprecation{" + "reason='" + this.reason + '\'' + ", replacement='" + + this.replacement + '\'' + '}'; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/DescriptionExtractor.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/DescriptionExtractor.java new file mode 100644 index 000000000..81a9ff9b4 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/DescriptionExtractor.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.text.BreakIterator; +import java.util.Locale; + +/** + * Utility to extract a description. + * + * @author Stephane Nicoll + */ +class DescriptionExtractor { + + private static final String NEW_LINE = System.getProperty("line.separator"); + + public String getShortDescription(String description) { + if (description == null) { + return null; + } + int dot = description.indexOf("."); + if (dot != -1) { + BreakIterator breakIterator = BreakIterator.getSentenceInstance(Locale.US); + breakIterator.setText(description); + String text = description + .substring(breakIterator.first(), breakIterator.next()).trim(); + return removeSpaceBetweenLine(text); + } + else { + String[] lines = description.split(NEW_LINE); + return lines[0].trim(); + } + } + + private String removeSpaceBetweenLine(String text) { + String[] lines = text.split(NEW_LINE); + StringBuilder sb = new StringBuilder(); + for (String line : lines) { + sb.append(line.trim()).append(" "); + } + return sb.toString().trim(); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Hints.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Hints.java new file mode 100644 index 000000000..26bdcb69d --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Hints.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.util.ArrayList; +import java.util.List; + +/** + * Hints of an item to provide the list of values and/or the name of the provider + * responsible to identify suitable values. If the type of the related item is a + * {@link java.util.Map} it can have both key and value hints. + * + * @author Stephane Nicoll + * @since 1.4.0 + */ +public class Hints { + + private final List keyHints = new ArrayList(); + + private final List keyProviders = new ArrayList(); + + private final List valueHints = new ArrayList(); + + private final List valueProviders = new ArrayList(); + + /** + * The list of well-defined keys, if any. Only applicable if the type of the related + * item is a {@link java.util.Map}. If no extra {@link ValueProvider provider} is + * specified, these values are to be considered a closed-set of the available keys for + * the map. + * @return the key hints + */ + public List getKeyHints() { + return this.keyHints; + } + + /** + * The value providers that are applicable to the keys of this item. Only applicable + * if the type of the related item is a {@link java.util.Map}. Only one + * {@link ValueProvider} is enabled for a key: the first in the list that is supported + * should be used. + * @return the key providers + */ + public List getKeyProviders() { + return this.keyProviders; + } + + /** + * The list of well-defined values, if any. If no extra {@link ValueProvider provider} + * is specified, these values are to be considered a closed-set of the available + * values for this item. + * @return the value hints + */ + public List getValueHints() { + return this.valueHints; + } + + /** + * The value providers that are applicable to this item. Only one + * {@link ValueProvider} is enabled for an item: the first in the list that is + * supported should be used. + * @return the value providers + */ + public List getValueProviders() { + return this.valueProviders; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java new file mode 100644 index 000000000..4d73902c9 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java @@ -0,0 +1,195 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.springframework.ide.eclipse.org.json.JSONArray; +import org.springframework.ide.eclipse.org.json.JSONObject; + +/** + * Read standard json metadata format as {@link ConfigurationMetadataRepository}. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +class JsonReader { + + private static final int BUFFER_SIZE = 4096; + + private final DescriptionExtractor descriptionExtractor = new DescriptionExtractor(); + + public RawConfigurationMetadata read(Object origin, InputStream in, Charset charset) + throws IOException { + JSONObject json = readJson(in, charset); + List groups = parseAllSources(json); + List items = parseAllItems(json); + List hints = parseAllHints(json); + return new RawConfigurationMetadata(origin, groups, items, hints); + } + + private List parseAllSources(JSONObject root) { + List result = new ArrayList(); + if (!root.has("groups")) { + return result; + } + JSONArray sources = root.getJSONArray("groups"); + for (int i = 0; i < sources.length(); i++) { + JSONObject source = sources.getJSONObject(i); + result.add(parseSource(source)); + } + return result; + } + + private List parseAllItems(JSONObject root) { + List result = new ArrayList(); + if (!root.has("properties")) { + return result; + } + JSONArray items = root.getJSONArray("properties"); + for (int i = 0; i < items.length(); i++) { + JSONObject item = items.getJSONObject(i); + result.add(parseItem(item)); + } + return result; + } + + private List parseAllHints(JSONObject root) { + List result = new ArrayList(); + if (!root.has("hints")) { + return result; + } + JSONArray items = root.getJSONArray("hints"); + for (int i = 0; i < items.length(); i++) { + JSONObject item = items.getJSONObject(i); + result.add(parseHint(item)); + } + return result; + } + + private ConfigurationMetadataSource parseSource(JSONObject json) { + ConfigurationMetadataSource source = new ConfigurationMetadataSource(); + source.setGroupId(json.getString("name")); + source.setType(json.optString("type", null)); + String description = json.optString("description", null); + source.setDescription(description); + source.setShortDescription( + this.descriptionExtractor.getShortDescription(description)); + source.setSourceType(json.optString("sourceType", null)); + source.setSourceMethod(json.optString("sourceMethod", null)); + return source; + } + + private ConfigurationMetadataItem parseItem(JSONObject json) { + ConfigurationMetadataItem item = new ConfigurationMetadataItem(); + item.setId(json.getString("name")); + item.setType(json.optString("type", null)); + String description = json.optString("description", null); + item.setDescription(description); + item.setShortDescription( + this.descriptionExtractor.getShortDescription(description)); + item.setDefaultValue(readItemValue(json.opt("defaultValue"))); + item.setDeprecation(parseDeprecation(json)); + item.setSourceType(json.optString("sourceType", null)); + item.setSourceMethod(json.optString("sourceMethod", null)); + return item; + } + + private ConfigurationMetadataHint parseHint(JSONObject json) { + ConfigurationMetadataHint hint = new ConfigurationMetadataHint(); + hint.setId(json.getString("name")); + if (json.has("values")) { + JSONArray values = json.getJSONArray("values"); + for (int i = 0; i < values.length(); i++) { + JSONObject value = values.getJSONObject(i); + ValueHint valueHint = new ValueHint(); + valueHint.setValue(readItemValue(value.get("value"))); + String description = value.optString("description", null); + valueHint.setDescription(description); + valueHint.setShortDescription( + this.descriptionExtractor.getShortDescription(description)); + hint.getValueHints().add(valueHint); + } + } + if (json.has("providers")) { + JSONArray providers = json.getJSONArray("providers"); + for (int i = 0; i < providers.length(); i++) { + JSONObject provider = providers.getJSONObject(i); + ValueProvider valueProvider = new ValueProvider(); + valueProvider.setName(provider.getString("name")); + if (provider.has("parameters")) { + JSONObject parameters = provider.getJSONObject("parameters"); + Iterator keys = parameters.keys(); + while (keys.hasNext()) { + String key = (String) keys.next(); + valueProvider.getParameters().put(key, + readItemValue(parameters.get(key))); + } + } + hint.getValueProviders().add(valueProvider); + } + } + return hint; + } + + private Deprecation parseDeprecation(JSONObject object) { + if (object.has("deprecation")) { + JSONObject deprecationJsonObject = object.getJSONObject("deprecation"); + Deprecation deprecation = new Deprecation(); + deprecation.setReason(deprecationJsonObject.optString("reason", null)); + deprecation + .setReplacement(deprecationJsonObject.optString("replacement", null)); + return deprecation; + } + return (object.optBoolean("deprecated") ? new Deprecation() : null); + } + + private Object readItemValue(Object value) { + if (value instanceof JSONArray) { + JSONArray array = (JSONArray) value; + Object[] content = new Object[array.length()]; + for (int i = 0; i < array.length(); i++) { + content[i] = array.get(i); + } + return content; + } + return value; + } + + private JSONObject readJson(InputStream in, Charset charset) throws IOException { + try { + StringBuilder out = new StringBuilder(); + InputStreamReader reader = new InputStreamReader(in, charset); + char[] buffer = new char[BUFFER_SIZE]; + int bytesRead = -1; + while ((bytesRead = reader.read(buffer)) != -1) { + out.append(buffer, 0, bytesRead); + } + return new JSONObject(out.toString()); + } + finally { + in.close(); + } + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/README.txt b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/README.txt new file mode 100644 index 000000000..f3e11fe93 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/README.txt @@ -0,0 +1,10 @@ +The source code in this package is taken from here: + +https://github.com/spring-projects/spring-boot/tree/fca6dbaf09c32202d9d958f815221aad54b9fc7b/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata + +Notes: + - This commit is from the master branch at a point in time where boot team is working on Boot 1.4.x on that branch. + +There are currently no modifications being made to that code at all to accomodate STS. So it may now be possible to consume it as a proper dependency. +However, keep in mind that we are using a modified copy of 'org.json' to allow controlling key order in json maps. So that probably +complicates things. diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/RawConfigurationMetadata.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/RawConfigurationMetadata.java new file mode 100644 index 000000000..a264b1e9b --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/RawConfigurationMetadata.java @@ -0,0 +1,106 @@ +/* + * Copyright 2012-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.util.ArrayList; +import java.util.List; + +/** + * A raw metadata structure. Used to initialize a {@link ConfigurationMetadataRepository}. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +class RawConfigurationMetadata { + + private final Object origin; + + private final List sources; + + private final List items; + + private final List hints; + + RawConfigurationMetadata(Object parsedFrom, + List sources, + List items, + List hints) { + this.origin = parsedFrom; + this.sources = new ArrayList(sources); + this.items = new ArrayList(items); + this.hints = new ArrayList(hints); + for (ConfigurationMetadataItem item : this.items) { + resolveName(item); + } + } + + public List getSources() { + return this.sources; + } + + public ConfigurationMetadataSource getSource(String type) { + for (ConfigurationMetadataSource source : this.sources) { + if (type.equals(source.getType())) { + return source; + } + } + return null; + } + + public List getItems() { + return this.items; + } + + public List getHints() { + return this.hints; + } + + /** + * Resolve the name of an item against this instance. + * @param item the item to resolve + * @see ConfigurationMetadataProperty#setName(String) + */ + private void resolveName(ConfigurationMetadataItem item) { + item.setName(item.getId()); // fallback + if (item.getSourceType() == null) { + return; + } + ConfigurationMetadataSource source = getSource(item.getSourceType()); + if (source != null) { + String groupId = source.getGroupId(); + String dottedPrefix = groupId + "."; + String id = item.getId(); + if (hasLength(groupId) && id.startsWith(dottedPrefix)) { + String name = id.substring(dottedPrefix.length(), id.length()); + item.setName(name); + } + } + } + + private static boolean hasLength(String string) { + return (string != null && string.length() > 0); + } + + @Override + public String toString() { + if (origin!=null) { + return "RawConfigurationMetadata("+origin+")"; + } + return super.toString(); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java new file mode 100644 index 000000000..e12ec4479 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java @@ -0,0 +1,130 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * The default {@link ConfigurationMetadataRepository} implementation. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +@SuppressWarnings("serial") +public class SimpleConfigurationMetadataRepository + implements ConfigurationMetadataRepository, Serializable { + + private final Map allGroups = new HashMap(); + + @Override + public Map getAllGroups() { + return Collections.unmodifiableMap(this.allGroups); + } + + @Override + public Map getAllProperties() { + Map properties = new HashMap(); + for (ConfigurationMetadataGroup group : this.allGroups.values()) { + properties.putAll(group.getProperties()); + } + return properties; + } + + /** + * Register the specified {@link ConfigurationMetadataSource sources}. + * @param sources the sources to add + */ + public void add(Collection sources) { + for (ConfigurationMetadataSource source : sources) { + String groupId = source.getGroupId(); + ConfigurationMetadataGroup group = this.allGroups.get(groupId); + if (group == null) { + group = new ConfigurationMetadataGroup(groupId); + this.allGroups.put(groupId, group); + } + String sourceType = source.getType(); + if (sourceType != null) { + putIfAbsent(group.getSources(), sourceType, source); + } + } + } + + /** + * Add a {@link ConfigurationMetadataProperty} with the + * {@link ConfigurationMetadataSource source} that defines it, if any. + * @param property the property to add + * @param source the source + */ + public void add(ConfigurationMetadataProperty property, + ConfigurationMetadataSource source) { + if (source != null) { + putIfAbsent(source.getProperties(), property.getId(), property); + } + putIfAbsent(getGroup(source).getProperties(), property.getId(), property); + } + + /** + * Merge the content of the specified repository to this repository. + * @param repository the repository to include + */ + public void include(ConfigurationMetadataRepository repository) { + for (ConfigurationMetadataGroup group : repository.getAllGroups().values()) { + ConfigurationMetadataGroup existingGroup = this.allGroups.get(group.getId()); + if (existingGroup == null) { + this.allGroups.put(group.getId(), group); + } + else { + // Merge properties + for (Map.Entry entry : group + .getProperties().entrySet()) { + putIfAbsent(existingGroup.getProperties(), entry.getKey(), + entry.getValue()); + } + // Merge sources + for (Map.Entry entry : group + .getSources().entrySet()) { + putIfAbsent(existingGroup.getSources(), entry.getKey(), + entry.getValue()); + } + } + } + + } + + private ConfigurationMetadataGroup getGroup(ConfigurationMetadataSource source) { + if (source == null) { + ConfigurationMetadataGroup rootGroup = this.allGroups.get(ROOT_GROUP); + if (rootGroup == null) { + rootGroup = new ConfigurationMetadataGroup(ROOT_GROUP); + this.allGroups.put(ROOT_GROUP, rootGroup); + } + return rootGroup; + } + return this.allGroups.get(source.getGroupId()); + } + + private void putIfAbsent(Map map, String key, V value) { + if (!map.containsKey(key)) { + map.put(key, value); + } + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueHint.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueHint.java new file mode 100644 index 000000000..043fc10dc --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueHint.java @@ -0,0 +1,97 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.Serializable; + +/** + * Hint for a value a given property may have. Provide the value and an optional + * description. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +@SuppressWarnings("serial") +public class ValueHint implements Serializable, Cloneable { + + public static ValueHint withValue(Object value) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + return hint; + } + + public ValueHint prefixWith(String prefix) { + try { + ValueHint clone = (ValueHint) this.clone(); + clone.setValue(prefix+value); + return clone; + } catch (CloneNotSupportedException e) { + //This is supposed to be impossble. + throw new RuntimeException(e); + } + } + + private Object value; + + private String description; + + private String shortDescription; + + /** + * Return the hint value. + * @return the value + */ + public Object getValue() { + return this.value; + } + + public void setValue(Object value) { + this.value = value; + } + + /** + * A description of this value, if any. Can be multi-lines. + * @return the description + * @see #getShortDescription() + */ + public String getDescription() { + return this.description; + } + + public void setDescription(String description) { + this.description = description; + } + + /** + * A single-line, single-sentence description of this hint, if any. + * @return the short description + * @see #getDescription() + */ + public String getShortDescription() { + return this.shortDescription; + } + + public void setShortDescription(String shortDescription) { + this.shortDescription = shortDescription; + } + + @Override + public String toString() { + return "ValueHint{" + "value=" + this.value + ", description='" + this.description + + '\'' + '}'; + } +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueProvider.java new file mode 100644 index 000000000..550181ee9 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueProvider.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.configurationmetadata; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Define a component that is able to provide the values of a property. + *

+ * Each provider is defined by a {@code name} and can have an arbitrary number of + * {@code parameters}. The available providers are defined in the Spring Boot + * documentation. + * + * @author Stephane Nicoll + * @since 1.3.0 + */ +@SuppressWarnings("serial") +public class ValueProvider implements Serializable { + + private String name; + + private final Map parameters = new LinkedHashMap(); + + /** + * Return the name of the provider. + * @return the name + */ + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * Return the parameters. + * @return the parameters + */ + public Map getParameters() { + return this.parameters; + } + + @Override + public String toString() { + return "ValueProvider{" + "name='" + this.name + ", parameters=" + this.parameters + + '}'; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/package-info.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/package-info.java new file mode 100644 index 000000000..e25ef89eb --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Spring Boot configuration meta-data parser. + */ +package org.springframework.boot.configurationmetadata; diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index 79efacdd4..ba51658a0 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -15,6 +15,7 @@ import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.TextDocumentSyncKind; import org.springframework.ide.vscode.boot.java.completions.BootJavaCompletionEngine; import org.springframework.ide.vscode.boot.java.completions.BootJavaReconcileEngine; +import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.gradle.GradleCore; import org.springframework.ide.vscode.commons.gradle.GradleProjectFinderStrategy; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; @@ -46,7 +47,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { private final JavaProjectFinder javaProjectFinder; private final VscodeCompletionEngineAdapter completionEngine; - public BootJavaLanguageServer(JavaProjectFinder javaProjectFinder) { + public BootJavaLanguageServer(JavaProjectFinder javaProjectFinder, SpringPropertyIndexProvider indexProvider) { this.javaProjectFinder = javaProjectFinder; SimpleTextDocumentService documents = getTextDocumentService(); @@ -56,7 +57,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { validateWith(doc, reconcileEngine); }); - ICompletionEngine bootCompletionEngine = new BootJavaCompletionEngine(javaProjectFinder); + ICompletionEngine bootCompletionEngine = new BootJavaCompletionEngine(javaProjectFinder, indexProvider); completionEngine = new VscodeCompletionEngineAdapter(this, bootCompletionEngine); completionEngine.setMaxCompletionsNumber(100); documents.onCompletion(completionEngine::getCompletions); diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/Main.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/Main.java index d8b545050..a1a5664de 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/Main.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/Main.java @@ -12,6 +12,7 @@ package org.springframework.ide.vscode.boot.java; import java.io.IOException; +import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.languageserver.LaunguageServerApp; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; @@ -26,7 +27,8 @@ public class Main { public static void main(String[] args) throws IOException, InterruptedException { LaunguageServerApp.start(() -> { JavaProjectFinder javaProjectFinder = BootJavaLanguageServer.DEFAULT_PROJECT_FINDER; - SimpleLanguageServer server = new BootJavaLanguageServer(javaProjectFinder); + DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder); + SimpleLanguageServer server = new BootJavaLanguageServer(javaProjectFinder, indexProvider); return server; }); } diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaCompletionEngine.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaCompletionEngine.java index 8d20b070c..6411c5e39 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaCompletionEngine.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/BootJavaCompletionEngine.java @@ -25,6 +25,7 @@ import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.NodeFinder; +import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine; @@ -38,11 +39,14 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; public class BootJavaCompletionEngine implements ICompletionEngine { private static final String SPRING_SCOPE = "org.springframework.context.annotation.Scope"; + private static final String SPRING_VALUE = "org.springframework.beans.factory.annotation.Value"; private JavaProjectFinder projectFinder; + private SpringPropertyIndexProvider indexProvider; - public BootJavaCompletionEngine(JavaProjectFinder projectFinder) { + public BootJavaCompletionEngine(JavaProjectFinder projectFinder, SpringPropertyIndexProvider indexProvider) { this.projectFinder = projectFinder; + this.indexProvider = indexProvider; } @Override @@ -105,6 +109,9 @@ public class BootJavaCompletionEngine implements ICompletionEngine { if (type.getQualifiedName().equals(SPRING_SCOPE)) { new ScopeCompletionProcessor().collectCompletionsForScopeAnnotation(node, annotation, type, completions, offset, doc); } + else if (type.getQualifiedName().equals(SPRING_VALUE)) { + new ValueCompletionProcessor(indexProvider.getIndex(doc)).collectCompletionsForValueAnnotation(node, annotation, type, completions, offset, doc); + } } private String[] getClasspathEntries(IDocument doc) throws Exception { diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java new file mode 100644 index 000000000..4c9d018b5 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java @@ -0,0 +1,158 @@ +/******************************************************************************* + * 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.completions; + +import static org.springframework.ide.vscode.commons.util.StringUtil.camelCaseToHyphens; + +import java.util.List; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.StringLiteral; +import org.springframework.ide.vscode.boot.metadata.PropertyInfo; +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; +import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; +import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +/** + * @author Martin Lippert + */ +public class ValueCompletionProcessor { + + private FuzzyMap index; + + public ValueCompletionProcessor(FuzzyMap index) { + this.index = index; + } + + public void collectCompletionsForValueAnnotation(ASTNode node, Annotation annotation, ITypeBinding type, + List completions, int offset, IDocument doc) { + + try { + // case: @Value(<*>) + if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) { + List> matches = findMatches(""); + + for (Match match : matches) { + + DocumentEdits edits = new DocumentEdits(doc); + edits.replace(offset, offset, "\"${" + match.data.getId() + "}\""); + + ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null); + completions.add(proposal); + } + } + // case: @Value(prefix<*>) + else if (node instanceof SimpleName && node.getParent() instanceof Annotation) { + String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition()); + + int startOffset = node.getStartPosition(); + int endOffset = node.getStartPosition() + node.getLength(); + + String proposalPrefix = "\""; + String proposalPostfix = "\""; + + List> matches = findMatches(prefix); + + for (Match match : matches) { + + DocumentEdits edits = new DocumentEdits(doc); + edits.replace(startOffset, endOffset, proposalPrefix + "${" + match.data.getId() + "}" + proposalPostfix); + + ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null); + completions.add(proposal); + } + } + // case: @Value("prefix<*>") + else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) { + if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { + + String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, offset - (node.getStartPosition() + 1)), offset - (node.getStartPosition() + 1)); + + int startOffset = offset - prefix.length(); + int endOffset = offset; + + + String prePrefix = doc.get(node.getStartPosition() + 1, offset - prefix.length() - node.getStartPosition() - 1); + + String preCompletion; + if (prePrefix.endsWith("${")) { + preCompletion = ""; + } + else if (prePrefix.endsWith("$")) { + preCompletion = "{"; + } + else { + preCompletion = "${"; + } + + String fullNodeContent = doc.get(node.getStartPosition(), node.getLength()); + String postCompletion = isClosingBracketMissing(fullNodeContent + preCompletion) ? "}" : ""; + + List> matches = findMatches(prefix); + + for (Match match : matches) { + + DocumentEdits edits = new DocumentEdits(doc); + edits.replace(startOffset, endOffset, preCompletion + match.data.getId() + postCompletion); + + ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null); + completions.add(proposal); + } + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + } + + private boolean isClosingBracketMissing(String fullNodeContent) { + int bracketOpens = 0; + + for (int i = 0; i < fullNodeContent.length(); i++) { + if (fullNodeContent.charAt(i) == '{') { + bracketOpens++; + } + else if (fullNodeContent.charAt(i) == '}') { + bracketOpens--; + } + } + + return bracketOpens > 0; + } + + public String identifyPropertyPrefix(String nodeContent, int offset) { + String result = nodeContent.substring(0, offset); + + int i = offset - 1; + while (i >= 0) { + char c = nodeContent.charAt(i); + if (c == '}' || c == '{' || c == '$' || c == '#') { + result = result.substring(i + 1, offset); + break; + } + i--; + } + + return result; + } + + private List> findMatches(String prefix) { + List> matches = index.find(camelCaseToHyphens(prefix)); + return matches; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValuePropertyKeyProposal.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValuePropertyKeyProposal.java new file mode 100644 index 000000000..3ecf24c09 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValuePropertyKeyProposal.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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.completions; + +import org.eclipse.lsp4j.CompletionItemKind; +import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; +import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; +import org.springframework.ide.vscode.commons.util.Renderable; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +/** + * @author Martin Lippert + */ +public class ValuePropertyKeyProposal implements ICompletionProposal { + + private DocumentEdits edits; + private String label; + private String detail; + private Renderable documentation; + + public ValuePropertyKeyProposal(DocumentEdits edits, String label, String detail, Renderable documentation) { + this.edits = edits; + this.label = label; + this.detail = detail; + this.documentation = documentation; + } + + @Override + public ICompletionProposal deemphasize() { + return null; + } + + @Override + public String getLabel() { + return this.label; + } + + @Override + public CompletionItemKind getKind() { + return CompletionItemKind.Property; + } + + @Override + public DocumentEdits getTextEdit() { + return this.edits; + } + + @Override + public String getDetail() { + return this.detail; + } + + @Override + public Renderable getDocumentation() { + return this.documentation; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/CachingValueProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/CachingValueProvider.java new file mode 100644 index 000000000..2b009422b --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/CachingValueProvider.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.metadata; + +import java.time.Duration; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.FuzzyMatcher; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader.InvalidCacheLoadException; + +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuples; + +/** + * A abstract {@link ValueProviderStrategy} that is mean to help speedup successive invocations of + * content assist with a similar 'query' string. + *

+ * This implementation is meant to be used for providers that use potentially lenghty/expensive searches + * to determine hints. Since content assist hints are requested by Eclipse CA framework directly on + * the UI thread, they can not simply perform a lengthy search and block UI thread until it finished. + *

+ * This implementation therefore does the following: + *

    + *
  • Limit the duration of time spent on the UI thread. + *
  • Cache results of searches for a limited time. + *
  • Speedup queries for successive queries by using the already cached result of a similar (prefix) query. + *
  • When the time spent on UI thread waiting for a current search exceeds the allowed time limit, + * return immediately with whatever results have been found so far. + *
+ * + * TODO: rather than an abstract class this should really be 'Wrapper' class that delegates to another + * {@link ValueProviderStrategy} and adds a cache in front of it. + * + * @author Kris De Volder + */ +public abstract class CachingValueProvider implements ValueProviderStrategy { + + private static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1000); + + /** + * Content assist is called inside UI thread and so doing something lenghty things + * like a JavaSearch will block the UI thread completely freezing the UI. So, we + * only return as many results as can be obtained within this hard TIMEOUT limit. + */ + public static Duration TIMEOUT = DEFAULT_TIMEOUT; + + /** + * The maximum number of results returned for a single request. Used to limit the + * values that are cached per entry. + */ + private int MAX_RESULTS = 500; + + private Cache, CacheEntry> cache = createCache(); + + private class CacheEntry { + boolean isComplete = false; + int count = 0; + Flux values; + + public CacheEntry(String query, Flux producer) { + values = producer + .take(MAX_RESULTS) + .cache(MAX_RESULTS); + values.subscribe(); // create infinite demand so that we actually force cache entries to be fetched upto the max. + } + + @Override + public String toString() { + return "CacheEntry [isComplete=" + isComplete + ", count=" + count + "]"; + } + + } + + @Override + public final Flux getValues(IJavaProject javaProject, String query) { + Tuple2 key = key(javaProject, query); + CacheEntry cached = null; + try { + cached = cache.get(key, () -> new CacheEntry(query, getValuesIncremental(javaProject, query))); + } catch (ExecutionException e) { + Log.log(e); + } + return cached.values; + } + + /** + * Tries to use an already cached, complete result for a query that is a prefix of the current query to speed things up. + *

+ * Falls back on doing a full-blown search if there's no usable 'prefix-query' in the cache. + */ + private Flux getValuesIncremental(IJavaProject javaProject, String query) { +// debug("trying to solve "+query+" incrementally"); + String subquery = query; + while (subquery.length()>=1) { + subquery = subquery.substring(0, subquery.length()-1); + CacheEntry cached = null; + try { + cached = cache.get(key(javaProject, subquery), () -> null); + } catch (ExecutionException | InvalidCacheLoadException e) { +// Log.log(e); + } + if (cached!=null) { + System.out.println("cached "+subquery+": "+cached); + if (cached.isComplete) { + return cached.values +// .doOnNext((hint) -> debug("filter["+query+"]: "+hint.getValue())) + .filter((hint) -> 0!=FuzzyMatcher.matchScore(query, hint.getValue().toString())); + } else { +// debug("subquery "+subquery+" cached but is incomplete"); + } + } + } +// debug("full search for: "+query); + return getValuesAsync(javaProject, query); + } + + protected abstract Flux getValuesAsync(IJavaProject javaProject, String query); + + private Tuple2 key(IJavaProject javaProject, String query) { + return Tuples.of(javaProject==null?null:javaProject.getElementName(), query); + } + + protected Cache createCache() { + return CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).expireAfterAccess(1, TimeUnit.MINUTES).build(); + } + + public static void restoreDefaults() { + TIMEOUT = DEFAULT_TIMEOUT; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java new file mode 100644 index 000000000..2618837bc --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2016-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.metadata; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; +import org.springframework.ide.vscode.commons.java.Flags; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.ide.vscode.commons.util.StringUtil; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import reactor.core.publisher.Flux; + +/** + * Provides the algorithm for 'class-reference' valueProvider. + *

+ * See: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc + * + * @author Kris De Volder + * @author Alex Boyko + */ +public class ClassReferenceProvider extends CachingValueProvider { + + /** + * Default value for the 'concrete' parameter. + */ + private static final boolean DEFAULT_CONCRETE = true; + + private static final ClassReferenceProvider UNTARGETTED_INSTANCE = new ClassReferenceProvider(null, DEFAULT_CONCRETE); + + public static final Function, ValueProviderStrategy> FACTORY = applyOn( + 1, TimeUnit.MINUTES, + (params) -> { + String target = getTarget(params); + Boolean concrete = getConcrete(params); + if (target!=null || concrete!=null) { + if (concrete==null) { + concrete = DEFAULT_CONCRETE; + } + return new ClassReferenceProvider(target, concrete); + } + return UNTARGETTED_INSTANCE; + } + ); + + private static Function applyOn(long duration, TimeUnit unit, Function func) { + Cache cache = CacheBuilder.newBuilder().expireAfterAccess(duration, unit).expireAfterWrite(duration, unit).build(); + return (k) -> { + try { + return cache.get(k, () -> func.apply(k)); + } catch (ExecutionException e) { + Log.log(e); + return null; + } + }; + } + + private static String getTarget(Map params) { + if (params!=null) { + Object obj = params.get("target"); + if (obj instanceof String) { + String target = (String) obj; + if (StringUtil.hasText(target)) { + return target; + } + } + } + return null; + } + + private static boolean isAbstract(IType type) { + try { + return type.isInterface() || Flags.isAbstract(type.getFlags()); + } catch (Exception e) { + Log.log(e); + return false; + } + } + + private static Boolean getConcrete(Map params) { + try { + if (params!=null) { + Object obj = params.get("concrete"); + if (obj instanceof String) { + String concrete = (String) obj; + return Boolean.valueOf(concrete); + } else if (obj instanceof Boolean) { + return (Boolean) obj; + } + } + } catch (Exception e) { + Log.log(e); + } + return null; + } + + /** + * Optional, fully qualified name of the 'target' type. Suggested hints should be a subtype of this type. + */ + private String target; + + /** + * Optional parameter, whether only concrete types should be suggested. Default value is true. + */ + private boolean concrete; + + private ClassReferenceProvider(String target, boolean concrete) { + this.target = target; + this.concrete = concrete; + } + + @Override + protected Flux getValuesAsync(IJavaProject javaProject, String query) { + IType targetType = target == null || target.isEmpty() ? javaProject.getClasspath().findType("java.lang.Object") : javaProject.getClasspath().findType(target); + if (targetType == null) { + return Flux.empty(); + } + Set allSubclasses = javaProject.getClasspath() + .allSubtypesOf(targetType) + .filter(t -> Flags.isPublic(t.getFlags()) && !concrete || !isAbstract(t)) + .collect(Collectors.toSet()) + .block(); + if (allSubclasses.isEmpty()) { + return Flux.empty(); + } else { + return javaProject.getClasspath() + .fuzzySearchTypes(query, type -> allSubclasses.contains(type)) + .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) + .flatMap(l -> Flux.fromIterable(l)) + .map(t -> StsValueHint.create(t.getT1())); + } + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java new file mode 100644 index 000000000..0e379d645 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2016, 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.metadata; + +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.ProgressService; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexProvider { + + private static final FuzzyMap EMPTY_INDEX = new SpringPropertyIndex(null, null); + + private JavaProjectFinder javaProjectFinder; + private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault()); + + private ProgressService progressService = (id, msg) -> { /*ignore*/ }; + + public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) { + this.javaProjectFinder = javaProjectFinder; + } + + @Override + public FuzzyMap getIndex(IDocument doc) { + IJavaProject jp = javaProjectFinder.find(doc); + if (jp!=null) { + return indexManager.get(jp, progressService); + } + return EMPTY_INDEX; + } + + public void setProgressService(ProgressService progressService) { + this.progressService = progressService; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java new file mode 100644 index 000000000..cd19604c6 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2015 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.metadata; + +import static org.springframework.ide.vscode.commons.util.StringUtil.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; +import org.springframework.ide.vscode.commons.util.StringUtil; + +/** + * An index navigator allows selecting subset of a property index as if + * navigating the index by selecting on a property + * + * @author Kris De Volder + */ +public class IndexNavigator { + + //Possible opitmization: we could cache prefix match candidate and extended match candidate + // since it is assumed that the index is immutable for the lifetime of + // the index navigator. + + private static final char NAV_CHAR = '.'; + + /** + * Property access in this navigator are interpreted relative + * to this prefix + */ + private String prefix = null; + private FuzzyMap index; + + private IndexNavigator(FuzzyMap index) { + this.index = index; + } + + private IndexNavigator(FuzzyMap index, String prefix) { + this.index = index; + this.prefix = prefix; + } + + public static IndexNavigator with(FuzzyMap index) { + return new IndexNavigator(index); + } + + public IndexNavigator selectSubProperty(String name) { + return new IndexNavigator(index, join(prefix, name)); + } + + protected String join(String prefix, String postfix) { + if (!hasText(prefix)) { + return postfix; + } else { + return prefix + NAV_CHAR + postfix; + } + } + + /** + * @return property info that is an exact match with the current prefix or + * null if there's no exact match + */ + public PropertyInfo getExactMatch() { + if (prefix!=null) { + PropertyInfo candidate = index.findLongestCommonPrefixEntry(prefix); + if (candidate.getId().equals(prefix)) { + return candidate; + } + } + return null; + } + + /** + * Get a property that has the current prefix as a 'true' prefix. A true prefix + * is a String that has the current prefix as a prefix and continues onward with + * a navigation operation. + */ + public PropertyInfo getExtensionCandidate() { + //If current prefix is null then all entries in the index are candidates since + // the index is at the 'root' of the tree and we don't need a '.' to navigate + String extendedPrefix = prefix==null?"":prefix + NAV_CHAR; + PropertyInfo candidate = index.findLongestCommonPrefixEntry(extendedPrefix); + if (candidate.getId().startsWith(extendedPrefix)) { + return candidate; + } + return null; + } + + public String getPrefix() { + return prefix; + } + + public List> findMatching(String query) { + if (!StringUtil.hasText(prefix)) { + return index.find(query); + } else { + String dottedPrefix = prefix +"."; + List> candidates = index.find(dottedPrefix + query); + if (!candidates.isEmpty()) { + //TODO: we can do better than this using treemap to narrow based on + // prefix + List> matches = new ArrayList>(candidates.size()); + for (Match match : candidates) { + if (match.data.getId().startsWith(dottedPrefix)){ + matches.add(match); + } + } + return matches; + } + } + return Collections.emptyList(); + } + + @Override + public String toString() { + return "IndexNavigator("+prefix+")"; + } + + public boolean isEmpty() { + return getExactMatch()==null && getExtensionCandidate()==null; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java new file mode 100644 index 000000000..2f9886766 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2016-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.metadata; + +import java.util.Map; +import java.util.function.Function; + +import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; +import org.springframework.ide.vscode.commons.java.IJavaProject; + +import reactor.core.publisher.Flux; +import reactor.util.function.Tuples; + +/** + * Provides the algorithm for 'logger-name' valueProvider. + *

+ * See: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc + * + * @author Kris De Volder + * @author Alex Boyko + */ +public class LoggerNameProvider extends CachingValueProvider { + + private static final ValueProviderStrategy INSTANCE = new LoggerNameProvider(); + public static final Function, ValueProviderStrategy> FACTORY = (params) -> INSTANCE; + + @Override + protected Flux getValuesAsync(IJavaProject javaProject, String query) { + return Flux.concat( + javaProject.getClasspath() + .fuzzySearchPackages(query) + .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())), + javaProject.getClasspath() + .fuzzySearchTypes(query, null) + .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())) + ) + .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) + .flatMap(l -> Flux.fromIterable(l)) + .map(t -> t.getT1()); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java new file mode 100644 index 000000000..328e1f3e1 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright (c) 2015 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.metadata; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.LinkedHashMap; + +import org.springframework.ide.eclipse.org.json.JSONArray; +import org.springframework.ide.eclipse.org.json.JSONObject; + +/** + * Helper class to manipulate data in a file presumed to contain + * spring-boot configuration data. + * + * @author Kris De Volder + * @author Alex Boyko + */ +public class MetadataManipulator { + + private abstract class Content { + public abstract String toString(); + public abstract void addProperty(JSONObject jsonObject) throws Exception; + } + + /** + * Content was parse as JSONObject. + */ + private class ParsedContent extends Content { + + private JSONObject object; + + public ParsedContent(JSONObject o) { + this.object = o; + } + + public String toString() { + return object.toString(indentFactor); + } + + @Override + public void addProperty(JSONObject propertyData) throws Exception { + JSONArray properties = object.getJSONArray("properties"); + properties.put(properties.length(), propertyData); + } + } + + /** + * Content that is 'unparsed' and just a bunch of text. + * Used only as a fallback when data in file can't + * be parsed. + *

+ * This content is manipulated by string manipulation. + * It is less reliable, but can be done even if the + * file data is not parseable. + */ + private class RawContent extends Content { + + private StringBuilder doc; + + public RawContent(String content) { + this.doc = new StringBuilder(content); + } + + @Override + public String toString() { + return doc.toString(); + } + + @Override + public void addProperty(JSONObject propertyData) throws Exception { + int insertAt = findLast(']'); + if (insertAt<0) { + //although we're not looking for much, we didn't find it! + //Funky file contents. Let's just insert something at end of file in a 'best effort' spirit. + insertAt = doc.length(); + } + insert(insertAt, "\n"); + + insert(insertAt, propertyData.toString(indentFactor)); + + int insertComma = findInsertCommaPos(insertAt); + if (insertComma>=0) { + insert(insertComma, ","); + } + } + + /** + * Maybe we need to add a comma in front of the new entry. This + * method finds if/where to stick this comma. + * @throws Exception + */ + private int findInsertCommaPos(int pos) throws Exception { + pos--; + while (pos>=0 && Character.isWhitespace(doc.charAt(pos))) { + pos--; + } + if (pos>=0) { + char c = doc.charAt(pos); + if (c == '}') { + //Add a comma after a '}' + return pos+1; + } + } + return -1; + } + + private int insert(int insertAt, String str) throws Exception { + if (insertAt < doc.length()) { + doc.replace(insertAt, insertAt, str); + } else { + doc.append(str); + } + return insertAt + str.length(); + } + + private int findLast(char toFind) throws Exception { + int pos = doc.length()-1; + while (pos>=0 && doc.charAt(pos)!=toFind) { + pos--; + } + //We got here either because + // - we found char at pos or.. + // - we reached position *before* start of file (i.e. -1) + return pos; + } + + } + + public interface ContentStore { + String getContents() throws Exception; + void setContents(String content) throws Exception; + } + + private static final String INITIAL_CONTENT = + "{\"properties\": [\n" + + "]}"; + + private static final String ENCODING = "UTF8"; + private ContentStore contentStore; + private Content fContent; + private int indentFactor = 2; + + public MetadataManipulator(ContentStore contentStore) { + this.contentStore = contentStore; + } + + public MetadataManipulator(final File file) { + this(new ContentStore() { + + @Override + public String getContents() throws Exception { + return new String(Files.readAllBytes(Paths.get(file.toURI())), ENCODING); + } + + @Override + public void setContents(String content) throws Exception { + Files.write(Paths.get(file.toURI()), content.getBytes(ENCODING)); + } + + }); + } + + private Content getContent() throws Exception { + if (fContent==null) { + fContent = readContent(); + } + return fContent; + } + + private Content readContent() throws Exception { + String content = contentStore.getContents(); + if (content.trim().isEmpty()) { + JSONObject o = initialContent(); + return new ParsedContent(o); + } else { + try { + return new ParsedContent(new JSONObject(content)); + } catch (Exception e) { + //couldn't parse? + return new RawContent(content); + } + } + } + + public void addDefaultInfo(String propertyName) throws Exception { + getContent().addProperty(createDefaultData(propertyName)); + } + + private JSONObject createDefaultData(String propertyName) throws Exception { + JSONObject obj = new JSONObject(new LinkedHashMap()); + obj.put("name", propertyName); + obj.put("type", String.class.getName()); + obj.put("description", "A description for '"+propertyName+"'"); + return obj; + } + + /** + * Generate the initial content (must be generated rather than being a constant to respect newline conventions + * on user's system. + */ + private JSONObject initialContent() throws Exception { + return new JSONObject(INITIAL_CONTENT); + } + + /** + * After manipulating the data, use this to persist changes back to the file. + */ + public void save() throws Exception { + contentStore.setContents(getContent().toString()); + } + + /** + * Determines whether the 'reliable' manipulations can be used (which is the case + * only if the data in the file is valid json). + */ + public boolean isReliable() throws Exception { + return getContent() instanceof ParsedContent; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java new file mode 100644 index 000000000..04a03ee07 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2016-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.metadata; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; + +import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; +import org.springframework.ide.vscode.commons.java.IClasspath; + +public class PropertiesLoader { + + private static final String MAIN_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/spring-configuration-metadata.json"; + + public static final String ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON = "META-INF/additional-spring-configuration-metadata.json"; + + /** + * The default classpath location for config metadata loaded when scanning .jar files on the classpath. + */ + public static final String[] JAR_META_DATA_LOCATIONS = { + MAIN_SPRING_CONFIGURATION_METADATA_JSON + //Not scanning 'additional' metadata because it integrated already in the main data. + }; + + /** + * The default classpath location for config metadata loaded when scanning project output folders. + */ + public static final String[] PROJECT_META_DATA_LOCATIONS = { + MAIN_SPRING_CONFIGURATION_METADATA_JSON, + ADDITIONAL_SPRING_CONFIGURATION_METADATA_JSON + }; + + private static final Logger LOG = Logger.getLogger(PropertiesLoader.class.getName()); + + private ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder.create(); + + public ConfigurationMetadataRepository load(IClasspath classPath) { + try { + classPath.getClasspathEntries().forEach(entry -> { + File fileEntry = entry.toFile(); + if (fileEntry.exists()) { + if (fileEntry.isDirectory()) { + loadFromOutputFolder(entry); + } else { + loadFromJar(entry); + } + } + }); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Failed to retrieve classpath", e); + } + ConfigurationMetadataRepository repository = builder.build(); + return repository; + } + + private void loadFromOutputFolder(Path outputFolderPath) { + if (outputFolderPath != null && Files.exists(outputFolderPath)) { + Arrays.stream(PROJECT_META_DATA_LOCATIONS).forEach(mdLoc -> { + loadFromJsonFile(outputFolderPath.resolve(mdLoc)); + }); + } + } + + private void loadFromJsonFile(Path mdf) { + if (Files.exists(mdf)) { + InputStream is = null; + try { + is = Files.newInputStream(mdf); + loadFromInputStream(mdf, is); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Error loading file '" + mdf + "'", e); + } finally { + if (is!=null) { + try { + is.close(); + } catch (IOException e) { + //ignore + } + } + } + } + } + + private void loadFromJar(Path f) { + JarFile jarFile = null; + try { + jarFile = new JarFile(f.toFile()); + //jarDump(jarFile); + for (String loc : JAR_META_DATA_LOCATIONS) { + ZipEntry e = jarFile.getEntry(loc); + if (e!=null) { + loadFrom(jarFile, e); + } + } + } catch (Throwable e) { + LOG.log(Level.SEVERE, "Error loading JAR file", e); + } finally { + if (jarFile!=null) { + try { + jarFile.close(); + } catch (IOException e) { + } + } + } + } + + + private void loadFrom(JarFile jarFile, ZipEntry ze) { + InputStream is = null; + try { + is = jarFile.getInputStream(ze); + loadFromInputStream(jarFile.getName()+"["+ze.getName()+"]", is); + } catch (Throwable e) { + LOG.log(Level.SEVERE, "Error loading JAR file", e); + } finally { + if (is!=null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + } + + private void loadFromInputStream(Object origin, InputStream is) throws IOException { + builder.withJsonResource(origin, is); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java new file mode 100644 index 000000000..4b3342f03 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright (c) 2014-2016 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.metadata; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource; +import org.springframework.boot.configurationmetadata.Deprecation; +import org.springframework.boot.configurationmetadata.ValueHint; +import org.springframework.boot.configurationmetadata.ValueProvider; +import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +/** + * Information about a spring property, basically, this is the same as + * + * {@link ConfigurationMetadataProperty} but augmented with information + * about {@link ConfigurationMetadataSource}s that declare the property. + * + * @author Kris De Volder + */ +public class PropertyInfo { + + /** + * Identifies a 'Source'. This is essentially the sames as {@link ConfigurationMetadataSource}. + * We could use {@link ConfigurationMetadataSource} directly, but this only contains + * the info that we actually use so takes less memory. + */ + public static class PropertySource { + private final String sourceType; + private final String sourceMethod; + public PropertySource(ConfigurationMetadataSource source) { + String st = source.getSourceType(); + this.sourceType = st!=null?st:source.getType(); + this.sourceMethod = source.getSourceMethod(); + } + @Override + public String toString() { + return sourceType+"::"+sourceMethod; + } + public String getSourceType() { + return sourceType; + } + public String getSourceMethod() { + return sourceMethod; + } + } + + final private String id; + private String type; + final private String name; + final private Object defaultValue; + final private String description; + private List sources; + private Deprecation deprecation; + private ImmutableList valueHints; + private ImmutableList keyHints; + private ValueProviderStrategy valueProvider; + private ValueProviderStrategy keyProvider; + + public PropertyInfo(String id, String type, String name, + Object defaultValue, String description, + Deprecation deprecation, + List valueHints, + List keyHints, + ValueProviderStrategy valueProvider, + ValueProviderStrategy keyProvider, + List sources) { + super(); + this.id = id; + this.type = type; + this.name = name; + this.defaultValue = defaultValue; + this.description = description; + this.deprecation = deprecation; + this.valueHints = valueHints==null?null:ImmutableList.copyOf(valueHints); + this.keyHints = keyHints==null?null:ImmutableList.copyOf(keyHints); + this.valueProvider = valueProvider; + this.keyProvider = keyProvider; + this.sources = sources; + } + public PropertyInfo(ValueProviderRegistry valueProviders, ConfigurationMetadataProperty prop) { + this( + prop.getId(), + prop.getType(), + prop.getName(), + prop.getDefaultValue(), + prop.getDescription(), + prop.getDeprecation(), + prop.getHints().getValueHints(), + prop.getHints().getKeyHints(), + valueProviders.resolve(prop.getHints().getValueProviders()), + valueProviders.resolve(prop.getHints().getKeyProviders()), + null + ); + for (ValueProvider h : prop.getHints().getValueProviders()) { + if (h.getName().equals("handle-as")) { + handleAs(h.getParameters().get("target")); + } + } + } + private void handleAs(Object targetObject) { +// debug("handle-as "+this.getId()+" -> "+targetObject); + if (targetObject instanceof String) { + this.type = (String)targetObject; + } + } + public String getId() { + return id; + } + public String getType() { + return type; + } + public String getName() { + return name; + } + public Object getDefaultValue() { + return defaultValue; + } + public String getDescription() { + return description; + } + + public List getSources() { + if (sources!=null) { + return sources; + } + return Collections.emptyList(); + } + + @Override + public String toString() { + return "PropertyInfo("+getId()+")"; + } + public void addSource(ConfigurationMetadataSource source) { + if (sources==null) { + sources = new ArrayList(); + } + sources.add(new PropertySource(source)); + } + + public PropertyInfo withId(String alias) { + if (alias.equals(id)) { + return this; + } + return new PropertyInfo(alias, type, name, defaultValue, description, deprecation, valueHints, keyHints, valueProvider, keyProvider, sources); + } + + public void setDeprecation(Deprecation d) { + this.deprecation = d; + } + + public boolean isDeprecated() { + return deprecation!=null; + } + + public String getDeprecationReason() { + return deprecation == null ? null : deprecation.getReason(); + } + + public String getDeprecationReplacement() { + return deprecation == null ? null : deprecation.getReplacement(); + } + + public void addValueHints(List hints) { + Builder builder = ImmutableList.builder(); + builder.addAll(valueHints); + builder.addAll(hints); + valueHints = builder.build(); + } + public void addKeyHints(List hints) { + Builder builder = ImmutableList.builder(); + builder.addAll(keyHints); + builder.addAll(hints); + keyHints = builder.build(); + } +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java new file mode 100644 index 000000000..beb3891ee --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2016-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.metadata; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; +import org.springframework.ide.vscode.commons.java.IJavaProject; + +import com.google.common.collect.ImmutableList; + +import reactor.core.publisher.Flux; + +/** + * @author Kris De Volder + */ +public class ResourceHintProvider implements ValueProviderStrategy { + + private static String[] CLASSPATH_PREFIXES = { + "classpath:", + "classpath*:" + }; + + private static final String[] URL_PREFIXES = new String[] { + "classpath:", + "classpath*:", + "file:", + "http://", + "https://" + }; + + @Override + public Flux getValues(IJavaProject javaProject, String query) { + for (String prefix : CLASSPATH_PREFIXES) { + if (query.startsWith(prefix)) { + return classpathHints + .getValues(javaProject, query.substring(prefix.length())) + .map((hint) -> hint.prefixWith(prefix)); + } + } + return Flux.fromIterable(urlPrefixHints); + } + + final private ImmutableList urlPrefixHints = ImmutableList.copyOf( + Arrays.stream(URL_PREFIXES) + .map(StsValueHint::create) + .collect(Collectors.toList()) + ); + + private ClasspathHints classpathHints = new ClasspathHints(); + + private static class ClasspathHints extends CachingValueProvider { + @Override + protected Flux getValuesAsync(IJavaProject javaProject, String query) { + return Flux.fromStream(javaProject.getClasspath().getClasspathResources().distinct().map(StsValueHint::create)); + } + } + + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java new file mode 100644 index 000000000..d88075998 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2014, 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.metadata; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.boot.metadata.util.Listener; +import org.springframework.ide.vscode.boot.metadata.util.ListenerManager; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.ProgressService; + +/** + * Support for Reconciling, Content Assist and Hover Text in spring properties + * file all make use of a per-project index of spring properties metadata extracted + * from project's classpath. This Index manager is responsible for keeping at most + * one index per-project and to keep the index up-to-date. + * + * @author Kris De Volder + */ +public class SpringPropertiesIndexManager extends ListenerManager> { + + private Map indexes = null; + private final ValueProviderRegistry valueProviders; + private static int progressIdCt = 0; + + public SpringPropertiesIndexManager(ValueProviderRegistry valueProviders) { + this.valueProviders = valueProviders; + } + + public synchronized FuzzyMap get(IJavaProject project, ProgressService progressService) { + if (indexes==null) { + indexes = new HashMap<>(); + } + SpringPropertyIndex index = indexes.get(project); + if (index==null) { + String progressId = getProgressId(); + if (progressService != null) { + progressService.progressEvent(progressId, "Indexing Spring Boot Properties..."); + } + + index = new SpringPropertyIndex(valueProviders, project.getClasspath()); + indexes.put(project, index); + + if (progressService != null) { + progressService.progressEvent(progressId, null); + } + } + return index; + } + + public synchronized void clear() { + if (indexes!=null) { + indexes.clear(); + for (Listener l : getListeners()) { + l.changed(this); + } + } + } + + private static synchronized String getProgressId() { + return DefaultSpringPropertyIndexProvider.class.getName()+ (progressIdCt++); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java new file mode 100644 index 000000000..b21ed7bef --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2015 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.metadata; + +import java.util.Collection; +import java.util.List; + +import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource; +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.util.StringUtil; + +public class SpringPropertyIndex extends FuzzyMap { + + private ValueProviderRegistry valueProviders; + + public SpringPropertyIndex(ValueProviderRegistry valueProviders, IClasspath projectPath) { + this.valueProviders = valueProviders; + if (projectPath!=null) { +// try { + PropertiesLoader loader = new PropertiesLoader(); + ConfigurationMetadataRepository metadata = loader.load(projectPath); + //^^^ Should be done in bg? It seems fast enough for now. + + Collection allEntries = metadata.getAllProperties().values(); + for (ConfigurationMetadataProperty item : allEntries) { + add(new PropertyInfo(valueProviders, item)); + } + + for (ConfigurationMetadataGroup group : metadata.getAllGroups().values()) { + for (ConfigurationMetadataSource source : group.getSources().values()) { + for (ConfigurationMetadataProperty prop : source.getProperties().values()) { + PropertyInfo info = get(prop.getId()); + info.addSource(source); + } + } + } + + // System.out.println(">>> spring properties metadata loaded "+this.size()+" items==="); + // dumpAsTestData(); + // System.out.println(">>> spring properties metadata loaded "+this.size()+" items==="); +// } catch (Exception e) { +// LOG.log +// } + } + } + + public void add(ConfigurationMetadataProperty propertyInfo) { + add(new PropertyInfo(valueProviders, propertyInfo)); + } + + /** + * Dumps out 'test data' based on the current contents of the index. This is not meant to be + * used in 'production' code. The idea is to call this method during development to dump a + * 'snapshot' of the index onto System.out. The data is printed in a forma so that it can be easily + * pasted/used into JUNit testing code. + */ + public void dumpAsTestData() { + List> allData = this.find(""); + for (Match match : allData) { + PropertyInfo d = match.data; + System.out.println("data(" + +dumpString(d.getId())+", " + +dumpString(d.getType())+", " + +dumpString(d.getDefaultValue())+", " + +dumpString(d.getDescription()) +");" + ); +// for (PropertySource source : d.getSources()) { +// String st = source.getSourceType(); +// String sm = source.getSourceMethod(); +// if (sm!=null) { +// System.out.println(d.getId() +" from: "+st+"::"+sm); +// } +// } + } + } + + private String dumpString(Object v) { + if (v==null) { + return "null"; + } + return dumpString(""+v); + } + + private String dumpString(String s) { + if (s==null) { + return "null"; + } else { + StringBuilder buf = new StringBuilder("\""); + for (char c : s.toCharArray()) { + switch (c) { + case '\r': + buf.append("\\r"); + break; + case '\n': + buf.append("\\n"); + break; + case '\\': + buf.append("\\\\"); + break; + case '\"': + buf.append("\\\""); + break; + default: + buf.append(c); + break; + } + } + buf.append("\""); + return buf.toString(); + } + } + + @Override + protected String getKey(PropertyInfo entry) { + return entry.getId(); + } + + /** + * Find the longest known property that is a prefix of the given name. Here prefix does not mean + * 'string prefix' but a prefix in the sense of treating '.' as a kind of separators. So + * 'prefix' is not allowed to end in the middle of a 'segment'. + */ + public static PropertyInfo findLongestValidProperty(FuzzyMap index, String name) { + int bracketPos = name.indexOf('['); + int endPos = bracketPos>=0?bracketPos:name.length(); + PropertyInfo prop = null; + String prefix = null; + while (endPos>0 && prop==null) { + prefix = name.substring(0, endPos); + String canonicalPrefix = StringUtil.camelCaseToHyphens(prefix); + prop = index.get(canonicalPrefix); + if (prop==null) { + endPos = name.lastIndexOf('.', endPos-1); + } + } + if (prop!=null) { + //We should meet caller's expectation that matched properties returned by this method + // match the names exactly even if we found them using relaxed name matching. + return prop.withId(prefix); + } + return null; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java new file mode 100644 index 000000000..41f750fb0 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2015 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.metadata; + +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.commons.util.text.IDocument; + + +@FunctionalInterface +public interface SpringPropertyIndexProvider { + FuzzyMap getIndex(IDocument doc); +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ValueProviderRegistry.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ValueProviderRegistry.java new file mode 100644 index 000000000..c7032b3de --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ValueProviderRegistry.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.metadata; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import org.springframework.boot.configurationmetadata.ValueProvider; +import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.CollectionUtil; + +import reactor.core.publisher.Flux; + +/** + * An instance of this class serves as a 'registry' that associates known + * {@link ValueProvider} ids to strategy objects used in the computation of completions + * for properties to which the provider is attached. + * + * @author Kris De Volder + */ +public class ValueProviderRegistry { + + private static ValueProviderRegistry DEFAULT; + + /** + * Creates a default {@link ValueProviderRegistry} which is initialized with all the known + * providers. (This is the one production code should use, test code might make use + * something else for mocking purposes). + */ + public synchronized static ValueProviderRegistry getDefault() { + if (DEFAULT==null) { + DEFAULT = new ValueProviderRegistry(); + DEFAULT.initializeDefaults(DEFAULT); + } + return DEFAULT; + } + + protected void initializeDefaults(ValueProviderRegistry r) { + def("logger-name", LoggerNameProvider.FACTORY); + def("class-reference", ClassReferenceProvider.FACTORY); + } + + private Map, ValueProviderStrategy>> registry = new HashMap<>(); + + public interface ValueProviderStrategy { + Flux getValues(IJavaProject javaProject, String query); + + default Collection getValuesNow(IJavaProject javaProject, String query) { + return this.getValues(javaProject, query) + .take(CachingValueProvider.TIMEOUT) + .collectList() + .block(); + } + } + + /** + * Defines a value provider by binding its id to a strategy. + */ + public void def(String id, Function, ValueProviderStrategy> algo) { + registry.put(id, algo); + } + + /** + * Resolve a list of {@link ValueProvider}s to a {@link ValueProviderStrategy}. + *

+ * Essentially this finds the first provider from the list which has a known name + * and uses that to iinstantiate a ValueProviderStrategy. Spring boot assumes that + * a list is provided to allow new providers to be defined that override older ones + * and these are added at the top of the list. Thus an older IDE can continue to + * function using the older provider further down the list whereas newer IDEs will + * use a 'better' one from higher up the list. + */ + public ValueProviderStrategy resolve(List providerDescriptors) { + if (CollectionUtil.hasElements(providerDescriptors)) { + for (ValueProvider descriptor : providerDescriptors) { + Function, ValueProviderStrategy> factory = registry.get(descriptor.getName()); + if (factory!=null) { + Map params = descriptor.getParameters(); + return factory.apply(params); + } + } + } + return null; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java new file mode 100644 index 000000000..33a565d4d --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2016-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.metadata.hints; + +import org.springframework.boot.configurationmetadata.Deprecation; +import org.springframework.boot.configurationmetadata.ValueHint; +import org.springframework.ide.vscode.boot.metadata.util.DeprecationUtil; +import org.springframework.ide.vscode.commons.java.IJavaElement; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.javadoc.IJavadoc; +import org.springframework.ide.vscode.commons.util.Assert; +import org.springframework.ide.vscode.commons.util.Renderable; +import org.springframework.ide.vscode.commons.util.Renderables; +import org.springframework.ide.vscode.commons.util.StringUtil; + +/** + * Sts version of {@link ValueHint} contains similar data, but accomoates + * a html snippet to be computed lazyly for the description. + *

+ * This is meant to support using data pulled from JavaDoc in enums as description. + * This data is a html snippet, whereas the data derived from spring-boot metadata is + * just plain text. + * + * @author Kris De Volder + */ +public class StsValueHint { + + + private final String value; + private final Renderable description; + private final Deprecation deprecation; + + /** + * Create a hint with a textual description. + *

+ * This constructor is private. Use one of the provided + * static 'create' methods instead. + */ + private StsValueHint(String value, Renderable description, Deprecation deprecation) { + this.value = value==null?"null":value.toString(); + Assert.isLegal(!this.value.startsWith("StsValueHint")); + this.description = description; + this.deprecation = deprecation; + } + + /** + * Creates a hint out of an IJavaElement. + */ + public static StsValueHint create(String value, IJavaElement javaElement) { + return new StsValueHint(value, javaDocSnippet(javaElement), DeprecationUtil.extract(javaElement)) { + @Override + public IJavaElement getJavaElement() { + return javaElement; + } + }; + } + + public static StsValueHint create(String value) { + return new StsValueHint(value, Renderables.NO_DESCRIPTION, null); + } + + public static StsValueHint create(ValueHint hint) { + return new StsValueHint(""+hint.getValue(), textSnippet(hint.getDescription()), null); + } + + public static StsValueHint create(IType klass) { + return new StsValueHint(klass.getFullyQualifiedName(), javaDocSnippet(klass), DeprecationUtil.extract(klass)) { + @Override + public IJavaElement getJavaElement() { + return klass; + } + }; + } + + /** + * Create a html snippet from a text snippet. + */ + private static Renderable textSnippet(String description) { + if (StringUtil.hasText(description)) { + return Renderables.text(description); + } + return Renderables.NO_DESCRIPTION; + } + + public String getValue() { + return value; + } + + public Renderable getDescription() { + return description; + } + + private static Renderable javaDocSnippet(IJavaElement je) { + return Renderables.lazy(() -> { + IJavadoc jdoc = je.getJavaDoc(); + if (jdoc != null) { + return jdoc.getRenderable(); + } else { + return Renderables.NO_DESCRIPTION; + } + }); + } + + @Override + public String toString() { + return "StsValueHint("+value+")"; + } + + public Deprecation getDeprecation() { + return deprecation; + } + + public IJavaElement getJavaElement() { + return null; + } + + public StsValueHint prefixWith(String prefix) { + StsValueHint it = this; + return new StsValueHint(prefix+getValue(), description, deprecation) { + @Override + public IJavaElement getJavaElement() { + return it.getJavaElement(); + } + }; + } + + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/ValueHintHoverInfo.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/ValueHintHoverInfo.java new file mode 100644 index 000000000..2ce773fe9 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/ValueHintHoverInfo.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2016-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.metadata.hints; + +import java.util.List; + +import org.springframework.ide.vscode.commons.util.Renderable; +import org.springframework.ide.vscode.commons.util.Renderables; + +import static org.springframework.ide.vscode.commons.util.Renderables.*; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; + +public class ValueHintHoverInfo { + + public static Renderable create(StsValueHint hint) { + Builder builder = ImmutableList.builder(); + builder.add(bold(""+hint.getValue())); + builder.add(paragraph(hint.getDescription())); + return concat(builder.build()); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/DeprecationUtil.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/DeprecationUtil.java new file mode 100644 index 000000000..9d9cd78b3 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/DeprecationUtil.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2016 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.metadata.util; + +import java.util.Optional; + +import org.springframework.boot.configurationmetadata.Deprecation; +import org.springframework.ide.vscode.commons.java.IAnnotatable; +import org.springframework.ide.vscode.commons.java.IJavaElement; + +import com.google.common.collect.ImmutableSet; + +public class DeprecationUtil { + + private static final ImmutableSet DEPRECATED_ANOT_NAMES = ImmutableSet.of( + "org.springframework.boot.context.properties.DeprecatedConfigurationProperty", + "DeprecatedConfigurationProperty", + "java.lang.Deprecated", + "Deprecated" + ); + + /** + * Extract {@link Deprecation} info from annotations on a {@link IJavaElement} + */ + public static Deprecation extract(IJavaElement je) { + Optional deprecation = Optional.empty(); + if (je instanceof IAnnotatable) { + deprecation = extract((IAnnotatable)je); + } + return deprecation.isPresent() ? deprecation.get() : null; + } + + /** + * Extract {@link Deprecation} info from annotations on a {@link IJavaElement} + */ + private static Optional extract(IAnnotatable m) { + return m.getAnnotations().filter(a -> DEPRECATED_ANOT_NAMES.contains(a.getElementName())).map(a -> { + Deprecation d = new Deprecation(); + a.getMemberValuePairs().forEach(pair -> { + String name = pair.getMemberName(); + if (name.equals("reason")) { + d.setReason((String) pair.getValue()); + } else if (name.equals("replacement")) { + d.setReplacement((String) pair.getValue()); + } + }); + return d; + }).findFirst(); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java new file mode 100644 index 000000000..ac551346f --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2014 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.metadata.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.logging.Logger; + +import org.springframework.ide.vscode.commons.util.FuzzyMatcher; +import org.springframework.ide.vscode.commons.util.StringUtil; + +/** + * A collection of data that can be searched with a simple 'fuzzy' string + * matching algorithm. Clients must override 'getKey' method to define how + * a search 'key' is associated with each data item. + *

+ * The collection can then be searched for items who's key matches + * simple 'fuzzy' patterns. + */ +public abstract class FuzzyMap implements Iterable { + + private static final Logger LOG = Logger.getLogger(FuzzyMap.class.getName()); + + public static class Match { + public double score; + public final E data; + private String pattern; + + public Match(String pattern, double score, E e) { + this.pattern = pattern; + this.score = score; + this.data = e; + } + public static Match getBest(Collection> matches) { + double bestScore = Double.NEGATIVE_INFINITY; + Match best = null; + for (Match match : matches) { + if (match.score>bestScore) { + best = match; + bestScore = match.score; + } + } + return best; + } + + @Override + public String toString() { + return "Match(score="+score+", data="+data+")"; + } + public String getPattern() { + return pattern; + } + } + + @Override + public Iterator iterator() { + return entries.values().iterator(); + } + + private TreeMap entries = new TreeMap(); + + protected abstract String getKey(E entry); + + public void add(E value) { + //This assumes no two entries have the same id. + String key = getKey(value); + E existing = entries.get(key); + if (existing==null) { + entries.put(getKey(value), value); + } else { + LOG.warning(FuzzyMap.class.getName()+": Multiple entries for key "+key+" some entries discarded"); + } + } + + /** + * Search for pattern. A pattern is just a sequence of characters which have to found in + * an entrie's key in the same order as they are in the pattern. + *

+ * Note that returned list doesn't yet have elements sorted according to score (instead they + * are sorted lexicographically thanks to the fact we use a Tree representation). + */ + public List> find(String pattern) { + if ("".equals(pattern)) { + //Special case because + // 1) no need to search. Matches everything + // 2) want to use different way of sorting / scoring. See https://issuetracker.springsource.com/browse/STS-4008 + ArrayList> matches = new ArrayList>(entries.size()); + for (E v : entries.values()) { + matches.add(new Match(pattern, 1.0, v)); + } + return matches; + } else { + //TODO: optimize somehow with a smarter index? (right now searches all map entries sequentially) + ArrayList> matches = new ArrayList>(); + for (Entry e : entries.entrySet()) { + String key = e.getKey(); + double score = FuzzyMatcher.matchScore(pattern, key); + if (score!=0.0) { + matches.add(new Match(pattern, score, e.getValue())); + } + } + return matches; + } + } + + /** + * Searches the index for the longest string which is both + * - a prefix of propertyName + * - a prefix of some key in the map. + * Note: If the map is empty, then this returns null, since + * no string, not even the empty string is a prefix of a + * key in the map. + */ + public String findValidPrefix(String propertyName) { + E best = findLongestCommonPrefixEntry(propertyName); + return best==null?null:StringUtil.commonPrefix(propertyName, getKey(best)); + } + + /** + * Find property with longest common prefix for given key. + */ + public E findLongestCommonPrefixEntry(String propertyName) { + //We can implementation this O(log(n)) because the properties are kept in a TreeMap which is sorted. + //This means that entries with common prefix will occur 'next to eachother' + //The 'best' entry must therefore be either the entry just before or just after + //the property we are searching for. + + Entry ceiln = entries.ceilingEntry(propertyName); + Entry floor = entries.floorEntry(propertyName); + Entry best; + if (floor==null || floor==ceiln) { + best = ceiln; + } else if (ceiln==null) { + best = floor; + } else { + int floorScore = floor==null?0:StringUtil.commonPrefixLength(floor.getKey(), propertyName); + int ceilnScore = ceiln==null?0:StringUtil.commonPrefixLength(ceiln.getKey(), propertyName); + best = floorScore>ceilnScore ? floor : ceiln; + } + return best==null?null:best.getValue(); + } + + /** + * Find an exact match if it exists. + */ + public E get(String id) { + return entries.get(id); + } + + public boolean isEmpty() { + return entries==null || entries.isEmpty(); + } + + public int size() { + return entries.size(); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/Listener.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/Listener.java new file mode 100644 index 000000000..10aa67fdc --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/Listener.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2014 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.metadata.util; + +/** + * @author Kris De Volder + */ +public interface Listener { + + void changed(T info); + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/ListenerManager.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/ListenerManager.java new file mode 100644 index 000000000..f3921598a --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/ListenerManager.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2014 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.metadata.util; + +import java.util.Arrays; + +import org.springframework.ide.vscode.commons.util.ListenerList; + +public class ListenerManager { + + private ListenerList listeners = new ListenerList<>(ListenerList.IDENTITY); + + public void addListener(T l) { + listeners.add(l); + } + + public void removeListener(T l) { + listeners.remove(l); + } + + @SuppressWarnings("unchecked") + public Iterable getListeners() { + return (Iterable) Arrays.asList(listeners.getListeners()); + } + + + +} diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java index 02f21e878..696ae5547 100644 --- a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ScopeCompletionTest.java @@ -27,6 +27,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFin import org.springframework.ide.vscode.languageserver.testharness.Editor; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; /** * @author Martin Lippert @@ -36,18 +37,21 @@ public class ScopeCompletionTest { private final JavaProjectFinder javaProjectFinder = (doc) -> getTestProject(); private LanguageServerHarness harness; + private PropertyIndexHarness indexHarness; private IJavaProject testProject; private Editor editor; + @Before public void setup() throws Exception { - testProject = ProjectsHarness.INSTANCE.mavenProject("test-scope-annotation"); + testProject = ProjectsHarness.INSTANCE.mavenProject("test-annotations"); + indexHarness = new PropertyIndexHarness(); harness = new LanguageServerHarness(new Callable() { @Override public BootJavaLanguageServer call() throws Exception { - BootJavaLanguageServer server = new BootJavaLanguageServer(javaProjectFinder); + BootJavaLanguageServer server = new BootJavaLanguageServer(javaProjectFinder, indexHarness.getIndexProvider()); return server; } }) { @@ -155,7 +159,7 @@ public class ScopeCompletionTest { } private void prepareCase(String selectedAnnotation, String annotationStatementBeforeTest) throws Exception { - InputStream resource = this.getClass().getResourceAsStream("/test-projects/test-scope-annotation/src/main/java/org/test/TestScopeCompletion.java"); + InputStream resource = this.getClass().getResourceAsStream("/test-projects/test-annotations/src/main/java/org/test/TestScopeCompletion.java"); String content = IOUtils.toString(resource); content = content.replace(selectedAnnotation, annotationStatementBeforeTest); diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java new file mode 100644 index 000000000..637096e01 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * 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.completions.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import java.util.List; +import java.util.concurrent.Callable; + +import org.apache.commons.io.IOUtils; +import org.eclipse.lsp4j.CompletionItem; +import org.junit.Before; +import org.junit.Test; +import org.springframework.ide.vscode.boot.java.BootJavaLanguageServer; +import org.springframework.ide.vscode.boot.java.completions.ValueCompletionProcessor; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.languageserver.testharness.Editor; +import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; +import org.springframework.ide.vscode.project.harness.PropertyIndexHarness; + +/** + * @author Martin Lippert + */ +public class ValueCompletionTest { + + private final JavaProjectFinder javaProjectFinder = (doc) -> getTestProject(); + + private LanguageServerHarness harness; + private IJavaProject testProject; + + private Editor editor; + + private PropertyIndexHarness indexHarness; + + @Before + public void setup() throws Exception { + testProject = ProjectsHarness.INSTANCE.mavenProject("test-annotations"); + indexHarness = new PropertyIndexHarness(); + + harness = new LanguageServerHarness(new Callable() { + @Override + public BootJavaLanguageServer call() throws Exception { + BootJavaLanguageServer server = new BootJavaLanguageServer(javaProjectFinder, indexHarness.getIndexProvider()); + return server; + } + }) { + @Override + protected String getFileExtension() { + return ".java"; + } + }; + harness.intialize(null); + } + + private IJavaProject getTestProject() { + return testProject; + } + + @Test + public void testPrefixIdentification() { + ValueCompletionProcessor processor = new ValueCompletionProcessor(null); + + assertEquals("pre", processor.identifyPropertyPrefix("pre", 3)); + assertEquals("pre", processor.identifyPropertyPrefix("prefix", 3)); + assertEquals("", processor.identifyPropertyPrefix("", 0)); + assertEquals("pre", processor.identifyPropertyPrefix("$pre", 4)); + + assertEquals("", processor.identifyPropertyPrefix("${pre", 0)); + assertEquals("", processor.identifyPropertyPrefix("${pre", 1)); + assertEquals("", processor.identifyPropertyPrefix("${pre", 2)); + assertEquals("p", processor.identifyPropertyPrefix("${pre", 3)); + assertEquals("pr", processor.identifyPropertyPrefix("${pre", 4)); + } + + @Test + public void testEmptyBracketsCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(<*>)"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"${data.prop2}\"<*>)", + "@Value(\"${else.prop3}\"<*>)", + "@Value(\"${spring.prop1}\"<*>)"); + } + + @Test + public void testOnlyDollarNoQoutesCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value($<*>)"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"${data.prop2}\"<*>)", + "@Value(\"${else.prop3}\"<*>)", + "@Value(\"${spring.prop1}\"<*>)"); + } + + @Test + public void testOnlyDollarCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"$<*>\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"${data.prop2}<*>\")", + "@Value(\"${else.prop3}<*>\")", + "@Value(\"${spring.prop1}<*>\")"); + } + + @Test + public void testDollarWithBracketsCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"${<*>}\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"${data.prop2<*>}\")", + "@Value(\"${else.prop3<*>}\")", + "@Value(\"${spring.prop1<*>}\")"); + } + + @Test + public void testEmptyStringLiteralCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"<*>\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"${data.prop2}<*>\")", + "@Value(\"${else.prop3}<*>\")", + "@Value(\"${spring.prop1}<*>\")"); + } + + @Test + public void testPlainPrefixCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(spri<*>)"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"${spring.prop1}\"<*>)"); + } + + @Test + public void testQoutedPrefixCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"spri<*>\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"${spring.prop1}<*>\")"); + } + + @Test + public void testRandomSpelExpressionNoCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"#{<*>}\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"#{${data.prop2}<*>}\")", + "@Value(\"#{${else.prop3}<*>}\")", + "@Value(\"#{${spring.prop1}<*>}\")"); + } + + @Test + public void testRandomSpelExpressionWithPropertyDollar() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"#{345$<*>}\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"#{345${data.prop2}<*>}\")", + "@Value(\"#{345${else.prop3}<*>}\")", + "@Value(\"#{345${spring.prop1}<*>}\")"); + } + + @Test + public void testRandomSpelExpressionWithPropertyDollerWithoutClosindBracket() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"#{345${<*>}\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"#{345${data.prop2}<*>}\")", + "@Value(\"#{345${else.prop3}<*>}\")", + "@Value(\"#{345${spring.prop1}<*>}\")"); + } + + @Test + public void testRandomSpelExpressionWithPropertyDollerWithClosingBracket() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"#{345${<*>}}\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"#{345${data.prop2<*>}}\")", + "@Value(\"#{345${else.prop3<*>}}\")", + "@Value(\"#{345${spring.prop1<*>}}\")"); + } + + @Test + public void testRandomSpelExpressionWithPropertyPrefixWithoutClosingBracket() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"#{345${spri<*>}\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"#{345${spring.prop1}<*>}\")"); + } + + @Test + public void testRandomSpelExpressionWithPropertyPrefixWithClosingBracket() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(\"#{345${spri<*>}}\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(\"#{345${spring.prop1<*>}}\")"); + } + + private void prepareDefaultIndexData() { + indexHarness.data("spring.prop1", "java.lang.String", null, null); + indexHarness.data("data.prop2", "java.lang.String", null, null); + indexHarness.data("else.prop3", "java.lang.String", null, null); + } + + private void prepareCase(String selectedAnnotation, String annotationStatementBeforeTest) throws Exception { + InputStream resource = this.getClass().getResourceAsStream("/test-projects/test-annotations/src/main/java/org/test/TestValueCompletion.java"); + String content = IOUtils.toString(resource); + + content = content.replace(selectedAnnotation, annotationStatementBeforeTest); + editor = new Editor(harness, content, "java"); + } + + private void assertAnnotationCompletions(String... completedAnnotations) throws Exception { + List completions = editor.getCompletions(); + int i = 0; + for (String expectedCompleted : completedAnnotations) { + Editor clonedEditor = editor.clone(); + clonedEditor.apply(completions.get(i++)); + assertTrue(clonedEditor.getText().contains(expectedCompleted)); + } + + assertEquals(i, completions.size()); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java index f7f95cf19..516a0c3d4 100644 --- a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/ProjectsHarness.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -29,7 +29,6 @@ import com.google.common.cache.CacheBuilder; * Test projects harness * * @author Alex Boyko - * */ public class ProjectsHarness { @@ -62,94 +61,13 @@ public class ProjectsHarness { } protected Path getProjectPath(String name) throws URISyntaxException, IOException { -// URI sourceLocation = ProjectsHarness.class.getProtectionDomain().getCodeSource().getLocation().toURI(); -// // file:/Users/aboyko/git/sts4/vscode-extensions/commons/project-test-harness/target/project-test-harness-0.0.1-SNAPSHOT.jar -// Path testProjectsPath = Paths.get(sourceLocation).getParent().getParent().resolve("test-projects").resolve(name); -// if (Files.exists(testProjectsPath)) { -// return testProjectsPath; -// } else { -// /* -// * If "test-projects" folder is not found then extract test project -// * from the jar's "test-projects" folder and copy it in the temp -// * folder -// */ - return getProjectPathFromClasspath(name); -// } + return getProjectPathFromClasspath(name); } private Path getProjectPathFromClasspath(String name) throws URISyntaxException, IOException { URI resource = ProjectsHarness.class.getResource("/test-projects/" + name).toURI(); -// if (resource.getScheme().equalsIgnoreCase("jar")) { -// return getProjectPathFromJar(resource); -// } else { - return Paths.get(resource); -// } - } - -// private Path getProjectPathFromJar(URI jar) throws IOException { -// final String[] array = jar.toString().split("!"); -// URI firstHalf = URI.create(array[0]); -// Path tempFolderPath = Paths.get(new File(System.getProperty(MavenCore.JAVA_IO_TMPDIR)).toURI()); -// FileSystem fs = FileSystems.newFileSystem(firstHalf, Collections.emptyMap()); -// try { -// Path path = fs.getPath(array[1]); -// Path projectCopyPath = tempFolderPath.resolve(path.getFileName().toString()); -// if (Files.exists(projectCopyPath)) { -// recursiveDelete(projectCopyPath); -// } -// recursiveCopy(path, tempFolderPath, StandardCopyOption.REPLACE_EXISTING); -// System.out.println("Copied test project to: " + projectCopyPath); -// return projectCopyPath; -// } finally { -// fs.close(); -// } -// } -// -// private static void recursiveCopy(Path source, Path target, CopyOption... options) throws IOException { -// Files.walkFileTree(source, new SimpleFileVisitor() { -// -// Path destination = target; -// -// @Override -// public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { -// destination = destination.resolve(dir.getFileName().toString()); -// Files.copy(dir, destination, options); -// return super.preVisitDirectory(dir, attrs); -// } -// -// @Override -// public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { -// Path newFile = destination.resolve(file.getFileName().toString()); -// Files.copy(file, newFile, options); -// return super.visitFile(file, attrs); -// } -// -// @Override -// public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { -// destination = destination.getParent(); -// return super.postVisitDirectory(dir, exc); -// } -// -// }); -// } -// -// private static void recursiveDelete(Path path) throws IOException { -// Files.walkFileTree(path, new SimpleFileVisitor() { -// -// @Override -// public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { -// Files.delete(file); -// return super.visitFile(file, attrs); -// } -// -// @Override -// public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { -// Files.delete(dir); -// return super.postVisitDirectory(dir, exc); -// } -// -// }); -// } + return Paths.get(resource); + } public MavenJavaProject mavenProject(String name) throws Exception { return (MavenJavaProject) project(ProjectType.MAVEN, name); diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java new file mode 100644 index 000000000..ab1063e03 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java @@ -0,0 +1,565 @@ +/******************************************************************************* + * 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.project.harness; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; +import org.springframework.boot.configurationmetadata.Deprecation; +import org.springframework.boot.configurationmetadata.ValueHint; +import org.springframework.boot.configurationmetadata.ValueProvider; +import org.springframework.ide.vscode.boot.metadata.PropertyInfo; +import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex; +import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; +import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry; +import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.text.IDocument; + +/** + * Provides some convenience apis for test code to create / use test data for a SpringPropertyIndex. + */ +public class PropertyIndexHarness { + + private Map datas = new LinkedHashMap<>(); + private ValueProviderRegistry valueProviders = ValueProviderRegistry.getDefault(); + private SpringPropertyIndex index = null; + private IJavaProject testProject = null; + + protected final SpringPropertyIndexProvider indexProvider = new SpringPropertyIndexProvider() { + @Override + public FuzzyMap getIndex(IDocument doc) { + synchronized (PropertyIndexHarness.this) { + if (index==null) { + IClasspath classpath = testProject == null ? null : testProject.getClasspath(); + index = new SpringPropertyIndex(valueProviders, classpath); + for (ConfigurationMetadataProperty propertyInfo : datas.values()) { + index.add(propertyInfo); + } + } + return index; + } + } + }; + + public synchronized void useProject(IJavaProject p) throws Exception { + index = null; + this.testProject = p; + } + + public class ItemConfigurer { + + private ConfigurationMetadataProperty item; + + public ItemConfigurer(ConfigurationMetadataProperty item) { + this.item = item; + } + + /** + * Add a provider with a single parameter. + * @return + */ + public ItemConfigurer provider(String name, String paramName, Object paramValue) { + ValueProvider provider = new ValueProvider(); + provider.setName(name); + provider.getParameters().put(paramName, paramValue); + item.getHints().getValueProviders().add(provider); + return this; + } + + /** + * Add a value hint. If description contains a '.' the dot is used + * to break description into a short and long description. + * @return + */ + public ItemConfigurer valueHint(Object value, String description) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + if (description!=null) { + int dotPos = description.indexOf('.'); + if (dotPos>=0) { + hint.setShortDescription( description.substring(0, dotPos)); + } + hint.setDescription(description); + } + item.getHints().getValueHints().add(hint); + return this; + } + } + + + public synchronized ItemConfigurer data(String id, String type, Object deflt, String description, + String... source + ) { + ConfigurationMetadataProperty item = new ConfigurationMetadataProperty(); + item.setId(id); + item.setDescription(description); + item.setType(type); + item.setDefaultValue(deflt); + index = null; + datas.put(item.getId(), item); + return new ItemConfigurer(item); + } + + public synchronized void keyHints(String id, String... hintValues) { + index = null; + List hints = datas.get(id).getHints().getKeyHints(); + for (String value : hintValues) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + hints.add(hint); + } + } + + public synchronized void valueHints(String id, String... hintValues) { + index = null; + List hints = datas.get(id).getHints().getValueHints(); + for (String value : hintValues) { + ValueHint hint = new ValueHint(); + hint.setValue(value); + hints.add(hint); + } + } + + public synchronized void deprecate(String key, String replacedBy, String reason) { + index = null; + ConfigurationMetadataProperty info = datas.get(key); + Deprecation d = new Deprecation(); + d.setReplacement(replacedBy); + d.setReason(reason); + info.setDeprecation(d); + } + + /** + * Call this method to add some default test data to the Completion engine's index. + * Note that this data is not added automatically, some test may want to use smaller + * test data sets. + */ + public void defaultTestData() { + data("banner.charset", "java.nio.charset.Charset", "UTF-8", "Banner file encoding."); + data("banner.location", "java.lang.String", "classpath:banner.txt", "Banner file location."); + data("debug", "java.lang.Boolean", "false", "Enable debug logs."); + data("flyway.check-location", "java.lang.Boolean", "false", "Check that migration scripts location exists."); + data("flyway.clean-on-validation-error", "java.lang.Boolean", null, null); + data("flyway.enabled", "java.lang.Boolean", "true", "Enable flyway."); + data("flyway.encoding", "java.lang.String", null, null); + data("flyway.ignore-failed-future-migration", "java.lang.Boolean", null, null); + data("flyway.init-description", "java.lang.String", null, null); + data("flyway.init-on-migrate", "java.lang.Boolean", null, null); + data("flyway.init-sqls", "java.util.List", null, "SQL statements to execute to initialize a connection immediately after obtaining\n it."); + data("flyway.init-version", "org.flywaydb.core.api.MigrationVersion", null, null); + data("flyway.locations", "java.util.List", null, "Locations of migrations scripts."); + data("flyway.out-of-order", "java.lang.Boolean", null, null); + data("flyway.password", "java.lang.String", null, "Login password of the database to migrate."); + data("flyway.placeholder-prefix", "java.lang.String", null, null); + data("flyway.placeholders", "java.util.Map", null, null); + data("flyway.placeholder-suffix", "java.lang.String", null, null); + data("flyway.schemas", "java.lang.String[]", null, null); + data("flyway.sql-migration-prefix", "java.lang.String", null, null); + data("flyway.sql-migration-separator", "java.lang.String", null, null); + data("flyway.sql-migration-suffix", "java.lang.String", null, null); + data("flyway.table", "java.lang.String", null, null); + data("flyway.target", "org.flywaydb.core.api.MigrationVersion", null, null); + data("flyway.url", "java.lang.String", null, "JDBC url of the database to migrate. If not set, the primary configured data source\n is used."); + data("flyway.user", "java.lang.String", null, "Login user of the database to migrate."); + data("flyway.validate-on-migrate", "java.lang.Boolean", null, null); + data("http.mappers.json-pretty-print", "java.lang.Boolean", null, "Enable json pretty print."); + data("http.mappers.json-sort-keys", "java.lang.Boolean", null, "Enable key sorting."); + data("liquibase.change-log", "java.lang.String", "classpath:/db/changelog/db.changelog-master.yaml", "Change log configuration path."); + data("liquibase.check-change-log-location", "java.lang.Boolean", "true", "Check the change log location exists."); + data("liquibase.contexts", "java.lang.String", null, "Comma-separated list of runtime contexts to use."); + data("liquibase.default-schema", "java.lang.String", null, "Default database schema."); + data("liquibase.drop-first", "java.lang.Boolean", "false", "Drop the database schema first."); + data("liquibase.enabled", "java.lang.Boolean", "true", "Enable liquibase support."); + data("liquibase.password", "java.lang.String", null, "Login password of the database to migrate."); + data("liquibase.url", "java.lang.String", null, "JDBC url of the database to migrate. If not set, the primary configured data source\n is used."); + data("liquibase.user", "java.lang.String", null, "Login user of the database to migrate."); + data("logging.config", "java.lang.String", null, "Location of the logging configuration file."); + data("logging.file", "java.lang.String", null, "Log file name."); + data("logging.level", "java.util.Map", null, "Log levels severity mapping. Use 'root' for the root logger."); + data("logging.path", "java.lang.String", null, "Location of the log file."); + data("multipart.file-size-threshold", "java.lang.String", "0", "Threshold after which files will be written to disk. Values can use the suffixed\n \"MB\" or \"KB\" to indicate a Megabyte or Kilobyte size."); + data("multipart.location", "java.lang.String", null, "Intermediate location of uploaded files."); + data("multipart.max-file-size", "java.lang.String", "1Mb", "Max file size. Values can use the suffixed \"MB\" or \"KB\" to indicate a Megabyte or\n Kilobyte size."); + data("multipart.max-request-size", "java.lang.String", "10Mb", "Max request size. Values can use the suffixed \"MB\" or \"KB\" to indicate a Megabyte\n or Kilobyte size."); + data("security.basic.enabled", "java.lang.Boolean", "true", "Enable basic authentication."); + data("security.basic.path", "java.lang.String[]", "[Ljava.lang.Object;@7abd0056", "Comma-separated list of paths to secure."); + data("security.basic.realm", "java.lang.String", "Spring", "HTTP basic realm name."); + data("security.enable-csrf", "java.lang.Boolean", "false", "Enable Cross Site Request Forgery support."); + data("security.filter-order", "java.lang.Integer", "0", "Security filter chain order."); + data("security.headers.cache", "java.lang.Boolean", "false", "Enable cache control HTTP headers."); + data("security.headers.content-type", "java.lang.Boolean", "false", "Enable \"X-Content-Type-Options\" header."); + data("security.headers.frame", "java.lang.Boolean", "false", "Enable \"X-Frame-Options\" header."); + data("security.headers.hsts", "org.springframework.boot.autoconfigure.security.SecurityProperties$Headers$HSTS", null, "HTTP Strict Transport Security (HSTS) mode (none, domain, all)."); + data("security.headers.xss", "java.lang.Boolean", "false", "Enable cross site scripting (XSS) protection."); + data("security.ignored", "java.util.List", null, "Comma-separated list of paths to exclude from the default secured paths."); + data("security.require-ssl", "java.lang.Boolean", "false", "Enable secure channel for all requests."); + data("security.sessions", "org.springframework.security.config.http.SessionCreationPolicy", null, "Session creation policy (always, never, if_required, stateless)."); + data("security.user.name", "java.lang.String", "user", "Default user name."); + data("security.user.password", "java.lang.String", null, "Password for the default user name."); + data("security.user.role", "java.util.List", null, "Granted roles for the default user name."); + data("server.address", "java.net.InetAddress", null, "Network address to which the server should bind to."); + data("server.context-parameters", "java.util.Map", null, "ServletContext parameters."); + data("server.context-path", "java.lang.String", null, "Context path of the application."); + data("server.port", "java.lang.Integer", null, "Server HTTP port."); + data("server.servlet-path", "java.lang.String", "/", "Path of the main dispatcher servlet."); + data("server.session-timeout", "java.lang.Integer", null, "Session timeout in seconds."); + data("server.ssl.ciphers", "java.lang.String[]", null, null); + data("server.ssl.client-auth", "org.springframework.boot.context.embedded.Ssl$ClientAuth", null, null); + data("server.ssl.key-alias", "java.lang.String", null, null); + data("server.ssl.key-password", "java.lang.String", null, null); + data("server.ssl.key-store", "java.lang.String", null, null); + data("server.ssl.key-store-password", "java.lang.String", null, null); + data("server.ssl.key-store-provider", "java.lang.String", null, null); + data("server.ssl.key-store-type", "java.lang.String", null, null); + data("server.ssl.protocol", "java.lang.String", null, null); + data("server.ssl.trust-store", "java.lang.String", null, null); + data("server.ssl.trust-store-password", "java.lang.String", null, null); + data("server.ssl.trust-store-provider", "java.lang.String", null, null); + data("server.ssl.trust-store-type", "java.lang.String", null, null); + data("server.tomcat.access-log-enabled", "java.lang.Boolean", "false", "Enable access log."); + data("server.tomcat.access-log-pattern", "java.lang.String", null, "Format pattern for access logs."); + data("server.tomcat.background-processor-delay", "java.lang.Integer", "30", "Delay in seconds between the invocation of backgroundProcess methods."); + data("server.tomcat.basedir", "java.io.File", null, "Tomcat base directory. If not specified a temporary directory will be used."); + data("server.tomcat.internal-proxies", "java.lang.String", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|192\\.168\\.\\d{1,3}\\.\\d{1,3}|169\\.254\\.\\d{1,3}\\.\\d{1,3}|127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "Regular expression that matches proxies that are to be trusted."); + data("server.tomcat.max-http-header-size", "java.lang.Integer", "0", "Maximum size in bytes of the HTTP message header."); + data("server.tomcat.max-threads", "java.lang.Integer", "0", "Maximum amount of worker threads."); + data("server.tomcat.port-header", "java.lang.String", null, "Name of the HTTP header used to override the original port value."); + data("server.tomcat.protocol-header", "java.lang.String", null, "Header that holds the incoming protocol, usually named \"X-Forwarded-Proto\".\n Configured as a RemoteIpValve only if remoteIpHeader is also set."); + data("server.tomcat.remote-ip-header", "java.lang.String", null, "Name of the http header from which the remote ip is extracted. Configured as a\n RemoteIpValve only if remoteIpHeader is also set."); + data("server.tomcat.uri-encoding", "java.lang.String", null, "Character encoding to use to decode the URI."); + data("server.undertow.buffer-size", "java.lang.Integer", null, "Size of each buffer in bytes."); + data("server.undertow.buffers-per-region", "java.lang.Integer", null, "Number of buffer per region."); + data("server.undertow.direct-buffers", "java.lang.Boolean", null, null); + data("server.undertow.io-threads", "java.lang.Integer", null, "Number of I/O threads to create for the worker."); + data("server.undertow.worker-threads", "java.lang.Integer", null, "Number of worker threads."); + data("spring.activemq.broker-url", "java.lang.String", null, "URL of the ActiveMQ broker. Auto-generated by default."); + data("spring.activemq.in-memory", "java.lang.Boolean", "true", "Specify if the default broker URL should be in memory. Ignored if an explicit\n broker has been specified."); + data("spring.activemq.password", "java.lang.String", null, "Login password of the broker."); + data("spring.activemq.pooled", "java.lang.Boolean", "false", "Specify if a PooledConnectionFactory should be created instead of a regular\n ConnectionFactory."); + data("spring.activemq.user", "java.lang.String", null, "Login user of the broker."); + data("spring.aop.auto", "java.lang.Boolean", "true", "Add @EnableAspectJAutoProxy."); + data("spring.aop.proxy-target-class", "java.lang.Boolean", "false", "Whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false)."); + data("spring.application.index", "java.lang.Integer", null, "Application index."); + data("spring.application.name", "java.lang.String", null, "Application name."); + data("spring.batch.initializer.enabled", "java.lang.Boolean", "true", "Create the required batch tables on startup if necessary."); + data("spring.batch.job.enabled", "java.lang.Boolean", "true", "Execute all Spring Batch jobs in the context on startup."); + data("spring.batch.job.names", "java.lang.String", "", "Comma-separated list of job names to execute on startup. By default, all Jobs\n found in the context are executed."); + data("spring.batch.schema", "java.lang.String", "classpath:org/springframework/batch/core/schema-@@platform@@.sql", "Path to the SQL file to use to initialize the database schema."); + data("spring.config.location", "java.lang.String", null, "Config file locations."); + data("spring.config.name", "java.lang.String", "application", "Config file name."); + data("spring.dao.exceptiontranslation.enabled", "java.lang.Boolean", "true", "Enable the PersistenceExceptionTranslationPostProcessor."); + data("spring.data.elasticsearch.cluster-name", "java.lang.String", "elasticsearch", "Elasticsearch cluster name."); + data("spring.data.elasticsearch.cluster-nodes", "java.lang.String", null, "Comma-separated list of cluster node addresses. If not specified, starts a client\n node."); + data("spring.data.elasticsearch.repositories.enabled", "java.lang.Boolean", "true", "Enable Elasticsearch repositories."); + data("spring.data.jpa.repositories.enabled", "java.lang.Boolean", "true", "Enable JPA repositories."); + data("spring.data.mongodb.authentication-database", "java.lang.String", null, "Authentication database name."); + data("spring.data.mongodb.database", "java.lang.String", null, "Database name."); + data("spring.data.mongodb.grid-fs-database", "java.lang.String", null, "GridFS database name."); + data("spring.data.mongodb.host", "java.lang.String", null, "Mongo server host."); + data("spring.data.mongodb.password", "char[]", null, "Login password of the mongo server."); + data("spring.data.mongodb.port", "java.lang.Integer", null, "Mongo server port."); + data("spring.data.mongodb.repositories.enabled", "java.lang.Boolean", "true", "Enable Mongo repositories."); + data("spring.data.mongodb.uri", "java.lang.String", "mongodb://localhost/test", "Mmongo database URI. When set, host and port are ignored."); + data("spring.data.mongodb.username", "java.lang.String", null, "Login user of the mongo server."); + data("spring.data.rest.base-uri", "java.net.URI", null, null); + data("spring.data.rest.default-page-size", "java.lang.Integer", null, null); + data("spring.data.rest.limit-param-name", "java.lang.String", null, null); + data("spring.data.rest.max-page-size", "java.lang.Integer", null, null); + data("spring.data.rest.page-param-name", "java.lang.String", null, null); + data("spring.data.rest.return-body-on-create", "java.lang.Boolean", null, null); + data("spring.data.rest.return-body-on-update", "java.lang.Boolean", null, null); + data("spring.data.rest.sort-param-name", "java.lang.String", null, null); + data("spring.data.solr.host", "java.lang.String", "http://127.0.0.1:8983/solr", "Solr host. Ignored if \"zk-host\" is set."); + data("spring.data.solr.repositories.enabled", "java.lang.Boolean", "true", "Enable Solr repositories."); + data("spring.data.solr.zk-host", "java.lang.String", null, "ZooKeeper host address in the form HOST:PORT."); + data("spring.datasource.abandon-when-percentage-full", "java.lang.Integer", null, null); + data("spring.datasource.access-to-underlying-connection-allowed", "java.lang.Boolean", null, null); + data("spring.datasource.alternate-username-allowed", "java.lang.Boolean", null, null); + data("spring.datasource.auto-commit", "java.lang.Boolean", null, null); + data("spring.datasource.catalog", "java.lang.String", null, null); + data("spring.datasource.commit-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.connection-customizer-class-name", "java.lang.String", null, null); + data("spring.datasource.connection-init-sql", "java.lang.String", null, null); + data("spring.datasource.connection-init-sqls", "java.util.Collection", null, null); + data("spring.datasource.connection-properties", "java.lang.String", null, null); + data("spring.datasource.connection-test-query", "java.lang.String", null, null); + data("spring.datasource.connection-timeout", "java.lang.Long", null, null); + data("spring.datasource.continue-on-error", "java.lang.Boolean", "false", "Do not stop if an error occurs while initializing the database."); + data("spring.datasource.data", "java.lang.String", null, "Data (DML) script resource reference."); + data("spring.datasource.data-source-class-name", "java.lang.String", null, null); + data("spring.datasource.data-source", "java.lang.Object", null, null); + data("spring.datasource.data-source-j-n-d-i", "java.lang.String", null, null); + data("spring.datasource.data-source-properties", "java.util.Properties", null, null); + data("spring.datasource.db-properties", "java.util.Properties", null, null); + data("spring.datasource.default-auto-commit", "java.lang.Boolean", null, null); + data("spring.datasource.default-catalog", "java.lang.String", null, null); + data("spring.datasource.default-read-only", "java.lang.Boolean", null, null); + data("spring.datasource.default-transaction-isolation", "java.lang.Integer", null, null); + data("spring.datasource.driver-class-name", "java.lang.String", null, "Fully qualified name of the JDBC driver. Auto-detected based on the URL by default."); + data("spring.datasource.fair-queue", "java.lang.Boolean", null, null); + data("spring.datasource.idle-timeout", "java.lang.Long", null, null); + data("spring.datasource.ignore-exception-on-pre-load", "java.lang.Boolean", null, null); + data("spring.datasource.initialization-fail-fast", "java.lang.Boolean", null, null); + data("spring.datasource.initialize", "java.lang.Boolean", "true", "Populate the database using 'data.sql'."); + data("spring.datasource.initial-size", "java.lang.Integer", null, null); + data("spring.datasource.init-s-q-l", "java.lang.String", null, null); + data("spring.datasource.isolate-internal-queries", "java.lang.Boolean", null, null); + data("spring.datasource.jdbc4-connection-test", "java.lang.Boolean", null, null); + data("spring.datasource.jdbc-interceptors", "java.lang.String", null, null); + data("spring.datasource.jdbc-url", "java.lang.String", null, null); + data("spring.datasource.jmx-enabled", "java.lang.Boolean", "false", "Enable JMX support (if provided by the underlying pool)."); + data("spring.datasource.jndi-name", "java.lang.String", null, "JNDI location of the datasource. Class, url, username & password are ignored when\n set."); + data("spring.datasource.leak-detection-threshold", "java.lang.Long", null, null); + data("spring.datasource.log-abandoned", "java.lang.Boolean", null, null); + data("spring.datasource.login-timeout", "java.lang.Integer", null, null); + data("spring.datasource.log-validation-errors", "java.lang.Boolean", null, null); + data("spring.datasource.max-active", "java.lang.Integer", null, null); + data("spring.datasource.max-age", "java.lang.Long", null, null); + data("spring.datasource.max-idle", "java.lang.Integer", null, null); + data("spring.datasource.maximum-pool-size", "java.lang.Integer", null, null); + data("spring.datasource.max-lifetime", "java.lang.Long", null, null); + data("spring.datasource.max-open-prepared-statements", "java.lang.Integer", null, null); + data("spring.datasource.max-wait", "java.lang.Integer", null, null); + data("spring.datasource.metric-registry", "java.lang.Object", null, null); + data("spring.datasource.min-evictable-idle-time-millis", "java.lang.Integer", null, null); + data("spring.datasource.min-idle", "java.lang.Integer", null, null); + data("spring.datasource.minimum-idle", "java.lang.Integer", null, null); + data("spring.datasource.name", "java.lang.String", null, null); + data("spring.datasource.num-tests-per-eviction-run", "java.lang.Integer", null, null); + data("spring.datasource.password", "java.lang.String", null, "Login password of the database."); + data("spring.datasource.platform", "java.lang.String", "all", "Platform to use in the schema resource (schema-${platform}.sql)."); + data("spring.datasource.pool-name", "java.lang.String", null, null); + data("spring.datasource.pool-prepared-statements", "java.lang.Boolean", null, null); + data("spring.datasource.propagate-interrupt-state", "java.lang.Boolean", null, null); + data("spring.datasource.read-only", "java.lang.Boolean", null, null); + data("spring.datasource.register-mbeans", "java.lang.Boolean", null, null); + data("spring.datasource.remove-abandoned", "java.lang.Boolean", null, null); + data("spring.datasource.remove-abandoned-timeout", "java.lang.Integer", null, null); + data("spring.datasource.rollback-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.schema", "java.lang.String", null, "Schema (DDL) script resource reference."); + data("spring.datasource.separator", "java.lang.String", ";", "Statement separator in SQL initialization scripts."); + data("spring.datasource.sql-script-encoding", "java.lang.String", null, "SQL scripts encoding."); + data("spring.datasource.suspect-timeout", "java.lang.Integer", null, null); + data("spring.datasource.test-on-borrow", "java.lang.Boolean", null, null); + data("spring.datasource.test-on-connect", "java.lang.Boolean", null, null); + data("spring.datasource.test-on-return", "java.lang.Boolean", null, null); + data("spring.datasource.test-while-idle", "java.lang.Boolean", null, null); + data("spring.datasource.time-between-eviction-runs-millis", "java.lang.Integer", null, null); + data("spring.datasource.transaction-isolation", "java.lang.String", null, null); + data("spring.datasource.url", "java.lang.String", null, "JDBC url of the database."); + data("spring.datasource.use-disposable-connection-facade", "java.lang.Boolean", null, null); + data("spring.datasource.use-equals", "java.lang.Boolean", null, null); + data("spring.datasource.use-lock", "java.lang.Boolean", null, null); + data("spring.datasource.username", "java.lang.String", null, "Login user of the database."); + data("spring.datasource.validation-interval", "java.lang.Long", null, null); + data("spring.datasource.validation-query", "java.lang.String", null, null); + data("spring.datasource.validation-query-timeout", "java.lang.Integer", null, null); + data("spring.datasource.validator-class-name", "java.lang.String", null, null); + data("spring.datasource.xa.data-source-class-name", "java.lang.String", null, "XA datasource fully qualified name."); + data("spring.datasource.xa.properties", "java.util.Map", null, "Properties to pass to the XA data source."); + data("spring.freemarker.allow-request-override", "java.lang.Boolean", null, "Set whether HttpServletRequest attributes are allowed to override (hide) controller\n generated model attributes of the same name."); + data("spring.freemarker.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.freemarker.char-set", "java.lang.String", null, null); + data("spring.freemarker.charset", "java.lang.String", null, "Template encoding."); + data("spring.freemarker.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.freemarker.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.freemarker.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.freemarker.expose-request-attributes", "java.lang.Boolean", null, "Set whether all request attributes should be added to the model prior to merging\n with the template."); + data("spring.freemarker.expose-session-attributes", "java.lang.Boolean", null, "Set whether all HttpSession attributes should be added to the model prior to\n merging with the template."); + data("spring.freemarker.expose-spring-macro-helpers", "java.lang.Boolean", null, "Set whether to expose a RequestContext for use by Spring's macro library, under the\n name \"springMacroRequestContext\"."); + data("spring.freemarker.prefix", "java.lang.String", null, "Prefix that gets prepended to view names when building a URL."); + data("spring.freemarker.request-context-attribute", "java.lang.String", null, "Name of the RequestContext attribute for all views."); + data("spring.freemarker.settings", "java.util.Map", null, "Well-known FreeMarker keys which will be passed to FreeMarker's Configuration."); + data("spring.freemarker.suffix", "java.lang.String", null, "Suffix that gets appended to view names when building a URL."); + data("spring.freemarker.template-loader-path", "java.lang.String[]", new String[] {"snuzzle" ,"buggles"}, "Comma-separated list of template paths."); + data("spring.freemarker.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.groovy.template.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.groovy.template.char-set", "java.lang.String", null, null); + data("spring.groovy.template.charset", "java.lang.String", null, "Template encoding."); + data("spring.groovy.template.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.groovy.template.configuration.auto-escape", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.auto-indent", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.auto-indent-string", "java.lang.String", null, null); + data("spring.groovy.template.configuration.auto-new-line", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.base-template-class", "java.lang.Class", null, null); + data("spring.groovy.template.configuration.cache-templates", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration.declaration-encoding", "java.lang.String", null, null); + data("spring.groovy.template.configuration.expand-empty-elements", "java.lang.Boolean", null, null); + data("spring.groovy.template.configuration", "java.util.Map", null, "Configuration to pass to TemplateConfiguration."); + data("spring.groovy.template.configuration.locale", "java.util.Locale", null, null); + data("spring.groovy.template.configuration.new-line-string", "java.lang.String", null, null); + data("spring.groovy.template.configuration.resource-loader-path", "java.lang.String", null, null); + data("spring.groovy.template.configuration.use-double-quotes", "java.lang.Boolean", null, null); + data("spring.groovy.template.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.groovy.template.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.groovy.template.prefix", "java.lang.String", "classpath:/templates/", "Prefix that gets prepended to view names when building a URL."); + data("spring.groovy.template.suffix", "java.lang.String", ".tpl", "Suffix that gets appended to view names when building a URL."); + data("spring.groovy.template.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.hornetq.embedded.cluster-password", "java.lang.String", null, "Cluster password. Randomly generated on startup by default"); + data("spring.hornetq.embedded.data-directory", "java.lang.String", null, "Journal file directory. Not necessary if persistence is turned off."); + data("spring.hornetq.embedded.enabled", "java.lang.Boolean", "true", "Enable embedded mode if the HornetQ server APIs are available."); + data("spring.hornetq.embedded.persistent", "java.lang.Boolean", "false", "Enable persistent store."); + data("spring.hornetq.embedded.queues", "java.lang.String[]", "[Ljava.lang.Object;@2f5ce114", "Comma-separate list of queues to create on startup."); + data("spring.hornetq.embedded.server-id", "java.lang.Integer", "0", "Server id. By default, an auto-incremented counter is used."); + data("spring.hornetq.embedded.topics", "java.lang.String[]", "[Ljava.lang.Object;@6272137a", "Comma-separate list of topics to create on startup."); + data("spring.hornetq.host", "java.lang.String", "localhost", "HornetQ broker host."); + data("spring.hornetq.mode", "org.springframework.boot.autoconfigure.jms.hornetq.HornetQMode", null, "HornetQ deployment mode, auto-detected by default. Can be explicitly set to\n \"native\" or \"embedded\"."); + data("spring.hornetq.port", "java.lang.Integer", "5445", "HornetQ broker port."); + data("spring.http.encoding.charset", "java.nio.charset.Charset", null, "Charset of HTTP requests and responses. Added to the \"Content-Type\" header if not\n set explicitly."); + data("spring.http.encoding.enabled", "java.lang.Boolean", "true", "Enable http encoding support."); + data("spring.http.encoding.force", "java.lang.Boolean", "true", "Force the encoding to the configured charset on HTTP requests and responses."); + data("spring.jackson.date-format", "java.lang.String", null, "Date format string (yyyy-MM-dd HH:mm:ss), or a fully-qualified date format class\n name."); + data("spring.jackson.deserialization", "java.util.Map", null, "Jackson on/off features that affect the way Java objects are deserialized."); + data("spring.jackson.generator", "java.util.Map", null, "Jackson on/off features for generators."); + data("spring.jackson.mapper", "java.util.Map", null, "Jackson general purpose on/off features."); + data("spring.jackson.parser", "java.util.Map", null, "Jackson on/off features for parsers."); + data("spring.jackson.property-naming-strategy", "java.lang.String", null, "One of the constants on Jackson's PropertyNamingStrategy\n (CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES). Can also be a fully-qualified class\n name of a PropertyNamingStrategy subclass."); + data("spring.jackson.serialization", "java.util.Map", null, "Jackson on/off features that affect the way Java objects are serialized."); + data("spring.jersey.filter.order", "java.lang.Integer", "0", "Jersey filter chain order."); + data("spring.jersey.init", "java.util.Map", null, "Init parameters to pass to Jersey."); + data("spring.jersey.type", "org.springframework.boot.autoconfigure.jersey.JerseyProperties$Type", null, "Jersey integration type. Can be either \"servlet\" or \"filter\"."); + data("spring.jms.jndi-name", "java.lang.String", null, "Connection factory JNDI name. When set, takes precedence to others connection\n factory auto-configurations."); + data("spring.jms.pub-sub-domain", "java.lang.Boolean", "false", "Specify if the default destination type is topic."); + data("spring.jmx.enabled", "java.lang.Boolean", "true", "Expose management beans to the JMX domain."); + data("spring.jpa.database", "org.springframework.orm.jpa.vendor.Database", null, "Target database to operate on, auto-detected by default. Can be alternatively set\n using the \"databasePlatform\" property."); + data("spring.jpa.database-platform", "java.lang.String", null, "Name of the target database to operate on, auto-detected by default. Can be\n alternatively set using the \"Database\" enum."); + data("spring.jpa.generate-ddl", "java.lang.Boolean", "false", "Initialize the schema on startup."); + data("spring.jpa.hibernate.ddl-auto", "java.lang.String", null, "DDL mode (\"none\", \"validate\", \"update\", \"create\", \"create-drop\"). This is\n actually a shortcut for the \"hibernate.hbm2ddl.auto\" property. Default to\n \"create-drop\" when using an embedded database, \"none\" otherwise."); + data("spring.jpa.hibernate.naming-strategy", "java.lang.Class", null, "Naming strategy fully qualified name."); + data("spring.jpa.open-in-view", "java.lang.Boolean", "true", "Register OpenEntityManagerInViewInterceptor. Binds a JPA EntityManager to the thread for the entire processing of the request."); + data("spring.jpa.properties", "java.util.Map", null, "Additional native properties to set on the JPA provider."); + data("spring.jpa.show-sql", "java.lang.Boolean", "false", "Enable logging of SQL statements."); + data("spring.jta.allow-multiple-lrc", "java.lang.Boolean", null, null); + data("spring.jta.asynchronous2-pc", "java.lang.Boolean", null, null); + data("spring.jta.background-recovery-interval", "java.lang.Integer", null, null); + data("spring.jta.background-recovery-interval-seconds", "java.lang.Integer", null, null); + data("spring.jta.current-node-only-recovery", "java.lang.Boolean", null, null); + data("spring.jta.debug-zero-resource-transaction", "java.lang.Boolean", null, null); + data("spring.jta.default-transaction-timeout", "java.lang.Integer", null, null); + data("spring.jta.disable-jmx", "java.lang.Boolean", null, null); + data("spring.jta.enabled", "java.lang.Boolean", "true", "Enable JTA support."); + data("spring.jta.exception-analyzer", "java.lang.String", null, null); + data("spring.jta.filter-log-status", "java.lang.Boolean", null, null); + data("spring.jta.force-batching-enabled", "java.lang.Boolean", null, null); + data("spring.jta.forced-write-enabled", "java.lang.Boolean", null, null); + data("spring.jta.graceful-shutdown-interval", "java.lang.Integer", null, null); + data("spring.jta.jndi-transaction-synchronization-registry-name", "java.lang.String", null, null); + data("spring.jta.jndi-user-transaction-name", "java.lang.String", null, null); + data("spring.jta.journal", "java.lang.String", null, null); + data("spring.jta.log-dir", "java.lang.String", null, "Transaction logs directory."); + data("spring.jta.log-part1-filename", "java.lang.String", null, null); + data("spring.jta.log-part2-filename", "java.lang.String", null, null); + data("spring.jta.max-log-size-in-mb", "java.lang.Integer", null, null); + data("spring.jta.resource-configuration-filename", "java.lang.String", null, null); + data("spring.jta.server-id", "java.lang.String", null, null); + data("spring.jta.skip-corrupted-logs", "java.lang.Boolean", null, null); + data("spring.jta.transaction-manager-id", "java.lang.String", null, "Transaction manager unique identifier."); + data("spring.jta.warn-about-zero-resource-transaction", "java.lang.Boolean", null, null); + data("spring.mail.default-encoding", "java.lang.String", "UTF-8", "Default MimeMessage encoding."); + data("spring.mail.host", "java.lang.String", null, "SMTP server host."); + data("spring.mail.password", "java.lang.String", null, "Login password of the SMTP server."); + data("spring.mail.port", "java.lang.Integer", null, "SMTP server port."); + data("spring.mail.properties", "java.util.Map", null, "Additional JavaMail session properties."); + data("spring.mail.username", "java.lang.String", null, "Login user of the SMTP server."); + data("spring.main.show-banner", "java.lang.Boolean", "true", "Display the banner when the application runs."); + data("spring.main.sources", "java.util.Set", null, "Sources (class name, package name or XML resource location) used to create the ApplicationContext."); + data("spring.main.web-environment", "java.lang.Boolean", null, "Run the application in a web environment (auto-detected by default)."); + data("spring.mandatory-file-encoding", "java.lang.String", null, "Expected character encoding the application must use."); + data("spring.messages.basename", "java.lang.String", "messages", "Comma-separated list of basenames, each following the ResourceBundle convention.\n Essentially a fully-qualified classpath location. If it doesn't contain a package\n qualifier (such as \"org.mypackage\"), it will be resolved from the classpath root."); + data("spring.messages.cache-seconds", "java.lang.Integer", "-1", "Loaded resource bundle files cache expiration, in seconds. When set to -1, bundles\n are cached forever."); + data("spring.messages.encoding", "java.lang.String", "utf-8", "Message bundles encoding."); + data("spring.mobile.devicedelegatingviewresolver.enabled", "java.lang.Boolean", "false", "Enable device view resolver."); + data("spring.mobile.devicedelegatingviewresolver.mobile-prefix", "java.lang.String", "mobile/", "Prefix that gets prepended to view names for mobile devices."); + data("spring.mobile.devicedelegatingviewresolver.mobile-suffix", "java.lang.String", "", "Suffix that gets appended to view names for mobile devices."); + data("spring.mobile.devicedelegatingviewresolver.normal-prefix", "java.lang.String", "", "Prefix that gets prepended to view names for normal devices."); + data("spring.mobile.devicedelegatingviewresolver.normal-suffix", "java.lang.String", "", "Suffix that gets appended to view names for normal devices."); + data("spring.mobile.devicedelegatingviewresolver.tablet-prefix", "java.lang.String", "tablet/", "Prefix that gets prepended to view names for tablet devices."); + data("spring.mobile.devicedelegatingviewresolver.tablet-suffix", "java.lang.String", "", "Suffix that gets appended to view names for tablet devices."); + data("spring.mobile.sitepreference.enabled", "java.lang.Boolean", "true", "Enable SitePreferenceHandler."); + data("spring.mvc.date-format", "java.lang.String", null, "Date format to use (e.g. dd/MM/yyyy)"); + data("spring.mvc.ignore-default-model-on-redirect", "java.lang.Boolean", "true", "If the the content of the \"default\" model should be ignored during redirect\n scenarios."); + data("spring.mvc.locale", "java.lang.String", null, "Locale to use."); + data("spring.mvc.message-codes-resolver-format", "org.springframework.validation.DefaultMessageCodesResolver$Format", null, "Formatting strategy for message codes (PREFIX_ERROR_CODE, POSTFIX_ERROR_CODE)."); + data("spring.profiles.active", "java.lang.String", null, "Comma-separated list of active profiles. Can be overridden by a command line switch."); + data("spring.profiles.include", "java.lang.String", null, "Unconditionally activate the specified comma separated profiles."); + data("spring.rabbitmq.addresses", "java.lang.String", null, "Comma-separated list of addresses to which the client should connect to."); + data("spring.rabbitmq.dynamic", "java.lang.Boolean", "true", "Create an AmqpAdmin bean."); + data("spring.rabbitmq.host", "java.lang.String", "localhost", "RabbitMQ host."); + data("spring.rabbitmq.password", "java.lang.String", null, "Login to authenticate against the broker."); + data("spring.rabbitmq.port", "java.lang.Integer", "5672", "RabbitMQ port."); + data("spring.rabbitmq.username", "java.lang.String", null, "Login user to authenticate to the broker."); + data("spring.rabbitmq.virtual-host", "java.lang.String", null, "Virtual host to use when connecting to the broker."); + data("spring.redis.database", "java.lang.Integer", "0", "Database index used by the connection factory."); + data("spring.redis.host", "java.lang.String", "localhost", "Redis server host."); + data("spring.redis.password", "java.lang.String", null, "Login password of the redis server."); + data("spring.redis.pool.max-active", "java.lang.Integer", "8", "Max number of connections that can be allocated by the pool at a given time.\n Use a negative value for no limit."); + data("spring.redis.pool.max-idle", "java.lang.Integer", "8", "Max number of \"idle\" connections in the pool. Use a negative value to indicate\n an unlimited number of idle connections."); + data("spring.redis.pool.max-wait", "java.lang.Integer", "-1", "Maximum amount of time (in milliseconds) a connection allocation should block\n before throwing an exception when the pool is exhausted. Use a negative value\n to block indefinitely."); + data("spring.redis.pool.min-idle", "java.lang.Integer", "0", "Target for the minimum number of idle connections to maintain in the pool. This\n setting only has an effect if it is positive."); + data("spring.redis.port", "java.lang.Integer", "6379", "Redis server port."); + data("spring.redis.sentinel.master", "java.lang.String", null, "Name of Redis server."); + data("spring.redis.sentinel.nodes", "java.lang.String", null, "Comma-separated list of host:port pairs."); + data("spring.resources.add-mappings", "java.lang.Boolean", "true", "Enable default resource handling."); + data("spring.resources.cache-period", "java.lang.Integer", null, "Cache period for the resources served by the resource handler, in seconds."); + data("spring.social.auto-connection-views", "java.lang.Boolean", "false", "Enable the connection status view for supported providers."); + data("spring.social.facebook.app-id", "java.lang.String", null, "Application id."); + data("spring.social.facebook.app-secret", "java.lang.String", null, "Application secret."); + data("spring.social.linkedin.app-id", "java.lang.String", null, "Application id."); + data("spring.social.linkedin.app-secret", "java.lang.String", null, "Application secret."); + data("spring.social.twitter.app-id", "java.lang.String", null, "Application id."); + data("spring.social.twitter.app-secret", "java.lang.String", null, "Application secret."); + data("spring.thymeleaf.cache", "java.lang.Boolean", "true", "Enable template caching."); + data("spring.thymeleaf.check-template-location", "java.lang.Boolean", "true", "Check that the templates location exists."); + data("spring.thymeleaf.content-type", "java.lang.String", "text/html", "Content-Type value."); + data("spring.thymeleaf.enabled", "java.lang.Boolean", "true", "Enable MVC Thymeleaf view resolution."); + data("spring.thymeleaf.encoding", "java.lang.String", "UTF-8", "Template encoding."); + data("spring.thymeleaf.excluded-view-names", "java.lang.String[]", null, "Comma-separated list of view names that should be excluded from resolution."); + data("spring.thymeleaf.mode", "java.lang.String", "HTML5", "Template mode to be applied to templates. See also StandardTemplateModeHandlers."); + data("spring.thymeleaf.prefix", "java.lang.String", "classpath:/templates/", "Prefix that gets prepended to view names when building a URL."); + data("spring.thymeleaf.suffix", "java.lang.String", ".html", "Suffix that gets appended to view names when building a URL."); + data("spring.thymeleaf.view-names", "java.lang.String[]", null, "Comma-separated list of view names that can be resolved."); + data("spring.velocity.allow-request-override", "java.lang.Boolean", null, "Set whether HttpServletRequest attributes are allowed to override (hide) controller\n generated model attributes of the same name."); + data("spring.velocity.cache", "java.lang.Boolean", null, "Enable template caching."); + data("spring.velocity.char-set", "java.lang.String", null, null); + data("spring.velocity.charset", "java.lang.String", null, "Template encoding."); + data("spring.velocity.check-template-location", "java.lang.Boolean", null, "Check that the templates location exists."); + data("spring.velocity.content-type", "java.lang.String", null, "Content-Type value."); + data("spring.velocity.date-tool-attribute", "java.lang.String", null, "Name of the DateTool helper object to expose in the Velocity context of the view."); + data("spring.velocity.enabled", "java.lang.Boolean", null, "Enable MVC view resolution for this technology."); + data("spring.velocity.expose-request-attributes", "java.lang.Boolean", null, "Set whether all request attributes should be added to the model prior to merging\n with the template."); + data("spring.velocity.expose-session-attributes", "java.lang.Boolean", null, "Set whether all HttpSession attributes should be added to the model prior to\n merging with the template."); + data("spring.velocity.expose-spring-macro-helpers", "java.lang.Boolean", null, "Set whether to expose a RequestContext for use by Spring's macro library, under the\n name \"springMacroRequestContext\"."); + data("spring.velocity.number-tool-attribute", "java.lang.String", null, "Name of the NumberTool helper object to expose in the Velocity context of the view."); + data("spring.velocity.prefer-file-system-access", "java.lang.Boolean", "true", "Prefer file system access for template loading. File system access enables hot\n detection of template changes."); + data("spring.velocity.prefix", "java.lang.String", null, "Prefix that gets prepended to view names when building a URL."); + data("spring.velocity.properties", "java.util.Map", null, "Additional velocity properties."); + data("spring.velocity.request-context-attribute", "java.lang.String", null, "Name of the RequestContext attribute for all views."); + data("spring.velocity.resource-loader-path", "java.lang.String", "classpath:/templates/", "Template path."); + data("spring.velocity.suffix", "java.lang.String", null, "Suffix that gets appended to view names when building a URL."); + data("spring.velocity.toolbox-config-location", "java.lang.String", null, "Velocity Toolbox config location, for example \"/WEB-INF/toolbox.xml\". Automatically\n loads a Velocity Tools toolbox definition file and expose all defined tools in the\n specified scopes."); + data("spring.velocity.view-names", "java.lang.String[]", null, "White list of view names that can be resolved."); + data("spring.view.prefix", "java.lang.String", null, "Spring MVC view prefix."); + data("spring.view.suffix", "java.lang.String", null, "Spring MVC view suffix."); + } + + public boolean isEmpty() { + return datas == null || datas.isEmpty(); + } + + public SpringPropertyIndexProvider getIndexProvider() { + return indexProvider; + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/.gitignore b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/.gitignore similarity index 100% rename from vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/.gitignore rename to vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/.gitignore diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/.mvn/wrapper/maven-wrapper.jar b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/.mvn/wrapper/maven-wrapper.jar rename to vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/.mvn/wrapper/maven-wrapper.jar diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/.mvn/wrapper/maven-wrapper.properties b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/.mvn/wrapper/maven-wrapper.properties rename to vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/.mvn/wrapper/maven-wrapper.properties diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/mvnw b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/mvnw similarity index 100% rename from vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/mvnw rename to vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/mvnw diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/mvnw.cmd b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/mvnw.cmd similarity index 100% rename from vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/mvnw.cmd rename to vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/mvnw.cmd diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/pom.xml b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/pom.xml similarity index 100% rename from vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/pom.xml rename to vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/pom.xml diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/src/main/java/org/test/TestScopeAnnotationApplication.java b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestAnnotationsApplication.java similarity index 64% rename from vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/src/main/java/org/test/TestScopeAnnotationApplication.java rename to vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestAnnotationsApplication.java index 90fb713da..3780b4b75 100644 --- a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/src/main/java/org/test/TestScopeAnnotationApplication.java +++ b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestAnnotationsApplication.java @@ -4,9 +4,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class TestScopeAnnotationApplication { +public class TestAnnotationsApplication { public static void main(String[] args) { - SpringApplication.run(TestScopeAnnotationApplication.class, args); + SpringApplication.run(TestAnnotationsApplication.class, args); } } diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/src/main/java/org/test/TestScopeCompletion.java b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestScopeCompletion.java similarity index 100% rename from vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-scope-annotation/src/main/java/org/test/TestScopeCompletion.java rename to vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestScopeCompletion.java diff --git a/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestValueCompletion.java b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestValueCompletion.java new file mode 100644 index 000000000..ba853018a --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/test/resources/test-projects/test-annotations/src/main/java/org/test/TestValueCompletion.java @@ -0,0 +1,17 @@ +package org.test; + +import org.springframework.beans.factory.annotation.Value; + +public class TestValueCompletion { + + @Value("onField") + private String value1; + + @Value("onMethod") + public void method1() { + } + + public void method2(@Value("onParameter") String parameter1) { + } + +} From d12b9cfbe236ea00050ac9e2979f1e9776c98cbe Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 10 Feb 2017 12:04:16 -0800 Subject: [PATCH 06/28] Update reactor to 3.0.5 due to broken CF support. --- vscode-extensions/commons/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode-extensions/commons/pom.xml b/vscode-extensions/commons/pom.xml index 4d3b0fe14..a3ccd0ed5 100644 --- a/vscode-extensions/commons/pom.xml +++ b/vscode-extensions/commons/pom.xml @@ -78,7 +78,7 @@ 2.10 0.1.0 - 3.0.4.RELEASE + 3.0.5.RELEASE 0.6.0.RELEASE 2.4.0.BUILD-SNAPSHOT From fbcbc47b981d2caaec25a23c30d9c4479b270fc7 Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 10 Feb 2017 12:12:54 -0800 Subject: [PATCH 07/28] Added support for CF dynamic values for domains --- .../commons/cloudfoundry/client/CFDomain.java | 17 +++++ .../cloudfoundry/client/CFDomainImpl.java | 52 +++++++++++++++ .../cloudfoundry/client/CFEntities.java | 3 + .../cloudfoundry/client/ClientRequests.java | 2 + .../client/cftarget/CFTarget.java | 33 ++++++++-- .../cloudfoundry/client/v2/CFWrappingV2.java | 8 ++- .../client/v2/DefaultClientRequestsV2.java | 16 +++++ .../cloudfoundry/client/CFClientTest.java | 51 +++++++++++++++ .../yaml/ManifestYamlCFDomainProvider.java | 64 +++++++++++++++++++ .../yaml/ManifestYamlLanguageServer.java | 33 ++++++++-- .../yaml/ManifestYmlHintProviders.java | 26 ++++++++ .../manifest/yaml/ManifestYmlSchema.java | 23 +++++-- .../manifest/yaml/ManifestYamlEditorTest.java | 39 +++++++++++ .../manifest/yaml/ManifestYmlSchemaTest.java | 24 ++++++- 14 files changed, 375 insertions(+), 16 deletions(-) create mode 100644 vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFDomain.java create mode 100644 vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFDomainImpl.java create mode 100644 vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainProvider.java create mode 100644 vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlHintProviders.java diff --git a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFDomain.java b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFDomain.java new file mode 100644 index 000000000..e231abde7 --- /dev/null +++ b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFDomain.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * 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.cloudfoundry.client; + +public interface CFDomain { + + String getName(); + +} diff --git a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFDomainImpl.java b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFDomainImpl.java new file mode 100644 index 000000000..a8e7be670 --- /dev/null +++ b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFDomainImpl.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * 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.cloudfoundry.client; + +class CFDomainImpl implements CFDomain { + + private final String name; + + public CFDomainImpl(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CFDomainImpl other = (CFDomainImpl) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + + +} diff --git a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFEntities.java b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFEntities.java index 9973cfdc3..395548f86 100644 --- a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFEntities.java +++ b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFEntities.java @@ -29,4 +29,7 @@ public class CFEntities { /* dasboard Url */ null); } + public static CFDomain createDomain(String name) { + return new CFDomainImpl(name); + } } diff --git a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/ClientRequests.java b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/ClientRequests.java index 407b85533..74e797579 100644 --- a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/ClientRequests.java +++ b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/ClientRequests.java @@ -17,4 +17,6 @@ public interface ClientRequests { List getBuildpacks() throws Exception; List getServices() throws Exception; + List getDomains() throws Exception; + } diff --git a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTarget.java b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTarget.java index 4c6ba97c1..f2087ddf4 100644 --- a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTarget.java +++ b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/cftarget/CFTarget.java @@ -15,6 +15,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientRequests; @@ -37,15 +38,21 @@ public class CFTarget { /* * Cached information */ - private final LoadingCache> buildpacksCache; - private final LoadingCache> servicesCache; + private LoadingCache> buildpacksCache; + private LoadingCache> servicesCache; + private LoadingCache> domainCache; private CFCallableContext callableContext; - public CFTarget(String targetName, CFClientParams params, ClientRequests requests, CFCallableContext callableContext) { + public CFTarget(String targetName, CFClientParams params, ClientRequests requests, + CFCallableContext callableContext) { this.params = params; this.requests = requests; this.targetName = targetName; this.callableContext = callableContext; + initCache(requests); + } + + private void initCache(ClientRequests requests) { CacheLoader> servicesLoader = new CacheLoader>() { @Override @@ -71,12 +78,23 @@ public class CFTarget { }; this.buildpacksCache = CacheBuilder.newBuilder() .expireAfterAccess(CFTargetCache.TARGET_EXPIRATION, TimeUnit.HOURS).build(buildpacksLoader); + + CacheLoader> domainLoader = new CacheLoader>() { + + @Override + public List load(String key) throws Exception { + return runAndCheckForFailure(() -> requests.getDomains()); + } + + }; + this.domainCache = CacheBuilder.newBuilder() + .expireAfterAccess(CFTargetCache.TARGET_EXPIRATION, TimeUnit.HOURS).build(domainLoader); } - + protected T runAndCheckForFailure(Callable callable) throws Exception { return callableContext.checkConnection(callable); } - + public boolean hasConnectionError() { return callableContext.hasConnectionError(); } @@ -102,6 +120,11 @@ public class CFTarget { String key = getName(); return this.servicesCache.get(key); } + + public List getDomains() throws Exception { + String key = getName(); + return this.domainCache.get(key); + } public ClientRequests getClientRequests() { return requests; diff --git a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/CFWrappingV2.java b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/CFWrappingV2.java index a3b38fd70..b16829bb4 100644 --- a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/CFWrappingV2.java +++ b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/CFWrappingV2.java @@ -11,10 +11,11 @@ package org.springframework.ide.vscode.commons.cloudfoundry.client.v2; import org.cloudfoundry.operations.buildpacks.Buildpack; -import org.cloudfoundry.operations.services.ServiceInstance; +import org.cloudfoundry.operations.domains.Domain; import org.cloudfoundry.operations.services.ServiceInstanceSummary; import org.cloudfoundry.operations.services.ServiceInstanceType; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFEntities; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; @@ -32,6 +33,11 @@ public class CFWrappingV2 { return CFEntities.createBuildpack(name); } + public static CFDomain wrap(Domain domain) { + String name = domain.getName(); + return CFEntities.createDomain(name); + } + public static CFServiceInstance wrap(ServiceInstanceSummary serviceInstance) { String name = serviceInstance.getName(); String plan = serviceInstance.getPlan(); diff --git a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/DefaultClientRequestsV2.java b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/DefaultClientRequestsV2.java index 3dd656f61..30646d1d4 100644 --- a/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/DefaultClientRequestsV2.java +++ b/vscode-extensions/commons/commons-cf/src/main/java/org/springframework/ide/vscode/commons/cloudfoundry/client/v2/DefaultClientRequestsV2.java @@ -18,6 +18,7 @@ import org.cloudfoundry.client.CloudFoundryClient; import org.cloudfoundry.operations.CloudFoundryOperations; import org.cloudfoundry.operations.DefaultCloudFoundryOperations; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientRequests; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientTimeouts; @@ -82,7 +83,22 @@ public class DefaultClientRequestsV2 implements ClientRequests { ) ); } + + @Override + public List getDomains() throws Exception { + return ReactorUtils.get(timeouts.getBuildpacksTimeout(), CancelationTokens.NULL, + log("operations.domains.list", + _operations + .domains() + .list() + .map(CFWrappingV2::wrap) + .collectList() + .map(ImmutableList::copyOf) + ) + ); + } + @Override public List getBuildpacks() throws Exception { return ReactorUtils.get(timeouts.getBuildpacksTimeout(), CancelationTokens.NULL, diff --git a/vscode-extensions/commons/commons-cf/src/test/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFClientTest.java b/vscode-extensions/commons/commons-cf/src/test/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFClientTest.java index 32d6a8a65..ddf745ba2 100644 --- a/vscode-extensions/commons/commons-cf/src/test/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFClientTest.java +++ b/vscode-extensions/commons/commons-cf/src/test/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFClientTest.java @@ -20,6 +20,7 @@ import java.util.concurrent.Callable; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFParamsProviderMessages; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTarget; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTargetCache; @@ -28,6 +29,8 @@ import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.Conne import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.NoTargetsException; import org.springframework.ide.vscode.commons.util.ExceptionUtil; +import com.google.common.collect.ImmutableList; + public class CFClientTest { MockCfCli cloudfoundry = new MockCfCli(); @@ -71,6 +74,54 @@ public class CFClientTest { assertError(() -> target.getBuildpacks(), ConnectionException.class, expectedMessages.noNetworkConnection()); } + @Test + public void testBuildpacksFromTarget() throws Exception { + ClientRequests client = cloudfoundry.client; + CFBuildpack buildpack = Mockito.mock(CFBuildpack.class); + when(buildpack.getName()).thenReturn("java_buildpack"); + when(client.getBuildpacks()).thenReturn(ImmutableList.of(buildpack)); + + CFTarget target = targetCache.getOrCreate().get(0); + assertEquals("java_buildpack", target.getBuildpacks().get(0).getName()); + } + + @Test + public void testDomainsFromTarget() throws Exception { + ClientRequests client = cloudfoundry.client; + CFDomain domain = Mockito.mock(CFDomain.class); + when(domain.getName()).thenReturn("cfapps.io"); + when(client.getDomains()).thenReturn(ImmutableList.of(domain)); + + CFTarget target = targetCache.getOrCreate().get(0); + assertEquals("cfapps.io", target.getDomains().get(0).getName()); + } + + @Test + public void testServicesFromTarget() throws Exception { + ClientRequests client = cloudfoundry.client; + CFServiceInstance service = Mockito.mock(CFServiceInstance.class); + when(service.getName()).thenReturn("appdb"); + when(service.getPlan()).thenReturn("spark"); + when(service.getService()).thenReturn("cleardb"); + + when(client.getServices()).thenReturn(ImmutableList.of(service)); + + CFTarget target = targetCache.getOrCreate().get(0); + assertEquals("appdb", target.getServices().get(0).getName()); + assertEquals("spark", target.getServices().get(0).getPlan()); + assertEquals("cleardb", target.getServices().get(0).getService()); + } + + @Test + public void testNoServicesFromTarget() throws Exception { + ClientRequests client = cloudfoundry.client; + + when(client.getServices()).thenReturn(ImmutableList.of()); + + CFTarget target = targetCache.getOrCreate().get(0); + assertTrue(target.getServices().isEmpty()); + } + protected void assertError(Callable callable, Class expected, String expectedMessage) throws Exception { Throwable error = null; diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainProvider.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainProvider.java new file mode 100644 index 000000000..235b162e2 --- /dev/null +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainProvider.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * 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.manifest.yaml; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; +import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTarget; +import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTargetCache; +import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint; +import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; + +public class ManifestYamlCFDomainProvider extends AbstractCFHintsProvider { + + public ManifestYamlCFDomainProvider(CFTargetCache cache) { + super(cache); + } + + @Override + public Collection getHints(List targets) throws Exception { + + List hints = new ArrayList<>(); + + for (CFTarget cfTarget : targets) { + + List domains = cfTarget.getDomains(); + if (domains != null && !domains.isEmpty()) { + + for (CFDomain domain : domains) { + String name = domain.getName(); + String label = getBuildpackLabel(cfTarget, domain); + YValueHint hint = new BasicYValueHint(name, label); + if (!hints.contains(hint)) { + hints.add(hint); + } + } + return hints; + } + } + // Contract for the reconciler: return null if values cannot be + // resolved. Otherwise + // return non-empty list + return !hints.isEmpty() ? hints : null; + } + + protected String getBuildpackLabel(CFTarget target, CFDomain domain) { + return domain.getName() + " (" + target.getName() + ")"; + } + + @Override + protected String getTypeName() { + return "Domain"; + } +} diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java index f1fe59da7..9d5e46522 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java @@ -63,10 +63,7 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer { YamlASTProvider parser = new YamlParser(yaml); - Callable> buildPacksProvider = getBuildpacksProvider(); - Callable> servicesProvider = getServicesProvider(); - - schema = new ManifestYmlSchema(buildPacksProvider, servicesProvider); + schema = new ManifestYmlSchema(getHintProviders()); YamlStructureProvider structureProvider = YamlStructureProvider.DEFAULT; YamlAssistContextProvider contextProvider = new SchemaBasedYamlAssistContextProvider(schema); @@ -98,6 +95,30 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer { documents.onHover(hoverEngine ::getHover); } + protected ManifestYmlHintProviders getHintProviders() { + Callable> buildPacksProvider = getBuildpacksProvider(); + Callable> servicesProvider = getServicesProvider(); + Callable> domainsProvider = getDomainsProvider(); + + return new ManifestYmlHintProviders() { + + @Override + public Callable> getServicesProvider() { + return servicesProvider; + } + + @Override + public Callable> getDomainsProvider() { + return domainsProvider; + } + + @Override + public Callable> getBuildpackProviders() { + return buildPacksProvider; + } + }; + } + private CFTargetCache getCfTargetCache() { if (cfTargetCache == null) { ClientParamsProvider paramsProvider = cfParamsProvider; @@ -114,6 +135,10 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer { private Callable> getServicesProvider() { return new ManifestYamlCFServicesProvider(getCfTargetCache()); } + + private Callable> getDomainsProvider() { + return new ManifestYamlCFDomainProvider(getCfTargetCache()); + } @Override protected ServerCapabilities getServerCapabilities() { diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlHintProviders.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlHintProviders.java new file mode 100644 index 000000000..09558ba5b --- /dev/null +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlHintProviders.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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.manifest.yaml; + +import java.util.Collection; +import java.util.concurrent.Callable; + +import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; + +public interface ManifestYmlHintProviders { + + Callable> getBuildpackProviders(); + + Callable> getServicesProvider(); + + Callable> getDomainsProvider(); + +} diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java index 8f2e0f722..1c2a46ec2 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java @@ -47,8 +47,13 @@ public class ManifestYmlSchema implements YamlSchema { return IntegerRange.exactly(1); } - public ManifestYmlSchema(Callable> buildpackProvider, Callable> servicesProvider) { - this.buildpackProvider = buildpackProvider; + + public ManifestYmlSchema(ManifestYmlHintProviders providers) { + this.buildpackProvider = providers.getBuildpackProviders(); + Callable> servicesProvider = providers.getServicesProvider(); + Callable> domainsProvider = providers.getDomainsProvider(); + + YTypeFactory f = new YTypeFactory(); TYPE_UTIL = f.TYPE_UTIL; @@ -63,7 +68,17 @@ public class ManifestYmlSchema implements YamlSchema { t_buildpack.addHintProvider(this.buildpackProvider); // t_buildpack.parseWith(ManifestYmlValueParsers.fromHints(t_buildpack.toString(), buildpackProvider)); } + + YAtomicType t_domain = f.yatomic("Domain"); + YAtomicType t_domains_string = f.yatomic("Domains"); + if (domainsProvider != null) { + t_domain.addHintProvider(domainsProvider); + t_domains_string.addHintProvider(domainsProvider); + } + + YType t_domains = f.yseq(t_domains_string); + YAtomicType t_service_string = f.yatomic("Service"); if (servicesProvider != null) { t_service_string.addHintProvider(servicesProvider); @@ -112,8 +127,8 @@ public class ManifestYmlSchema implements YamlSchema { f.yprop("buildpack", t_buildpack), f.yprop("command", t_string), f.yprop("disk_quota", t_memory), - f.yprop("domain", t_string), - f.yprop("domains", t_strings), + f.yprop("domain", t_domain), + f.yprop("domains", t_domains), f.yprop("env", t_env), f.yprop("host", t_string), f.yprop("hosts", t_strings), diff --git a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java index 4444cf279..94d089d50 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java +++ b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java @@ -25,6 +25,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFBuildpack; +import org.springframework.ide.vscode.commons.cloudfoundry.client.CFDomain; import org.springframework.ide.vscode.commons.cloudfoundry.client.CFServiceInstance; import org.springframework.ide.vscode.commons.cloudfoundry.client.ClientRequests; import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.NoTargetsException; @@ -936,6 +937,44 @@ public class ManifestYamlEditorTest { when(cfClient.getBuildpacks()).thenReturn(ImmutableList.of(buildPack)); assertDoesNotContainCompletions("buildpack: <*>", "buildpack: wrong_buildpack<*>"); } + + @Test + public void domainContentAssist() throws Exception { + ClientRequests cfClient = cloudfoundry.client; + CFDomain domain = Mockito.mock(CFDomain.class); + when(domain.getName()).thenReturn("cfapps.io"); + when(cfClient.getDomains()).thenReturn(ImmutableList.of(domain)); + + assertContainsCompletions("domain: <*>", "domain: cfapps.io<*>"); + } + + @Test + public void domainContentAssistDoesNotContainCompletion() throws Exception { + ClientRequests cfClient = cloudfoundry.client; + CFDomain domain = Mockito.mock(CFDomain.class); + when(domain.getName()).thenReturn("cfapps.io"); + when(cfClient.getDomains()).thenReturn(ImmutableList.of(domain)); + assertDoesNotContainCompletions("domain: <*>", "domain: wrong.cfapps.io<*>"); + } + + @Test + public void domainsContentAssist() throws Exception { + ClientRequests cfClient = cloudfoundry.client; + CFDomain domain = Mockito.mock(CFDomain.class); + when(domain.getName()).thenReturn("cfapps.io"); + when(cfClient.getDomains()).thenReturn(ImmutableList.of(domain)); + + assertContainsCompletions("domains:\n" + " - <*>", "cfapps.io"); + } + + @Test + public void domainsContentAssistWrongDomain() throws Exception { + ClientRequests cfClient = cloudfoundry.client; + CFDomain domain = Mockito.mock(CFDomain.class); + when(domain.getName()).thenReturn("cfapps.io"); + when(cfClient.getDomains()).thenReturn(ImmutableList.of(domain)); + assertDoesNotContainCompletions("domains:\n" + " - <*>", "wrong.cfapps.io"); + } ////////////////////////////////////////////////////////////////////////////// diff --git a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java index 5d7af42c8..a1e726ee9 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java +++ b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java @@ -14,13 +14,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import org.junit.Test; import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.util.StringUtil; import org.springframework.ide.vscode.commons.yaml.schema.YTypedProperty; +import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YSeqType; import org.springframework.ide.vscode.manifest.yaml.ManifestYmlSchema; @@ -81,7 +84,7 @@ public class ManifestYmlSchemaTest { "timeout" }; - ManifestYmlSchema schema = new ManifestYmlSchema(null, null); + ManifestYmlSchema schema = new ManifestYmlSchema(EMPTY_PROVIDERS); @Test public void toplevelProperties() throws Exception { @@ -150,5 +153,22 @@ public class ManifestYmlSchemaTest { } return builder.build(); } - + + private static final ManifestYmlHintProviders EMPTY_PROVIDERS = new ManifestYmlHintProviders() { + + @Override + public Callable> getServicesProvider() { + return null; + } + + @Override + public Callable> getDomainsProvider() { + return null; + } + + @Override + public Callable> getBuildpackProviders() { + return null; + } + }; } From 4e826a91d71851667dbd6d94fd7565e2e1ca02d2 Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 10 Feb 2017 12:14:22 -0800 Subject: [PATCH 08/28] Renamed domains provider --- ...DomainProvider.java => ManifestYamlCFDomainsProvider.java} | 4 ++-- .../ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/{ManifestYamlCFDomainProvider.java => ManifestYamlCFDomainsProvider.java} (93%) diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainProvider.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java similarity index 93% rename from vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainProvider.java rename to vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java index 235b162e2..141951149 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainProvider.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java @@ -20,9 +20,9 @@ import org.springframework.ide.vscode.commons.cloudfoundry.client.cftarget.CFTar import org.springframework.ide.vscode.commons.yaml.schema.BasicYValueHint; import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; -public class ManifestYamlCFDomainProvider extends AbstractCFHintsProvider { +public class ManifestYamlCFDomainsProvider extends AbstractCFHintsProvider { - public ManifestYamlCFDomainProvider(CFTargetCache cache) { + public ManifestYamlCFDomainsProvider(CFTargetCache cache) { super(cache); } diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java index 9d5e46522..bce95e518 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlLanguageServer.java @@ -137,7 +137,7 @@ public class ManifestYamlLanguageServer extends SimpleLanguageServer { } private Callable> getDomainsProvider() { - return new ManifestYamlCFDomainProvider(getCfTargetCache()); + return new ManifestYamlCFDomainsProvider(getCfTargetCache()); } @Override From e0dd23498fa3bc8abed17602991eb0c7d013c2f6 Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 10 Feb 2017 13:21:26 -0800 Subject: [PATCH 09/28] Added unknown host test for domains --- .../vscode/commons/cloudfoundry/client/CFClientTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vscode-extensions/commons/commons-cf/src/test/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFClientTest.java b/vscode-extensions/commons/commons-cf/src/test/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFClientTest.java index ddf745ba2..e53dddd29 100644 --- a/vscode-extensions/commons/commons-cf/src/test/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFClientTest.java +++ b/vscode-extensions/commons/commons-cf/src/test/java/org/springframework/ide/vscode/commons/cloudfoundry/client/CFClientTest.java @@ -73,6 +73,14 @@ public class CFClientTest { CFTarget target = targetCache.getOrCreate().get(0); assertError(() -> target.getBuildpacks(), ConnectionException.class, expectedMessages.noNetworkConnection()); } + + @Test + public void testUnknownHostDomains() throws Exception { + ClientRequests client = cloudfoundry.client; + when(client.getDomains()).thenThrow(new UnknownHostException("api.run.pivotal.io")); + CFTarget target = targetCache.getOrCreate().get(0); + assertError(() -> target.getDomains(), ConnectionException.class, expectedMessages.noNetworkConnection()); + } @Test public void testBuildpacksFromTarget() throws Exception { From d68745d3fa7014f21e3e06c1db8cc0afdb1a724f Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 10 Feb 2017 13:25:08 -0800 Subject: [PATCH 10/28] Renamed domains label method --- .../vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java index 141951149..c2b0f0c5f 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java @@ -38,7 +38,7 @@ public class ManifestYamlCFDomainsProvider extends AbstractCFHintsProvider { for (CFDomain domain : domains) { String name = domain.getName(); - String label = getBuildpackLabel(cfTarget, domain); + String label = getLabel(cfTarget, domain); YValueHint hint = new BasicYValueHint(name, label); if (!hints.contains(hint)) { hints.add(hint); @@ -53,7 +53,7 @@ public class ManifestYamlCFDomainsProvider extends AbstractCFHintsProvider { return !hints.isEmpty() ? hints : null; } - protected String getBuildpackLabel(CFTarget target, CFDomain domain) { + protected String getLabel(CFTarget target, CFDomain domain) { return domain.getName() + " (" + target.getName() + ")"; } From c290646ff19479e9bd8fe3c9a497221d0139ab4a Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 10 Feb 2017 13:58:06 -0800 Subject: [PATCH 11/28] Added routes property to manifest yaml schema --- .../manifest/yaml/ManifestYmlSchema.java | 1 + .../description-by-prop-name/routes.html | 12 +++++++++++ .../description-by-prop-name/routes.md | 14 +++++++++++++ .../manifest/yaml/ManifestYamlEditorTest.java | 20 +++++++++++++++++++ .../manifest/yaml/ManifestYmlSchemaTest.java | 2 ++ 5 files changed, 49 insertions(+) create mode 100644 vscode-extensions/vscode-manifest-yaml/src/main/resources/description-by-prop-name/routes.html create mode 100644 vscode-extensions/vscode-manifest-yaml/src/main/resources/description-by-prop-name/routes.md diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java index 1c2a46ec2..6672b8fdf 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java @@ -139,6 +139,7 @@ public class ManifestYmlSchema implements YamlSchema { f.yprop("no-route", t_boolean), f.yprop("path", t_path), f.yprop("random-route", t_boolean), + f.yprop("routes", t_strings), f.yprop("services", t_services), f.yprop("stack", t_string), f.yprop("timeout", t_pos_integer), diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/resources/description-by-prop-name/routes.html b/vscode-extensions/vscode-manifest-yaml/src/main/resources/description-by-prop-name/routes.html new file mode 100644 index 000000000..49b219ea2 --- /dev/null +++ b/vscode-extensions/vscode-manifest-yaml/src/main/resources/description-by-prop-name/routes.html @@ -0,0 +1,12 @@ +

Use the routes attribute to provide multiple HTTP and TCP routes. Each route for this app is created if it does not already exist.

+

This attribute is a combination of push options that include --hostname, -d, and --route-path.

+
+---
+  ...
+  routes:
+  - route: example.com
+  - route: www.example.com/foo
+  - route: tcp-example.com:1234
+
+ +

The routes attribute cannot be used in conjunction with the following attributes: host, hosts, domain, domains, and no-hostname. An error will result.

diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/resources/description-by-prop-name/routes.md b/vscode-extensions/vscode-manifest-yaml/src/main/resources/description-by-prop-name/routes.md new file mode 100644 index 000000000..754921e46 --- /dev/null +++ b/vscode-extensions/vscode-manifest-yaml/src/main/resources/description-by-prop-name/routes.md @@ -0,0 +1,14 @@ +Use the `routes` attribute to provide multiple HTTP and TCP routes. Each route for this app is created if it does not already exist. + +This attribute is a combination of `push` options that include `--hostname`, `-d`, and `--route-path`. + +``` +--- + ... + routes: + - route: example.com + - route: www.example.com/foo + - route: tcp-example.com:1234 +``` + +The `routes` attribute cannot be used in conjunction with the following attributes: `host`, `hosts`, `domain`, `domains`, and `no-hostname`. An error will result. \ No newline at end of file diff --git a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java index 94d089d50..fc228b671 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java +++ b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java @@ -279,6 +279,9 @@ public class ManifestYamlEditorTest { // --------------- "random-route: <*>", // --------------- + "routes:\n"+ + "- <*>", + // --------------- "services:\n"+ "- <*>", // --------------- @@ -354,6 +357,10 @@ public class ManifestYamlEditorTest { "- random-route: <*>", // --------------- "applications:\n" + + "- routes:\n"+ + " - <*>", + // --------------- + "applications:\n" + "- services:\n"+ " - <*>", // --------------- @@ -432,6 +439,8 @@ public class ManifestYamlEditorTest { " no-route: true\n" + " path: somepath/app.jar\n" + " random-route: true\n" + + " routes:\n" + + " - tcp-example.com:1234\n" + " services:\n" + " - instance_ABC\n" + " - instance_XYZ\n" + @@ -457,6 +466,7 @@ public class ManifestYamlEditorTest { editor.assertIsHoverRegion("no-route"); editor.assertIsHoverRegion("path"); editor.assertIsHoverRegion("random-route"); + editor.assertIsHoverRegion("routes"); editor.assertIsHoverRegion("services"); editor.assertIsHoverRegion("stack"); editor.assertIsHoverRegion("timeout"); @@ -479,6 +489,7 @@ public class ManifestYamlEditorTest { editor.assertHoverContains("no-route", "You can use the `no-route` attribute with a value of `true` to prevent a route from being created for your application"); editor.assertHoverContains("path", "You can use the `path` attribute to tell Cloud Foundry where to find your application"); editor.assertHoverContains("random-route", "Use the `random-route` attribute to create a URL that includes the app name and random words"); + editor.assertHoverContains("routes", "Each route for this app is created if it does not already exist"); editor.assertHoverContains("services", "The `services` block consists of a heading, then one or more service instance names"); editor.assertHoverContains("stack", "Use the `stack` attribute to specify which stack to deploy your application to."); editor.assertHoverContains("timeout", "The `timeout` attribute defines the number of seconds Cloud Foundry allocates for starting your application"); @@ -596,6 +607,10 @@ public class ManifestYamlEditorTest { "- random-route: <*>", // --------------- "applications:\n" + + "- routes:\n"+ + " - <*>", + // --------------- + "applications:\n" + "- services:\n"+ " - <*>", // --------------- @@ -680,6 +695,11 @@ public class ManifestYamlEditorTest { "- name: test" , // --------------------- "applications:\n" + + "- routes:\n" + + " - <*>\n" + + "- name: test" + ,// --------------------- + "applications:\n" + "- services:\n" + " - <*>\n" + "- name: test" diff --git a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java index a1e726ee9..1c98a65bd 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java +++ b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchemaTest.java @@ -55,6 +55,7 @@ public class ManifestYmlSchemaTest { "no-route", "path", "random-route", + "routes", "services", "stack", "timeout" @@ -79,6 +80,7 @@ public class ManifestYmlSchemaTest { "no-route", "path", "random-route", + "routes", "services", "stack", "timeout" From 528bf7996ad627b5b605827d0d56af1b80f32de4 Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 10 Feb 2017 15:41:29 -0800 Subject: [PATCH 12/28] Fixed YType of routes property Should be a map, not a String. --- .../ide/vscode/manifest/yaml/ManifestYmlSchema.java | 5 ++++- .../ide/vscode/manifest/yaml/ManifestYamlEditorTest.java | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java index 6672b8fdf..155f2f727 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java @@ -93,6 +93,9 @@ public class ManifestYmlSchema implements YamlSchema { YType t_string = f.yatomic("String"); YType t_strings = f.yseq(t_string); + YType t_route = f.ymap(t_string, t_string); + YType t_routes = f.yseq(t_route); + YAtomicType t_memory = f.yatomic("Memory"); t_memory.addHints("256M", "512M", "1024M"); t_memory.parseWith(ManifestYmlValueParsers.MEMORY); @@ -139,7 +142,7 @@ public class ManifestYmlSchema implements YamlSchema { f.yprop("no-route", t_boolean), f.yprop("path", t_path), f.yprop("random-route", t_boolean), - f.yprop("routes", t_strings), + f.yprop("routes", t_routes), f.yprop("services", t_services), f.yprop("stack", t_string), f.yprop("timeout", t_pos_integer), diff --git a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java index fc228b671..8534b6806 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java +++ b/vscode-extensions/vscode-manifest-yaml/src/test/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlEditorTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. +f * Copyright (c) 2016, 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 @@ -440,7 +440,7 @@ public class ManifestYamlEditorTest { " path: somepath/app.jar\n" + " random-route: true\n" + " routes:\n" + - " - tcp-example.com:1234\n" + + " - route: tcp-example.com:1234\n" + " services:\n" + " - instance_ABC\n" + " - instance_XYZ\n" + From 91a560b81e795cf4176d6ae83a76fccfb5c721b7 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Sat, 11 Feb 2017 16:27:02 +0100 Subject: [PATCH 13/28] updated to changed versions of textmate.java --- .../META-INF/MANIFEST.MF | 4 ++-- .../META-INF/MANIFEST.MF | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/eclipse-language-servers/org.springframework.boot.ide.cloudfoundry.server/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.boot.ide.cloudfoundry.server/META-INF/MANIFEST.MF index f27450134..89b9d13c4 100644 --- a/eclipse-language-servers/org.springframework.boot.ide.cloudfoundry.server/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.boot.ide.cloudfoundry.server/META-INF/MANIFEST.MF @@ -10,8 +10,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0", org.eclipse.ui.genericeditor;bundle-version="1.0.0", org.eclipse.jface.text;bundle-version="3.11.100", org.eclipse.jdt.ui;bundle-version="3.13.0", - org.eclipse.tm4e.core;bundle-version="1.0.0", - org.eclipse.tm4e.ui;bundle-version="1.0.0" + org.eclipse.tm4e.core;bundle-version="0.1.0", + org.eclipse.tm4e.ui;bundle-version="0.1.0" Import-Package: org.eclipse.jface.preference, org.eclipse.lsp4j.jsonrpc.messages;version="0.1.0.v20170117-0759", org.eclipse.lsp4j.services;version="0.1.0.v20170117-0759", diff --git a/eclipse-language-servers/org.springframework.boot.ide.properties.servers/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.boot.ide.properties.servers/META-INF/MANIFEST.MF index c4e708121..ee2ce9ef6 100644 --- a/eclipse-language-servers/org.springframework.boot.ide.properties.servers/META-INF/MANIFEST.MF +++ b/eclipse-language-servers/org.springframework.boot.ide.properties.servers/META-INF/MANIFEST.MF @@ -10,8 +10,8 @@ Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0", org.eclipse.ui.genericeditor;bundle-version="1.0.0", org.eclipse.jface.text;bundle-version="3.11.100", org.eclipse.jdt.ui;bundle-version="3.13.0", - org.eclipse.tm4e.core;bundle-version="1.0.0", - org.eclipse.tm4e.ui;bundle-version="1.0.0", + org.eclipse.tm4e.core;bundle-version="0.1.0", + org.eclipse.tm4e.ui;bundle-version="0.1.0", org.eclipse.lsp4j, org.eclipse.ui.workbench, org.eclipse.jface From a5e9f45ea1abd227ef2f969974392bd115063c56 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Sat, 11 Feb 2017 16:29:26 +0100 Subject: [PATCH 14/28] implemented simple content-assist contributor for JDT that connects to an LS --- .../build.properties | 1 + .../feature.xml | 21 +++ .../pom.xml | 15 +++ .../.classpath | 7 + .../.project | 28 ++++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../META-INF/MANIFEST.MF | 21 +++ .../build.properties | 6 + .../plugin.xml | 29 ++++ .../pom.xml | 56 ++++++++ .../boot/ide/java/servers/Constants.java | 20 +++ ...ingBootJavaCompletionProposalComputer.java | 64 +++++++++ .../servers/SpringBootJavaLanguageServer.java | 126 ++++++++++++++++++ .../category.xml | 4 + eclipse-language-servers/pom.xml | 8 ++ 15 files changed, 413 insertions(+) create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/build.properties create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/feature.xml create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/pom.xml create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/.classpath create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/.project create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/.settings/org.eclipse.jdt.core.prefs create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/META-INF/MANIFEST.MF create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/build.properties create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/plugin.xml create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/pom.xml create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/Constants.java create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaCompletionProposalComputer.java create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaLanguageServer.java diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/build.properties b/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/build.properties new file mode 100644 index 000000000..64f93a9f0 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/build.properties @@ -0,0 +1 @@ +bin.includes = feature.xml diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/feature.xml b/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/feature.xml new file mode 100644 index 000000000..310128412 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/feature.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/pom.xml b/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/pom.xml new file mode 100644 index 000000000..e8c8474ff --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers.feature/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + org.springframework.boot.ide + org.springframework.boot.ide.servers + 4.0.0-SNAPSHOT + ../pom.xml + + + org.springframework.boot.ide.java.servers.feature + eclipse-feature + + diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/.classpath b/eclipse-language-servers/org.springframework.boot.ide.java.servers/.classpath new file mode 100644 index 000000000..eca7bdba8 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/.project b/eclipse-language-servers/org.springframework.boot.ide.java.servers/.project new file mode 100644 index 000000000..1dac181c4 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/.project @@ -0,0 +1,28 @@ + + + org.springframework.boot.ide.java.servers + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/.settings/org.eclipse.jdt.core.prefs b/eclipse-language-servers/org.springframework.boot.ide.java.servers/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..0c68a61dc --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/META-INF/MANIFEST.MF b/eclipse-language-servers/org.springframework.boot.ide.java.servers/META-INF/MANIFEST.MF new file mode 100644 index 000000000..e93d746a6 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Servers +Bundle-SymbolicName: org.springframework.boot.ide.java.servers;singleton:=true +Bundle-Version: 4.0.0.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.eclipse.jdt.launching;bundle-version="3.9.0", + org.eclipse.core.runtime;bundle-version="3.12.0", + org.eclipse.lsp4e;bundle-version="0.1.0", + org.eclipse.ui.genericeditor;bundle-version="1.0.0", + org.eclipse.jface.text;bundle-version="3.11.100", + org.eclipse.jdt.ui;bundle-version="3.13.0", + org.eclipse.lsp4j, + org.eclipse.ui.workbench, + org.eclipse.jface, + org.eclipse.xtext.xbase.lib +Import-Package: com.google.gson;version="2.7.0", + org.eclipse.jface.preference, + org.eclipse.lsp4j.jsonrpc.messages;version="0.1.0.v20170117-0759", + org.eclipse.lsp4j.services;version="0.1.0.v20170117-0759", + org.osgi.framework diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/build.properties b/eclipse-language-servers/org.springframework.boot.ide.java.servers/build.properties new file mode 100644 index 000000000..3b7db0f69 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/build.properties @@ -0,0 +1,6 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + servers/ diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/plugin.xml b/eclipse-language-servers/org.springframework.boot.ide.java.servers/plugin.xml new file mode 100644 index 000000000..64c30df54 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/plugin.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/pom.xml b/eclipse-language-servers/org.springframework.boot.ide.java.servers/pom.xml new file mode 100644 index 000000000..1308d7ebd --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + + org.springframework.boot.ide + org.springframework.boot.ide.servers + 4.0.0-SNAPSHOT + ../pom.xml + + + org.springframework.boot.ide.java.servers + eclipse-plugin + + + + org.springframework.ide.vscode + vscode-boot-java + 0.0.1-SNAPSHOT + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + copy + prepare-package + + copy + + + + + org.springframework.ide.vscode + vscode-boot-java + true + ${project.build.directory}/../servers + + + true + true + + + + + + + + diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/Constants.java b/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/Constants.java new file mode 100644 index 000000000..622375721 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/Constants.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * 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.boot.ide.java.servers; + +/** + * @author Martin Lippert + */ +public class Constants { + + public static final String PLUGIN_ID = "org.springframework.boot.ide.java.servers"; + +} diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaCompletionProposalComputer.java b/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaCompletionProposalComputer.java new file mode 100644 index 000000000..fd1b72ae7 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaCompletionProposalComputer.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * 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.boot.ide.java.servers; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext; +import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.lsp4e.operations.completion.LSContentAssistProcessor; + +/** + * @author Martin Lippert + */ +@SuppressWarnings("restriction") +public class SpringBootJavaCompletionProposalComputer implements IJavaCompletionProposalComputer { + + private LSContentAssistProcessor lsContentAssistProcessor; + + public SpringBootJavaCompletionProposalComputer() { + lsContentAssistProcessor = new LSContentAssistProcessor(); + } + + @Override + public void sessionStarted() { + } + + @Override + public List computeCompletionProposals(ContentAssistInvocationContext context, + IProgressMonitor monitor) { + System.out.println("Spring Boot Java Completion Proposal - compute completion proposals"); + + ICompletionProposal[] proposals = lsContentAssistProcessor.computeCompletionProposals(context.getViewer(), context.getInvocationOffset()); + return Arrays.asList(proposals); + } + + @Override + public List computeContextInformation(ContentAssistInvocationContext context, + IProgressMonitor monitor) { + IContextInformation[] contextInformation = lsContentAssistProcessor.computeContextInformation(context.getViewer(), context.getInvocationOffset()); + return Arrays.asList(contextInformation); + } + + @Override + public String getErrorMessage() { + return lsContentAssistProcessor.getErrorMessage(); + } + + @Override + public void sessionEnded() { + } + +} diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaLanguageServer.java b/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaLanguageServer.java new file mode 100644 index 000000000..4369f3e62 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaLanguageServer.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * 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.boot.ide.java.servers; + +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jdt.internal.launching.StandardVMType; +import org.eclipse.jdt.launching.IVMInstall; +import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider; +import org.eclipse.lsp4j.jsonrpc.messages.Message; +import org.eclipse.lsp4j.jsonrpc.messages.NotificationMessage; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.ui.PlatformUI; +import org.osgi.framework.Bundle; + +import com.google.gson.JsonObject; + +/** + * @author Martin Lippert + */ +@SuppressWarnings("restriction") +public class SpringBootJavaLanguageServer extends ProcessStreamConnectionProvider { + + public SpringBootJavaLanguageServer() { + List commands = new ArrayList<>(); + commands.add(getJDKLocation()); + + commands.add("-Xdebug"); + commands.add("-Xrunjdwp:server=y,transport=dt_socket,address=4000,suspend=n"); + + commands.add("-jar"); + commands.add(getLanguageServerJARLocation()); + + String workingDir = getWorkingDirLocation(); + + setCommands(commands); + setWorkingDirectory(workingDir); + } + + public void handleMessage(Message message, LanguageServer languageServer, String rootPath) { + if (message instanceof NotificationMessage) { + NotificationMessage notificationMessage = (NotificationMessage) message; + if ("sts/progress".equals(notificationMessage.getMethod())) { + JsonObject params = (JsonObject) notificationMessage.getParams(); + String status = params.has("statusMsg") ? params.get("statusMsg").getAsString() : ""; + showStatusMessage(status); + } + } + } + + private void showStatusMessage(final String status) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + IStatusLineManager statusLineManager = getStatusLineManager(); + if (statusLineManager != null) { + statusLineManager.setMessage(status); + } + } + }); + } + + private IStatusLineManager getStatusLineManager() { + try { + return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor().getEditorSite().getActionBars().getStatusLineManager(); + } + catch (NullPointerException e) { + return null; + } + } + + protected String getJDKLocation() { + IVMInstall jdk = JavaRuntime.getDefaultVMInstall(); + File javaExecutable = StandardVMType.findJavaExecutable(jdk.getInstallLocation()); + return javaExecutable.getAbsolutePath(); + } + + protected String getLanguageServerJARLocation() { + String languageServer = "vscode-boot-java-0.0.1-SNAPSHOT.jar"; + + Bundle bundle = Platform.getBundle(Constants.PLUGIN_ID); + File dataFile = bundle.getDataFile(languageServer); +// if (!dataFile.exists()) { + try { + copyLanguageServerJAR(languageServer); + } + catch (Exception e) { + e.printStackTrace(); + } +// } + + return dataFile.getAbsolutePath(); + } + + protected String getWorkingDirLocation() { + // TODO: identify a reasonable working directory for the language server process + return System.getProperty("user.dir"); + } + + protected void copyLanguageServerJAR(String languageServerJarName) throws Exception { + Bundle bundle = Platform.getBundle(Constants.PLUGIN_ID); + InputStream stream = FileLocator.openStream( bundle, new Path("servers/" + languageServerJarName), false ); + + File dataFile = bundle.getDataFile(languageServerJarName); + Files.copy(stream, dataFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + +} diff --git a/eclipse-language-servers/org.springframework.boot.ide.servers.repository/category.xml b/eclipse-language-servers/org.springframework.boot.ide.servers.repository/category.xml index d11905dad..0f9aacb75 100644 --- a/eclipse-language-servers/org.springframework.boot.ide.servers.repository/category.xml +++ b/eclipse-language-servers/org.springframework.boot.ide.servers.repository/category.xml @@ -10,6 +10,10 @@ + + + + diff --git a/eclipse-language-servers/pom.xml b/eclipse-language-servers/pom.xml index bd1f778a1..1ffa6e0e4 100644 --- a/eclipse-language-servers/pom.xml +++ b/eclipse-language-servers/pom.xml @@ -30,6 +30,9 @@ org.springframework.boot.ide.properties.servers org.springframework.boot.ide.properties.servers.feature + org.springframework.boot.ide.java.servers + org.springframework.boot.ide.java.servers.feature + org.springframework.boot.ide.cloudfoundry.server org.springframework.boot.ide.cloudfoundry.server.feature @@ -81,6 +84,11 @@ p2 http://download.eclipse.org/tools/orbit/downloads/drops/S20161205183421/repository/ + + lsp4j-snapshots + p2 + http://services.typefox.io/open-source/jenkins/job/lsp4j/job/master/lastStableBuild/artifact/build/p2-repository/ + lsp4e p2 From 5e61656faa49be88940bbd063c188664491e7d27 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Sat, 11 Feb 2017 16:30:11 +0100 Subject: [PATCH 15/28] replaced code duplication with original spring boot configuration metadata implementation --- vscode-extensions/vscode-boot-java/pom.xml | 29 +-- .../ConfigurationMetadataGroup.java | 76 ------ .../ConfigurationMetadataHint.java | 74 ------ .../ConfigurationMetadataItem.java | 60 ----- .../ConfigurationMetadataProperty.java | 190 -------------- .../ConfigurationMetadataRepository.java | 47 ---- ...gurationMetadataRepositoryJsonBuilder.java | 231 ------------------ .../ConfigurationMetadataSource.java | 131 ---------- .../configurationmetadata/Deprecation.java | 66 ----- .../DescriptionExtractor.java | 58 ----- .../boot/configurationmetadata/Hints.java | 82 ------- .../configurationmetadata/JsonReader.java | 195 --------------- .../boot/configurationmetadata/README.txt | 10 - .../RawConfigurationMetadata.java | 106 -------- ...SimpleConfigurationMetadataRepository.java | 130 ---------- .../boot/configurationmetadata/ValueHint.java | 97 -------- .../configurationmetadata/ValueProvider.java | 66 ----- .../configurationmetadata/package-info.java | 20 -- .../boot/metadata/MetadataManipulator.java | 11 +- .../boot/metadata/PropertiesLoader.java | 8 +- 20 files changed, 25 insertions(+), 1662 deletions(-) delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataGroup.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataHint.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataItem.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataProperty.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepository.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilder.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataSource.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/DescriptionExtractor.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Hints.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/README.txt delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/RawConfigurationMetadata.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueHint.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueProvider.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/package-info.java diff --git a/vscode-extensions/vscode-boot-java/pom.xml b/vscode-extensions/vscode-boot-java/pom.xml index 7214d4cb3..de3f34aba 100644 --- a/vscode-extensions/vscode-boot-java/pom.xml +++ b/vscode-extensions/vscode-boot-java/pom.xml @@ -4,14 +4,14 @@ 4.0.0 vscode-boot-java jar - + org.springframework.ide.vscode commons-parent 0.0.1-SNAPSHOT ../commons/pom.xml - + @@ -22,12 +22,8 @@ true - - project-repo - file://${project.basedir}/repo - - + distribution-repository @@ -35,13 +31,8 @@ file://${basedir}/dist - + - - org.springframework.ide.eclipse - org.json - 1.0 - org.springframework.ide.vscode commons-maven @@ -57,7 +48,7 @@ commons-language-server ${project.version} - + org.eclipse.jdt @@ -71,6 +62,12 @@ 2.4 + + org.springframework.boot + spring-boot-configuration-metadata + 1.5.1.RELEASE + + org.springframework.ide.vscode @@ -78,7 +75,7 @@ ${project.version} test - + @@ -116,6 +113,6 @@ - + diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataGroup.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataGroup.java deleted file mode 100644 index ea6428e85..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataGroup.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -/** - * Gather a collection of {@link ConfigurationMetadataProperty properties} that are - * sharing a {@link #getId() common prefix}. Provide access to all the - * {@link ConfigurationMetadataSource sources} that have contributed properties to the - * group. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -@SuppressWarnings("serial") -public class ConfigurationMetadataGroup implements Serializable { - - private final String id; - - private final Map sources = new HashMap(); - - private final Map properties = new HashMap(); - - public ConfigurationMetadataGroup(String id) { - this.id = id; - } - - /** - * Return the id of the group, used as a common prefix for all properties associated - * to it. - * @return the id of the group - */ - public String getId() { - return this.id; - } - - /** - * Return the {@link ConfigurationMetadataSource sources} defining the properties of - * this group. - * @return the sources of the group - */ - public Map getSources() { - return this.sources; - } - - /** - * Return the {@link ConfigurationMetadataProperty properties} defined in this group. - *

- * A property may appear more than once for a given source, potentially with - * conflicting type or documentation. This is a "merged" view of the properties of - * this group. - * @return the properties of the group - * @see ConfigurationMetadataSource#getProperties() - */ - public Map getProperties() { - return this.properties; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataHint.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataHint.java deleted file mode 100644 index 9f5e1f204..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataHint.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.util.ArrayList; -import java.util.List; - -/** - * A raw view of a hint used for parsing only. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -class ConfigurationMetadataHint { - - private static final String KEY_SUFFIX = ".keys"; - - private static final String VALUE_SUFFIX = ".values"; - - private String id; - - private final List valueHints = new ArrayList(); - - private final List valueProviders = new ArrayList(); - - public boolean isMapKeyHints() { - return (this.id != null && this.id.endsWith(KEY_SUFFIX)); - } - - public boolean isMapValueHints() { - return (this.id != null && this.id.endsWith(VALUE_SUFFIX)); - } - - public String resolveId() { - if (isMapKeyHints()) { - return this.id.substring(0, this.id.length() - KEY_SUFFIX.length()); - } - if (isMapValueHints()) { - return this.id.substring(0, this.id.length() - VALUE_SUFFIX.length()); - } - return this.id; - } - - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - - public List getValueHints() { - return this.valueHints; - } - - public List getValueProviders() { - return this.valueProviders; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataItem.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataItem.java deleted file mode 100644 index 4001c4da1..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataItem.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -/** - * An extension of {@link ConfigurationMetadataProperty} that provides a reference to its - * source. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -class ConfigurationMetadataItem extends ConfigurationMetadataProperty { - - private String sourceType; - - private String sourceMethod; - - /** - * The class name of the source that contributed this property. For example, if the - * property was from a class annotated with {@code @ConfigurationProperties} this - * attribute would contain the fully qualified name of that class. - * @return the source type - */ - public String getSourceType() { - return this.sourceType; - } - - public void setSourceType(String sourceType) { - this.sourceType = sourceType; - } - - /** - * The full name of the method (including parenthesis and argument types) that - * contributed this property. For example, the name of a getter in a - * {@code @ConfigurationProperties} annotated class. - * @return the source method - */ - public String getSourceMethod() { - return this.sourceMethod; - } - - public void setSourceMethod(String sourceMethod) { - this.sourceMethod = sourceMethod; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataProperty.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataProperty.java deleted file mode 100644 index 997746490..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataProperty.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.Serializable; -import java.util.List; - -/** - * Define a configuration property. Each property is fully identified by its - * {@link #getId() id} which is composed of a namespace prefix (the - * {@link ConfigurationMetadataGroup#getId() group id}), if any and the {@link #getName() - * name} of the property. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -@SuppressWarnings("serial") -public class ConfigurationMetadataProperty implements Serializable { - - private String id; - - private String name; - - private String type; - - private String description; - - private String shortDescription; - - private Object defaultValue; - - private final Hints hints = new Hints(); - - private Deprecation deprecation; - - /** - * The full identifier of the property, in lowercase dashed form (e.g. - * my.group.simple-property) - * @return the property id - */ - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - - /** - * The name of the property, in lowercase dashed form (e.g. simple-property). If this - * item does not belong to any group, the id is returned. - * @return the property name - */ - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - /** - * The class name of the data type of the property. For example, - * {@code java.lang.String}. - *

- * For consistency, the type of a primitive is specified using its wrapper - * counterpart, i.e. {@code boolean} becomes {@code java.lang.Boolean}. If the type - * holds generic information, these are provided as well, i.e. a {@code HashMap} of - * String to Integer would be defined as {@code java.util.HashMap - * }. - *

- * Note that this class may be a complex type that gets converted from a String as - * values are bound. - * @return the property type - */ - public String getType() { - return this.type; - } - - public void setType(String type) { - this.type = type; - } - - /** - * A description of the property, if any. Can be multi-lines. - * @return the property description - * @see #getShortDescription() - */ - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - /** - * A single-line, single-sentence description of this property, if any. - * @return the property short description - * @see #getDescription() - */ - public String getShortDescription() { - return this.shortDescription; - } - - public void setShortDescription(String shortDescription) { - this.shortDescription = shortDescription; - } - - /** - * The default value, if any. - * @return the default value - */ - public Object getDefaultValue() { - return this.defaultValue; - } - - public void setDefaultValue(Object defaultValue) { - this.defaultValue = defaultValue; - } - - /** - * Return the hints of this item. - * @return the hints - */ - public Hints getHints() { - return this.hints; - } - - /** - * The list of well-defined values, if any. If no extra {@link ValueProvider provider} - * is specified, these values are to be considered a closed-set of the available - * values for this item. - * @return the value hints - * @see #getHints() - */ - @Deprecated - public List getValueHints() { - return this.hints.getValueHints(); - } - - /** - * The value providers that are applicable to this item. Only one - * {@link ValueProvider} is enabled for an item: the first in the list that is - * supported should be used. - * @return the value providers - * @see #getHints() - */ - @Deprecated - public List getValueProviders() { - return this.hints.getValueProviders(); - } - - /** - * The {@link Deprecation} for this property, if any. - * @return the deprecation - * @see #isDeprecated() - */ - public Deprecation getDeprecation() { - return this.deprecation; - } - - public void setDeprecation(Deprecation deprecation) { - this.deprecation = deprecation; - } - - /** - * Specify if the property is deprecated. - * @return if the property is deprecated - * @see #getDeprecation() - */ - public boolean isDeprecated() { - return this.deprecation != null; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepository.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepository.java deleted file mode 100644 index 95122ac2a..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepository.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.util.Map; - -/** - * A repository of configuration metadata. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public interface ConfigurationMetadataRepository { - - /** - * Defines the name of the "root" group, that is the group that gathers all the - * properties that aren't attached to a specific group. - */ - String ROOT_GROUP = "_ROOT_GROUP_"; - - /** - * Return the groups, indexed by id. - * @return all configuration meta-data groups - */ - Map getAllGroups(); - - /** - * Return the properties, indexed by id. - * @return all configuration meta-data properties - */ - Map getAllProperties(); - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilder.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilder.java deleted file mode 100644 index b7d096fd8..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataRepositoryJsonBuilder.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.springframework.ide.eclipse.org.json.JSONException; - -/** - * Load a {@link ConfigurationMetadataRepository} from the content of arbitrary - * resource(s). - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -public final class ConfigurationMetadataRepositoryJsonBuilder { - - /** - * UTF-8 Charset. - */ - public static final Charset UTF_8 = Charset.forName("UTF-8"); - - private Charset defaultCharset = UTF_8; - - private final JsonReader reader = new JsonReader(); - - private final List rawDatas = new ArrayList<>(); - - private ConfigurationMetadataRepositoryJsonBuilder(Charset defaultCharset) { - this.defaultCharset = defaultCharset; - } - - /** - * Add the content of a {@link ConfigurationMetadataRepository} defined by the - * specified {@link InputStream} json document using the default charset. If this - * metadata repository holds items that were loaded previously, these are ignored. - *

- * Leaves the stream open when done. - * @param origin optional information object to help identify where the inputstream came from - * @param inputStream the source input stream - * @return this builder - * @throws IOException in case of I/O errors - */ - public ConfigurationMetadataRepositoryJsonBuilder withJsonResource( - Object origin, InputStream inputStream) throws IOException { - return withJsonResource(origin, inputStream, this.defaultCharset); - } - - /** - * Add the content of a {@link ConfigurationMetadataRepository} defined by the - * specified {@link InputStream} json document using the specified {@link Charset}. If - * this metadata repository holds items that were loaded previously, these are - * ignored. - *

- * Leaves the stream open when done. - * @param origin optional information object to help identify where the inputstream came from - * @param inputStream the source input stream - * @param charset the charset of the input - * @return this builder - * @throws IOException in case of I/O errors - */ - public ConfigurationMetadataRepositoryJsonBuilder withJsonResource( - Object origin, InputStream inputStream, Charset charset) throws IOException { - if (inputStream == null) { - throw new IllegalArgumentException("InputStream must not be null."); - } - this.rawDatas.add(parseRaw(origin, inputStream, charset)); - return this; - } - - /** - * Build a {@link ConfigurationMetadataRepository} with the current state of this - * builder. - * @return this builder - */ - public ConfigurationMetadataRepository build() { - SimpleConfigurationMetadataRepository result = new SimpleConfigurationMetadataRepository(); - result.include(create(rawDatas)); - return result; - } - - private RawConfigurationMetadata parseRaw(Object origin, InputStream in, Charset charset) - throws IOException { - try { - return this.reader.read(origin, in, charset); - } - catch (IOException ex) { - throw new IllegalArgumentException( - "Failed to read configuration " + "metadata", ex); - } - catch (JSONException ex) { - throw new IllegalArgumentException( - "Invalid configuration " + "metadata document", ex); - } - } - - private SimpleConfigurationMetadataRepository create( - Iterable metadatas) { - SimpleConfigurationMetadataRepository repository = new SimpleConfigurationMetadataRepository(); - - for (RawConfigurationMetadata metadata : metadatas) { - repository.add(metadata.getSources()); - } - for (RawConfigurationMetadata metadata : metadatas) { - for (ConfigurationMetadataItem item : metadata.getItems()) { - ConfigurationMetadataSource source = getSource(metadata, item); - repository.add(item, source); - } - } - for (RawConfigurationMetadata metadata : metadatas) { - Map allProperties = repository - .getAllProperties(); - for (ConfigurationMetadataHint hint : metadata.getHints()) { - ConfigurationMetadataProperty property = allProperties.get(hint.getId()); - if (property != null) { - addValueHints(property, hint); - } - else { - String id = hint.resolveId(); - property = allProperties.get(id); - if (property != null) { - if (hint.isMapKeyHints()) { - addMapHints(property, hint); - } - else { - addValueHints(property, hint); - } - } - } - } - } - return repository; - } - - private void addValueHints(ConfigurationMetadataProperty property, - ConfigurationMetadataHint hint) { - addAll(property.getHints().getValueHints(), hint.getValueHints()); - property.getHints().getValueProviders().addAll(hint.getValueProviders()); - } - - private void addMapHints(ConfigurationMetadataProperty property, - ConfigurationMetadataHint hint) { - addAll(property.getHints().getKeyHints(), hint.getValueHints()); - property.getHints().getKeyProviders().addAll(hint.getValueProviders()); - } - - /** - * Add a bunch of hints to a list, but guard against duplicates. - */ - private void addAll(List existing, List toAdd) { - if (existing.isEmpty()) { - existing.addAll(toAdd); - } else if (toAdd.isEmpty()) { - //nothing to add - } else { - Set existingValues = existing - .stream() - .map((hint) -> ""+hint.getValue()) - .collect(Collectors.toSet()); - for (ValueHint hint : toAdd) { - if (!existingValues.contains(""+hint.getValue())) { - existing.add(hint); - } - } - } - } - - private ConfigurationMetadataSource getSource(RawConfigurationMetadata metadata, - ConfigurationMetadataItem item) { - if (item.getSourceType() != null) { - return metadata.getSource(item.getSourceType()); - } - return null; - } - - /** - * Create a new builder instance using {@link #UTF_8} as the default charset and the - * specified json resource. - * @param inputStreams the source input streams - * @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance. - * @throws IOException on error - */ - public static ConfigurationMetadataRepositoryJsonBuilder create( - InputStream... inputStreams) throws IOException { - ConfigurationMetadataRepositoryJsonBuilder builder = create(); - for (InputStream inputStream : inputStreams) { - builder = builder.withJsonResource(null, inputStream); - } - return builder; - } - - /** - * Create a new builder instance using {@link #UTF_8} as the default charset. - * @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance. - */ - public static ConfigurationMetadataRepositoryJsonBuilder create() { - return create(UTF_8); - } - - /** - * Create a new builder instance using the specified default {@link Charset}. - * @param defaultCharset the default charset to use - * @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance. - */ - public static ConfigurationMetadataRepositoryJsonBuilder create( - Charset defaultCharset) { - return new ConfigurationMetadataRepositoryJsonBuilder(defaultCharset); - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataSource.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataSource.java deleted file mode 100644 index 9c1dad953..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ConfigurationMetadataSource.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -/** - * A source of configuration metadata. Also defines where the source is declared, for - * instance if it is defined as a {@code @Bean}. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -@SuppressWarnings("serial") -public class ConfigurationMetadataSource implements Serializable { - - private String groupId; - - private String type; - - private String description; - - private String shortDescription; - - private String sourceType; - - private String sourceMethod; - - private final Map properties = new HashMap(); - - /** - * The identifier of the group to which this source is associated. - * @return the group id - */ - public String getGroupId() { - return this.groupId; - } - - void setGroupId(String groupId) { - this.groupId = groupId; - } - - /** - * The type of the source. Usually this is the fully qualified name of a class that - * defines configuration items. This class may or may not be available at runtime. - * @return the type - */ - public String getType() { - return this.type; - } - - void setType(String type) { - this.type = type; - } - - /** - * A description of this source, if any. Can be multi-lines. - * @return the description - * @see #getShortDescription() - */ - public String getDescription() { - return this.description; - } - - void setDescription(String description) { - this.description = description; - } - - /** - * A single-line, single-sentence description of this source, if any. - * @return the short description - * @see #getDescription() - */ - public String getShortDescription() { - return this.shortDescription; - } - - public void setShortDescription(String shortDescription) { - this.shortDescription = shortDescription; - } - - /** - * The type where this source is defined. This can be identical to the - * {@link #getType() type} if the source is self-defined. - * @return the source type - */ - public String getSourceType() { - return this.sourceType; - } - - void setSourceType(String sourceType) { - this.sourceType = sourceType; - } - - /** - * The method name that defines this source, if any. - * @return the source method - */ - public String getSourceMethod() { - return this.sourceMethod; - } - - void setSourceMethod(String sourceMethod) { - this.sourceMethod = sourceMethod; - } - - /** - * Return the properties defined by this source. - * @return the properties - */ - public Map getProperties() { - return this.properties; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java deleted file mode 100644 index 8261a2a85..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Deprecation.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.Serializable; - -/** - * Indicate that a property is deprecated. Provide additional information about the - * deprecation. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -@SuppressWarnings("serial") -public class Deprecation implements Serializable { - - private String reason; - - private String replacement; - - /** - * A reason why the related property is deprecated, if any. Can be multi-lines. - * @return the deprecation reason - */ - public String getReason() { - return this.reason; - } - - public void setReason(String reason) { - this.reason = reason; - } - - /** - * The full name of the property that replaces the related deprecated property, if - * any. - * @return the replacement property name - */ - public String getReplacement() { - return this.replacement; - } - - public void setReplacement(String replacement) { - this.replacement = replacement; - } - - @Override - public String toString() { - return "Deprecation{" + "reason='" + this.reason + '\'' + ", replacement='" - + this.replacement + '\'' + '}'; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/DescriptionExtractor.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/DescriptionExtractor.java deleted file mode 100644 index 81a9ff9b4..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/DescriptionExtractor.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.text.BreakIterator; -import java.util.Locale; - -/** - * Utility to extract a description. - * - * @author Stephane Nicoll - */ -class DescriptionExtractor { - - private static final String NEW_LINE = System.getProperty("line.separator"); - - public String getShortDescription(String description) { - if (description == null) { - return null; - } - int dot = description.indexOf("."); - if (dot != -1) { - BreakIterator breakIterator = BreakIterator.getSentenceInstance(Locale.US); - breakIterator.setText(description); - String text = description - .substring(breakIterator.first(), breakIterator.next()).trim(); - return removeSpaceBetweenLine(text); - } - else { - String[] lines = description.split(NEW_LINE); - return lines[0].trim(); - } - } - - private String removeSpaceBetweenLine(String text) { - String[] lines = text.split(NEW_LINE); - StringBuilder sb = new StringBuilder(); - for (String line : lines) { - sb.append(line.trim()).append(" "); - } - return sb.toString().trim(); - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Hints.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Hints.java deleted file mode 100644 index 26bdcb69d..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/Hints.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.util.ArrayList; -import java.util.List; - -/** - * Hints of an item to provide the list of values and/or the name of the provider - * responsible to identify suitable values. If the type of the related item is a - * {@link java.util.Map} it can have both key and value hints. - * - * @author Stephane Nicoll - * @since 1.4.0 - */ -public class Hints { - - private final List keyHints = new ArrayList(); - - private final List keyProviders = new ArrayList(); - - private final List valueHints = new ArrayList(); - - private final List valueProviders = new ArrayList(); - - /** - * The list of well-defined keys, if any. Only applicable if the type of the related - * item is a {@link java.util.Map}. If no extra {@link ValueProvider provider} is - * specified, these values are to be considered a closed-set of the available keys for - * the map. - * @return the key hints - */ - public List getKeyHints() { - return this.keyHints; - } - - /** - * The value providers that are applicable to the keys of this item. Only applicable - * if the type of the related item is a {@link java.util.Map}. Only one - * {@link ValueProvider} is enabled for a key: the first in the list that is supported - * should be used. - * @return the key providers - */ - public List getKeyProviders() { - return this.keyProviders; - } - - /** - * The list of well-defined values, if any. If no extra {@link ValueProvider provider} - * is specified, these values are to be considered a closed-set of the available - * values for this item. - * @return the value hints - */ - public List getValueHints() { - return this.valueHints; - } - - /** - * The value providers that are applicable to this item. Only one - * {@link ValueProvider} is enabled for an item: the first in the list that is - * supported should be used. - * @return the value providers - */ - public List getValueProviders() { - return this.valueProviders; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java deleted file mode 100644 index 4d73902c9..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/JsonReader.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.springframework.ide.eclipse.org.json.JSONArray; -import org.springframework.ide.eclipse.org.json.JSONObject; - -/** - * Read standard json metadata format as {@link ConfigurationMetadataRepository}. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -class JsonReader { - - private static final int BUFFER_SIZE = 4096; - - private final DescriptionExtractor descriptionExtractor = new DescriptionExtractor(); - - public RawConfigurationMetadata read(Object origin, InputStream in, Charset charset) - throws IOException { - JSONObject json = readJson(in, charset); - List groups = parseAllSources(json); - List items = parseAllItems(json); - List hints = parseAllHints(json); - return new RawConfigurationMetadata(origin, groups, items, hints); - } - - private List parseAllSources(JSONObject root) { - List result = new ArrayList(); - if (!root.has("groups")) { - return result; - } - JSONArray sources = root.getJSONArray("groups"); - for (int i = 0; i < sources.length(); i++) { - JSONObject source = sources.getJSONObject(i); - result.add(parseSource(source)); - } - return result; - } - - private List parseAllItems(JSONObject root) { - List result = new ArrayList(); - if (!root.has("properties")) { - return result; - } - JSONArray items = root.getJSONArray("properties"); - for (int i = 0; i < items.length(); i++) { - JSONObject item = items.getJSONObject(i); - result.add(parseItem(item)); - } - return result; - } - - private List parseAllHints(JSONObject root) { - List result = new ArrayList(); - if (!root.has("hints")) { - return result; - } - JSONArray items = root.getJSONArray("hints"); - for (int i = 0; i < items.length(); i++) { - JSONObject item = items.getJSONObject(i); - result.add(parseHint(item)); - } - return result; - } - - private ConfigurationMetadataSource parseSource(JSONObject json) { - ConfigurationMetadataSource source = new ConfigurationMetadataSource(); - source.setGroupId(json.getString("name")); - source.setType(json.optString("type", null)); - String description = json.optString("description", null); - source.setDescription(description); - source.setShortDescription( - this.descriptionExtractor.getShortDescription(description)); - source.setSourceType(json.optString("sourceType", null)); - source.setSourceMethod(json.optString("sourceMethod", null)); - return source; - } - - private ConfigurationMetadataItem parseItem(JSONObject json) { - ConfigurationMetadataItem item = new ConfigurationMetadataItem(); - item.setId(json.getString("name")); - item.setType(json.optString("type", null)); - String description = json.optString("description", null); - item.setDescription(description); - item.setShortDescription( - this.descriptionExtractor.getShortDescription(description)); - item.setDefaultValue(readItemValue(json.opt("defaultValue"))); - item.setDeprecation(parseDeprecation(json)); - item.setSourceType(json.optString("sourceType", null)); - item.setSourceMethod(json.optString("sourceMethod", null)); - return item; - } - - private ConfigurationMetadataHint parseHint(JSONObject json) { - ConfigurationMetadataHint hint = new ConfigurationMetadataHint(); - hint.setId(json.getString("name")); - if (json.has("values")) { - JSONArray values = json.getJSONArray("values"); - for (int i = 0; i < values.length(); i++) { - JSONObject value = values.getJSONObject(i); - ValueHint valueHint = new ValueHint(); - valueHint.setValue(readItemValue(value.get("value"))); - String description = value.optString("description", null); - valueHint.setDescription(description); - valueHint.setShortDescription( - this.descriptionExtractor.getShortDescription(description)); - hint.getValueHints().add(valueHint); - } - } - if (json.has("providers")) { - JSONArray providers = json.getJSONArray("providers"); - for (int i = 0; i < providers.length(); i++) { - JSONObject provider = providers.getJSONObject(i); - ValueProvider valueProvider = new ValueProvider(); - valueProvider.setName(provider.getString("name")); - if (provider.has("parameters")) { - JSONObject parameters = provider.getJSONObject("parameters"); - Iterator keys = parameters.keys(); - while (keys.hasNext()) { - String key = (String) keys.next(); - valueProvider.getParameters().put(key, - readItemValue(parameters.get(key))); - } - } - hint.getValueProviders().add(valueProvider); - } - } - return hint; - } - - private Deprecation parseDeprecation(JSONObject object) { - if (object.has("deprecation")) { - JSONObject deprecationJsonObject = object.getJSONObject("deprecation"); - Deprecation deprecation = new Deprecation(); - deprecation.setReason(deprecationJsonObject.optString("reason", null)); - deprecation - .setReplacement(deprecationJsonObject.optString("replacement", null)); - return deprecation; - } - return (object.optBoolean("deprecated") ? new Deprecation() : null); - } - - private Object readItemValue(Object value) { - if (value instanceof JSONArray) { - JSONArray array = (JSONArray) value; - Object[] content = new Object[array.length()]; - for (int i = 0; i < array.length(); i++) { - content[i] = array.get(i); - } - return content; - } - return value; - } - - private JSONObject readJson(InputStream in, Charset charset) throws IOException { - try { - StringBuilder out = new StringBuilder(); - InputStreamReader reader = new InputStreamReader(in, charset); - char[] buffer = new char[BUFFER_SIZE]; - int bytesRead = -1; - while ((bytesRead = reader.read(buffer)) != -1) { - out.append(buffer, 0, bytesRead); - } - return new JSONObject(out.toString()); - } - finally { - in.close(); - } - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/README.txt b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/README.txt deleted file mode 100644 index f3e11fe93..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/README.txt +++ /dev/null @@ -1,10 +0,0 @@ -The source code in this package is taken from here: - -https://github.com/spring-projects/spring-boot/tree/fca6dbaf09c32202d9d958f815221aad54b9fc7b/spring-boot-tools/spring-boot-configuration-metadata/src/main/java/org/springframework/boot/configurationmetadata - -Notes: - - This commit is from the master branch at a point in time where boot team is working on Boot 1.4.x on that branch. - -There are currently no modifications being made to that code at all to accomodate STS. So it may now be possible to consume it as a proper dependency. -However, keep in mind that we are using a modified copy of 'org.json' to allow controlling key order in json maps. So that probably -complicates things. diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/RawConfigurationMetadata.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/RawConfigurationMetadata.java deleted file mode 100644 index a264b1e9b..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/RawConfigurationMetadata.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2012-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.util.ArrayList; -import java.util.List; - -/** - * A raw metadata structure. Used to initialize a {@link ConfigurationMetadataRepository}. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -class RawConfigurationMetadata { - - private final Object origin; - - private final List sources; - - private final List items; - - private final List hints; - - RawConfigurationMetadata(Object parsedFrom, - List sources, - List items, - List hints) { - this.origin = parsedFrom; - this.sources = new ArrayList(sources); - this.items = new ArrayList(items); - this.hints = new ArrayList(hints); - for (ConfigurationMetadataItem item : this.items) { - resolveName(item); - } - } - - public List getSources() { - return this.sources; - } - - public ConfigurationMetadataSource getSource(String type) { - for (ConfigurationMetadataSource source : this.sources) { - if (type.equals(source.getType())) { - return source; - } - } - return null; - } - - public List getItems() { - return this.items; - } - - public List getHints() { - return this.hints; - } - - /** - * Resolve the name of an item against this instance. - * @param item the item to resolve - * @see ConfigurationMetadataProperty#setName(String) - */ - private void resolveName(ConfigurationMetadataItem item) { - item.setName(item.getId()); // fallback - if (item.getSourceType() == null) { - return; - } - ConfigurationMetadataSource source = getSource(item.getSourceType()); - if (source != null) { - String groupId = source.getGroupId(); - String dottedPrefix = groupId + "."; - String id = item.getId(); - if (hasLength(groupId) && id.startsWith(dottedPrefix)) { - String name = id.substring(dottedPrefix.length(), id.length()); - item.setName(name); - } - } - } - - private static boolean hasLength(String string) { - return (string != null && string.length() > 0); - } - - @Override - public String toString() { - if (origin!=null) { - return "RawConfigurationMetadata("+origin+")"; - } - return super.toString(); - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java deleted file mode 100644 index e12ec4479..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/SimpleConfigurationMetadataRepository.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * The default {@link ConfigurationMetadataRepository} implementation. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -@SuppressWarnings("serial") -public class SimpleConfigurationMetadataRepository - implements ConfigurationMetadataRepository, Serializable { - - private final Map allGroups = new HashMap(); - - @Override - public Map getAllGroups() { - return Collections.unmodifiableMap(this.allGroups); - } - - @Override - public Map getAllProperties() { - Map properties = new HashMap(); - for (ConfigurationMetadataGroup group : this.allGroups.values()) { - properties.putAll(group.getProperties()); - } - return properties; - } - - /** - * Register the specified {@link ConfigurationMetadataSource sources}. - * @param sources the sources to add - */ - public void add(Collection sources) { - for (ConfigurationMetadataSource source : sources) { - String groupId = source.getGroupId(); - ConfigurationMetadataGroup group = this.allGroups.get(groupId); - if (group == null) { - group = new ConfigurationMetadataGroup(groupId); - this.allGroups.put(groupId, group); - } - String sourceType = source.getType(); - if (sourceType != null) { - putIfAbsent(group.getSources(), sourceType, source); - } - } - } - - /** - * Add a {@link ConfigurationMetadataProperty} with the - * {@link ConfigurationMetadataSource source} that defines it, if any. - * @param property the property to add - * @param source the source - */ - public void add(ConfigurationMetadataProperty property, - ConfigurationMetadataSource source) { - if (source != null) { - putIfAbsent(source.getProperties(), property.getId(), property); - } - putIfAbsent(getGroup(source).getProperties(), property.getId(), property); - } - - /** - * Merge the content of the specified repository to this repository. - * @param repository the repository to include - */ - public void include(ConfigurationMetadataRepository repository) { - for (ConfigurationMetadataGroup group : repository.getAllGroups().values()) { - ConfigurationMetadataGroup existingGroup = this.allGroups.get(group.getId()); - if (existingGroup == null) { - this.allGroups.put(group.getId(), group); - } - else { - // Merge properties - for (Map.Entry entry : group - .getProperties().entrySet()) { - putIfAbsent(existingGroup.getProperties(), entry.getKey(), - entry.getValue()); - } - // Merge sources - for (Map.Entry entry : group - .getSources().entrySet()) { - putIfAbsent(existingGroup.getSources(), entry.getKey(), - entry.getValue()); - } - } - } - - } - - private ConfigurationMetadataGroup getGroup(ConfigurationMetadataSource source) { - if (source == null) { - ConfigurationMetadataGroup rootGroup = this.allGroups.get(ROOT_GROUP); - if (rootGroup == null) { - rootGroup = new ConfigurationMetadataGroup(ROOT_GROUP); - this.allGroups.put(ROOT_GROUP, rootGroup); - } - return rootGroup; - } - return this.allGroups.get(source.getGroupId()); - } - - private void putIfAbsent(Map map, String key, V value) { - if (!map.containsKey(key)) { - map.put(key, value); - } - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueHint.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueHint.java deleted file mode 100644 index 043fc10dc..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueHint.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.Serializable; - -/** - * Hint for a value a given property may have. Provide the value and an optional - * description. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -@SuppressWarnings("serial") -public class ValueHint implements Serializable, Cloneable { - - public static ValueHint withValue(Object value) { - ValueHint hint = new ValueHint(); - hint.setValue(value); - return hint; - } - - public ValueHint prefixWith(String prefix) { - try { - ValueHint clone = (ValueHint) this.clone(); - clone.setValue(prefix+value); - return clone; - } catch (CloneNotSupportedException e) { - //This is supposed to be impossble. - throw new RuntimeException(e); - } - } - - private Object value; - - private String description; - - private String shortDescription; - - /** - * Return the hint value. - * @return the value - */ - public Object getValue() { - return this.value; - } - - public void setValue(Object value) { - this.value = value; - } - - /** - * A description of this value, if any. Can be multi-lines. - * @return the description - * @see #getShortDescription() - */ - public String getDescription() { - return this.description; - } - - public void setDescription(String description) { - this.description = description; - } - - /** - * A single-line, single-sentence description of this hint, if any. - * @return the short description - * @see #getDescription() - */ - public String getShortDescription() { - return this.shortDescription; - } - - public void setShortDescription(String shortDescription) { - this.shortDescription = shortDescription; - } - - @Override - public String toString() { - return "ValueHint{" + "value=" + this.value + ", description='" + this.description - + '\'' + '}'; - } -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueProvider.java deleted file mode 100644 index 550181ee9..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/ValueProvider.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.configurationmetadata; - -import java.io.Serializable; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Define a component that is able to provide the values of a property. - *

- * Each provider is defined by a {@code name} and can have an arbitrary number of - * {@code parameters}. The available providers are defined in the Spring Boot - * documentation. - * - * @author Stephane Nicoll - * @since 1.3.0 - */ -@SuppressWarnings("serial") -public class ValueProvider implements Serializable { - - private String name; - - private final Map parameters = new LinkedHashMap(); - - /** - * Return the name of the provider. - * @return the name - */ - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - /** - * Return the parameters. - * @return the parameters - */ - public Map getParameters() { - return this.parameters; - } - - @Override - public String toString() { - return "ValueProvider{" + "name='" + this.name + ", parameters=" + this.parameters - + '}'; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/package-info.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/package-info.java deleted file mode 100644 index e25ef89eb..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/boot/configurationmetadata/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2012-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Spring Boot configuration meta-data parser. - */ -package org.springframework.boot.configurationmetadata; diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java index 328e1f3e1..ed10f06c7 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java @@ -15,8 +15,8 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.LinkedHashMap; -import org.springframework.ide.eclipse.org.json.JSONArray; -import org.springframework.ide.eclipse.org.json.JSONObject; +import org.json.JSONArray; +import org.json.JSONObject; /** * Helper class to manipulate data in a file presumed to contain @@ -44,7 +44,12 @@ public class MetadataManipulator { } public String toString() { - return object.toString(indentFactor); + try { + return object.toString(indentFactor); + } + catch (Exception e) { + return null; + } } @Override diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java index 04a03ee07..e3d6e66f8 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java @@ -84,7 +84,7 @@ public class PropertiesLoader { InputStream is = null; try { is = Files.newInputStream(mdf); - loadFromInputStream(mdf, is); + loadFromInputStream(is); } catch (Exception e) { LOG.log(Level.SEVERE, "Error loading file '" + mdf + "'", e); } finally { @@ -127,7 +127,7 @@ public class PropertiesLoader { InputStream is = null; try { is = jarFile.getInputStream(ze); - loadFromInputStream(jarFile.getName()+"["+ze.getName()+"]", is); + loadFromInputStream(is); } catch (Throwable e) { LOG.log(Level.SEVERE, "Error loading JAR file", e); } finally { @@ -140,8 +140,8 @@ public class PropertiesLoader { } } - private void loadFromInputStream(Object origin, InputStream is) throws IOException { - builder.withJsonResource(origin, is); + private void loadFromInputStream(InputStream is) throws IOException { + builder.withJsonResource(is); } } From 32d44e60a9767e270e6577ecb6d1981e84eaa7f8 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Tue, 14 Feb 2017 11:43:51 +0100 Subject: [PATCH 16/28] simplified spring property index handling by removing a lot of value-specific stuff --- .../completions/ValueCompletionProcessor.java | 22 +- .../boot/metadata/CachingValueProvider.java | 148 ----------- .../boot/metadata/ClassReferenceProvider.java | 154 ------------ .../DefaultSpringPropertyIndexProvider.java | 7 +- .../vscode/boot/metadata/IndexNavigator.java | 133 ---------- .../boot/metadata/LoggerNameProvider.java | 52 ---- .../boot/metadata/MetadataManipulator.java | 235 ------------------ .../boot/metadata/PropertiesLoader.java | 2 +- .../vscode/boot/metadata/PropertyInfo.java | 190 -------------- .../boot/metadata/ResourceHintProvider.java | 71 ------ .../SpringPropertiesIndexManager.java | 23 +- .../boot/metadata/SpringPropertyIndex.java | 126 ++++------ .../metadata/SpringPropertyIndexProvider.java | 5 +- .../boot/metadata/ValueProviderRegistry.java | 98 -------- .../boot/metadata/hints/StsValueHint.java | 137 ---------- .../metadata/hints/ValueHintHoverInfo.java | 33 --- .../boot/metadata/util/DeprecationUtil.java | 59 ----- .../vscode/boot/metadata/util/Listener.java | 20 -- .../boot/metadata/util/ListenerManager.java | 36 --- .../project/harness/PropertyIndexHarness.java | 7 +- 20 files changed, 75 insertions(+), 1483 deletions(-) delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/CachingValueProvider.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ValueProviderRegistry.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/ValueHintHoverInfo.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/DeprecationUtil.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/Listener.java delete mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/ListenerManager.java diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java index 4c9d018b5..4a43b0f19 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java @@ -19,7 +19,7 @@ import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StringLiteral; -import org.springframework.ide.vscode.boot.metadata.PropertyInfo; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; @@ -31,9 +31,9 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; */ public class ValueCompletionProcessor { - private FuzzyMap index; + private FuzzyMap index; - public ValueCompletionProcessor(FuzzyMap index) { + public ValueCompletionProcessor(FuzzyMap index) { this.index = index; } @@ -43,9 +43,9 @@ public class ValueCompletionProcessor { try { // case: @Value(<*>) if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) { - List> matches = findMatches(""); + List> matches = findMatches(""); - for (Match match : matches) { + for (Match match : matches) { DocumentEdits edits = new DocumentEdits(doc); edits.replace(offset, offset, "\"${" + match.data.getId() + "}\""); @@ -64,9 +64,9 @@ public class ValueCompletionProcessor { String proposalPrefix = "\""; String proposalPostfix = "\""; - List> matches = findMatches(prefix); + List> matches = findMatches(prefix); - for (Match match : matches) { + for (Match match : matches) { DocumentEdits edits = new DocumentEdits(doc); edits.replace(startOffset, endOffset, proposalPrefix + "${" + match.data.getId() + "}" + proposalPostfix); @@ -101,9 +101,9 @@ public class ValueCompletionProcessor { String fullNodeContent = doc.get(node.getStartPosition(), node.getLength()); String postCompletion = isClosingBracketMissing(fullNodeContent + preCompletion) ? "}" : ""; - List> matches = findMatches(prefix); + List> matches = findMatches(prefix); - for (Match match : matches) { + for (Match match : matches) { DocumentEdits edits = new DocumentEdits(doc); edits.replace(startOffset, endOffset, preCompletion + match.data.getId() + postCompletion); @@ -150,8 +150,8 @@ public class ValueCompletionProcessor { return result; } - private List> findMatches(String prefix) { - List> matches = index.find(camelCaseToHyphens(prefix)); + private List> findMatches(String prefix) { + List> matches = index.find(camelCaseToHyphens(prefix)); return matches; } diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/CachingValueProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/CachingValueProvider.java deleted file mode 100644 index 2b009422b..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/CachingValueProvider.java +++ /dev/null @@ -1,148 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.metadata; - -import java.time.Duration; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - -import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; -import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.util.FuzzyMatcher; -import org.springframework.ide.vscode.commons.util.Log; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader.InvalidCacheLoadException; - -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; -import reactor.util.function.Tuples; - -/** - * A abstract {@link ValueProviderStrategy} that is mean to help speedup successive invocations of - * content assist with a similar 'query' string. - *

- * This implementation is meant to be used for providers that use potentially lenghty/expensive searches - * to determine hints. Since content assist hints are requested by Eclipse CA framework directly on - * the UI thread, they can not simply perform a lengthy search and block UI thread until it finished. - *

- * This implementation therefore does the following: - *

    - *
  • Limit the duration of time spent on the UI thread. - *
  • Cache results of searches for a limited time. - *
  • Speedup queries for successive queries by using the already cached result of a similar (prefix) query. - *
  • When the time spent on UI thread waiting for a current search exceeds the allowed time limit, - * return immediately with whatever results have been found so far. - *
- * - * TODO: rather than an abstract class this should really be 'Wrapper' class that delegates to another - * {@link ValueProviderStrategy} and adds a cache in front of it. - * - * @author Kris De Volder - */ -public abstract class CachingValueProvider implements ValueProviderStrategy { - - private static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1000); - - /** - * Content assist is called inside UI thread and so doing something lenghty things - * like a JavaSearch will block the UI thread completely freezing the UI. So, we - * only return as many results as can be obtained within this hard TIMEOUT limit. - */ - public static Duration TIMEOUT = DEFAULT_TIMEOUT; - - /** - * The maximum number of results returned for a single request. Used to limit the - * values that are cached per entry. - */ - private int MAX_RESULTS = 500; - - private Cache, CacheEntry> cache = createCache(); - - private class CacheEntry { - boolean isComplete = false; - int count = 0; - Flux values; - - public CacheEntry(String query, Flux producer) { - values = producer - .take(MAX_RESULTS) - .cache(MAX_RESULTS); - values.subscribe(); // create infinite demand so that we actually force cache entries to be fetched upto the max. - } - - @Override - public String toString() { - return "CacheEntry [isComplete=" + isComplete + ", count=" + count + "]"; - } - - } - - @Override - public final Flux getValues(IJavaProject javaProject, String query) { - Tuple2 key = key(javaProject, query); - CacheEntry cached = null; - try { - cached = cache.get(key, () -> new CacheEntry(query, getValuesIncremental(javaProject, query))); - } catch (ExecutionException e) { - Log.log(e); - } - return cached.values; - } - - /** - * Tries to use an already cached, complete result for a query that is a prefix of the current query to speed things up. - *

- * Falls back on doing a full-blown search if there's no usable 'prefix-query' in the cache. - */ - private Flux getValuesIncremental(IJavaProject javaProject, String query) { -// debug("trying to solve "+query+" incrementally"); - String subquery = query; - while (subquery.length()>=1) { - subquery = subquery.substring(0, subquery.length()-1); - CacheEntry cached = null; - try { - cached = cache.get(key(javaProject, subquery), () -> null); - } catch (ExecutionException | InvalidCacheLoadException e) { -// Log.log(e); - } - if (cached!=null) { - System.out.println("cached "+subquery+": "+cached); - if (cached.isComplete) { - return cached.values -// .doOnNext((hint) -> debug("filter["+query+"]: "+hint.getValue())) - .filter((hint) -> 0!=FuzzyMatcher.matchScore(query, hint.getValue().toString())); - } else { -// debug("subquery "+subquery+" cached but is incomplete"); - } - } - } -// debug("full search for: "+query); - return getValuesAsync(javaProject, query); - } - - protected abstract Flux getValuesAsync(IJavaProject javaProject, String query); - - private Tuple2 key(IJavaProject javaProject, String query) { - return Tuples.of(javaProject==null?null:javaProject.getElementName(), query); - } - - protected Cache createCache() { - return CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).expireAfterAccess(1, TimeUnit.MINUTES).build(); - } - - public static void restoreDefaults() { - TIMEOUT = DEFAULT_TIMEOUT; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java deleted file mode 100644 index 2618837bc..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java +++ /dev/null @@ -1,154 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016-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.metadata; - -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; -import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; -import org.springframework.ide.vscode.commons.java.Flags; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.java.IType; -import org.springframework.ide.vscode.commons.util.Log; -import org.springframework.ide.vscode.commons.util.StringUtil; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - -import reactor.core.publisher.Flux; - -/** - * Provides the algorithm for 'class-reference' valueProvider. - *

- * See: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc - * - * @author Kris De Volder - * @author Alex Boyko - */ -public class ClassReferenceProvider extends CachingValueProvider { - - /** - * Default value for the 'concrete' parameter. - */ - private static final boolean DEFAULT_CONCRETE = true; - - private static final ClassReferenceProvider UNTARGETTED_INSTANCE = new ClassReferenceProvider(null, DEFAULT_CONCRETE); - - public static final Function, ValueProviderStrategy> FACTORY = applyOn( - 1, TimeUnit.MINUTES, - (params) -> { - String target = getTarget(params); - Boolean concrete = getConcrete(params); - if (target!=null || concrete!=null) { - if (concrete==null) { - concrete = DEFAULT_CONCRETE; - } - return new ClassReferenceProvider(target, concrete); - } - return UNTARGETTED_INSTANCE; - } - ); - - private static Function applyOn(long duration, TimeUnit unit, Function func) { - Cache cache = CacheBuilder.newBuilder().expireAfterAccess(duration, unit).expireAfterWrite(duration, unit).build(); - return (k) -> { - try { - return cache.get(k, () -> func.apply(k)); - } catch (ExecutionException e) { - Log.log(e); - return null; - } - }; - } - - private static String getTarget(Map params) { - if (params!=null) { - Object obj = params.get("target"); - if (obj instanceof String) { - String target = (String) obj; - if (StringUtil.hasText(target)) { - return target; - } - } - } - return null; - } - - private static boolean isAbstract(IType type) { - try { - return type.isInterface() || Flags.isAbstract(type.getFlags()); - } catch (Exception e) { - Log.log(e); - return false; - } - } - - private static Boolean getConcrete(Map params) { - try { - if (params!=null) { - Object obj = params.get("concrete"); - if (obj instanceof String) { - String concrete = (String) obj; - return Boolean.valueOf(concrete); - } else if (obj instanceof Boolean) { - return (Boolean) obj; - } - } - } catch (Exception e) { - Log.log(e); - } - return null; - } - - /** - * Optional, fully qualified name of the 'target' type. Suggested hints should be a subtype of this type. - */ - private String target; - - /** - * Optional parameter, whether only concrete types should be suggested. Default value is true. - */ - private boolean concrete; - - private ClassReferenceProvider(String target, boolean concrete) { - this.target = target; - this.concrete = concrete; - } - - @Override - protected Flux getValuesAsync(IJavaProject javaProject, String query) { - IType targetType = target == null || target.isEmpty() ? javaProject.getClasspath().findType("java.lang.Object") : javaProject.getClasspath().findType(target); - if (targetType == null) { - return Flux.empty(); - } - Set allSubclasses = javaProject.getClasspath() - .allSubtypesOf(targetType) - .filter(t -> Flags.isPublic(t.getFlags()) && !concrete || !isAbstract(t)) - .collect(Collectors.toSet()) - .block(); - if (allSubclasses.isEmpty()) { - return Flux.empty(); - } else { - return javaProject.getClasspath() - .fuzzySearchTypes(query, type -> allSubclasses.contains(type)) - .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) - .flatMap(l -> Flux.fromIterable(l)) - .map(t -> StsValueHint.create(t.getT1())); - } - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java index 0e379d645..96b11c23b 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.boot.metadata; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; @@ -19,10 +20,10 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexProvider { - private static final FuzzyMap EMPTY_INDEX = new SpringPropertyIndex(null, null); + private static final FuzzyMap EMPTY_INDEX = new SpringPropertyIndex(null); private JavaProjectFinder javaProjectFinder; - private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(ValueProviderRegistry.getDefault()); + private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(); private ProgressService progressService = (id, msg) -> { /*ignore*/ }; @@ -31,7 +32,7 @@ public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexPr } @Override - public FuzzyMap getIndex(IDocument doc) { + public FuzzyMap getIndex(IDocument doc) { IJavaProject jp = javaProjectFinder.find(doc); if (jp!=null) { return indexManager.get(jp, progressService); diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java deleted file mode 100644 index cd19604c6..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java +++ /dev/null @@ -1,133 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.metadata; - -import static org.springframework.ide.vscode.commons.util.StringUtil.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; -import org.springframework.ide.vscode.commons.util.StringUtil; - -/** - * An index navigator allows selecting subset of a property index as if - * navigating the index by selecting on a property - * - * @author Kris De Volder - */ -public class IndexNavigator { - - //Possible opitmization: we could cache prefix match candidate and extended match candidate - // since it is assumed that the index is immutable for the lifetime of - // the index navigator. - - private static final char NAV_CHAR = '.'; - - /** - * Property access in this navigator are interpreted relative - * to this prefix - */ - private String prefix = null; - private FuzzyMap index; - - private IndexNavigator(FuzzyMap index) { - this.index = index; - } - - private IndexNavigator(FuzzyMap index, String prefix) { - this.index = index; - this.prefix = prefix; - } - - public static IndexNavigator with(FuzzyMap index) { - return new IndexNavigator(index); - } - - public IndexNavigator selectSubProperty(String name) { - return new IndexNavigator(index, join(prefix, name)); - } - - protected String join(String prefix, String postfix) { - if (!hasText(prefix)) { - return postfix; - } else { - return prefix + NAV_CHAR + postfix; - } - } - - /** - * @return property info that is an exact match with the current prefix or - * null if there's no exact match - */ - public PropertyInfo getExactMatch() { - if (prefix!=null) { - PropertyInfo candidate = index.findLongestCommonPrefixEntry(prefix); - if (candidate.getId().equals(prefix)) { - return candidate; - } - } - return null; - } - - /** - * Get a property that has the current prefix as a 'true' prefix. A true prefix - * is a String that has the current prefix as a prefix and continues onward with - * a navigation operation. - */ - public PropertyInfo getExtensionCandidate() { - //If current prefix is null then all entries in the index are candidates since - // the index is at the 'root' of the tree and we don't need a '.' to navigate - String extendedPrefix = prefix==null?"":prefix + NAV_CHAR; - PropertyInfo candidate = index.findLongestCommonPrefixEntry(extendedPrefix); - if (candidate.getId().startsWith(extendedPrefix)) { - return candidate; - } - return null; - } - - public String getPrefix() { - return prefix; - } - - public List> findMatching(String query) { - if (!StringUtil.hasText(prefix)) { - return index.find(query); - } else { - String dottedPrefix = prefix +"."; - List> candidates = index.find(dottedPrefix + query); - if (!candidates.isEmpty()) { - //TODO: we can do better than this using treemap to narrow based on - // prefix - List> matches = new ArrayList>(candidates.size()); - for (Match match : candidates) { - if (match.data.getId().startsWith(dottedPrefix)){ - matches.add(match); - } - } - return matches; - } - } - return Collections.emptyList(); - } - - @Override - public String toString() { - return "IndexNavigator("+prefix+")"; - } - - public boolean isEmpty() { - return getExactMatch()==null && getExtensionCandidate()==null; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java deleted file mode 100644 index 2f9886766..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016-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.metadata; - -import java.util.Map; -import java.util.function.Function; - -import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; -import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; -import org.springframework.ide.vscode.commons.java.IJavaProject; - -import reactor.core.publisher.Flux; -import reactor.util.function.Tuples; - -/** - * Provides the algorithm for 'logger-name' valueProvider. - *

- * See: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-docs/src/main/asciidoc/appendix-configuration-metadata.adoc - * - * @author Kris De Volder - * @author Alex Boyko - */ -public class LoggerNameProvider extends CachingValueProvider { - - private static final ValueProviderStrategy INSTANCE = new LoggerNameProvider(); - public static final Function, ValueProviderStrategy> FACTORY = (params) -> INSTANCE; - - @Override - protected Flux getValuesAsync(IJavaProject javaProject, String query) { - return Flux.concat( - javaProject.getClasspath() - .fuzzySearchPackages(query) - .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())), - javaProject.getClasspath() - .fuzzySearchTypes(query, null) - .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())) - ) - .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) - .flatMap(l -> Flux.fromIterable(l)) - .map(t -> t.getT1()); - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java deleted file mode 100644 index ed10f06c7..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/MetadataManipulator.java +++ /dev/null @@ -1,235 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 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.metadata; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.LinkedHashMap; - -import org.json.JSONArray; -import org.json.JSONObject; - -/** - * Helper class to manipulate data in a file presumed to contain - * spring-boot configuration data. - * - * @author Kris De Volder - * @author Alex Boyko - */ -public class MetadataManipulator { - - private abstract class Content { - public abstract String toString(); - public abstract void addProperty(JSONObject jsonObject) throws Exception; - } - - /** - * Content was parse as JSONObject. - */ - private class ParsedContent extends Content { - - private JSONObject object; - - public ParsedContent(JSONObject o) { - this.object = o; - } - - public String toString() { - try { - return object.toString(indentFactor); - } - catch (Exception e) { - return null; - } - } - - @Override - public void addProperty(JSONObject propertyData) throws Exception { - JSONArray properties = object.getJSONArray("properties"); - properties.put(properties.length(), propertyData); - } - } - - /** - * Content that is 'unparsed' and just a bunch of text. - * Used only as a fallback when data in file can't - * be parsed. - *

- * This content is manipulated by string manipulation. - * It is less reliable, but can be done even if the - * file data is not parseable. - */ - private class RawContent extends Content { - - private StringBuilder doc; - - public RawContent(String content) { - this.doc = new StringBuilder(content); - } - - @Override - public String toString() { - return doc.toString(); - } - - @Override - public void addProperty(JSONObject propertyData) throws Exception { - int insertAt = findLast(']'); - if (insertAt<0) { - //although we're not looking for much, we didn't find it! - //Funky file contents. Let's just insert something at end of file in a 'best effort' spirit. - insertAt = doc.length(); - } - insert(insertAt, "\n"); - - insert(insertAt, propertyData.toString(indentFactor)); - - int insertComma = findInsertCommaPos(insertAt); - if (insertComma>=0) { - insert(insertComma, ","); - } - } - - /** - * Maybe we need to add a comma in front of the new entry. This - * method finds if/where to stick this comma. - * @throws Exception - */ - private int findInsertCommaPos(int pos) throws Exception { - pos--; - while (pos>=0 && Character.isWhitespace(doc.charAt(pos))) { - pos--; - } - if (pos>=0) { - char c = doc.charAt(pos); - if (c == '}') { - //Add a comma after a '}' - return pos+1; - } - } - return -1; - } - - private int insert(int insertAt, String str) throws Exception { - if (insertAt < doc.length()) { - doc.replace(insertAt, insertAt, str); - } else { - doc.append(str); - } - return insertAt + str.length(); - } - - private int findLast(char toFind) throws Exception { - int pos = doc.length()-1; - while (pos>=0 && doc.charAt(pos)!=toFind) { - pos--; - } - //We got here either because - // - we found char at pos or.. - // - we reached position *before* start of file (i.e. -1) - return pos; - } - - } - - public interface ContentStore { - String getContents() throws Exception; - void setContents(String content) throws Exception; - } - - private static final String INITIAL_CONTENT = - "{\"properties\": [\n" + - "]}"; - - private static final String ENCODING = "UTF8"; - private ContentStore contentStore; - private Content fContent; - private int indentFactor = 2; - - public MetadataManipulator(ContentStore contentStore) { - this.contentStore = contentStore; - } - - public MetadataManipulator(final File file) { - this(new ContentStore() { - - @Override - public String getContents() throws Exception { - return new String(Files.readAllBytes(Paths.get(file.toURI())), ENCODING); - } - - @Override - public void setContents(String content) throws Exception { - Files.write(Paths.get(file.toURI()), content.getBytes(ENCODING)); - } - - }); - } - - private Content getContent() throws Exception { - if (fContent==null) { - fContent = readContent(); - } - return fContent; - } - - private Content readContent() throws Exception { - String content = contentStore.getContents(); - if (content.trim().isEmpty()) { - JSONObject o = initialContent(); - return new ParsedContent(o); - } else { - try { - return new ParsedContent(new JSONObject(content)); - } catch (Exception e) { - //couldn't parse? - return new RawContent(content); - } - } - } - - public void addDefaultInfo(String propertyName) throws Exception { - getContent().addProperty(createDefaultData(propertyName)); - } - - private JSONObject createDefaultData(String propertyName) throws Exception { - JSONObject obj = new JSONObject(new LinkedHashMap()); - obj.put("name", propertyName); - obj.put("type", String.class.getName()); - obj.put("description", "A description for '"+propertyName+"'"); - return obj; - } - - /** - * Generate the initial content (must be generated rather than being a constant to respect newline conventions - * on user's system. - */ - private JSONObject initialContent() throws Exception { - return new JSONObject(INITIAL_CONTENT); - } - - /** - * After manipulating the data, use this to persist changes back to the file. - */ - public void save() throws Exception { - contentStore.setContents(getContent().toString()); - } - - /** - * Determines whether the 'reliable' manipulations can be used (which is the case - * only if the data in the file is valid json). - */ - public boolean isReliable() throws Exception { - return getContent() instanceof ParsedContent; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java index e3d6e66f8..79779f133 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 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 diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java deleted file mode 100644 index 4b3342f03..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertyInfo.java +++ /dev/null @@ -1,190 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014-2016 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.metadata; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; -import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource; -import org.springframework.boot.configurationmetadata.Deprecation; -import org.springframework.boot.configurationmetadata.ValueHint; -import org.springframework.boot.configurationmetadata.ValueProvider; -import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; - -/** - * Information about a spring property, basically, this is the same as - * - * {@link ConfigurationMetadataProperty} but augmented with information - * about {@link ConfigurationMetadataSource}s that declare the property. - * - * @author Kris De Volder - */ -public class PropertyInfo { - - /** - * Identifies a 'Source'. This is essentially the sames as {@link ConfigurationMetadataSource}. - * We could use {@link ConfigurationMetadataSource} directly, but this only contains - * the info that we actually use so takes less memory. - */ - public static class PropertySource { - private final String sourceType; - private final String sourceMethod; - public PropertySource(ConfigurationMetadataSource source) { - String st = source.getSourceType(); - this.sourceType = st!=null?st:source.getType(); - this.sourceMethod = source.getSourceMethod(); - } - @Override - public String toString() { - return sourceType+"::"+sourceMethod; - } - public String getSourceType() { - return sourceType; - } - public String getSourceMethod() { - return sourceMethod; - } - } - - final private String id; - private String type; - final private String name; - final private Object defaultValue; - final private String description; - private List sources; - private Deprecation deprecation; - private ImmutableList valueHints; - private ImmutableList keyHints; - private ValueProviderStrategy valueProvider; - private ValueProviderStrategy keyProvider; - - public PropertyInfo(String id, String type, String name, - Object defaultValue, String description, - Deprecation deprecation, - List valueHints, - List keyHints, - ValueProviderStrategy valueProvider, - ValueProviderStrategy keyProvider, - List sources) { - super(); - this.id = id; - this.type = type; - this.name = name; - this.defaultValue = defaultValue; - this.description = description; - this.deprecation = deprecation; - this.valueHints = valueHints==null?null:ImmutableList.copyOf(valueHints); - this.keyHints = keyHints==null?null:ImmutableList.copyOf(keyHints); - this.valueProvider = valueProvider; - this.keyProvider = keyProvider; - this.sources = sources; - } - public PropertyInfo(ValueProviderRegistry valueProviders, ConfigurationMetadataProperty prop) { - this( - prop.getId(), - prop.getType(), - prop.getName(), - prop.getDefaultValue(), - prop.getDescription(), - prop.getDeprecation(), - prop.getHints().getValueHints(), - prop.getHints().getKeyHints(), - valueProviders.resolve(prop.getHints().getValueProviders()), - valueProviders.resolve(prop.getHints().getKeyProviders()), - null - ); - for (ValueProvider h : prop.getHints().getValueProviders()) { - if (h.getName().equals("handle-as")) { - handleAs(h.getParameters().get("target")); - } - } - } - private void handleAs(Object targetObject) { -// debug("handle-as "+this.getId()+" -> "+targetObject); - if (targetObject instanceof String) { - this.type = (String)targetObject; - } - } - public String getId() { - return id; - } - public String getType() { - return type; - } - public String getName() { - return name; - } - public Object getDefaultValue() { - return defaultValue; - } - public String getDescription() { - return description; - } - - public List getSources() { - if (sources!=null) { - return sources; - } - return Collections.emptyList(); - } - - @Override - public String toString() { - return "PropertyInfo("+getId()+")"; - } - public void addSource(ConfigurationMetadataSource source) { - if (sources==null) { - sources = new ArrayList(); - } - sources.add(new PropertySource(source)); - } - - public PropertyInfo withId(String alias) { - if (alias.equals(id)) { - return this; - } - return new PropertyInfo(alias, type, name, defaultValue, description, deprecation, valueHints, keyHints, valueProvider, keyProvider, sources); - } - - public void setDeprecation(Deprecation d) { - this.deprecation = d; - } - - public boolean isDeprecated() { - return deprecation!=null; - } - - public String getDeprecationReason() { - return deprecation == null ? null : deprecation.getReason(); - } - - public String getDeprecationReplacement() { - return deprecation == null ? null : deprecation.getReplacement(); - } - - public void addValueHints(List hints) { - Builder builder = ImmutableList.builder(); - builder.addAll(valueHints); - builder.addAll(hints); - valueHints = builder.build(); - } - public void addKeyHints(List hints) { - Builder builder = ImmutableList.builder(); - builder.addAll(keyHints); - builder.addAll(hints); - keyHints = builder.build(); - } -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java deleted file mode 100644 index beb3891ee..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016-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.metadata; - -import java.util.Arrays; -import java.util.stream.Collectors; - -import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry.ValueProviderStrategy; -import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; -import org.springframework.ide.vscode.commons.java.IJavaProject; - -import com.google.common.collect.ImmutableList; - -import reactor.core.publisher.Flux; - -/** - * @author Kris De Volder - */ -public class ResourceHintProvider implements ValueProviderStrategy { - - private static String[] CLASSPATH_PREFIXES = { - "classpath:", - "classpath*:" - }; - - private static final String[] URL_PREFIXES = new String[] { - "classpath:", - "classpath*:", - "file:", - "http://", - "https://" - }; - - @Override - public Flux getValues(IJavaProject javaProject, String query) { - for (String prefix : CLASSPATH_PREFIXES) { - if (query.startsWith(prefix)) { - return classpathHints - .getValues(javaProject, query.substring(prefix.length())) - .map((hint) -> hint.prefixWith(prefix)); - } - } - return Flux.fromIterable(urlPrefixHints); - } - - final private ImmutableList urlPrefixHints = ImmutableList.copyOf( - Arrays.stream(URL_PREFIXES) - .map(StsValueHint::create) - .collect(Collectors.toList()) - ); - - private ClasspathHints classpathHints = new ClasspathHints(); - - private static class ClasspathHints extends CachingValueProvider { - @Override - protected Flux getValuesAsync(IJavaProject javaProject, String query) { - return Flux.fromStream(javaProject.getClasspath().getClasspathResources().distinct().map(StsValueHint::create)); - } - } - - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java index d88075998..289050e7b 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java @@ -13,9 +13,8 @@ package org.springframework.ide.vscode.boot.metadata; import java.util.HashMap; import java.util.Map; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.boot.metadata.util.Listener; -import org.springframework.ide.vscode.boot.metadata.util.ListenerManager; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; @@ -27,20 +26,19 @@ import org.springframework.ide.vscode.commons.languageserver.ProgressService; * * @author Kris De Volder */ -public class SpringPropertiesIndexManager extends ListenerManager> { +public class SpringPropertiesIndexManager { private Map indexes = null; - private final ValueProviderRegistry valueProviders; private static int progressIdCt = 0; - public SpringPropertiesIndexManager(ValueProviderRegistry valueProviders) { - this.valueProviders = valueProviders; + public SpringPropertiesIndexManager() { } - public synchronized FuzzyMap get(IJavaProject project, ProgressService progressService) { + public synchronized FuzzyMap get(IJavaProject project, ProgressService progressService) { if (indexes==null) { indexes = new HashMap<>(); } + SpringPropertyIndex index = indexes.get(project); if (index==null) { String progressId = getProgressId(); @@ -48,7 +46,7 @@ public class SpringPropertiesIndexManager extends ListenerManager l : getListeners()) { - l.changed(this); - } - } - } private static synchronized String getProgressId() { return DefaultSpringPropertyIndexProvider.class.getName()+ (progressIdCt++); diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java index b21ed7bef..5784dc64e 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Pivotal, Inc. + * Copyright (c) 2015, 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 @@ -11,71 +11,41 @@ package org.springframework.ide.vscode.boot.metadata; import java.util.Collection; -import java.util.List; -import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; -import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource; import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IClasspath; -import org.springframework.ide.vscode.commons.util.StringUtil; -public class SpringPropertyIndex extends FuzzyMap { +public class SpringPropertyIndex extends FuzzyMap { - private ValueProviderRegistry valueProviders; - - public SpringPropertyIndex(ValueProviderRegistry valueProviders, IClasspath projectPath) { - this.valueProviders = valueProviders; + public SpringPropertyIndex(IClasspath projectPath) { if (projectPath!=null) { -// try { - PropertiesLoader loader = new PropertiesLoader(); - ConfigurationMetadataRepository metadata = loader.load(projectPath); - //^^^ Should be done in bg? It seems fast enough for now. - - Collection allEntries = metadata.getAllProperties().values(); - for (ConfigurationMetadataProperty item : allEntries) { - add(new PropertyInfo(valueProviders, item)); - } - - for (ConfigurationMetadataGroup group : metadata.getAllGroups().values()) { - for (ConfigurationMetadataSource source : group.getSources().values()) { - for (ConfigurationMetadataProperty prop : source.getProperties().values()) { - PropertyInfo info = get(prop.getId()); - info.addSource(source); - } - } - } - - // System.out.println(">>> spring properties metadata loaded "+this.size()+" items==="); - // dumpAsTestData(); - // System.out.println(">>> spring properties metadata loaded "+this.size()+" items==="); -// } catch (Exception e) { -// LOG.log -// } + PropertiesLoader loader = new PropertiesLoader(); + ConfigurationMetadataRepository metadata = loader.load(projectPath); + Collection allEntries = metadata.getAllProperties().values(); + for (ConfigurationMetadataProperty item : allEntries) { + add(item); + } } } - public void add(ConfigurationMetadataProperty propertyInfo) { - add(new PropertyInfo(valueProviders, propertyInfo)); - } - /** * Dumps out 'test data' based on the current contents of the index. This is not meant to be * used in 'production' code. The idea is to call this method during development to dump a * 'snapshot' of the index onto System.out. The data is printed in a forma so that it can be easily * pasted/used into JUNit testing code. */ - public void dumpAsTestData() { - List> allData = this.find(""); - for (Match match : allData) { - PropertyInfo d = match.data; - System.out.println("data(" - +dumpString(d.getId())+", " - +dumpString(d.getType())+", " - +dumpString(d.getDefaultValue())+", " - +dumpString(d.getDescription()) +");" - ); +// public void dumpAsTestData() { +// List> allData = this.find(""); +// for (Match match : allData) { +// PropertyInfo d = match.data; +// System.out.println("data(" +// +dumpString(d.getId())+", " +// +dumpString(d.getType())+", " +// +dumpString(d.getDefaultValue())+", " +// +dumpString(d.getDescription()) +");" +// ); // for (PropertySource source : d.getSources()) { // String st = source.getSourceType(); // String sm = source.getSourceMethod(); @@ -83,15 +53,15 @@ public class SpringPropertyIndex extends FuzzyMap { // System.out.println(d.getId() +" from: "+st+"::"+sm); // } // } - } - } +// } +// } - private String dumpString(Object v) { - if (v==null) { - return "null"; - } - return dumpString(""+v); - } +// private String dumpString(Object v) { +// if (v==null) { +// return "null"; +// } +// return dumpString(""+v); +// } private String dumpString(String s) { if (s==null) { @@ -123,7 +93,7 @@ public class SpringPropertyIndex extends FuzzyMap { } @Override - protected String getKey(PropertyInfo entry) { + protected String getKey(ConfigurationMetadataProperty entry) { return entry.getId(); } @@ -132,25 +102,25 @@ public class SpringPropertyIndex extends FuzzyMap { * 'string prefix' but a prefix in the sense of treating '.' as a kind of separators. So * 'prefix' is not allowed to end in the middle of a 'segment'. */ - public static PropertyInfo findLongestValidProperty(FuzzyMap index, String name) { - int bracketPos = name.indexOf('['); - int endPos = bracketPos>=0?bracketPos:name.length(); - PropertyInfo prop = null; - String prefix = null; - while (endPos>0 && prop==null) { - prefix = name.substring(0, endPos); - String canonicalPrefix = StringUtil.camelCaseToHyphens(prefix); - prop = index.get(canonicalPrefix); - if (prop==null) { - endPos = name.lastIndexOf('.', endPos-1); - } - } - if (prop!=null) { - //We should meet caller's expectation that matched properties returned by this method - // match the names exactly even if we found them using relaxed name matching. - return prop.withId(prefix); - } - return null; - } +// public static PropertyInfo findLongestValidProperty(FuzzyMap index, String name) { +// int bracketPos = name.indexOf('['); +// int endPos = bracketPos>=0?bracketPos:name.length(); +// PropertyInfo prop = null; +// String prefix = null; +// while (endPos>0 && prop==null) { +// prefix = name.substring(0, endPos); +// String canonicalPrefix = StringUtil.camelCaseToHyphens(prefix); +// prop = index.get(canonicalPrefix); +// if (prop==null) { +// endPos = name.lastIndexOf('.', endPos-1); +// } +// } +// if (prop!=null) { +// //We should meet caller's expectation that matched properties returned by this method +// // match the names exactly even if we found them using relaxed name matching. +// return prop.withId(prefix); +// } +// return null; +// } } diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java index 41f750fb0..570996f5a 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Pivotal, Inc. + * Copyright (c) 2015, 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 @@ -10,11 +10,12 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.metadata; +import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; @FunctionalInterface public interface SpringPropertyIndexProvider { - FuzzyMap getIndex(IDocument doc); + FuzzyMap getIndex(IDocument doc); } diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ValueProviderRegistry.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ValueProviderRegistry.java deleted file mode 100644 index c7032b3de..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/ValueProviderRegistry.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.metadata; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import org.springframework.boot.configurationmetadata.ValueProvider; -import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; -import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.util.CollectionUtil; - -import reactor.core.publisher.Flux; - -/** - * An instance of this class serves as a 'registry' that associates known - * {@link ValueProvider} ids to strategy objects used in the computation of completions - * for properties to which the provider is attached. - * - * @author Kris De Volder - */ -public class ValueProviderRegistry { - - private static ValueProviderRegistry DEFAULT; - - /** - * Creates a default {@link ValueProviderRegistry} which is initialized with all the known - * providers. (This is the one production code should use, test code might make use - * something else for mocking purposes). - */ - public synchronized static ValueProviderRegistry getDefault() { - if (DEFAULT==null) { - DEFAULT = new ValueProviderRegistry(); - DEFAULT.initializeDefaults(DEFAULT); - } - return DEFAULT; - } - - protected void initializeDefaults(ValueProviderRegistry r) { - def("logger-name", LoggerNameProvider.FACTORY); - def("class-reference", ClassReferenceProvider.FACTORY); - } - - private Map, ValueProviderStrategy>> registry = new HashMap<>(); - - public interface ValueProviderStrategy { - Flux getValues(IJavaProject javaProject, String query); - - default Collection getValuesNow(IJavaProject javaProject, String query) { - return this.getValues(javaProject, query) - .take(CachingValueProvider.TIMEOUT) - .collectList() - .block(); - } - } - - /** - * Defines a value provider by binding its id to a strategy. - */ - public void def(String id, Function, ValueProviderStrategy> algo) { - registry.put(id, algo); - } - - /** - * Resolve a list of {@link ValueProvider}s to a {@link ValueProviderStrategy}. - *

- * Essentially this finds the first provider from the list which has a known name - * and uses that to iinstantiate a ValueProviderStrategy. Spring boot assumes that - * a list is provided to allow new providers to be defined that override older ones - * and these are added at the top of the list. Thus an older IDE can continue to - * function using the older provider further down the list whereas newer IDEs will - * use a 'better' one from higher up the list. - */ - public ValueProviderStrategy resolve(List providerDescriptors) { - if (CollectionUtil.hasElements(providerDescriptors)) { - for (ValueProvider descriptor : providerDescriptors) { - Function, ValueProviderStrategy> factory = registry.get(descriptor.getName()); - if (factory!=null) { - Map params = descriptor.getParameters(); - return factory.apply(params); - } - } - } - return null; - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java deleted file mode 100644 index 33a565d4d..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016-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.metadata.hints; - -import org.springframework.boot.configurationmetadata.Deprecation; -import org.springframework.boot.configurationmetadata.ValueHint; -import org.springframework.ide.vscode.boot.metadata.util.DeprecationUtil; -import org.springframework.ide.vscode.commons.java.IJavaElement; -import org.springframework.ide.vscode.commons.java.IType; -import org.springframework.ide.vscode.commons.javadoc.IJavadoc; -import org.springframework.ide.vscode.commons.util.Assert; -import org.springframework.ide.vscode.commons.util.Renderable; -import org.springframework.ide.vscode.commons.util.Renderables; -import org.springframework.ide.vscode.commons.util.StringUtil; - -/** - * Sts version of {@link ValueHint} contains similar data, but accomoates - * a html snippet to be computed lazyly for the description. - *

- * This is meant to support using data pulled from JavaDoc in enums as description. - * This data is a html snippet, whereas the data derived from spring-boot metadata is - * just plain text. - * - * @author Kris De Volder - */ -public class StsValueHint { - - - private final String value; - private final Renderable description; - private final Deprecation deprecation; - - /** - * Create a hint with a textual description. - *

- * This constructor is private. Use one of the provided - * static 'create' methods instead. - */ - private StsValueHint(String value, Renderable description, Deprecation deprecation) { - this.value = value==null?"null":value.toString(); - Assert.isLegal(!this.value.startsWith("StsValueHint")); - this.description = description; - this.deprecation = deprecation; - } - - /** - * Creates a hint out of an IJavaElement. - */ - public static StsValueHint create(String value, IJavaElement javaElement) { - return new StsValueHint(value, javaDocSnippet(javaElement), DeprecationUtil.extract(javaElement)) { - @Override - public IJavaElement getJavaElement() { - return javaElement; - } - }; - } - - public static StsValueHint create(String value) { - return new StsValueHint(value, Renderables.NO_DESCRIPTION, null); - } - - public static StsValueHint create(ValueHint hint) { - return new StsValueHint(""+hint.getValue(), textSnippet(hint.getDescription()), null); - } - - public static StsValueHint create(IType klass) { - return new StsValueHint(klass.getFullyQualifiedName(), javaDocSnippet(klass), DeprecationUtil.extract(klass)) { - @Override - public IJavaElement getJavaElement() { - return klass; - } - }; - } - - /** - * Create a html snippet from a text snippet. - */ - private static Renderable textSnippet(String description) { - if (StringUtil.hasText(description)) { - return Renderables.text(description); - } - return Renderables.NO_DESCRIPTION; - } - - public String getValue() { - return value; - } - - public Renderable getDescription() { - return description; - } - - private static Renderable javaDocSnippet(IJavaElement je) { - return Renderables.lazy(() -> { - IJavadoc jdoc = je.getJavaDoc(); - if (jdoc != null) { - return jdoc.getRenderable(); - } else { - return Renderables.NO_DESCRIPTION; - } - }); - } - - @Override - public String toString() { - return "StsValueHint("+value+")"; - } - - public Deprecation getDeprecation() { - return deprecation; - } - - public IJavaElement getJavaElement() { - return null; - } - - public StsValueHint prefixWith(String prefix) { - StsValueHint it = this; - return new StsValueHint(prefix+getValue(), description, deprecation) { - @Override - public IJavaElement getJavaElement() { - return it.getJavaElement(); - } - }; - } - - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/ValueHintHoverInfo.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/ValueHintHoverInfo.java deleted file mode 100644 index 2ce773fe9..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/ValueHintHoverInfo.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016-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.metadata.hints; - -import java.util.List; - -import org.springframework.ide.vscode.commons.util.Renderable; -import org.springframework.ide.vscode.commons.util.Renderables; - -import static org.springframework.ide.vscode.commons.util.Renderables.*; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; - -public class ValueHintHoverInfo { - - public static Renderable create(StsValueHint hint) { - Builder builder = ImmutableList.builder(); - builder.add(bold(""+hint.getValue())); - builder.add(paragraph(hint.getDescription())); - return concat(builder.build()); - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/DeprecationUtil.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/DeprecationUtil.java deleted file mode 100644 index 9d9cd78b3..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/DeprecationUtil.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Pivotal, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Pivotal, Inc. - initial API and implementation - *******************************************************************************/ -package org.springframework.ide.vscode.boot.metadata.util; - -import java.util.Optional; - -import org.springframework.boot.configurationmetadata.Deprecation; -import org.springframework.ide.vscode.commons.java.IAnnotatable; -import org.springframework.ide.vscode.commons.java.IJavaElement; - -import com.google.common.collect.ImmutableSet; - -public class DeprecationUtil { - - private static final ImmutableSet DEPRECATED_ANOT_NAMES = ImmutableSet.of( - "org.springframework.boot.context.properties.DeprecatedConfigurationProperty", - "DeprecatedConfigurationProperty", - "java.lang.Deprecated", - "Deprecated" - ); - - /** - * Extract {@link Deprecation} info from annotations on a {@link IJavaElement} - */ - public static Deprecation extract(IJavaElement je) { - Optional deprecation = Optional.empty(); - if (je instanceof IAnnotatable) { - deprecation = extract((IAnnotatable)je); - } - return deprecation.isPresent() ? deprecation.get() : null; - } - - /** - * Extract {@link Deprecation} info from annotations on a {@link IJavaElement} - */ - private static Optional extract(IAnnotatable m) { - return m.getAnnotations().filter(a -> DEPRECATED_ANOT_NAMES.contains(a.getElementName())).map(a -> { - Deprecation d = new Deprecation(); - a.getMemberValuePairs().forEach(pair -> { - String name = pair.getMemberName(); - if (name.equals("reason")) { - d.setReason((String) pair.getValue()); - } else if (name.equals("replacement")) { - d.setReplacement((String) pair.getValue()); - } - }); - return d; - }).findFirst(); - } - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/Listener.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/Listener.java deleted file mode 100644 index 10aa67fdc..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/Listener.java +++ /dev/null @@ -1,20 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 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.metadata.util; - -/** - * @author Kris De Volder - */ -public interface Listener { - - void changed(T info); - -} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/ListenerManager.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/ListenerManager.java deleted file mode 100644 index f3921598a..000000000 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/ListenerManager.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 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.metadata.util; - -import java.util.Arrays; - -import org.springframework.ide.vscode.commons.util.ListenerList; - -public class ListenerManager { - - private ListenerList listeners = new ListenerList<>(ListenerList.IDENTITY); - - public void addListener(T l) { - listeners.add(l); - } - - public void removeListener(T l) { - listeners.remove(l); - } - - @SuppressWarnings("unchecked") - public Iterable getListeners() { - return (Iterable) Arrays.asList(listeners.getListeners()); - } - - - -} diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java index ab1063e03..cb008c92f 100644 --- a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java @@ -18,10 +18,8 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataPrope import org.springframework.boot.configurationmetadata.Deprecation; import org.springframework.boot.configurationmetadata.ValueHint; import org.springframework.boot.configurationmetadata.ValueProvider; -import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; -import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry; import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; @@ -33,17 +31,16 @@ import org.springframework.ide.vscode.commons.util.text.IDocument; public class PropertyIndexHarness { private Map datas = new LinkedHashMap<>(); - private ValueProviderRegistry valueProviders = ValueProviderRegistry.getDefault(); private SpringPropertyIndex index = null; private IJavaProject testProject = null; protected final SpringPropertyIndexProvider indexProvider = new SpringPropertyIndexProvider() { @Override - public FuzzyMap getIndex(IDocument doc) { + public FuzzyMap getIndex(IDocument doc) { synchronized (PropertyIndexHarness.this) { if (index==null) { IClasspath classpath = testProject == null ? null : testProject.getClasspath(); - index = new SpringPropertyIndex(valueProviders, classpath); + index = new SpringPropertyIndex(classpath); for (ConfigurationMetadataProperty propertyInfo : datas.values()) { index.add(propertyInfo); } From 9033da0e9321fbb9dcca2e20c128ff06ea9eaf42 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Tue, 14 Feb 2017 14:15:04 +0100 Subject: [PATCH 17/28] moved FuzzyMap to commons-util module --- .../ide/vscode/commons}/util/FuzzyMap.java | 2 +- .../vscode/commons/util}/FuzzyMapTest.java | 9 +- .../completions/ValueCompletionProcessor.java | 4 +- .../DefaultSpringPropertyIndexProvider.java | 15 +- .../SpringPropertiesIndexManager.java | 2 +- .../boot/metadata/SpringPropertyIndex.java | 2 +- .../metadata/SpringPropertyIndexProvider.java | 2 +- .../project/harness/PropertyIndexHarness.java | 2 +- .../boot/BootPropertiesLanguageServer.java | 2 +- .../boot/common/CommonLanguageTools.java | 2 +- .../common/PropertyCompletionFactory.java | 2 +- .../DefaultSpringPropertyIndexProvider.java | 2 +- .../vscode/boot/metadata/IndexNavigator.java | 6 +- .../SpringPropertiesIndexManager.java | 2 +- .../boot/metadata/SpringPropertyIndex.java | 2 +- .../metadata/SpringPropertyIndexProvider.java | 2 +- .../vscode/boot/metadata/util/FuzzyMap.java | 170 ------------------ ...opertiesCompletionProposalsCalculator.java | 12 +- .../hover/PropertiesHoverCalculator.java | 2 +- .../SpringPropertiesReconcileEngine.java | 2 +- .../ApplicationYamlAssistContext.java | 5 +- .../ApplicationYamlReconcileEngine.java | 2 +- .../editor/harness/PropertyIndexHarness.java | 2 +- .../boot/metadata/PropertiesIndexTest.java | 5 +- 24 files changed, 44 insertions(+), 214 deletions(-) rename vscode-extensions/{vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata => commons/commons-util/src/main/java/org/springframework/ide/vscode/commons}/util/FuzzyMap.java (98%) rename vscode-extensions/{vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata => commons/commons-util/src/test/java/org/springframework/ide/vscode/commons/util}/FuzzyMapTest.java (93%) delete mode 100644 vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FuzzyMap.java similarity index 98% rename from vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java rename to vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FuzzyMap.java index ac551346f..4df5f91a9 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java +++ b/vscode-extensions/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FuzzyMap.java @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.metadata.util; +package org.springframework.ide.vscode.commons.util; import java.util.ArrayList; import java.util.Collection; diff --git a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/FuzzyMapTest.java b/vscode-extensions/commons/commons-util/src/test/java/org/springframework/ide/vscode/commons/util/FuzzyMapTest.java similarity index 93% rename from vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/FuzzyMapTest.java rename to vscode-extensions/commons/commons-util/src/test/java/org/springframework/ide/vscode/commons/util/FuzzyMapTest.java index 0d5f9d812..8ceac034e 100644 --- a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/FuzzyMapTest.java +++ b/vscode-extensions/commons/commons-util/src/test/java/org/springframework/ide/vscode/commons/util/FuzzyMapTest.java @@ -8,18 +8,17 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.metadata; +package org.springframework.ide.vscode.commons.util; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.junit.Test; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; -import org.springframework.ide.vscode.commons.util.FuzzyMatcher; +import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; public class FuzzyMapTest { diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java index 4a43b0f19..e448f1dfc 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java @@ -20,10 +20,10 @@ import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StringLiteral; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; +import org.springframework.ide.vscode.commons.util.FuzzyMap; +import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.util.text.IDocument; /** diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java index 96b11c23b..2871fb420 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java @@ -12,29 +12,30 @@ package org.springframework.ide.vscode.boot.metadata; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexProvider { - + private static final FuzzyMap EMPTY_INDEX = new SpringPropertyIndex(null); private JavaProjectFinder javaProjectFinder; private SpringPropertiesIndexManager indexManager = new SpringPropertiesIndexManager(); - - private ProgressService progressService = (id, msg) -> { /*ignore*/ }; - + + private ProgressService progressService = (id, msg) -> { + /* ignore */ }; + public DefaultSpringPropertyIndexProvider(JavaProjectFinder javaProjectFinder) { this.javaProjectFinder = javaProjectFinder; } - + @Override public FuzzyMap getIndex(IDocument doc) { IJavaProject jp = javaProjectFinder.find(doc); - if (jp!=null) { + if (jp != null) { return indexManager.get(jp, progressService); } return EMPTY_INDEX; diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java index 289050e7b..a7d3e6550 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java @@ -14,9 +14,9 @@ import java.util.HashMap; import java.util.Map; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; +import org.springframework.ide.vscode.commons.util.FuzzyMap; /** * Support for Reconciling, Content Assist and Hover Text in spring properties diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java index 5784dc64e..44a244d7e 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java @@ -14,8 +14,8 @@ import java.util.Collection; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.util.FuzzyMap; public class SpringPropertyIndex extends FuzzyMap { diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java index 570996f5a..7bd2ee815 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java @@ -11,7 +11,7 @@ package org.springframework.ide.vscode.boot.metadata; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java index cb008c92f..0295f438c 100644 --- a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/project/harness/PropertyIndexHarness.java @@ -20,9 +20,9 @@ import org.springframework.boot.configurationmetadata.ValueHint; import org.springframework.boot.configurationmetadata.ValueProvider; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; /** diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java index 89b3ebe6e..067c2c145 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/BootPropertiesLanguageServer.java @@ -18,7 +18,6 @@ import org.springframework.ide.vscode.boot.common.RelaxedNameConfig; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.boot.properties.completions.SpringPropertiesCompletionEngine; import org.springframework.ide.vscode.boot.properties.hover.PropertiesHoverInfoProvider; import org.springframework.ide.vscode.boot.properties.reconcile.SpringPropertiesReconcileEngine; @@ -40,6 +39,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocu import org.springframework.ide.vscode.commons.maven.JavaProjectWithClasspathFileFinderStrategy; import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.MavenProjectFinderStrategy; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.TextDocument; import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider; diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/common/CommonLanguageTools.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/common/CommonLanguageTools.java index 1b75f4878..661e2c7b2 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/common/CommonLanguageTools.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/common/CommonLanguageTools.java @@ -26,11 +26,11 @@ import org.springframework.ide.vscode.boot.metadata.types.Type; import org.springframework.ide.vscode.boot.metadata.types.TypeParser; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.boot.properties.reconcile.PropertyNavigator; import org.springframework.ide.vscode.commons.languageserver.LanguageIds; import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion; import org.springframework.ide.vscode.commons.util.CollectionUtil; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.text.TextDocument; diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/common/PropertyCompletionFactory.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/common/PropertyCompletionFactory.java index 5ef8fe78d..0d84eb5b9 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/common/PropertyCompletionFactory.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/common/PropertyCompletionFactory.java @@ -16,11 +16,11 @@ import org.springframework.ide.vscode.boot.metadata.types.Type; import org.springframework.ide.vscode.boot.metadata.types.TypeParser; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypedProperty; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.hover.YPropertyInfoTemplates; diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java index 0e379d645..685fc56f9 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/DefaultSpringPropertyIndexProvider.java @@ -11,10 +11,10 @@ package org.springframework.ide.vscode.boot.metadata; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; public class DefaultSpringPropertyIndexProvider implements SpringPropertyIndexProvider { diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java index cd19604c6..ffa4f3b6a 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/IndexNavigator.java @@ -10,14 +10,14 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.metadata; -import static org.springframework.ide.vscode.commons.util.StringUtil.*; +import static org.springframework.ide.vscode.commons.util.StringUtil.hasText; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; +import org.springframework.ide.vscode.commons.util.FuzzyMap; +import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.util.StringUtil; /** diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java index d88075998..79830ab41 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertiesIndexManager.java @@ -13,11 +13,11 @@ package org.springframework.ide.vscode.boot.metadata; import java.util.HashMap; import java.util.Map; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.boot.metadata.util.Listener; import org.springframework.ide.vscode.boot.metadata.util.ListenerManager; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; +import org.springframework.ide.vscode.commons.util.FuzzyMap; /** * Support for Reconciling, Content Assist and Hover Text in spring properties diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java index b21ed7bef..cc6f74e01 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndex.java @@ -17,8 +17,8 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataGroup import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository; import org.springframework.boot.configurationmetadata.ConfigurationMetadataSource; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.StringUtil; public class SpringPropertyIndex extends FuzzyMap { diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java index 41f750fb0..daad71928 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/SpringPropertyIndexProvider.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.metadata; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java deleted file mode 100644 index ac551346f..000000000 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/metadata/util/FuzzyMap.java +++ /dev/null @@ -1,170 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 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.metadata.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map.Entry; -import java.util.TreeMap; -import java.util.logging.Logger; - -import org.springframework.ide.vscode.commons.util.FuzzyMatcher; -import org.springframework.ide.vscode.commons.util.StringUtil; - -/** - * A collection of data that can be searched with a simple 'fuzzy' string - * matching algorithm. Clients must override 'getKey' method to define how - * a search 'key' is associated with each data item. - *

- * The collection can then be searched for items who's key matches - * simple 'fuzzy' patterns. - */ -public abstract class FuzzyMap implements Iterable { - - private static final Logger LOG = Logger.getLogger(FuzzyMap.class.getName()); - - public static class Match { - public double score; - public final E data; - private String pattern; - - public Match(String pattern, double score, E e) { - this.pattern = pattern; - this.score = score; - this.data = e; - } - public static Match getBest(Collection> matches) { - double bestScore = Double.NEGATIVE_INFINITY; - Match best = null; - for (Match match : matches) { - if (match.score>bestScore) { - best = match; - bestScore = match.score; - } - } - return best; - } - - @Override - public String toString() { - return "Match(score="+score+", data="+data+")"; - } - public String getPattern() { - return pattern; - } - } - - @Override - public Iterator iterator() { - return entries.values().iterator(); - } - - private TreeMap entries = new TreeMap(); - - protected abstract String getKey(E entry); - - public void add(E value) { - //This assumes no two entries have the same id. - String key = getKey(value); - E existing = entries.get(key); - if (existing==null) { - entries.put(getKey(value), value); - } else { - LOG.warning(FuzzyMap.class.getName()+": Multiple entries for key "+key+" some entries discarded"); - } - } - - /** - * Search for pattern. A pattern is just a sequence of characters which have to found in - * an entrie's key in the same order as they are in the pattern. - *

- * Note that returned list doesn't yet have elements sorted according to score (instead they - * are sorted lexicographically thanks to the fact we use a Tree representation). - */ - public List> find(String pattern) { - if ("".equals(pattern)) { - //Special case because - // 1) no need to search. Matches everything - // 2) want to use different way of sorting / scoring. See https://issuetracker.springsource.com/browse/STS-4008 - ArrayList> matches = new ArrayList>(entries.size()); - for (E v : entries.values()) { - matches.add(new Match(pattern, 1.0, v)); - } - return matches; - } else { - //TODO: optimize somehow with a smarter index? (right now searches all map entries sequentially) - ArrayList> matches = new ArrayList>(); - for (Entry e : entries.entrySet()) { - String key = e.getKey(); - double score = FuzzyMatcher.matchScore(pattern, key); - if (score!=0.0) { - matches.add(new Match(pattern, score, e.getValue())); - } - } - return matches; - } - } - - /** - * Searches the index for the longest string which is both - * - a prefix of propertyName - * - a prefix of some key in the map. - * Note: If the map is empty, then this returns null, since - * no string, not even the empty string is a prefix of a - * key in the map. - */ - public String findValidPrefix(String propertyName) { - E best = findLongestCommonPrefixEntry(propertyName); - return best==null?null:StringUtil.commonPrefix(propertyName, getKey(best)); - } - - /** - * Find property with longest common prefix for given key. - */ - public E findLongestCommonPrefixEntry(String propertyName) { - //We can implementation this O(log(n)) because the properties are kept in a TreeMap which is sorted. - //This means that entries with common prefix will occur 'next to eachother' - //The 'best' entry must therefore be either the entry just before or just after - //the property we are searching for. - - Entry ceiln = entries.ceilingEntry(propertyName); - Entry floor = entries.floorEntry(propertyName); - Entry best; - if (floor==null || floor==ceiln) { - best = ceiln; - } else if (ceiln==null) { - best = floor; - } else { - int floorScore = floor==null?0:StringUtil.commonPrefixLength(floor.getKey(), propertyName); - int ceilnScore = ceiln==null?0:StringUtil.commonPrefixLength(ceiln.getKey(), propertyName); - best = floorScore>ceilnScore ? floor : ceiln; - } - return best==null?null:best.getValue(); - } - - /** - * Find an exact match if it exists. - */ - public E get(String id) { - return entries.get(id); - } - - public boolean isEmpty() { - return entries==null || entries.isEmpty(); - } - - public int size() { - return entries.size(); - } - -} diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/completions/PropertiesCompletionProposalsCalculator.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/completions/PropertiesCompletionProposalsCalculator.java index 1d28fae29..2b6ff9e5e 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/completions/PropertiesCompletionProposalsCalculator.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/completions/PropertiesCompletionProposalsCalculator.java @@ -11,7 +11,11 @@ package org.springframework.ide.vscode.boot.properties.completions; -import static org.springframework.ide.vscode.boot.common.CommonLanguageTools.*; +import static org.springframework.ide.vscode.boot.common.CommonLanguageTools.SPACES; +import static org.springframework.ide.vscode.boot.common.CommonLanguageTools.findLongestValidProperty; +import static org.springframework.ide.vscode.boot.common.CommonLanguageTools.getValueHints; +import static org.springframework.ide.vscode.boot.common.CommonLanguageTools.getValueType; +import static org.springframework.ide.vscode.boot.common.CommonLanguageTools.isValuePrefixChar; import static org.springframework.ide.vscode.commons.util.StringUtil.camelCaseToHyphens; import java.util.ArrayList; @@ -28,11 +32,9 @@ import org.springframework.ide.vscode.boot.metadata.hints.ValueHintHoverInfo; import org.springframework.ide.vscode.boot.metadata.types.Type; import org.springframework.ide.vscode.boot.metadata.types.TypeParser; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; -import org.springframework.ide.vscode.boot.metadata.types.TypedProperty; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.BeanPropertyNameMode; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; +import org.springframework.ide.vscode.boot.metadata.types.TypedProperty; import org.springframework.ide.vscode.boot.properties.reconcile.PropertyNavigator; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; @@ -41,6 +43,8 @@ import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion import org.springframework.ide.vscode.commons.languageserver.util.PrefixFinder; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.CollectionUtil; +import org.springframework.ide.vscode.commons.util.FuzzyMap; +import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.util.FuzzyMatcher; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.text.IDocument; diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverCalculator.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverCalculator.java index 603c5f1ea..cfdb1dc2c 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverCalculator.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/hover/PropertiesHoverCalculator.java @@ -29,10 +29,10 @@ import org.springframework.ide.vscode.boot.metadata.hints.StsValueHint; import org.springframework.ide.vscode.boot.metadata.types.Type; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion; import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.Renderable; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.util.text.IRegion; diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertiesReconcileEngine.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertiesReconcileEngine.java index 06df1224f..7a19d1d46 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertiesReconcileEngine.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/properties/reconcile/SpringPropertiesReconcileEngine.java @@ -25,12 +25,12 @@ import org.springframework.ide.vscode.boot.metadata.types.Type; import org.springframework.ide.vscode.boot.metadata.types.TypeParser; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.boot.properties.quickfix.ReplaceDeprecatedPropertyQuickfix; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion; import org.springframework.ide.vscode.commons.util.BadLocationException; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.ValueParser; import org.springframework.ide.vscode.commons.util.text.IDocument; diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/yaml/completions/ApplicationYamlAssistContext.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/yaml/completions/ApplicationYamlAssistContext.java index 4e75f3eca..1c7948bc3 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/yaml/completions/ApplicationYamlAssistContext.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/yaml/completions/ApplicationYamlAssistContext.java @@ -17,7 +17,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Stream; import org.springframework.boot.configurationmetadata.Deprecation; import org.springframework.ide.vscode.boot.common.InformationTemplates; @@ -34,8 +33,6 @@ import org.springframework.ide.vscode.boot.metadata.types.TypeUtil; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.BeanPropertyNameMode; import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode; import org.springframework.ide.vscode.boot.metadata.types.TypedProperty; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IJavaElement; import org.springframework.ide.vscode.commons.java.IMember; @@ -46,6 +43,8 @@ import org.springframework.ide.vscode.commons.languageserver.completion.LazyProp import org.springframework.ide.vscode.commons.languageserver.completion.ScoreableProposal; import org.springframework.ide.vscode.commons.languageserver.util.DocumentRegion; import org.springframework.ide.vscode.commons.util.CollectionUtil; +import org.springframework.ide.vscode.commons.util.FuzzyMap; +import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.util.FuzzyMatcher; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.Renderable; diff --git a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlReconcileEngine.java b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlReconcileEngine.java index 1835a1682..f7670fe3f 100644 --- a/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlReconcileEngine.java +++ b/vscode-extensions/vscode-boot-properties/src/main/java/org/springframework/ide/vscode/boot/yaml/reconcile/ApplicationYamlReconcileEngine.java @@ -16,9 +16,9 @@ import org.springframework.ide.vscode.boot.metadata.IndexNavigator; import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider; import org.springframework.ide.vscode.commons.yaml.reconcile.YamlASTReconciler; diff --git a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java index 0a727548d..4b74d7e51 100644 --- a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java +++ b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/editor/harness/PropertyIndexHarness.java @@ -22,9 +22,9 @@ import org.springframework.ide.vscode.boot.metadata.PropertyInfo; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.text.IDocument; /** diff --git a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/PropertiesIndexTest.java b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/PropertiesIndexTest.java index 4fd9ae0b4..f0df01135 100644 --- a/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/PropertiesIndexTest.java +++ b/vscode-extensions/vscode-boot-properties/src/test/java/org/springframework/ide/vscode/boot/metadata/PropertiesIndexTest.java @@ -15,12 +15,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import org.junit.Test; -import org.springframework.ide.vscode.boot.metadata.PropertyInfo; -import org.springframework.ide.vscode.boot.metadata.SpringPropertiesIndexManager; -import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry; -import org.springframework.ide.vscode.boot.metadata.util.FuzzyMap; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.ProgressService; +import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.project.harness.ProjectsHarness; /** From 04ad221c032487fd70d50d2ab799cc5a22f349e3 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Tue, 14 Feb 2017 16:48:58 +0100 Subject: [PATCH 18/28] updated textmate4eclipse repo URL --- eclipse-language-servers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eclipse-language-servers/pom.xml b/eclipse-language-servers/pom.xml index 1ffa6e0e4..ab0e7382c 100644 --- a/eclipse-language-servers/pom.xml +++ b/eclipse-language-servers/pom.xml @@ -97,7 +97,7 @@ tm4e p2 - http://oss.opensagres.fr/textmate/1.0.0-SNAPSHOT/ + http://oss.opensagres.fr/textmate/0.1.0-SNAPSHOT/ From 01c31f29e13cd1ae39659d3d18b66a8abba4a6f1 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Wed, 15 Feb 2017 16:22:38 +0100 Subject: [PATCH 19/28] PT ##139933339: simple language server responds correctly to a shutdown message now, and exists itself when exit message arrives --- .../commons/languageserver/util/SimpleLanguageServer.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java index 3191933a3..858c5a050 100644 --- a/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java +++ b/vscode-extensions/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/util/SimpleLanguageServer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016-2017 Pivotal, Inc. + * Copyright (c) 2016, 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 @@ -37,7 +37,6 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcil import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemSeverity; import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem; import org.springframework.ide.vscode.commons.util.BadLocationException; -import org.springframework.ide.vscode.commons.util.Futures; import org.springframework.ide.vscode.commons.util.text.TextDocument; import reactor.core.publisher.Mono; @@ -118,14 +117,14 @@ public abstract class SimpleLanguageServer implements LanguageServer, LanguageCl @Override public CompletableFuture shutdown() { - return Futures.of(null); + return CompletableFuture.completedFuture(new Object()); } @Override public void exit() { + System.exit(0); } - public Path getWorkspaceRoot() { return workspaceRoot; } From e0ec74dab8e7450dc1a2ae4a340ca467d1033526 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Wed, 15 Feb 2017 16:23:28 +0100 Subject: [PATCH 20/28] language server is always copied into bundle data directory to simplify development turnaround cycles --- .../servers/SpringBootPropertiesLanguageServer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eclipse-language-servers/org.springframework.boot.ide.properties.servers/src/org/springframework/boot/ide/properties/servers/SpringBootPropertiesLanguageServer.java b/eclipse-language-servers/org.springframework.boot.ide.properties.servers/src/org/springframework/boot/ide/properties/servers/SpringBootPropertiesLanguageServer.java index c160c5c8c..3d68a38e5 100644 --- a/eclipse-language-servers/org.springframework.boot.ide.properties.servers/src/org/springframework/boot/ide/properties/servers/SpringBootPropertiesLanguageServer.java +++ b/eclipse-language-servers/org.springframework.boot.ide.properties.servers/src/org/springframework/boot/ide/properties/servers/SpringBootPropertiesLanguageServer.java @@ -94,14 +94,14 @@ public class SpringBootPropertiesLanguageServer extends ProcessStreamConnectionP Bundle bundle = Platform.getBundle(Constants.PLUGIN_ID); File dataFile = bundle.getDataFile(languageServer); - if (!dataFile.exists()) { +// if (!dataFile.exists()) { try { copyLanguageServerJAR(languageServer); } catch (Exception e) { e.printStackTrace(); } - } +// } return dataFile.getAbsolutePath(); } From 4bb58c21bd69d037fe21f96d3a554ea586051b61 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Wed, 15 Feb 2017 18:14:16 -0500 Subject: [PATCH 21/28] Removed unused code --- .../ide/vscode/commons/gradle/GradleCore.java | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleCore.java b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleCore.java index 6cbd3c9bd..206148de7 100644 --- a/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleCore.java +++ b/vscode-extensions/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleCore.java @@ -59,35 +59,6 @@ public class GradleCore { this.configuration = configuration; } - public GradleCoreProject readProject(File projectDir) throws GradleException { - ProjectConnection connection = null; - try { - GradleConnector gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir); - configuration.configure(gradleConnector); - connection = gradleConnector.connect();; - final EclipseProject project = connection.getModel(EclipseProject.class); - final BuildEnvironment build = connection.getModel(BuildEnvironment.class); - return new GradleCoreProject() { - - @Override - public EclipseProject getProject() { - return project; - } - - @Override - public BuildEnvironment getBuildEnvironment() { - return build; - } - }; - } catch (GradleConnectionException e) { - throw new GradleException(e); - } finally { - if (connection != null) { - connection.close(); - } - } - } - public T getModel(File projectDir, Class modelType) throws GradleException { ProjectConnection connection = null; try { From 5fd13719f21dd2d05ade94dae8c7f1c653eb52d6 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Fri, 17 Feb 2017 10:05:04 +0100 Subject: [PATCH 22/28] improved value content assist to work with member value pairs and refactored code to reduce code duplications --- .../completions/ValueCompletionProcessor.java | 124 +++++++++++------- .../completions/test/ValueCompletionTest.java | 51 +++++++ 2 files changed, 124 insertions(+), 51 deletions(-) diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java index e448f1dfc..f0bfa5194 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/completions/ValueCompletionProcessor.java @@ -17,11 +17,13 @@ import java.util.List; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MemberValuePair; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.StringLiteral; import org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty; import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits; import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal; +import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.FuzzyMap; import org.springframework.ide.vscode.commons.util.FuzzyMap.Match; import org.springframework.ide.vscode.commons.util.text.IDocument; @@ -56,61 +58,24 @@ public class ValueCompletionProcessor { } // case: @Value(prefix<*>) else if (node instanceof SimpleName && node.getParent() instanceof Annotation) { - String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition()); - - int startOffset = node.getStartPosition(); - int endOffset = node.getStartPosition() + node.getLength(); - - String proposalPrefix = "\""; - String proposalPostfix = "\""; - - List> matches = findMatches(prefix); - - for (Match match : matches) { - - DocumentEdits edits = new DocumentEdits(doc); - edits.replace(startOffset, endOffset, proposalPrefix + "${" + match.data.getId() + "}" + proposalPostfix); - - ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null); - completions.add(proposal); - } + computeProposalsForSimpleName(node, completions, offset, doc); + } + // case: @Value(value=<*>) + else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair + && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { + computeProposalsForSimpleName(node, completions, offset, doc); } // case: @Value("prefix<*>") else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) { if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { - - String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, offset - (node.getStartPosition() + 1)), offset - (node.getStartPosition() + 1)); - - int startOffset = offset - prefix.length(); - int endOffset = offset; - - - String prePrefix = doc.get(node.getStartPosition() + 1, offset - prefix.length() - node.getStartPosition() - 1); - - String preCompletion; - if (prePrefix.endsWith("${")) { - preCompletion = ""; - } - else if (prePrefix.endsWith("$")) { - preCompletion = "{"; - } - else { - preCompletion = "${"; - } - - String fullNodeContent = doc.get(node.getStartPosition(), node.getLength()); - String postCompletion = isClosingBracketMissing(fullNodeContent + preCompletion) ? "}" : ""; - - List> matches = findMatches(prefix); - - for (Match match : matches) { - - DocumentEdits edits = new DocumentEdits(doc); - edits.replace(startOffset, endOffset, preCompletion + match.data.getId() + postCompletion); - - ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null); - completions.add(proposal); - } + computeProposalsForStringLiteral(node, completions, offset, doc); + } + } + // case: @Value(value="prefix<*>") + else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair + && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { + if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { + computeProposalsForStringLiteral(node, completions, offset, doc); } } } @@ -118,6 +83,63 @@ public class ValueCompletionProcessor { e.printStackTrace(); } } + + private void computeProposalsForSimpleName(ASTNode node, List completions, int offset, + IDocument doc) { + String prefix = identifyPropertyPrefix(node.toString(), offset - node.getStartPosition()); + + int startOffset = node.getStartPosition(); + int endOffset = node.getStartPosition() + node.getLength(); + + String proposalPrefix = "\""; + String proposalPostfix = "\""; + + List> matches = findMatches(prefix); + + for (Match match : matches) { + + DocumentEdits edits = new DocumentEdits(doc); + edits.replace(startOffset, endOffset, proposalPrefix + "${" + match.data.getId() + "}" + proposalPostfix); + + ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null); + completions.add(proposal); + } + } + + private void computeProposalsForStringLiteral(ASTNode node, List completions, int offset, + IDocument doc) throws BadLocationException { + String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, offset - (node.getStartPosition() + 1)), offset - (node.getStartPosition() + 1)); + + int startOffset = offset - prefix.length(); + int endOffset = offset; + + String prePrefix = doc.get(node.getStartPosition() + 1, offset - prefix.length() - node.getStartPosition() - 1); + + String preCompletion; + if (prePrefix.endsWith("${")) { + preCompletion = ""; + } + else if (prePrefix.endsWith("$")) { + preCompletion = "{"; + } + else { + preCompletion = "${"; + } + + String fullNodeContent = doc.get(node.getStartPosition(), node.getLength()); + String postCompletion = isClosingBracketMissing(fullNodeContent + preCompletion) ? "}" : ""; + + List> matches = findMatches(prefix); + + for (Match match : matches) { + + DocumentEdits edits = new DocumentEdits(doc); + edits.replace(startOffset, endOffset, preCompletion + match.data.getId() + postCompletion); + + ValuePropertyKeyProposal proposal = new ValuePropertyKeyProposal(edits, match.data.getId(), match.data.getName(), null); + completions.add(proposal); + } + } private boolean isClosingBracketMissing(String fullNodeContent) { int bracketOpens = 0; diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java index 637096e01..3a4e25eb2 100644 --- a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/completions/test/ValueCompletionTest.java @@ -95,6 +95,24 @@ public class ValueCompletionTest { "@Value(\"${spring.prop1}\"<*>)"); } + @Test + public void testEmptyBracketsCompletionWithParamName() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(value=<*>)"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(value=\"${data.prop2}\"<*>)", + "@Value(value=\"${else.prop3}\"<*>)", + "@Value(value=\"${spring.prop1}\"<*>)"); + } + + @Test + public void testEmptyBracketsCompletionWithWrongParamName() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(another=<*>)"); + prepareDefaultIndexData(); + assertAnnotationCompletions(); + } + @Test public void testOnlyDollarNoQoutesCompletion() throws Exception { prepareCase("@Value(\"onField\")", "@Value($<*>)"); @@ -106,6 +124,17 @@ public class ValueCompletionTest { "@Value(\"${spring.prop1}\"<*>)"); } + @Test + public void testOnlyDollarNoQoutesWithParamCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(value=$<*>)"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(value=\"${data.prop2}\"<*>)", + "@Value(value=\"${else.prop3}\"<*>)", + "@Value(value=\"${spring.prop1}\"<*>)"); + } + @Test public void testOnlyDollarCompletion() throws Exception { prepareCase("@Value(\"onField\")", "@Value(\"$<*>\")"); @@ -117,6 +146,17 @@ public class ValueCompletionTest { "@Value(\"${spring.prop1}<*>\")"); } + @Test + public void testOnlyDollarWithParamCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(value=\"$<*>\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(value=\"${data.prop2}<*>\")", + "@Value(value=\"${else.prop3}<*>\")", + "@Value(value=\"${spring.prop1}<*>\")"); + } + @Test public void testDollarWithBracketsCompletion() throws Exception { prepareCase("@Value(\"onField\")", "@Value(\"${<*>}\")"); @@ -128,6 +168,17 @@ public class ValueCompletionTest { "@Value(\"${spring.prop1<*>}\")"); } + @Test + public void testDollarWithBracketsWithParamCompletion() throws Exception { + prepareCase("@Value(\"onField\")", "@Value(value=\"${<*>}\")"); + prepareDefaultIndexData(); + + assertAnnotationCompletions( + "@Value(value=\"${data.prop2<*>}\")", + "@Value(value=\"${else.prop3<*>}\")", + "@Value(value=\"${spring.prop1<*>}\")"); + } + @Test public void testEmptyStringLiteralCompletion() throws Exception { prepareCase("@Value(\"onField\")", "@Value(\"<*>\")"); From e1f1086b1c33b10c58e8e3fb087a0c64c8cc851c Mon Sep 17 00:00:00 2001 From: nsingh Date: Mon, 20 Feb 2017 11:21:36 -0800 Subject: [PATCH 23/28] route is now a required property of routes --- .../ide/vscode/manifest/yaml/ManifestYmlSchema.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java index 155f2f727..1974ca8c5 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYmlSchema.java @@ -22,6 +22,7 @@ import org.springframework.ide.vscode.commons.yaml.schema.YType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.AbstractType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YAtomicType; +import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YBeanType; import org.springframework.ide.vscode.commons.yaml.schema.YTypeFactory.YTypedPropertyImpl; import org.springframework.ide.vscode.commons.yaml.schema.YTypeUtil; import org.springframework.ide.vscode.commons.yaml.schema.YValueHint; @@ -93,8 +94,12 @@ public class ManifestYmlSchema implements YamlSchema { YType t_string = f.yatomic("String"); YType t_strings = f.yseq(t_string); - YType t_route = f.ymap(t_string, t_string); - YType t_routes = f.yseq(t_route); + // "routes" has nested required property "route": + // routes: + // - route: someroute.io + + YBeanType route = f.ybean("Route"); + route.addProperty(f.yprop("route", t_string).isRequired(true)); YAtomicType t_memory = f.yatomic("Memory"); t_memory.addHints("256M", "512M", "1024M"); @@ -142,7 +147,7 @@ public class ManifestYmlSchema implements YamlSchema { f.yprop("no-route", t_boolean), f.yprop("path", t_path), f.yprop("random-route", t_boolean), - f.yprop("routes", t_routes), + f.yprop("routes", f.yseq(route)), f.yprop("services", t_services), f.yprop("stack", t_string), f.yprop("timeout", t_pos_integer), From f64e972eff52441297a5d5bd2ad02194a933862d Mon Sep 17 00:00:00 2001 From: nsingh Date: Mon, 20 Feb 2017 11:42:24 -0800 Subject: [PATCH 24/28] Remove CF api name from domain CA value --- .../ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java index c2b0f0c5f..4617dbcf9 100644 --- a/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java +++ b/vscode-extensions/vscode-manifest-yaml/src/main/java/org/springframework/ide/vscode/manifest/yaml/ManifestYamlCFDomainsProvider.java @@ -54,7 +54,7 @@ public class ManifestYamlCFDomainsProvider extends AbstractCFHintsProvider { } protected String getLabel(CFTarget target, CFDomain domain) { - return domain.getName() + " (" + target.getName() + ")"; + return domain.getName(); } @Override From 68ea3cf060b7c0e7992a36d94b118e3bbd723b21 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Tue, 21 Feb 2017 12:08:28 +0100 Subject: [PATCH 25/28] prototype implemented for boot property hovers with values actuator data --- .../boot/java/BootJavaLanguageServer.java | 8 +- .../java/hover/BootJavaHoverProvider.java | 140 +++++++++++++ .../boot/java/hover/ValueHoverProvider.java | 186 ++++++++++++++++++ .../boot/java/hover/test/ValueHoverTest.java | 52 +++++ 4 files changed, 384 insertions(+), 2 deletions(-) create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/hover/BootJavaHoverProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/hover/ValueHoverProvider.java create mode 100644 vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/hover/test/ValueHoverTest.java diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java index ba51658a0..94c306f0b 100644 --- a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServer.java @@ -15,6 +15,7 @@ import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.TextDocumentSyncKind; import org.springframework.ide.vscode.boot.java.completions.BootJavaCompletionEngine; import org.springframework.ide.vscode.boot.java.completions.BootJavaReconcileEngine; +import org.springframework.ide.vscode.boot.java.hover.BootJavaHoverProvider; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.commons.gradle.GradleCore; import org.springframework.ide.vscode.commons.gradle.GradleProjectFinderStrategy; @@ -24,6 +25,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.DefaultJavaPro import org.springframework.ide.vscode.commons.languageserver.java.IJavaProjectFinderStrategy; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.reconcile.IReconcileEngine; +import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler; import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; import org.springframework.ide.vscode.commons.maven.JavaProjectWithClasspathFileFinderStrategy; @@ -44,11 +46,9 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { new JavaProjectWithClasspathFileFinderStrategy() }); - private final JavaProjectFinder javaProjectFinder; private final VscodeCompletionEngineAdapter completionEngine; public BootJavaLanguageServer(JavaProjectFinder javaProjectFinder, SpringPropertyIndexProvider indexProvider) { - this.javaProjectFinder = javaProjectFinder; SimpleTextDocumentService documents = getTextDocumentService(); IReconcileEngine reconcileEngine = new BootJavaReconcileEngine(); @@ -62,6 +62,9 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { completionEngine.setMaxCompletionsNumber(100); documents.onCompletion(completionEngine::getCompletions); documents.onCompletionResolve(completionEngine::resolveCompletion); + + HoverHandler hoverInfoProvider = new BootJavaHoverProvider(this, javaProjectFinder); + documents.onHover(hoverInfoProvider); } public void setMaxCompletionsNumber(int number) { @@ -76,6 +79,7 @@ public class BootJavaLanguageServer extends SimpleLanguageServer { CompletionOptions completionProvider = new CompletionOptions(); completionProvider.setResolveProvider(false); c.setCompletionProvider(completionProvider); + c.setHoverProvider(true); return c; } diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/hover/BootJavaHoverProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/hover/BootJavaHoverProvider.java new file mode 100644 index 000000000..f6b7d3379 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/hover/BootJavaHoverProvider.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.hover; + +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.NodeFinder; +import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.TextDocumentPositionParams; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; +import org.springframework.ide.vscode.commons.languageserver.util.HoverHandler; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService; +import org.springframework.ide.vscode.commons.util.text.IDocument; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public class BootJavaHoverProvider implements HoverHandler { + + private static final String SPRING_VALUE = "org.springframework.beans.factory.annotation.Value"; + + private JavaProjectFinder projectFinder; + private SimpleLanguageServer server; + + public BootJavaHoverProvider(SimpleLanguageServer server, JavaProjectFinder projectFinder) { + this.server = server; + this.projectFinder = projectFinder; + } + + @Override + public CompletableFuture handle(TextDocumentPositionParams params) { + SimpleTextDocumentService documents = server.getTextDocumentService(); + TextDocument doc = documents.get(params).copy(); + if (doc != null) { + try { + int offset = doc.toOffset(params.getPosition()); + CompletableFuture hoverResult = provideHover(doc, offset); + if (hoverResult != null) { + return hoverResult; + } + } + catch (Exception e) { + } + } + + return SimpleTextDocumentService.NO_HOVER; + } + + private CompletableFuture provideHover(TextDocument document, int offset) throws Exception { + ASTParser parser = ASTParser.newParser(AST.JLS8); + Map options = JavaCore.getOptions(); + JavaCore.setComplianceOptions(JavaCore.VERSION_1_8, options); + parser.setCompilerOptions(options); + parser.setKind(ASTParser.K_COMPILATION_UNIT); + parser.setStatementsRecovery(true); + parser.setBindingsRecovery(true); + parser.setResolveBindings(true); + + String[] classpathEntries = getClasspathEntries(document); + String[] sourceEntries = new String[] {}; + parser.setEnvironment(classpathEntries, sourceEntries, null, true); + + String docURI = document.getUri(); + String unitName = docURI.substring(docURI.lastIndexOf("/")); + parser.setUnitName(unitName); + parser.setSource(document.get(0, document.getLength()).toCharArray()); + + CompilationUnit cu = (CompilationUnit) parser.createAST(null); + ASTNode node = NodeFinder.perform(cu, offset, 0); + + if (node != null) { + System.out.println("AST node found: " + node.getClass().getName()); + return provideHoverForAnnotation(node, offset, document); + } + + return null; + } + + private CompletableFuture provideHoverForAnnotation(ASTNode node, int offset, TextDocument doc) { + Annotation annotation = null; + ASTNode exactNode = node; + + while (node != null && !(node instanceof Annotation)) { + node = node.getParent(); + } + + if (node != null) { + annotation = (Annotation) node; + ITypeBinding type = annotation.resolveTypeBinding(); + if (type != null) { + String qualifiedName = type.getQualifiedName(); + if (qualifiedName != null && qualifiedName.startsWith("org.springframework")) { + return provideHoverForSpringAnnotation(exactNode, annotation, type, offset, doc); + } + } + } + + return null; + } + + private CompletableFuture provideHoverForSpringAnnotation(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc) { + if (type.getQualifiedName().equals(SPRING_VALUE)) { + return new ValueHoverProvider().provideHoverForValueAnnotation(node, annotation, type, offset, doc); + } + + return null; + } + + private String[] getClasspathEntries(IDocument doc) throws Exception { + IJavaProject project = this.projectFinder.find(doc); + IClasspath classpath = project.getClasspath(); + Stream classpathEntries = classpath.getClasspathEntries(); + return classpathEntries + .filter(path -> path.toFile().exists()) + .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/hover/ValueHoverProvider.java b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/hover/ValueHoverProvider.java new file mode 100644 index 000000000..08115e1d6 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/main/java/org/springframework/ide/vscode/boot/java/hover/ValueHoverProvider.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.hover; + +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.io.IOUtils; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MemberValuePair; +import org.eclipse.jdt.core.dom.StringLiteral; +import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.Range; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public class ValueHoverProvider { + + public CompletableFuture provideHoverForValueAnnotation(ASTNode node, Annotation annotation, + ITypeBinding type, int offset, TextDocument doc) { + + try { + // case: @Value("prefix<*>") + if (node instanceof StringLiteral && node.getParent() instanceof Annotation) { + if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { + return provideHover(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc); + } + } + // case: @Value(value="prefix<*>") + else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair + && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) { + if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) { + return provideHover(node.toString(), offset - node.getStartPosition(), node.getStartPosition(), doc); + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + private CompletableFuture provideHover(String value, int offset, int nodeStartOffset, TextDocument doc) { + + try { + LocalRange range = getPropertyRange(value, offset); + if (range != null) { + String propertyKey = value.substring(range.getStart(), range.getEnd()); + + JSONObject properties = getPropertiesFromProcess(); + if (propertyKey != null && properties != null) { + Iterator keys = properties.keys(); + while (keys.hasNext()) { + String key = (String) keys.next(); + JSONObject props = properties.getJSONObject(key); + + if (props.has(propertyKey)) { + String propertyValue = props.getString(propertyKey); + + Range hoverRange = doc.toRange(nodeStartOffset + range.getStart(), range.getEnd() - range.getStart()); + + Hover hover = new Hover(); + List hoverContent = new ArrayList<>(); + + hoverContent.add("property value for " + propertyKey); + hoverContent.add(propertyValue); + hoverContent.add("coming from:"); + hoverContent.add(key); + + hover.setContents(hoverContent); + hover.setRange(hoverRange); + + return CompletableFuture.completedFuture(hover); + } + } + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + public JSONObject getPropertiesFromProcess() { + try { + URL url = new URL("http://localhost:8080/env"); + + URLConnection con = url.openConnection(); + InputStream in = con.getInputStream(); + String encoding = con.getContentEncoding(); + encoding = encoding == null ? "UTF-8" : encoding; + String body = IOUtils.toString(in, encoding); + + JSONTokener tokener = new JSONTokener(body); + JSONObject jsonData = new JSONObject(tokener); + + return jsonData; + } + catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + public String getPropertyKey(String value, int offset) { + LocalRange range = getPropertyRange(value, offset); + if (range != null) { + return value.substring(range.getStart(), range.getEnd()); + } + return null; + } + + public LocalRange getPropertyRange(String value, int offset) { + int start = -1; + int end = -1; + + for (int i = offset - 1; i >= 0; i--) { + if (value.charAt(i) == '{') { + start = i + 1; + break; + } + else if (value.charAt(i) == '}') { + break; + } + } + + for(int i = offset; i < value.length(); i++) { + if (value.charAt(i) == '{' || value.charAt(i) == '$') { + break; + } + else if (value.charAt(i) == '}') { + end = i; + break; + } + } + + if (start > 0 && start < value.length() && end > 0 && end <= value.length() && start < end) { + return new LocalRange(start, end); + } + + return null; + } + + public static class LocalRange { + private int start; + private int end; + + public LocalRange(int start, int end) { + this.start = start; + this.end = end; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + + } + +} diff --git a/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/hover/test/ValueHoverTest.java b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/hover/test/ValueHoverTest.java new file mode 100644 index 000000000..63a3401a9 --- /dev/null +++ b/vscode-extensions/vscode-boot-java/src/test/java/org/springframework/ide/vscode/boot/java/hover/test/ValueHoverTest.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2017 Pivotal, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.hover.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.springframework.ide.vscode.boot.java.hover.ValueHoverProvider; + +/** + * @author Martin Lippert + */ +public class ValueHoverTest { + + @Test + public void testGetPropertyFromValue() { + ValueHoverProvider provider = new ValueHoverProvider(); + + assertNull(provider.getPropertyKey("${spring}", 0)); + assertNull(provider.getPropertyKey("${spring}", 1)); + assertEquals("spring", provider.getPropertyKey("${spring}", 2)); + assertEquals("spring", provider.getPropertyKey("${spring}", 3)); + assertEquals("spring", provider.getPropertyKey("${spring}", 8)); + assertNull(provider.getPropertyKey("${spring}", 9)); + + assertNull(provider.getPropertyKey("abc ${spring} and other stuff", 0)); + assertNull(provider.getPropertyKey("abc ${spring} and other stuff", 5)); + assertEquals("spring", provider.getPropertyKey("abc ${spring} and other stuff", 6)); + assertEquals("spring", provider.getPropertyKey("abc ${spring} and other stuff", 12)); + assertNull(provider.getPropertyKey("abc ${spring} and other stuff", 13)); + + assertNull(provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 5)); + assertEquals("spring", provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 6)); + assertEquals("spring", provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 12)); + assertNull(provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 13)); + + assertNull(provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 19)); + assertEquals("boot", provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 20)); + assertEquals("boot", provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 24)); + assertNull(provider.getPropertyKey("abc ${spring} and ${boot} other stuff", 25)); + } + +} From 13c75be06091bcb240d14399464ba3f3babee022 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Thu, 23 Feb 2017 13:06:49 +0100 Subject: [PATCH 26/28] added support for lsp-based hovers in JDT editor --- .../plugin.xml | 9 ++++ .../servers/SpringBootJavaHoverProvider.java | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaHoverProvider.java diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/plugin.xml b/eclipse-language-servers/org.springframework.boot.ide.java.servers/plugin.xml index 64c30df54..4066c2d39 100644 --- a/eclipse-language-servers/org.springframework.boot.ide.java.servers/plugin.xml +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/plugin.xml @@ -25,5 +25,14 @@ needsSortingAfterFiltering="false"> + + + + diff --git a/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaHoverProvider.java b/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaHoverProvider.java new file mode 100644 index 000000000..eabc1d7b0 --- /dev/null +++ b/eclipse-language-servers/org.springframework.boot.ide.java.servers/src/org/springframework/boot/ide/java/servers/SpringBootJavaHoverProvider.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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.boot.ide.java.servers; + +import org.eclipse.jdt.ui.text.java.hover.IJavaEditorTextHover; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.lsp4e.operations.hover.LSBasedHover; +import org.eclipse.ui.IEditorPart; + +/** + * @author Martin Lippert + */ +@SuppressWarnings("restriction") +public class SpringBootJavaHoverProvider implements IJavaEditorTextHover { + + private LSBasedHover lsBasedHover; + + public SpringBootJavaHoverProvider() { + lsBasedHover = new LSBasedHover(); + } + + @Override + public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { + return this.lsBasedHover.getHoverInfo(textViewer, hoverRegion); + } + + @Override + public IRegion getHoverRegion(ITextViewer textViewer, int offset) { + return this.lsBasedHover.getHoverRegion(textViewer, offset); + } + + @Override + public void setEditor(IEditorPart editor) { + } + +} From 9e9ebe2a564f5bda5d137ff45f86068cb6120237 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Thu, 23 Feb 2017 21:33:51 +0100 Subject: [PATCH 27/28] added boot java language server to sts4 distribution build --- .../org.springframework.boot.ide.product.e47/category.xml | 5 +++++ .../org.springframework.boot.ide.product | 1 + .../org.springframework.boot.ide.repository.e47/category.xml | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/eclipse-distribution/org.springframework.boot.ide.product.e47/category.xml b/eclipse-distribution/org.springframework.boot.ide.product.e47/category.xml index 6b92a63b4..7c3f559ae 100644 --- a/eclipse-distribution/org.springframework.boot.ide.product.e47/category.xml +++ b/eclipse-distribution/org.springframework.boot.ide.product.e47/category.xml @@ -55,4 +55,9 @@ + + + + diff --git a/eclipse-distribution/org.springframework.boot.ide.product.e47/org.springframework.boot.ide.product b/eclipse-distribution/org.springframework.boot.ide.product.e47/org.springframework.boot.ide.product index 55a168fbe..7b6a82a93 100644 --- a/eclipse-distribution/org.springframework.boot.ide.product.e47/org.springframework.boot.ide.product +++ b/eclipse-distribution/org.springframework.boot.ide.product.e47/org.springframework.boot.ide.product @@ -83,6 +83,7 @@ openFile + diff --git a/eclipse-distribution/org.springframework.boot.ide.repository.e47/category.xml b/eclipse-distribution/org.springframework.boot.ide.repository.e47/category.xml index 6b92a63b4..7c3f559ae 100644 --- a/eclipse-distribution/org.springframework.boot.ide.repository.e47/category.xml +++ b/eclipse-distribution/org.springframework.boot.ide.repository.e47/category.xml @@ -55,4 +55,9 @@ + + + + From 1a5a4f509b38ba6d9a819a541f86532e7238cf13 Mon Sep 17 00:00:00 2001 From: Martin Lippert Date: Fri, 24 Feb 2017 16:33:50 +0100 Subject: [PATCH 28/28] added boot-java language server to vscode-extensions build on Concourse --- concourse/pipeline.yml | 16 ++++++++++++++++ concourse/tasks/build-website.sh | 5 +++++ concourse/tasks/build-website.yml | 1 + 3 files changed, 22 insertions(+) diff --git a/concourse/pipeline.yml b/concourse/pipeline.yml index 59756d40b..6777872e7 100644 --- a/concourse/pipeline.yml +++ b/concourse/pipeline.yml @@ -39,6 +39,14 @@ resources: secret_access_key: {{s3_secretkey}} region_name: {{s3_region}} regexp: sts4/vscode-extensions/snapshots/vscode-boot-properties-(.*).vsix +- name: s3-boot-java-vsix-snapshot + type: s3 + source: + bucket: {{s3_bucket}} + access_key_id: {{s3_accesskey}} + secret_access_key: {{s3_secretkey}} + region_name: {{s3_region}} + regexp: sts4/vscode-extensions/snapshots/vscode-boot-java-(.*).vsix - name: s3-concourse-vsix-snapshot type: s3 source: @@ -103,6 +111,10 @@ jobs: params: file: vsix-files/vscode-boot-properties-*.vsix acl: public-read + - put: s3-boot-java-vsix-snapshot + params: + file: vsix-files/vscode-boot-java-*.vsix + acl: public-read - put: s3-concourse-vsix-snapshot params: file: vsix-files/vscode-concourse-*.vsix @@ -144,6 +156,10 @@ jobs: passed: - build-vsix-snapshots trigger: true + - get: s3-boot-java-vsix-snapshot + passed: + - build-vsix-snapshots + trigger: true - get: s3-concourse-vsix-snapshot passed: - build-vsix-snapshots diff --git a/concourse/tasks/build-website.sh b/concourse/tasks/build-website.sh index 997920bce..adfa01748 100755 --- a/concourse/tasks/build-website.sh +++ b/concourse/tasks/build-website.sh @@ -15,6 +15,8 @@ export vscode_manifest_yaml=$(basename s3-manifest-yaml-vsix-${dist_type}/*.vsix echo "vscode_manifest_yaml=$vscode_manifest_yaml" export vscode_boot_properties=$(basename s3-boot-properties-vsix-${dist_type}/*.vsix) echo "vscode_boot_properties=$vscode_boot_properties" +export vscode_boot_java=$(basename s3-boot-java-vsix-${dist_type}/*.vsix) +echo "vscode_boot_java=$vscode_boot_java" export vscode_concourse=$(basename s3-concourse-vsix-${dist_type}/*.vsix) echo "vscode_concourse=$vscode_concourse" @@ -23,6 +25,9 @@ envsubst > "$target/vscode-extensions-snippet.html" << XXXXXX
  • Spring Boot Property Language Server: ${vscode_boot_properties}
  • +
  • Spring Boot Java Language Server: + ${vscode_boot_java} +
  • Cloud Foundry Manifest Language Server: ${vscode_manifest_yaml}
  • diff --git a/concourse/tasks/build-website.yml b/concourse/tasks/build-website.yml index e10e27b36..ac4840efd 100644 --- a/concourse/tasks/build-website.yml +++ b/concourse/tasks/build-website.yml @@ -2,6 +2,7 @@ inputs: - name: sts4 - name: s3-manifest-yaml-vsix-snapshot - name: s3-boot-properties-vsix-snapshot +- name: s3-boot-java-vsix-snapshot - name: s3-concourse-vsix-snapshot outputs: - name: website