diff --git a/headless-services/commons/commons-gradle/.settings/org.eclipse.jdt.ui.prefs b/headless-services/commons/commons-gradle/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..c743e1c07 --- /dev/null +++ b/headless-services/commons/commons-gradle/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,59 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=false +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=false +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=false +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=false +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java index ac7f98169..c30693197 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleJavaProject.java @@ -16,45 +16,52 @@ import java.nio.file.Path; import org.springframework.ide.vscode.commons.java.AbstractJavaProject; import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache; import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath; +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.Log; /** * Implementation of Gradle Java project - * + * * @author Alex Boyko * */ public class GradleJavaProject extends AbstractJavaProject { - - private DelegatingCachedClasspath classpath; - private File projectDir; - - public GradleJavaProject(GradleCore gradle, File projectDir, Path projectDataCache) { - super(projectDataCache); + + private final File projectDir; + + private GradleJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File projectDir) { + super(fileObserver, projectDir.toURI(), projectDataCache, classpath); this.projectDir = projectDir; - File file = projectDataCache == null ? null + } + + public static GradleJavaProject create(FileObserver fileObserver, GradleCore gradle, File projectDir, Path projectDataCache) { + File file = projectDataCache == null + ? null : projectDataCache.resolve(ClasspathFileBasedCache.CLASSPATH_DATA_CACHE_FILE).toFile(); ClasspathFileBasedCache fileBasedCache = new ClasspathFileBasedCache(file); - this.classpath = new DelegatingCachedClasspath( + IClasspath classpath = new DelegatingCachedClasspath( () -> new GradleProjectClasspath(gradle, projectDir), fileBasedCache ); + return new GradleJavaProject(fileObserver, projectDataCache, classpath, projectDir); } - - public GradleJavaProject(GradleCore gradle, File projectDir) { - this(gradle, projectDir, null); - if (!classpath.isCached()) { + + public static GradleJavaProject create(FileObserver fileObserver, GradleCore gradle, File projectDir) { + GradleJavaProject thiss = create(fileObserver, gradle, projectDir, null); + if (!thiss.getClasspath().isCached()) { try { - classpath.update(); + thiss.getClasspath().update(); } catch (Exception e) { Log.log(e); } } + return thiss; } - + @Override public String getElementName() { - if (classpath.getName() == null) { + if (getClasspath().getName() == null) { return projectDir.getName(); } else { return super.getElementName(); @@ -66,13 +73,17 @@ public class GradleJavaProject extends AbstractJavaProject { } @Override - public DelegatingCachedClasspath getClasspath() { - return classpath; + public DelegatingCachedClasspath getClasspath() { + return (DelegatingCachedClasspath) super.getClasspath(); } - + boolean update() throws Exception { - return classpath.update(); + return getClasspath().update(); + } + + @Override + public String toString() { + return "GradleJavaProject("+getElementName()+")"; } - } diff --git a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java index 657a0cd66..9035f55ee 100644 --- a/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java +++ b/headless-services/commons/commons-gradle/src/main/java/org/springframework/ide/vscode/commons/gradle/GradleProjectCache.java @@ -21,19 +21,19 @@ import org.springframework.ide.vscode.commons.languageserver.util.ShowMessageExc /** * Tests whether document belongs to a Gradle project - * + * * @author Alex Boyko * */ public class GradleProjectCache extends AbstractFileToProjectCache { private GradleCore gradle; - + public GradleProjectCache(Sts4LanguageServer server, GradleCore gradle, boolean asyncUpdate, Path projectCacheFolder) { super(server, asyncUpdate, projectCacheFolder); this.gradle = gradle; } - + @Override protected boolean update(GradleJavaProject project) { try { @@ -48,7 +48,7 @@ public class GradleProjectCache extends AbstractFileToProjectCache path.toFile()).collect(Collectors.toList()), - jarFile -> findIndexFile(jarFile), (classpathResource) -> { - try { - String javaVersion = getJavaRuntimeMinorVersion(); - if (javaVersion == null) { - javaVersion = "8"; - } - URL javadocUrl = new URL("https://docs.oracle.com/javase/" + javaVersion + "/docs/api/"); - return new HtmlJavadocProvider( - (type) -> SourceUrlProviderFromSourceContainer.JAVADOC_FOLDER_URL_SUPPLIER - .sourceUrl(javadocUrl, type.getFullyQualifiedName())); - } catch (MalformedURLException e) { - Log.log(e); - return null; - } - }) }; - } - - public EclipseProject getRootProject() { +// @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("https://docs.oracle.com/javase/" + javaVersion + "/docs/api/"); +// return new HtmlJavadocProvider( +// (type) -> SourceUrlProviderFromSourceContainer.JAVADOC_FOLDER_URL_SUPPLIER +// .sourceUrl(javadocUrl, type.getFullyQualifiedName())); +// } catch (MalformedURLException e) { +// Log.log(e); +// return null; +// } +// }) }; +// } + + private EclipseProject getRootProject() { EclipseProject root = project; if (root == null) { return root; @@ -99,7 +100,7 @@ public class GradleProjectClasspath extends JandexClasspath { } else { Builder entries = ImmutableList.builder(); for (EclipseExternalDependency dep : project.getClasspath()) { - entries.add(new CPE(Classpath.ENTRY_KIND_BINARY, dep.getFile().toPath().toString())); + entries.add(new CPE(Classpath.ENTRY_KIND_BINARY, dep.getFile().getAbsolutePath())); } for (EclipseProjectDependency dep : project.getProjectDependencies()) { EclipseProject peer = findPeer(root, dep.getTargetProject().getName()); @@ -109,50 +110,24 @@ public class GradleProjectClasspath extends JandexClasspath { )); } } + for (EclipseSourceDirectory sf : project.getSourceDirectories()) { + File sourceFolder = sf.getDirectory(); + String of = sf.getOutput(); + entries.add(CPE.source(sourceFolder.getAbsoluteFile(), new File(project.getProjectDirectory(), of))); + } return entries.build(); } } - + private EclipseProject findPeer(EclipseProject root, String name) { return root.getChildren().stream().filter(p -> p.getName().equals(name)).findFirst().orElse(null); } - + @Override - public ImmutableList getClasspathResources() { - if (project == null) { - return ImmutableList.of(); - } else { - return ImmutableList.copyOf(project.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(); - } - }).toArray(String[]::new)); - } - } - - public Path getOutputFolder() { - return project == null ? null : project.getProjectDirectory().toPath().resolve(project.getOutputLocation().getPath()); - } - public String getName() { return project == null ? null : project.getName(); } - public boolean exists() { - return project != null; - } - - @Override - protected IJavadocProvider createHtmlJavdocProvider(File classpathResource) { - return null; - } - public String getGradleVersion() throws GradleException { if (buildEnvironment == null) { throw new GradleException(new Exception("Cannot find Gradle version")); @@ -160,7 +135,7 @@ public class GradleProjectClasspath extends JandexClasspath { return buildEnvironment.getGradle().getGradleVersion(); } } - + public File getGradleHome() throws GradleException { if (buildEnvironment == null) { throw new GradleException(new Exception("Cannot find Gradle home folder")); @@ -168,11 +143,11 @@ public class GradleProjectClasspath extends JandexClasspath { return buildEnvironment.getGradle().getGradleUserHome(); } } - + public String getJavaRuntimeVersion() { return System.getProperty(JAVA_RUNTIME_VERSION); } - + public String getJavaRuntimeMinorVersion() { String fullVersion = getJavaRuntimeVersion(); String[] tokenized = fullVersion.split("\\."); @@ -183,7 +158,7 @@ public class GradleProjectClasspath extends JandexClasspath { return null; } } - + private String getJavaHome() { if (buildEnvironment == null) { return System.getProperty(JAVA_HOME); @@ -191,7 +166,7 @@ public class GradleProjectClasspath extends JandexClasspath { return buildEnvironment.getJava().getJavaHome().toString(); } } - + private Stream getJreLibs() { String s = System.getProperty(JAVA_BOOT_CLASS_PATH); return Arrays.stream(s.split(File.pathSeparator)) @@ -200,22 +175,7 @@ public class GradleProjectClasspath extends JandexClasspath { .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"); - } - + @Override public boolean equals(Object obj) { if (obj instanceof GradleProjectClasspath) { @@ -224,20 +184,4 @@ public class GradleProjectClasspath extends JandexClasspath { return false; } - @Override - public ImmutableList getSourceFolders() { - return ImmutableList.copyOf(project.getSourceDirectories().stream() - .map(dir -> dir.getDirectory().toPath().toString()).collect(Collectors.toList())); - } - - @Override - public ClasspathData createClasspathData() throws Exception { - return ClasspathData.from(getName(), getClasspathEntries(), getClasspathResources(), getOutputFolder()); - } - - @Override - public Optional sourceContainer(File classpathResource) { - return Optional.empty(); - } - } diff --git a/headless-services/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java b/headless-services/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java index 817b5afec..6e827abd1 100644 --- a/headless-services/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java +++ b/headless-services/commons/commons-gradle/src/test/java/org/springframework/ide/vscode/commons/gradle/GradleProjectTest.java @@ -37,19 +37,21 @@ import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CP import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService; import org.springframework.ide.vscode.commons.util.BasicFileObserver; +import static org.springframework.ide.vscode.languageserver.testharness.ClasspathTestUtil.*; + import com.google.common.collect.ImmutableList; /** * Tests covering Gradle project data - * + * * @author Alex Boyko * */ public class GradleProjectTest { - + private Sts4LanguageServer server; private BasicFileObserver fileObserver; - + @Before public void setup() throws Exception { fileObserver = new BasicFileObserver(); @@ -58,7 +60,7 @@ public class GradleProjectTest { when(workspaceService.getFileObserver()).thenReturn(fileObserver); when(server.getWorkspaceService()).thenReturn(workspaceService); } - + private static void writeContent(File file, String content) throws IOException { FileWriter writer = null; try { @@ -68,39 +70,40 @@ public class GradleProjectTest { writer.close(); } } - + private GradleJavaProject getGradleProject(String projectName) throws Exception { Path testProjectPath = Paths.get(GradleProjectTest.class.getResource("/" + projectName).toURI()); - return new GradleJavaProject(GradleCore.getDefault(), testProjectPath.toFile()); + return GradleJavaProject.create(fileObserver, GradleCore.getDefault(), testProjectPath.toFile()); } @Test public void testEclipseGradleProject() throws Exception { GradleJavaProject project = getGradleProject("empty-gradle-project"); ImmutableList calculatedClassPath = project.getClasspath().getClasspathEntries(); - assertEquals(48, calculatedClassPath.size()); + assertEquals(51, calculatedClassPath.size()); } - + @Test public void outputFolder() throws Exception { GradleJavaProject project = getGradleProject("test-app-1"); - assertTrue(project.getClasspath().getOutputFolder().toString().contains("/bin")); + String of = getOutputFolder(project).toString(); + assertTrue(of.endsWith("/bin") || of.endsWith("/bin/main")); } - + @Test public void gradleClasspathResource() throws Exception { GradleJavaProject project = getGradleProject("test-app-1"); - List resources = project.getClasspath().getClasspathResources(); + List resources = project.getClasspathResources(); assertArrayEquals(new String[] {"test-resource-1.txt"}, resources.toArray(new String[resources.size()])); } - + @Test public void testGradleFileChanges() throws Exception { Path testProjectPath = Paths.get(GradleProjectTest.class.getResource("/empty-gradle-project").toURI()); File gradleFile = testProjectPath.resolve(GradleCore.GRADLE_BUILD_FILE).toFile(); - + String gradelFileContents = Files.contentOf(gradleFile, Charset.defaultCharset()); - + try { GradleProjectCache manager = new GradleProjectCache(server, GradleCore.getDefault(), false, null); IJavaProject[] projectChanged = new IJavaProject[] { null }; @@ -108,7 +111,7 @@ public class GradleProjectTest { manager.addListener(new Listener() { @Override public void created(IJavaProject project) {} - + @Override public void changed(IJavaProject project) { projectChanged[0] = project; @@ -118,25 +121,25 @@ public class GradleProjectTest { projectDeleted[0] = project; } }); - + // Get the project from cache GradleJavaProject cachedProject = manager.project(gradleFile); assertNotNull(cachedProject); - + ImmutableList calculatedClassPath = cachedProject.getClasspath().getClasspathEntries(); - assertEquals(48, calculatedClassPath.size()); - + assertEquals(51, calculatedClassPath.size()); + fileObserver.notifyFileChanged(gradleFile.toURI().toString()); assertNull(projectChanged[0]); - + writeContent(gradleFile, Files.contentOf(testProjectPath.resolve("build.newgradle").toFile(), Charset.defaultCharset())); fileObserver.notifyFileChanged(gradleFile.toURI().toString()); assertNotNull(projectChanged[0]); assertEquals(cachedProject, projectChanged[0]); calculatedClassPath = cachedProject.getClasspath().getClasspathEntries(); - assertEquals(49, calculatedClassPath.size()); - - + assertEquals(52, calculatedClassPath.size()); + + fileObserver.notifyFileDeleted(gradleFile.toURI().toString()); assertEquals(cachedProject, projectDeleted[0]); } finally { @@ -165,5 +168,5 @@ public class GradleProjectTest { GradleJavaProject gradleProject = (GradleJavaProject) project.get(); assertEquals(new File(GradleProjectTest.class.getResource("/test-app-2").toURI()), gradleProject.getLocation()); } - + } diff --git a/headless-services/commons/commons-java/.classpath b/headless-services/commons/commons-java/.classpath index 5e8a55fef..4f8e155a9 100644 --- a/headless-services/commons/commons-java/.classpath +++ b/headless-services/commons/commons-java/.classpath @@ -23,5 +23,11 @@ + + + + + + diff --git a/headless-services/commons/commons-java/.settings/org.eclipse.core.resources.prefs b/headless-services/commons/commons-java/.settings/org.eclipse.core.resources.prefs index e9441bb12..cdfe4f1b6 100644 --- a/headless-services/commons/commons-java/.settings/org.eclipse.core.resources.prefs +++ b/headless-services/commons/commons-java/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,5 @@ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8 encoding/=UTF-8 diff --git a/headless-services/commons/commons-java/.settings/org.eclipse.jdt.ui.prefs b/headless-services/commons/commons-java/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..c743e1c07 --- /dev/null +++ b/headless-services/commons/commons-java/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,59 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=false +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=false +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=false +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=false +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java index edd0554a3..f0781caeb 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexClasspath.java @@ -11,58 +11,73 @@ package org.springframework.ide.vscode.commons.jandex; import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.springframework.ide.vscode.commons.java.ClasspathIndex; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavadocProvider; import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; +import org.springframework.ide.vscode.commons.util.CollectorUtil; +import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.Log; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; +import reactor.core.Disposable; +import reactor.core.Disposables; import reactor.core.publisher.Flux; import reactor.util.function.Tuple2; /** * Classpath with Jandex Java index for searching types - * + * * @author Alex Boyko * */ -public abstract class JandexClasspath implements IClasspath { - +public final class JandexClasspath implements ClasspathIndex { + public static JavadocProviderTypes providerType = JavadocProviderTypes.HTML; - + public enum JavadocProviderTypes { // JAVA_PARSER, //Used to be based on githb java parser. If need something back that can extract docs from source code, we have to implement // based on JDT parser. But at the moment this wasn't being used so just got removed. HTML } - + private Supplier javaIndex; - - public JandexClasspath() { + private final IClasspath classpath; + private final FileObserver fileObserver; + + public JandexClasspath(IClasspath classpath, FileObserver fileObserver) { + this.fileObserver = fileObserver; + this.classpath = classpath; this.javaIndex = Suppliers.synchronizedSupplier(Suppliers.memoize(() -> createIndex())); } - + protected JandexIndex createIndex() { - Collection classpathEntries = ImmutableList.of(); + attachFolderListeners(); + Collection classpathEntries = ImmutableList.of(); try { - classpathEntries = getClasspathEntryPaths(); - for (Path path : classpathEntries) { - System.out.println(path); - } + classpathEntries = IClasspathUtil.getBinaryRoots(classpath); } catch (Exception e) { Log.log(e); } - return new JandexIndex(classpathEntries.stream().map(p -> p.toFile()).collect(Collectors.toList()), jarFile -> findIndexFile(jarFile), classpathResource -> { + return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), classpathResource -> { switch (providerType) { // case JAVA_PARSER: // return createParserJavadocProvider(classpathResource); @@ -73,23 +88,63 @@ public abstract class JandexClasspath implements IClasspath { } }, getBaseIndices()); } - + + private Disposable.Composite subscriptions = Disposables.composite(); + + private void attachFolderListeners() { + synchronized (this) { + subscriptions.dispose(); + subscriptions = Disposables.composite(); + } + for (File cpe : IClasspathUtil.getBinaryRoots(classpath)) { + if (!cpe.toString().endsWith(".jar")) { + final List rebuildGlobPattern = Arrays.asList(cpe.toString().replace(File.separator, "/") + "/**/*.class"); + Disposable disposable = fileObserver.onAnyChange(rebuildGlobPattern, (uri) -> reindex()); + subscriptions.add(disposable); + } + } + } + + @Override + public void dispose() { + Composite toDispose = subscriptions; + subscriptions = null; + if (toDispose!=null) { + toDispose.dispose(); + } + } + + private IJavadocProvider createHtmlJavdocProvider(File binaryClasspathRoot) { + CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, binaryClasspathRoot); + return JavaDocProviders.createFor(cpe); + } + + @Override + public Optional sourceContainer(File binaryClasspathRoot) { + CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, binaryClasspathRoot); + return cpe == null ? Optional.empty() : Optional.ofNullable(cpe.getSourceContainerUrl()); + } + protected JandexIndex[] getBaseIndices() { return new JandexIndex[0]; } - + + @Override public IType findType(String fqName) { return javaIndex.get().findType(fqName); } - + + @Override public Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { return javaIndex.get().fuzzySearchTypes(searchTerm, typeFilter); } - + + @Override public Flux> fuzzySearchPackages(String searchTerm) { return javaIndex.get().fuzzySearchPackages(searchTerm); } + @Override public Flux allSubtypesOf(IType type) { return javaIndex.get().allSubtypesOf(type); } @@ -101,21 +156,35 @@ public abstract class JandexClasspath implements IClasspath { } return new File(indexFolder.toString(), jarFile.getName() + "-" + jarFile.lastModified() + ".jdx"); } - + protected File getIndexFolder() { return JandexIndex.getIndexFolder(); } - + @Override public Optional findClasspathResourceContainer(String fqName) { return javaIndex.get().findClasspathResourceForType(fqName); } - - @Override - public void reindex() { + + private void reindex() { this.javaIndex = Suppliers.synchronizedSupplier(Suppliers.memoize(() -> createIndex())); } - abstract protected IJavadocProvider createHtmlJavdocProvider(File classpathResource); - + @Override + public ImmutableList getClasspathResources() { + return IClasspathUtil.getSourceFolders(classpath) + .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(); + } + }) + .collect(CollectorUtil.toImmutableList()); + } + } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java index 8bb376491..db3d32e1a 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/AbstractJavaProject.java @@ -10,19 +10,23 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; +import java.net.URI; import java.nio.file.Path; +import org.springframework.ide.vscode.commons.util.FileObserver; + /** * Abstract java project. Has a folder to store some project calculated data to speed up access - * + * * @author Alex Boyko * */ -public abstract class AbstractJavaProject implements IJavaProject { - +public abstract class AbstractJavaProject extends JavaProject { + final protected Path projectDataCache; - - public AbstractJavaProject(Path projectDataCache) { + + public AbstractJavaProject(FileObserver fileObserver, URI loactionUri, Path projectDataCache, IClasspath classpath) { + super(fileObserver, loactionUri, classpath); this.projectDataCache = projectDataCache; } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/BootProjectUtil.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/BootProjectUtil.java index d1e797ae3..0b5c0894c 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/BootProjectUtil.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/BootProjectUtil.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; +import java.io.File; import java.nio.file.Path; import org.springframework.ide.vscode.commons.util.Log; @@ -20,7 +21,7 @@ public class BootProjectUtil { try { IClasspath cp = jp.getClasspath(); if (cp!=null) { - return cp.getClasspathEntryPaths().stream().anyMatch(cpe -> isBootEntry(cpe)); + return IClasspathUtil.getBinaryRoots(cp).stream().anyMatch(cpe -> isBootEntry(cpe)); } } catch (Exception e) { Log.log(e); @@ -28,8 +29,8 @@ public class BootProjectUtil { return false; } - private static boolean isBootEntry(Path cpe) { - String name = cpe.getFileName().toString(); + private static boolean isBootEntry(File cpe) { + String name = cpe.getName(); return name.endsWith(".jar") && name.startsWith("spring-boot"); } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java index f7c509933..0bc3d028a 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathData.java @@ -10,39 +10,50 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashSet; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; -import org.springframework.ide.vscode.commons.util.Log; -import com.google.common.base.Objects; +import com.google.common.collect.ImmutableSet; -public class ClasspathData { +public class ClasspathData implements IClasspath { + + private static final Logger log = LoggerFactory.getLogger(ClasspathData.class); + + final public static ClasspathData EMPTY_CLASSPATH_DATA = new ClasspathData( + null, + Collections.emptySet() + ); - final public static ClasspathData EMPTY_CLASSPATH_DATA = new ClasspathData(null, Collections.emptySet(), - Collections.emptySet(), null); - private String name; private Set classpathEntries; - private Set classpathResources; - private String outputFolder; - public ClasspathData() { - } + public ClasspathData() {} - public ClasspathData(String name, Set classpathEntries, Set classpathResources, String outputFolder) { + public ClasspathData(String name, Collection classpathEntries) { this.name = name; - this.classpathEntries = classpathEntries; - this.classpathResources = classpathResources; - this.outputFolder = outputFolder; + this.classpathEntries = ImmutableSet.copyOf(classpathEntries); } + public static ClasspathData from(IClasspath d) { + Collection entries = null; + try { + entries = d.getClasspathEntries(); + } catch (Exception e) { + log.error("", e); + } + return new ClasspathData( + d.getName(), + entries==null ? ImmutableSet.of() : entries + ); + } + + @Override public String getName() { return name; } @@ -51,6 +62,7 @@ public class ClasspathData { this.name = name; } + @Override public Set getClasspathEntries() { return classpathEntries; } @@ -59,47 +71,38 @@ public class ClasspathData { this.classpathEntries = classpathEntries; } - public Set getClasspathResources() { - return classpathResources; - } - - public void setClasspathResources(Set classpathResources) { - this.classpathResources = classpathResources; - } - - public String getOutputFolder() { - return outputFolder; - } - - public void setOutputFolder(String outputFolder) { - this.outputFolder = outputFolder; - } - public static ClasspathData getEmptyClasspathData() { return EMPTY_CLASSPATH_DATA; } @Override - public boolean equals(Object obj) { - if (obj instanceof ClasspathData) { - ClasspathData other = (ClasspathData) obj; - try { - return Objects.equal(name, other.name) && Objects.equal(classpathEntries, other.classpathEntries) - && Objects.equal(classpathResources, other.classpathResources) - && Objects.equal(outputFolder, outputFolder); - } catch (Throwable t) { - Log.log(t); - } - } - return false; + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((classpathEntries == null) ? 0 : classpathEntries.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; } - - public static ClasspathData from(String name, Collection classpathEntries, - Collection classpathResources, Path outputFolder) { - return new ClasspathData(name, - new LinkedHashSet<>(classpathEntries), - new LinkedHashSet<>(classpathResources), - outputFolder==null ? null : outputFolder.toString() - ); + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ClasspathData other = (ClasspathData) obj; + if (classpathEntries == null) { + if (other.classpathEntries != null) + return false; + } else if (!classpathEntries.equals(other.classpathEntries)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; } } \ No newline at end of file diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathFileBasedCache.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathFileBasedCache.java index eab66e9a3..ac5827e19 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathFileBasedCache.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathFileBasedCache.java @@ -11,32 +11,22 @@ package org.springframework.ide.vscode.commons.java; import java.io.File; -import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; -import java.util.Collections; -import java.util.stream.Collectors; -import org.json.JSONArray; -import org.json.JSONObject; -import org.json.JSONTokener; import org.springframework.ide.vscode.commons.util.Log; import com.fasterxml.jackson.databind.ObjectMapper; public class ClasspathFileBasedCache { - + public static final ClasspathFileBasedCache NULL = new ClasspathFileBasedCache(null); - + public static final String CLASSPATH_DATA_CACHE_FILE = "classpath-data.json"; - - private static final String OUTPUT_FOLDER_PROPERTY = "outputFolder"; - private static final String CLASSPATH_RESOURCES_PROPERTY = "classpathResources"; - private static final String CLASSPATH_ENTRIES_PROPERTY = "classpathEntries"; - private static final String NAME_PROPERTY = "name"; + final private File file; - + public ClasspathFileBasedCache(File file) { super(); this.file = file; @@ -63,7 +53,7 @@ public class ClasspathFileBasedCache { } } } - + public boolean isCached() { return file != null && file.exists(); } @@ -80,7 +70,7 @@ public class ClasspathFileBasedCache { return ClasspathData.EMPTY_CLASSPATH_DATA; } - + public void delete() { if (file != null && file.exists()) { file.delete(); diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathIndex.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathIndex.java new file mode 100644 index 000000000..38b5b46a6 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/ClasspathIndex.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2018 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.java; + +import java.io.File; +import java.net.URL; +import java.util.Optional; +import java.util.function.Predicate; + +import com.google.common.collect.ImmutableList; + +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + +public interface ClasspathIndex extends Disposable { + + IType findType(String fqName); + Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter); + Flux> fuzzySearchPackages(String searchTerm); + Flux allSubtypesOf(IType type); + Optional findClasspathResourceContainer(String fqName); + + //Maybe the stuff below is another interface? Something that provides operations + // on classpaths? + + Optional sourceContainer(File binaryClasspathRoot); + + /** + * Classpath resources paths relative to the source folder path + * @return classpath resource relative paths + */ + ImmutableList getClasspathResources(); + + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java index 21423c872..46b37f55d 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/DelegatingCachedClasspath.java @@ -10,14 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; -import java.io.File; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Predicate; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; import org.springframework.ide.vscode.commons.util.Assert; @@ -25,44 +19,41 @@ import org.springframework.ide.vscode.commons.util.Assert; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; - /** - * + * * This wrapper around a classpath manages classpath data from and to a file-based cache (e.g. ".sts4-cache/classpath-data.json") with classpath data obtained - * from a project (e.g., maven or gradle project) through an "update" operation. - * + * from a project (e.g., maven or gradle project) through an "update" operation. + * * The cached classpath data is written to the file and loaded from it when instance of this classpath is created - * + * * NOTE: Classpath data may not be available until an actual update is requested on this wrapper. - * + * * As the wrapper is a classpath itself ,it delegates to the underlying classpath for classpath operations (e.g. getting classpath entries, resources, etc..). However, the data may not * be available until update is performed. - * + * * The wrapper caches some of classpath data such as *
  • Classpath entries
  • *
  • Classpath resources
  • *
  • Output folder
  • *
  • Projects' name
  • - * - * + * + * * Implementation is somewhat experimental at the moment... - * + * * @author Alex Boyko * * @param a subclass of {@link IClasspath} the delegated to classpath created from current data */ -public class DelegatingCachedClasspath implements IClasspath { - +public class DelegatingCachedClasspath implements IClasspath { + private AtomicReference cachedData; - private Callable classpathCreator; - private AtomicReference cachedClasspath; + private Callable classpathCreator; + private AtomicReference cachedClasspath; private final ClasspathFileBasedCache fileBasedCache; - - - public DelegatingCachedClasspath(Callable delegateCreator, ClasspathFileBasedCache fileCache) { + + + public DelegatingCachedClasspath(Callable delegateCreator, ClasspathFileBasedCache fileCache) { super(); Assert.isLegal(delegateCreator != null); this.fileBasedCache = fileCache != null ? fileCache : ClasspathFileBasedCache.NULL; @@ -74,8 +65,8 @@ public class DelegatingCachedClasspath implements IClasspa private ClasspathData loadFileBasedCache(ClasspathFileBasedCache fileCache) { return fileCache != null ? fileCache.load() : ClasspathData.EMPTY_CLASSPATH_DATA; } - - public T delegate() { + + public IClasspath delegate() { return cachedClasspath.get(); } @@ -84,26 +75,15 @@ public class DelegatingCachedClasspath implements IClasspa return cachedData.get().getName(); } - @Override - public Path getOutputFolder() { - String of = cachedData.get().getOutputFolder(); - return of == null ? null : Paths.get(of); - } - @Override public ImmutableList getClasspathEntries() throws Exception { return ImmutableList.copyOf(cachedData.get().getClasspathEntries()); } - @Override - public ImmutableList getClasspathResources() { - return ImmutableList.copyOf(cachedData.get().getClasspathResources()); - } - public boolean isCached() { return fileBasedCache.isCached(); } - + public boolean update() throws Exception { try { final ClasspathData newData = createClasspathData(); @@ -119,74 +99,20 @@ public class DelegatingCachedClasspath implements IClasspa throw e; } } - - @Override - public boolean exists() { - T t = cachedClasspath.get(); - return t != null && t.exists(); - } - @Override - public IType findType(String fqName) { - T t = cachedClasspath.get(); - return t == null ? null : t.findType(fqName); - } - - @Override - public Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { - T t = cachedClasspath.get(); - return t == null ? Flux.empty() : t.fuzzySearchTypes(searchTerm, typeFilter); - } - - @Override - public Flux> fuzzySearchPackages(String searchTerm) { - T t = cachedClasspath.get(); - return t == null ? Flux.empty() : t.fuzzySearchPackages(searchTerm); - } - - @Override - public Flux allSubtypesOf(IType type) { - T t = cachedClasspath.get(); - return t == null ? Flux.empty() : t.allSubtypesOf(type); - } - - @Override - public ClasspathData createClasspathData() throws Exception { - T newDelegate = classpathCreator.call(); + private ClasspathData createClasspathData() throws Exception { + IClasspath newDelegate = classpathCreator.call(); cachedClasspath.set(newDelegate); if (newDelegate != null) { - ClasspathData data = newDelegate.createClasspathData(); + ClasspathData data = createClasspathData(newDelegate); if (data != null) { return data; } - } + } return ClasspathData.EMPTY_CLASSPATH_DATA; } - @Override - public ImmutableList getSourceFolders() { - T t = cachedClasspath.get(); - return t == null ? ImmutableList.of() : t.getSourceFolders(); + private ClasspathData createClasspathData(IClasspath d) { + return ClasspathData.from(d); } - - @Override - public Optional findClasspathResourceContainer(String fqName) { - T t = cachedClasspath.get(); - return t == null ? Optional.empty() : t.findClasspathResourceContainer(fqName); - } - - @Override - public void reindex() { - T t = cachedClasspath.get(); - if (t != null) { - t.reindex(); - } - } - - @Override - public Optional sourceContainer(File classpathResource) { - T t = cachedClasspath.get(); - return t == null ? Optional.empty() : t.sourceContainer(classpathResource); - } - } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java index bff3270ed..e2ffcaf79 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspath.java @@ -10,88 +10,30 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; -import java.io.File; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Optional; -import java.util.function.Predicate; -import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; -import com.google.common.collect.ImmutableList; - -import reactor.core.publisher.Flux; -import reactor.util.function.Tuple2; - /** * Classpath for a Java artifact - * + * * @author Kris De Volder * @author Alex Boyko * */ 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(); - + public static final Logger log = LoggerFactory.getLogger(IClasspath.class); + + String getName(); + /** * Classpath entries paths - * + * * @return collection of classpath entries in a form file/folder paths * @throws Exception */ Collection getClasspathEntries() throws Exception; - - /** - * Classpath resources paths relative to the source folder path - * @return classpath resource relative paths - */ - ImmutableList getClasspathResources(); - - ImmutableList getSourceFolders(); - - Optional findClasspathResourceContainer(String fqName); - - ClasspathData createClasspathData() throws Exception; - - void reindex(); - - Optional sourceContainer(File classpathResource); - - @Deprecated - default Collection getClasspathEntryPaths() throws Exception { - LinkedHashSet entries = new LinkedHashSet<>(); - for (CPE cpe : this.getClasspathEntries()) { - if (Classpath.ENTRY_KIND_BINARY.equals(cpe.getKind())) { - entries.add(Paths.get(cpe.getPath())); - } else if (Classpath.ENTRY_KIND_SOURCE.equals(cpe.getKind())) { - String of = cpe.getOutputFolder(); - if (of!=null) { - entries.add(Paths.get(cpe.getOutputFolder())); - } else { - Path op = getOutputFolder(); - if (op!=null) { - entries.add(op); - } - } - } - } - return ImmutableList.copyOf(entries); } -} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspathUtil.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspathUtil.java new file mode 100644 index 000000000..0dbe046f7 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IClasspathUtil.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2018 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.java; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; +import org.springframework.ide.vscode.commons.util.Assert; + +import com.google.common.collect.ImmutableList; + +public class IClasspathUtil { + + private static final Logger log = LoggerFactory.getLogger(IClasspath.class); + + public static CPE findEntryForBinaryRoot(IClasspath cp, File binaryClasspathtRoot) { + try { + for (CPE cpe : cp.getClasspathEntries()) { + if (correspondsToBinaryLocation(cpe, binaryClasspathtRoot)) { + return cpe; + } + } + } catch (Exception e) { + log.error("", e); + } + return null; + } + + public static List getBinaryRoots(IClasspath cp) { + ImmutableList.Builder roots = ImmutableList.builder(); + try { + for (CPE cpe : cp.getClasspathEntries()) { + File loc = binaryLocation(cpe); + if (loc!=null) { + roots.add(loc); + } + } + } catch (Exception e) { + log.error("", e); + } + return roots.build(); + } + + private static boolean correspondsToBinaryLocation(CPE cpe, File classpathEntryFile) { + classpathEntryFile = canonicalFile(classpathEntryFile); + File canonicalFile = binaryLocation(cpe); + return Objects.equals(canonicalFile, classpathEntryFile); + } + + private static File binaryLocation(CPE cpe) { + switch (cpe.getKind()) { + case Classpath.ENTRY_KIND_BINARY: + return canonicalFile(cpe.getPath()); + case Classpath.ENTRY_KIND_SOURCE: + return canonicalFile(cpe.getOutputFolder()); + default: + throw new IllegalStateException("Missing switch case?"); + } + } + + private static File canonicalFile(String _f) { + if (_f!=null) { + File f = new File(_f); + return canonicalFile(f); + } + return null; + } + + private static File canonicalFile(File f) { + try { + return f.getCanonicalFile(); + } catch (IOException e) { + return f.getAbsoluteFile(); + } + } + + public static Stream getSourceFolders(IClasspath classpath) { + try { + if (classpath != null) { + return classpath.getClasspathEntries().stream().filter(Classpath::isSource) + .map(cpe -> new File(cpe.getPath())); + } + } catch (Exception e) { + log.error("", e); + } + return Stream.empty(); + } + + public static Stream getOutputFolders(IClasspath classpath) { + try { + return classpath.getClasspathEntries().stream() + .filter(Classpath::isSource) + .map(cpe -> new File(cpe.getOutputFolder())); + } catch (Exception e) { + log.error("", e); + } + return Stream.empty(); + } +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java index 06c2e3b31..94f5fdabd 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java @@ -10,27 +10,57 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java; +import java.io.File; +import java.net.URL; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + import org.springframework.ide.vscode.commons.javadoc.IJavadoc; +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + public interface IJavaProject extends IJavaElement { - + final static String PROJECT_CACHE_FOLDER = ".sts4-cache"; - + IClasspath getClasspath(); + ClasspathIndex getIndex(); @Override default String getElementName() { return getClasspath().getName(); } - @Override - default IJavadoc getJavaDoc() { - return null; + default IType findType(String fqName) { + return getIndex().findType(fqName); } - @Override - default boolean exists() { - return getClasspath().exists(); + default Flux allSubtypesOf(IType targetType) { + return getIndex().allSubtypesOf(targetType); + } + + default Flux> fuzzySearchTypes(String searchTerm, Predicate typeFilter) { + return getIndex().fuzzySearchTypes(searchTerm, typeFilter); + } + + default Optional sourceContainer(File classpathResource) { + return getIndex().sourceContainer(classpathResource); + } + + default List getClasspathResources() { + return getIndex().getClasspathResources(); + } + + default Optional findClasspathResourceContainer(String fqName) { + return getIndex().findClasspathResourceContainer(fqName); + } + + + @Override + default IJavadoc getJavaDoc() { + //?? why is this here ?? + return null; } - } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java new file mode 100644 index 000000000..fd42e56e2 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JavaProject.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2018 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.java; + +import java.io.File; +import java.net.URI; + +import org.springframework.ide.vscode.commons.jandex.JandexClasspath; +import org.springframework.ide.vscode.commons.util.FileObserver; + +import reactor.core.Disposable; + +public class JavaProject implements IJavaProject, Disposable { + + private final IClasspath classpath; + private ClasspathIndex index; + private URI uri; + private final FileObserver fileObserver; + + public JavaProject(FileObserver fileObserver, URI uri, IClasspath classpath) { + super(); + this.classpath = classpath; + this.fileObserver = fileObserver; + } + + @Override + public IClasspath getClasspath() { + return classpath; + } + + @Override + public synchronized ClasspathIndex getIndex() { + if (index==null) { + index = new JandexClasspath(classpath, fileObserver); + } + return index; + } + + public URI getLocationUri() { + return uri; + } + + @Override + public void dispose() { + Disposable toDispose = null; + synchronized (this) { + toDispose = index; + index = null; + } + if (toDispose!=null) { + toDispose.dispose(); + } + } + + @Override + public boolean exists() { + return new File(uri).exists(); + } +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/JavaDocProviders.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/JavaDocProviders.java new file mode 100644 index 000000000..af2ed9d99 --- /dev/null +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/JavaDocProviders.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2018 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.javadoc; + +import java.net.URL; + +import org.springframework.ide.vscode.commons.java.IJavadocProvider; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; + +public class JavaDocProviders { + + public static IJavadocProvider createFor(CPE classpathEntry) { + if (classpathEntry!=null && classpathEntry.getJavadocContainerUrl() != null) { + URL containerUrl = classpathEntry.getJavadocContainerUrl(); + + TypeUrlProviderFromContainerUrl urlProvider = isJarUrl(containerUrl) + ? TypeUrlProviderFromContainerUrl.JAR_JAVADOC_URL_PROVIDER + : TypeUrlProviderFromContainerUrl.JAVADOC_FOLDER_URL_SUPPLIER; + return new HtmlJavadocProvider( + type -> urlProvider.url(classpathEntry.getJavadocContainerUrl(), type.getFullyQualifiedName()) + ); + } + return null; + } + + private static boolean isJarUrl(URL containerUrl) { + return containerUrl.toString().endsWith(".jar"); + } + +} diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/SourceUrlProviderFromSourceContainer.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/TypeUrlProviderFromContainerUrl.java similarity index 50% rename from headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/SourceUrlProviderFromSourceContainer.java rename to headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/TypeUrlProviderFromContainerUrl.java index fd8e0f398..f50c6a822 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/SourceUrlProviderFromSourceContainer.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/javadoc/TypeUrlProviderFromContainerUrl.java @@ -15,44 +15,44 @@ import java.net.URL; import java.nio.file.Paths; @FunctionalInterface -public interface SourceUrlProviderFromSourceContainer { +public interface TypeUrlProviderFromContainerUrl { static String extractTopLevelType(String fqName) { int innerTypeIdx = fqName.indexOf('$'); return innerTypeIdx > 0 ? fqName.substring(0, innerTypeIdx) : fqName; } - public static final SourceUrlProviderFromSourceContainer JAR_SOURCE_URL_PROVIDER = (sourceContainerUrl, fqName) -> { - StringBuilder sourceUrlStr = new StringBuilder(); - sourceUrlStr.append("jar:"); - sourceUrlStr.append(sourceContainerUrl); - sourceUrlStr.append("!"); - sourceUrlStr.append('/'); - sourceUrlStr.append(extractTopLevelType(fqName).replaceAll("\\.", "/")); - sourceUrlStr.append(".java"); - return new URL(sourceUrlStr.toString()); + public static final TypeUrlProviderFromContainerUrl JAR_SOURCE_URL_PROVIDER = (jarSourceUrl, fqName) -> { + StringBuilder urlStr = new StringBuilder(); + urlStr.append("jar:"); + urlStr.append(jarSourceUrl); + urlStr.append("!"); + urlStr.append('/'); + urlStr.append(extractTopLevelType(fqName).replaceAll("\\.", "/")); + urlStr.append(".java"); + return new URL(urlStr.toString()); }; - public static final SourceUrlProviderFromSourceContainer SOURCE_FOLDER_URL_SUPPLIER = (sourceContainerUrl, fqName) -> { + public static final TypeUrlProviderFromContainerUrl SOURCE_FOLDER_URL_SUPPLIER = (sourceContainerUrl, fqName) -> { return Paths.get(sourceContainerUrl.toURI()).resolve(extractTopLevelType(fqName).replaceAll("\\.", "/") + ".java").toUri().toURL(); }; - public static final SourceUrlProviderFromSourceContainer JAR_JAVADOC_URL_PROVIDER = (javadocContainerUrl, fqName) -> { - StringBuilder sourceUrlStr = new StringBuilder(); - sourceUrlStr.append("jar:"); - sourceUrlStr.append(javadocContainerUrl); - sourceUrlStr.append("!"); - sourceUrlStr.append('/'); + public static final TypeUrlProviderFromContainerUrl JAR_JAVADOC_URL_PROVIDER = (javadocContainerUrl, fqName) -> { + StringBuilder urlStr = new StringBuilder(); + urlStr.append("jar:"); + urlStr.append(javadocContainerUrl); + urlStr.append("!"); + urlStr.append('/'); // Inner classes are in separate Top.Nesting1.Nesting2.Nesting3.MyType.html files - sourceUrlStr.append(fqName.replaceAll("\\.", "/").replaceAll("\\$", ".")); - sourceUrlStr.append(".html"); - return new URL(sourceUrlStr.toString()); + urlStr.append(fqName.replaceAll("\\.", "/").replaceAll("\\$", ".")); + urlStr.append(".html"); + return new URL(urlStr.toString()); }; - public static final SourceUrlProviderFromSourceContainer JAVADOC_FOLDER_URL_SUPPLIER = (sourceContainerUrl, fqName) -> { - String urlStr = sourceContainerUrl.toString(); + public static final TypeUrlProviderFromContainerUrl JAVADOC_FOLDER_URL_SUPPLIER = (javadocContainerUrl, fqName) -> { + String urlStr = javadocContainerUrl.toString(); StringBuilder sb = new StringBuilder(urlStr); if (!urlStr.endsWith("/")) { sb.append('/'); @@ -62,6 +62,6 @@ public interface SourceUrlProviderFromSourceContainer { return new URL(sb.toString()); }; - URL sourceUrl(URL sourceContainerUrl, String fqName) throws Exception; + URL url(URL containerUrl, String fqName) throws Exception; } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractFileToProjectCache.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractFileToProjectCache.java index d1f55ffc2..73786c7b3 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractFileToProjectCache.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/languageserver/java/AbstractFileToProjectCache.java @@ -60,14 +60,6 @@ public abstract class AbstractFileToProjectCache

    extends notifyProjectDeleted(project); dispose(); })); - - Path outputFolder = project.getClasspath().getOutputFolder(); - if (outputFolder != null) { - final List rebuildGlobPattern = Arrays.asList(outputFolder.toString().replace(File.separator, "/") + "/**/*.class"); - subscriptions.add(getFileObserver().onFileChanged(rebuildGlobPattern, (uri) -> project.getClasspath().reindex())); - subscriptions.add(getFileObserver().onFileCreated(rebuildGlobPattern, (uri) -> project.getClasspath().reindex())); - subscriptions.add(getFileObserver().onFileDeleted(rebuildGlobPattern, (uri) -> project.getClasspath().reindex())); - } } private void dispose() { diff --git a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java new file mode 100644 index 000000000..56d5ba1fc --- /dev/null +++ b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JandexClasspathTest.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2018 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.jandex; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.File; +import java.util.function.BiConsumer; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.springframework.ide.vscode.commons.java.ClasspathData; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; +import org.springframework.ide.vscode.commons.util.BasicFileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver; + +import com.google.common.collect.ImmutableList; +import com.google.common.io.Files; + +public class JandexClasspathTest { + + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + class TestProject { + String name; + File root; + File testClassesFolder; + File outputFolder; + BasicFileObserver fileObserver = new BasicFileObserver(); + + TestProject(String name) throws Exception { + this.name = name; + this.root = new File(JandexClasspathTest.class.getResource("/" + name ).toURI()); + testClassesFolder = new File(root, "bin"); + this.outputFolder = folder.newFolder(); + } + + void createClass(String fqName) throws Exception { + String relativePath = fqName.replace('.', '/')+".class"; + File classFile = new File(testClassesFolder, relativePath); + File target = new File(outputFolder, relativePath); + target.getParentFile().mkdirs(); + Files.copy(classFile, target); + fileObserver.notifyFileCreated(target.toURI().toString()); + } + + ClasspathData getClasspath() { + return new ClasspathData(name, ImmutableList.of( + CPE.source(new File(root, "src"), outputFolder) + )); + } + + JandexClasspath getJandexClasspath() { + return new JandexClasspath(getClasspath(), fileObserver); + } + + public void deleteClass(String fqName, BiConsumer eventNoficator) { + String relativePath = fqName.replace('.', '/')+".class"; + File classFile = new File(outputFolder, relativePath); + classFile.delete(); + eventNoficator.accept(fileObserver, classFile.toURI().toString()); + } + + public void deleteClass(String fqName) { + deleteClass(fqName, (fileObserver, path) -> fileObserver.notifyFileDeleted(path)); + } + } + + @Test public void classfileChangesShouldTriggerReindexing() throws Exception { + TestProject project = new TestProject("simple-java-project"); + project.createClass("demo.Hello"); + + JandexClasspath subject = project.getJandexClasspath(); + + assertNotNull(subject.findType("demo.Hello")); + assertNull(subject.findType("demo.Goodbye")); + + project.createClass("demo.Goodbye"); + + assertNotNull(subject.findType("demo.Hello")); + assertNotNull(subject.findType("demo.Goodbye")); + + project.deleteClass("demo.Hello"); + + assertNull(subject.findType("demo.Hello")); + assertNotNull(subject.findType("demo.Goodbye")); + + project.deleteClass("demo.Goodbye", (fileObserver, deletedFile) -> fileObserver.notifyFileChanged(deletedFile)); + + assertNull(subject.findType("demo.Hello")); + assertNull(subject.findType("demo.Goodbye")); + } +} diff --git a/headless-services/commons/commons-java/src/test/resources/simple-java-project/.classpath b/headless-services/commons/commons-java/src/test/resources/simple-java-project/.classpath new file mode 100644 index 000000000..51a8bbad8 --- /dev/null +++ b/headless-services/commons/commons-java/src/test/resources/simple-java-project/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/headless-services/commons/commons-java/src/test/resources/simple-java-project/.gitignore b/headless-services/commons/commons-java/src/test/resources/simple-java-project/.gitignore new file mode 100644 index 000000000..a6353ef19 --- /dev/null +++ b/headless-services/commons/commons-java/src/test/resources/simple-java-project/.gitignore @@ -0,0 +1,2 @@ +!bin/ +!*.class \ No newline at end of file diff --git a/headless-services/commons/commons-java/src/test/resources/simple-java-project/.project b/headless-services/commons/commons-java/src/test/resources/simple-java-project/.project new file mode 100644 index 000000000..9ccf53fab --- /dev/null +++ b/headless-services/commons/commons-java/src/test/resources/simple-java-project/.project @@ -0,0 +1,17 @@ + + + simple-java-project + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/headless-services/commons/commons-java/src/test/resources/simple-java-project/.settings/org.eclipse.jdt.core.prefs b/headless-services/commons/commons-java/src/test/resources/simple-java-project/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..3a2153707 --- /dev/null +++ b/headless-services/commons/commons-java/src/test/resources/simple-java-project/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +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.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +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/headless-services/commons/commons-java/src/test/resources/simple-java-project/bin/demo/Goodbye.class b/headless-services/commons/commons-java/src/test/resources/simple-java-project/bin/demo/Goodbye.class new file mode 100644 index 000000000..3416f5500 Binary files /dev/null and b/headless-services/commons/commons-java/src/test/resources/simple-java-project/bin/demo/Goodbye.class differ diff --git a/headless-services/commons/commons-java/src/test/resources/simple-java-project/bin/demo/Hello.class b/headless-services/commons/commons-java/src/test/resources/simple-java-project/bin/demo/Hello.class new file mode 100644 index 000000000..74d4b2d38 Binary files /dev/null and b/headless-services/commons/commons-java/src/test/resources/simple-java-project/bin/demo/Hello.class differ diff --git a/headless-services/commons/commons-java/src/test/resources/simple-java-project/src/demo/Goodbye.java b/headless-services/commons/commons-java/src/test/resources/simple-java-project/src/demo/Goodbye.java new file mode 100644 index 000000000..b78eb7c69 --- /dev/null +++ b/headless-services/commons/commons-java/src/test/resources/simple-java-project/src/demo/Goodbye.java @@ -0,0 +1,5 @@ +package demo; + +public class Goodbye { + +} diff --git a/headless-services/commons/commons-java/src/test/resources/simple-java-project/src/demo/Hello.java b/headless-services/commons/commons-java/src/test/resources/simple-java-project/src/demo/Hello.java new file mode 100644 index 000000000..11c2c424f --- /dev/null +++ b/headless-services/commons/commons-java/src/test/resources/simple-java-project/src/demo/Hello.java @@ -0,0 +1,5 @@ +package demo; + +public class Hello { + +} diff --git a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/Classpath.java b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/Classpath.java index 6136ffc52..dc999e2dd 100644 --- a/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/Classpath.java +++ b/headless-services/commons/commons-language-server/src/main/java/org/springframework/ide/vscode/commons/languageserver/jdt/ls/Classpath.java @@ -1,20 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2018 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.languageserver.jdt.ls; - +import java.io.File; +import java.net.URL; import java.util.List; +import org.springframework.ide.vscode.commons.util.Assert; + public class Classpath { public static final String ENTRY_KIND_SOURCE = "source"; public static final String ENTRY_KIND_BINARY = "binary"; private List entries; - private String defaultOutputFolder; - public Classpath(List entries, String defaultOutputFolder) { + public Classpath(List entries) { super(); this.entries = entries; - this.defaultOutputFolder = defaultOutputFolder; } public List getEntries() { @@ -25,33 +36,34 @@ public class Classpath { this.entries = entries; } - public String getDefaultOutputFolder() { - return defaultOutputFolder; - } - - public void setDefaultOutputFolder(String defaultOutputFolder) { - this.defaultOutputFolder = defaultOutputFolder; - } - @Override public String toString() { - return "Classpath [entries=" + entries + ", defaultOutputFolder=" + defaultOutputFolder + "]"; + return "Classpath [entries=" + entries + "]"; } public static class CPE { - private String kind; - private String path; - /** - * This only applies for 'source' entries. - */ + // TODO: it seems like a good idea to make all classpath entries the same in that they all have + // - a place with source code + // - a place with compiled code + // - a place with java doc + // So it seems like we should be able to chnage this so that the same named attribute is used + // in both cases (rather then one be 'getOutputFolder' and one 'getPath' to obtain location of the compiled code). + + private String kind; + + private String path; // TODO: Change to File, Path or URL? private String outputFolder; + private URL sourceContainerUrl; + private URL javadocContainerUrl; + private boolean isSystem = false; public String getOutputFolder() { return outputFolder; } public void setOutputFolder(String outputFolder) { + Assert.isLegal(outputFolder==null || new File(outputFolder).isAbsolute()); this.outputFolder = outputFolder; } @@ -60,7 +72,7 @@ public class Classpath { public CPE(String kind, String path) { super(); this.kind = kind; - this.path = path; + setPath(path); } public String getKind() { @@ -76,16 +88,54 @@ public class Classpath { } public void setPath(String path) { + Assert.isLegal(path == null || new File(path).isAbsolute()); this.path = path; } + public URL getJavadocContainerUrl() { + return javadocContainerUrl; + } + + public void setJavadocContainerUrl(URL javadocContainerUrl) { + this.javadocContainerUrl = javadocContainerUrl; + } + + public URL getSourceContainerUrl() { + return sourceContainerUrl; + } + + public void setSourceContainerUrl(URL sourceContainerUrl) { + this.sourceContainerUrl = sourceContainerUrl; + } + + public static CPE binary(String path) { + return new CPE(ENTRY_KIND_BINARY, path); + } + + public static CPE source(File sourceFolder, File outputFolder) { + CPE cpe = new CPE(ENTRY_KIND_SOURCE, sourceFolder.getAbsolutePath()); + cpe.setOutputFolder(outputFolder.getAbsolutePath()); + return cpe; + } + + public boolean isSystem() { + return isSystem; + } + + public void setSystem(boolean isSystem) { + this.isSystem = isSystem; + } + @Override public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + (isSystem ? 1231 : 1237); + result = prime * result + ((javadocContainerUrl == null) ? 0 : javadocContainerUrl.hashCode()); result = prime * result + ((kind == null) ? 0 : kind.hashCode()); result = prime * result + ((outputFolder == null) ? 0 : outputFolder.hashCode()); result = prime * result + ((path == null) ? 0 : path.hashCode()); + result = prime * result + ((sourceContainerUrl == null) ? 0 : sourceContainerUrl.hashCode()); return result; } @@ -98,6 +148,13 @@ public class Classpath { if (getClass() != obj.getClass()) return false; CPE other = (CPE) obj; + if (isSystem != other.isSystem) + return false; + if (javadocContainerUrl == null) { + if (other.javadocContainerUrl != null) + return false; + } else if (!javadocContainerUrl.equals(other.javadocContainerUrl)) + return false; if (kind == null) { if (other.kind != null) return false; @@ -113,12 +170,17 @@ public class Classpath { return false; } else if (!path.equals(other.path)) return false; + if (sourceContainerUrl == null) { + if (other.sourceContainerUrl != null) + return false; + } else if (!sourceContainerUrl.equals(other.sourceContainerUrl)) + return false; return true; } @Override public String toString() { - return "CPE [kind=" + kind + ", path=" + path + ", outputFolder=" + outputFolder + "]"; + return "CPE [kind=" + kind + ", path=" + path + ", isSystem=" + isSystem + "]"; } } diff --git a/headless-services/commons/commons-maven/.settings/org.eclipse.jdt.ui.prefs b/headless-services/commons/commons-maven/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..c743e1c07 --- /dev/null +++ b/headless-services/commons/commons-maven/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,59 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=false +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=false +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=false +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=false +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java index 9c5580124..6c9711889 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java @@ -57,7 +57,7 @@ import org.eclipse.aether.util.graph.visitor.CloningDependencyVisitor; import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor; import org.springframework.ide.vscode.commons.jandex.JandexIndex; import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider; -import org.springframework.ide.vscode.commons.javadoc.SourceUrlProviderFromSourceContainer; +import org.springframework.ide.vscode.commons.javadoc.TypeUrlProviderFromContainerUrl; import org.springframework.ide.vscode.commons.util.Log; import com.google.common.base.Supplier; @@ -95,7 +95,7 @@ public class MavenCore { javaVersion = "8"; } URL javadocUrl = new URL("https://docs.oracle.com/javase/" + javaVersion + "/docs/api/"); - return new HtmlJavadocProvider((type) -> SourceUrlProviderFromSourceContainer.JAVADOC_FOLDER_URL_SUPPLIER.sourceUrl(javadocUrl, type.getFullyQualifiedName())); + return new HtmlJavadocProvider((type) -> TypeUrlProviderFromContainerUrl.JAVADOC_FOLDER_URL_SUPPLIER.url(javadocUrl, type.getFullyQualifiedName())); } catch (MalformedURLException e) { Log.log(e); return null; diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java index ccad4991b..b83717d96 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java @@ -16,7 +16,9 @@ import java.nio.file.Path; import org.springframework.ide.vscode.commons.java.AbstractJavaProject; import org.springframework.ide.vscode.commons.java.ClasspathFileBasedCache; import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath; +import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.Log; /** @@ -27,35 +29,40 @@ import org.springframework.ide.vscode.commons.util.Log; */ public class MavenJavaProject extends AbstractJavaProject { - private DelegatingCachedClasspath classpath; - private File pom; - - public MavenJavaProject(MavenCore maven, File pom, Path projectDataCache) { - super(projectDataCache); + private final File pom; + + private MavenJavaProject(FileObserver fileObserver, Path projectDataCache, IClasspath classpath, File pom) { + super(fileObserver, pom.getParentFile().toURI(), projectDataCache, classpath); this.pom = pom; - File file = projectDataCache == null ? null + } + + public static MavenJavaProject create(FileObserver fileObserver, MavenCore maven, File pom, Path projectDataCache) { + File file = projectDataCache == null + ? null : projectDataCache.resolve(ClasspathFileBasedCache.CLASSPATH_DATA_CACHE_FILE).toFile(); ClasspathFileBasedCache fileBasedCache = new ClasspathFileBasedCache(file); - this.classpath = new DelegatingCachedClasspath<>( + DelegatingCachedClasspath classpath = new DelegatingCachedClasspath( () -> new MavenProjectClasspath(maven, pom), fileBasedCache - ); + ); + return new MavenJavaProject(fileObserver, projectDataCache, classpath, pom); } - public MavenJavaProject(MavenCore maven, File pom) { - this(maven, pom, null); - if (!classpath.isCached()) { + public static MavenJavaProject create(FileObserver fileObserver, MavenCore maven, File pom) { + MavenJavaProject thiss = create(fileObserver, maven, pom, null); + if (!thiss.getClasspath().isCached()) { try { - classpath.update(); + thiss.getClasspath().update(); } catch (Exception e) { Log.log(e); } } + return thiss; } @Override public String getElementName() { - if (classpath.getName() == null) { + if (getClasspath().getName() == null) { return pom.getParentFile().getName(); } else { return super.getElementName(); @@ -63,12 +70,12 @@ public class MavenJavaProject extends AbstractJavaProject { } @Override - public DelegatingCachedClasspath getClasspath() { - return classpath; + public DelegatingCachedClasspath getClasspath() { + return (DelegatingCachedClasspath) super.getClasspath(); } boolean update() throws Exception { - return classpath.update(); + return getClasspath().update(); } public File pom() { @@ -77,7 +84,6 @@ public class MavenJavaProject extends AbstractJavaProject { @Override public String toString() { - return "MavenJavaProject("+classpath.getName()+")"; + return "MavenJavaProject("+getElementName()+")"; } - } diff --git a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectCache.java b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectCache.java index f4c7a0d8e..6dad6c2f1 100644 --- a/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectCache.java +++ b/headless-services/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectCache.java @@ -47,7 +47,7 @@ public class MavenProjectCache extends AbstractFileToProjectCache resolveClasspathEntries(MavenProject project) throws Exception { - return ImmutableList.copyOf( - Stream.concat( - projectDependencies(project).stream().map(a -> a.getFile().toPath()), - projectOutput(project).stream().map(f -> f.toPath()) - ) - .map(path -> new CPE(Classpath.ENTRY_KIND_BINARY, path.toString())) - .collect(Collectors.toList())); + LinkedHashSet entries = new LinkedHashSet<>(); + safe(() -> maven.getJreLibs().forEach(path -> safe(() -> { + CPE cpe = CPE.binary(path.toString()); + String javaVersion = maven.getJavaRuntimeMinorVersion(); + if (javaVersion == null) { + javaVersion = "8"; + } + cpe.setJavadocContainerUrl(new URL("https://docs.oracle.com/javase/" + javaVersion + "/docs/api/")); + cpe.setSystem(true); + entries.add(cpe); + }))); + //Add jar dependencies... + for (Artifact a : projectDependencies(project)) { + File f = a.getFile(); + if (f!=null) { + CPE cpe = CPE.binary(a.getFile().toPath().toString()); + safe(() -> { //add javadoc + Artifact jdoc = maven.getJavadoc(a, project.getRemoteArtifactRepositories()); + if (jdoc!=null) { + cpe.setJavadocContainerUrl(jdoc.getFile().toURI().toURL()); + } + }); + safe(() -> { //add source + Artifact source = maven.getSources(a, project.getRemoteArtifactRepositories()); + if (source!=null) { + cpe.setSourceContainerUrl(source.getFile().toURI().toURL()); + } + }); + entries.add(cpe); + } + } + //Add source folders... + { //main/java + File sourceFolder = new File(project.getBuild().getSourceDirectory()); + File outputFolder = new File(project.getBuild().getOutputDirectory()); + CPE cpe = CPE.source(sourceFolder, outputFolder); + safe(() -> { + String reportingDir = project.getModel().getReporting().getOutputDirectory(); + if (reportingDir!=null) { + File apidocs = new File(new File(reportingDir), "apidocs"); + cpe.setJavadocContainerUrl(apidocs.toURI().toURL()); + } + }); + entries.add(cpe); + } + { //main/resources + for (Resource resource : project.getBuild().getResources()) { + File sourceFolder = new File(resource.getDirectory()); + String targetPath = resource.getTargetPath(); + if (targetPath==null) { + targetPath = project.getBuild().getOutputDirectory(); + } + entries.add(CPE.source(sourceFolder, new File(targetPath))); + } + } + { //test/resources + for (Resource resource : project.getBuild().getTestResources()) { + File sourceFolder = new File(resource.getDirectory()); + String targetPath = resource.getTargetPath(); + if (targetPath==null) { + targetPath = project.getBuild().getTestOutputDirectory(); + } + entries.add(CPE.source(sourceFolder, targetPath==null ? null : new File(targetPath))); + } + } + { //test/java + File sourceFolder = new File(project.getBuild().getTestSourceDirectory()); + File outputFolder = new File(project.getBuild().getTestOutputDirectory()); + CPE cpe = CPE.source(sourceFolder, outputFolder); + safe(() -> { + String reportingDir = project.getModel().getReporting().getOutputDirectory(); + if (reportingDir!=null) { + File apidocs = new File(new File(reportingDir), "apidocs"); + cpe.setJavadocContainerUrl(apidocs.toURI().toURL()); + } + }); + entries.add(cpe); + } + return ImmutableList.copyOf(entries); } @Override public ImmutableList getClasspathEntries() throws Exception { return cachedData != null ? ImmutableList.copyOf(cachedData.getClasspathEntries()) : ImmutableList.of(); } - + private Set projectDependencies(MavenProject project) { return project == null ? Collections.emptySet() : project.getArtifacts(); } - + private List projectOutput(MavenProject project) { if (project == null) { return Collections.emptyList(); @@ -120,133 +195,22 @@ public class MavenProjectClasspath extends JandexClasspath { return Arrays.asList(new File(project.getBuild().getOutputDirectory()), new File(project.getBuild().getTestOutputDirectory())); } } - - private Path resolveOutputFolder(MavenProject project) { - return project == null ? null : new File(project.getBuild().getOutputDirectory()).toPath(); - } - - public Path getOutputFolder() { - if (cachedData!=null) { - String of = cachedData.getOutputFolder(); - return of == null ? null : Paths.get(of); + + private static void safe(RunnableWithException do_stuff) { + try { + do_stuff.run(); + } catch (Exception e) { +// log.error("", e); } - return null; - } - - private ImmutableList resolveClasspathResources(MavenProject project) { - if (project == null) { - return ImmutableList.of(); - } - return ImmutableList.copyOf(project.getBuild().getResources().stream().filter(resource -> new File(resource.getDirectory()).exists()).flatMap(resource -> { - DirectoryScanner scanner = new DirectoryScanner(); - scanner.setBasedir(resource.getDirectory()); - if (resource.getIncludes() != null && !resource.getIncludes().isEmpty()) { - scanner.setIncludes(resource.getIncludes().toArray(new String[resource.getIncludes().size()])); - } - if (resource.getExcludes() != null && !resource.getExcludes().isEmpty()) { - scanner.setExcludes(resource.getExcludes().toArray(new String[resource.getExcludes().size()])); - } - scanner.setCaseSensitive(false); - scanner.scan(); - return Arrays.stream(scanner.getIncludedFiles()); - }).toArray(String[]::new)); - } - - @Override - public ImmutableList getClasspathResources() { - return cachedData != null ? ImmutableList.copyOf(cachedData.getClasspathResources()) : ImmutableList.of(); } - /* - * Roaster lib is experiment to generate javadoc from source. Commented out for now since roaster lib is taken out - */ -// private IJavadocProvider createRoasterJavadocProvider(File classpathResource) { -// if (classpathResource.isDirectory()) { -// if (classpathResource.toString().startsWith(project.getBuild().getOutputDirectory())) { -// return new RoasterJavadocProvider(type -> { -// return SourceUrlProviderFromSourceContainer.SOURCE_FOLDER_URL_SUPPLIER -// .sourceUrl(new File(project.getBuild().getSourceDirectory()).toURI().toURL(), type); -// }); -// } else if (classpathResource.toString().startsWith(project.getBuild().getTestOutputDirectory())) { -// return new RoasterJavadocProvider(type -> { -// return SourceUrlProviderFromSourceContainer.SOURCE_FOLDER_URL_SUPPLIER -// .sourceUrl(new File(project.getBuild().getTestSourceDirectory()).toURI().toURL(), type); -// }); -// } else { -// throw new IllegalArgumentException("Cannot find source folder for " + classpathResource); -// } -// } else { -// // Assume it's a JAR file -// return new RoasterJavadocProvider(type -> { -// try { -// Artifact artifact = getArtifactFromJarFile(classpathResource).get(); -// URL sourceContainer = maven.getSources(artifact).getFile().toURI().toURL(); -// return SourceUrlProviderFromSourceContainer.JAR_SOURCE_URL_PROVIDER.sourceUrl(sourceContainer, -// type); -// } catch (MavenException e) { -// Log.log("Failed to find sources JAR for " + classpathResource, e); -// } catch (MalformedURLException e) { -// Log.log("Invalid URL for sources JAR for " + classpathResource, e); -// } -// return null; -// }); -// } -// } - + private ClasspathData createClasspathData() throws Exception { + MavenProject project = createMavenProject(); - @Override - public Optional sourceContainer(File classpathResource) { - if (cachedData == null) { - return Optional.empty(); - } - return cachedData.artifacts.stream().filter(a -> classpathResource.equals(a.getFile())).findFirst().map(artifact -> { - try { - return maven.getSources(artifact, cachedData.remoteArtifactRepositories).getFile().toURI().toURL(); - } catch (MalformedURLException e) { - Log.log("Invalid URL for sources JAR for " + classpathResource, e); - return null; - } catch (MavenException e) { - Log.log("Failed to find sources JAR for " + classpathResource, e); - return null; - } - }); - } + ImmutableList entries = resolveClasspathEntries(project); + String name = project.getArtifact().getArtifactId(); - @Override - protected IJavadocProvider createHtmlJavdocProvider(File classpathResource) { - if (cachedData == null) { - return null; - } - if (classpathResource.isDirectory()) { - if (classpathResource.toString().startsWith(cachedData.outputDirectory)) { - return new HtmlJavadocProvider(type -> { - return SourceUrlProviderFromSourceContainer.JAVADOC_FOLDER_URL_SUPPLIER - .sourceUrl(new File(cachedData.reportingOutputDirectory, "apidocs").toURI().toURL(), type.getFullyQualifiedName()); - }); - } else if (classpathResource.toString().startsWith(cachedData.testOutputDirectory)) { - return new HtmlJavadocProvider(type -> { - return SourceUrlProviderFromSourceContainer.JAVADOC_FOLDER_URL_SUPPLIER - .sourceUrl(new File(cachedData.reportingOutputDirectory, "apidocs").toURI().toURL(), type.getFullyQualifiedName()); - }); - } else { - throw new IllegalArgumentException("Cannot find source folder for " + classpathResource); - } - } else { - // Assume it's a JAR file - return new HtmlJavadocProvider(type -> { - try { - Artifact artifact = cachedData.artifacts.stream().filter(a -> classpathResource.equals(a.getFile())).findFirst().get(); - URL sourceContainer = maven.getJavadoc(artifact, cachedData.remoteArtifactRepositories).getFile().toURI().toURL(); - return SourceUrlProviderFromSourceContainer.JAR_JAVADOC_URL_PROVIDER.sourceUrl(sourceContainer, - type.getFullyQualifiedName()); - } catch (MavenException e) { - Log.log("Failed to find sources JAR for " + classpathResource, e); - } catch (MalformedURLException e) { - Log.log("Invalid URL for sources JAR for " + classpathResource, e); - } - return null; - }); - } + return new ClasspathData(name, new LinkedHashSet<>(entries)); } @Override @@ -265,95 +229,4 @@ public class MavenProjectClasspath extends JandexClasspath { return false; } - @Override - public ImmutableList getSourceFolders() { - return cachedData != null ? ImmutableList.of(cachedData.sourceDirectory) : ImmutableList.of(); - } - - @Override - public MavenClasspathData createClasspathData() throws Exception { - MavenProject project = createMavenProject(); - - ImmutableList entries = resolveClasspathEntries(project); - String name = project.getArtifact().getArtifactId(); - ImmutableList resources = resolveClasspathResources(project); - Path outputFolder = resolveOutputFolder(project); - - MavenClasspathData data = new MavenClasspathData(name, new LinkedHashSet<>(entries), - new LinkedHashSet<>(resources), outputFolder); - - data.outputDirectory = project.getBuild().getOutputDirectory(); - data.reportingOutputDirectory = project.getModel().getReporting().getOutputDirectory(); - data.testOutputDirectory = project.getBuild().getTestOutputDirectory(); - data.testSourceDirectory = project.getBuild().getTestSourceDirectory(); - data.artifacts = project.getArtifacts(); - data.remoteArtifactRepositories = project.getRemoteArtifactRepositories(); - data.sourceDirectory = project.getBuild().getSourceDirectory(); - return data; - } - - static class MavenClasspathData extends ClasspathData { - - private String testSourceDirectory; - private List remoteArtifactRepositories; - private Set artifacts; - private String testOutputDirectory; - private String reportingOutputDirectory; - private String outputDirectory; - private String sourceDirectory; - - public MavenClasspathData(String name, Set classpathEntries, Set classpathResources, - Path outputFolder) { - super(name, classpathEntries, classpathResources, outputFolder == null ? null : outputFolder.toString()); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - MavenClasspathData other = (MavenClasspathData) obj; - if (artifacts == null) { - if (other.artifacts != null) - return false; - } else if (!artifacts.equals(other.artifacts)) - return false; - if (outputDirectory == null) { - if (other.outputDirectory != null) - return false; - } else if (!outputDirectory.equals(other.outputDirectory)) - return false; - if (remoteArtifactRepositories == null) { - if (other.remoteArtifactRepositories != null) - return false; - } else if (!remoteArtifactRepositories.equals(other.remoteArtifactRepositories)) - return false; - if (reportingOutputDirectory == null) { - if (other.reportingOutputDirectory != null) - return false; - } else if (!reportingOutputDirectory.equals(other.reportingOutputDirectory)) - return false; - if (sourceDirectory == null) { - if (other.sourceDirectory != null) - return false; - } else if (!sourceDirectory.equals(other.sourceDirectory)) - return false; - if (testOutputDirectory == null) { - if (other.testOutputDirectory != null) - return false; - } else if (!testOutputDirectory.equals(other.testOutputDirectory)) - return false; - if (testSourceDirectory == null) { - if (other.testSourceDirectory != null) - return false; - } else if (!testSourceDirectory.equals(other.testSourceDirectory)) - return false; - return true; - } - - - } } diff --git a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java index ffcd2e6ba..4433f10e4 100644 --- a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java +++ b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/HtmlJavadocTest.java @@ -28,19 +28,22 @@ 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.util.BasicFileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; public class HtmlJavadocTest { + private static FileObserver fileObserver = new BasicFileObserver(); private static Supplier projectSupplier = Suppliers.memoize(() -> { Path testProjectPath; try { 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(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return MavenJavaProject.create(fileObserver, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); } catch (Exception e) { return null; } @@ -52,7 +55,7 @@ public class HtmlJavadocTest { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("java.util.Map"); + IType type = project.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 +71,7 @@ public class HtmlJavadocTest { Assume.assumeTrue(javaVersionHigherThan(6)); MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("java.util.ArrayList"); + IType type = project.findType("java.util.ArrayList"); assertNotNull(type); IMethod method = type.getMethod("", Stream.empty()); assertNotNull(method); @@ -86,7 +89,7 @@ public class HtmlJavadocTest { public void html_testEmptyJavadocClass() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("hello.Application"); + IType type = project.findType("hello.Application"); assertNotNull(type); assertNull(type.getJavaDoc()); } @@ -95,7 +98,7 @@ public class HtmlJavadocTest { public void html_testFieldAndMethodJavadocForJar() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("org.springframework.boot.SpringApplication"); + IType type = project.findType("org.springframework.boot.SpringApplication"); assertNotNull(type); IField field = type.getField("BANNER_LOCATION_PROPERTY_VALUE"); @@ -135,7 +138,7 @@ public class HtmlJavadocTest { public void html_testInnerClassJavadocForOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("hello.Greeting$TestInnerClass"); + IType type = project.findType("hello.Greeting$TestInnerClass"); assertNotNull(type); IJavadoc javaDoc = type.getJavaDoc(); assertNotNull(javaDoc); @@ -168,7 +171,7 @@ public class HtmlJavadocTest { public void html_testInnerClassLevel2_JavadocForOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("hello.Greeting$TestInnerClass$TestInnerClassLevel2"); + IType type = project.findType("hello.Greeting$TestInnerClass$TestInnerClassLevel2"); assertNotNull(type); IJavadoc javaDoc = type.getJavaDoc(); assertNotNull(javaDoc); @@ -200,7 +203,7 @@ public class HtmlJavadocTest { @Test public void html_testJavadocOutputFolder() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("hello.Greeting"); + IType type = project.findType("hello.Greeting"); assertNotNull(type); String expected = "
    Comment for Greeting class
    "; @@ -237,7 +240,7 @@ public class HtmlJavadocTest { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("java.util.ArrayList"); + IType type = project.findType("java.util.ArrayList"); assertNotNull(type); IMethod method = type.getMethod("size", Stream.empty()); assertNotNull(method); @@ -258,7 +261,7 @@ public class HtmlJavadocTest { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("java.util.Map$Entry"); + IType type = project.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 +275,7 @@ public class HtmlJavadocTest { public void html_testNoJavadocClass() throws Exception { MavenJavaProject project = projectSupplier.get();; - IType type = project.getClasspath().findType("hello.GreetingController"); + IType type = project.findType("hello.GreetingController"); assertNotNull(type); assertNull(type.getJavaDoc()); } @@ -281,7 +284,7 @@ public class HtmlJavadocTest { public void html_testNoJavadocField() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("hello.GreetingController"); + IType type = project.findType("hello.GreetingController"); assertNotNull(type); IField field = type.getField("template"); assertNotNull(field); @@ -302,7 +305,7 @@ public class HtmlJavadocTest { public void html_testNoJavadocMethod() throws Exception { MavenJavaProject project = projectSupplier.get(); - IType type = project.getClasspath().findType("hello.Application"); + IType type = project.findType("hello.Application"); assertNotNull(type); IMethod method = type.getMethod("corsConfigurer", Stream.empty()); assertNotNull(method); diff --git a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java index e556eff8b..c3f39e37e 100644 --- a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java +++ b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java @@ -32,6 +32,8 @@ import org.springframework.ide.vscode.commons.java.IPrimitiveType; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.java.IVoidType; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; +import org.springframework.ide.vscode.commons.util.BasicFileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -39,15 +41,19 @@ import com.google.common.cache.LoadingCache; import reactor.util.function.Tuple2; +import static org.springframework.ide.vscode.languageserver.testharness.ClasspathTestUtil.*; + public class JavaIndexTest { + private static BasicFileObserver fileObserver = new BasicFileObserver(); + private static LoadingCache mavenProjectsCache = CacheBuilder.newBuilder().build(new CacheLoader() { @Override 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(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return MavenJavaProject.create(fileObserver, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); } }); @@ -85,28 +91,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.getClasspath().findType("org.springframework.test.web.client.ExpectedCount"); + IType type = project.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.getClasspath().findType("hello.Greeting"); + IType type = project.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.getClasspath().findType("hello.NonExistentClass"); + IType type = project.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.getClasspath().findType("java.util.ArrayList"); + IType type = project.findType("java.util.ArrayList"); assertNotNull(type); IMethod m = type.getMethod("clear", Stream.empty()); assertEquals("clear", m.getElementName()); @@ -117,7 +123,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.getClasspath().findType("java.util.ArrayList"); + IType type = project.findType("java.util.ArrayList"); assertNotNull(type); IMethod m = type.getMethod("", Stream.empty()); assertEquals(type.getElementName(), m.getElementName()); @@ -128,7 +134,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.getClasspath().findType("java.util.ArrayList"); + IType type = project.findType("java.util.ArrayList"); assertNotNull(type); IMethod m = type.getMethod("", Stream.of(IPrimitiveType.INT)); assertEquals(m.getDeclaringType().getElementName(), m.getElementName()); @@ -139,7 +145,7 @@ public class JavaIndexTest { @Test public void testFindJarResource() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); - Optional jar = project.getClasspath().findClasspathResourceContainer("org.springframework.boot.autoconfigure.SpringBootApplication"); + Optional jar = project.findClasspathResourceContainer("org.springframework.boot.autoconfigure.SpringBootApplication"); assertTrue(jar.isPresent()); assertEquals("spring-boot-autoconfigure-1.4.1.RELEASE.jar", jar.get().getName()); } @@ -147,9 +153,9 @@ public class JavaIndexTest { @Test public void testFindJavaResource() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); - Optional file = project.getClasspath().findClasspathResourceContainer("hello.GreetingController"); + Optional file = project.findClasspathResourceContainer("hello.GreetingController"); assertTrue(file.isPresent()); assertTrue(file.get().exists()); - assertEquals(project.getClasspath().getOutputFolder().toString(), file.get().toString()); + assertEquals(getOutputFolder(project).toString(), file.get().toString()); } } diff --git a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectCacheTest.java b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectCacheTest.java index 0fadbbb49..ac665d248 100644 --- a/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectCacheTest.java +++ b/headless-services/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/MavenProjectCacheTest.java @@ -133,7 +133,7 @@ public class MavenProjectCacheTest { assertNotNull(cachedProject); ImmutableList calculatedClassPath = cachedProject.getClasspath().getClasspathEntries(); - assertEquals(48, calculatedClassPath.size()); + assertEquals(50, calculatedClassPath.stream().filter(cpe -> !cpe.isSystem()).count()); fileObserver.notifyFileChanged(pomFile.toURI().toString()); assertNull(projectChanged[0]); @@ -144,7 +144,7 @@ public class MavenProjectCacheTest { assertNotNull(projectChanged[0]); assertEquals(cachedProject, projectChanged[0]); calculatedClassPath = cachedProject.getClasspath().getClasspathEntries(); - assertEquals(49, calculatedClassPath.size()); + assertEquals(51, calculatedClassPath.stream().filter(cpe -> !cpe.isSystem()).count()); fileObserver.notifyFileDeleted(pomFile.toURI().toString()); assertEquals(cachedProject, projectDeleted[0]); @@ -190,7 +190,7 @@ public class MavenProjectCacheTest { }).get(30, TimeUnit.SECONDS); assertTrue(classpathCacheFile.exists()); - assertEquals(48, project.getClasspath().getClasspathEntries().size()); + assertEquals(50, project.getClasspath().getClasspathEntries().stream().filter(cpe -> !cpe.isSystem()).count()); progressDone.set(false); @@ -199,7 +199,7 @@ public class MavenProjectCacheTest { // Check loaded from cache file project = cache.project(pomFile); - assertEquals(48, project.getClasspath().getClasspathEntries().size()); + assertEquals(50, project.getClasspath().getClasspathEntries().stream().filter(cpe -> !cpe.isSystem()).count()); } @Test diff --git a/headless-services/commons/commons-util/pom.xml b/headless-services/commons/commons-util/pom.xml index 8ff2aa571..2b26e9c2f 100644 --- a/headless-services/commons/commons-util/pom.xml +++ b/headless-services/commons/commons-util/pom.xml @@ -44,6 +44,11 @@ javax.inject 1 + + io.projectreactor + reactor-core + ${reactor-version} + com.kotcrab.remark diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/BasicFileObserver.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/BasicFileObserver.java index bf52adb86..0d7303cf9 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/BasicFileObserver.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/BasicFileObserver.java @@ -24,6 +24,8 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.ImmutablePair; +import reactor.core.Disposable; + /** * Basic implementation of File Observer interface * @@ -87,10 +89,26 @@ public class BasicFileObserver implements FileObserver { Path path = Paths.get(URI.create(uri)); registry.values().stream() .filter(pair -> pair.left.stream() - .filter(matcher -> matcher.matches(path)) + .filter(matcher -> + matcher.matches(path) + ) .findFirst() .isPresent()) .forEach(pair -> pair.right.accept(uri)); } + @Override + public Disposable onAnyChange(List globPattern, Consumer handler) { + String[] ids = { + onFileChanged(globPattern, handler), + onFileCreated(globPattern, handler), + onFileDeleted(globPattern, handler) + }; + return () -> { + for (String id : ids) { + unsubscribe(id); + } + }; + } + } diff --git a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FileObserver.java b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FileObserver.java index a73f0d78d..0f47ecec1 100644 --- a/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FileObserver.java +++ b/headless-services/commons/commons-util/src/main/java/org/springframework/ide/vscode/commons/util/FileObserver.java @@ -13,6 +13,8 @@ package org.springframework.ide.vscode.commons.util; import java.util.List; import java.util.function.Consumer; +import reactor.core.Disposable; + /** * Object able to add/remove {@link FileListener} objects and fire events via these listeners * @@ -28,5 +30,7 @@ public interface FileObserver { String onFileDeleted(List globPattern, Consumer handler); boolean unsubscribe(String subscriptionId); + + Disposable onAnyChange(List globPattern, Consumer handler); } diff --git a/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/ClasspathTestUtil.java b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/ClasspathTestUtil.java new file mode 100644 index 000000000..7e006e376 --- /dev/null +++ b/headless-services/commons/language-server-test-harness/src/main/java/org/springframework/ide/vscode/languageserver/testharness/ClasspathTestUtil.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2018 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.languageserver.testharness; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; + +public class ClasspathTestUtil { + + public static Path getOutputFolder(IJavaProject jp) throws Exception { + for (CPE cpe : jp.getClasspath().getClasspathEntries()) { + if (Classpath.isSource(cpe)) { + if (cpe.getPath().endsWith("main/java")) { + return Paths.get(cpe.getOutputFolder()); + } + } + } + return null; + } +} diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/Classpath.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/Classpath.java index 330f621cc..aec2b1857 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/Classpath.java +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/Classpath.java @@ -1,20 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2018 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.tooling.jdt.ls.commons.classpath; +import java.io.File; +import java.net.URL; import java.util.List; +import org.eclipse.core.runtime.Assert; + public class Classpath { - + public static final String ENTRY_KIND_SOURCE = "source"; public static final String ENTRY_KIND_BINARY = "binary"; - public static final String OUTPUT_LOCATION = "output_location"; private List entries; - private String defaultOutputFolder; - public Classpath(List entries, String defaultOutputFolder) { + public Classpath(List entries) { super(); this.entries = entries; - this.defaultOutputFolder = defaultOutputFolder; } public List getEntries() { @@ -25,38 +36,43 @@ public class Classpath { this.entries = entries; } - public String getDefaultOutputFolder() { - return defaultOutputFolder; - } - - public void setDefaultOutputFolder(String defaultOutputFolder) { - this.defaultOutputFolder = defaultOutputFolder; - } - @Override public String toString() { - return "Classpath [entries=" + entries + ", defaultOutputFolder=" + defaultOutputFolder + "]"; + return "Classpath [entries=" + entries + "]"; } public static class CPE { + + // TODO: it seems like a good idea to make all classpath entries the same in that they all have + // - a place with source code + // - a place with compiled code + // - a place with java doc + // So it seems like we should be able to chnage this so that the same named attribute is used + // in both cases (rather then one be 'getOutputFolder' and one 'getPath' to obtain location of the compiled code). + private String kind; - private String path; + + private String path; // TODO: Change to File, Path or URL? private String outputFolder; + private URL sourceContainerUrl; + private URL javadocContainerUrl; + private boolean isSystem = false; public String getOutputFolder() { return outputFolder; } public void setOutputFolder(String outputFolder) { + Assert.isLegal(new File(outputFolder).isAbsolute()); this.outputFolder = outputFolder; } public CPE() {} - public CPE(String kind, String path) { + private CPE(String kind, String path) { super(); this.kind = kind; - this.path = path; + setPath(path); } public String getKind() { @@ -72,13 +88,47 @@ public class Classpath { } public void setPath(String path) { + Assert.isLegal(path == null || new File(path).isAbsolute()); this.path = path; } - @Override - public String toString() { - return "CPE [kind=" + kind + ", path=" + path + ", outputFolder=" + outputFolder + "]"; + public URL getJavadocContainerUrl() { + return javadocContainerUrl; + } + + public void setJavadocContainerUrl(URL javadocContainerUrl) { + this.javadocContainerUrl = javadocContainerUrl; + } + + public URL getSourceContainerUrl() { + return sourceContainerUrl; + } + + public void setSourceContainerUrl(URL sourceContainerUrl) { + this.sourceContainerUrl = sourceContainerUrl; + } + + public static CPE binary(String path) { + return new CPE(ENTRY_KIND_BINARY, path); + } + + public static CPE source(File sourceFolder, File outputFolder) { + CPE cpe = new CPE(ENTRY_KIND_SOURCE, sourceFolder.getAbsolutePath()); + cpe.setOutputFolder(outputFolder.getAbsolutePath()); + return cpe; + } + + public boolean isSystem() { + return isSystem; + } + + public void setSystem(boolean isSystem) { + this.isSystem = isSystem; } } + public static boolean isSource(CPE e) { + return e!=null && Classpath.ENTRY_KIND_SOURCE.equals(e.getKind()); + } + } \ No newline at end of file diff --git a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java index 23d0bf373..40d3e1079 100644 --- a/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java +++ b/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.commons/src/org/springframework/tooling/jdt/ls/commons/classpath/ClasspathUtil.java @@ -39,7 +39,7 @@ public class ClasspathUtil { switch (kind) { case Classpath.ENTRY_KIND_BINARY: { String path = entry.getPath().toString(); - CPE cpe = new CPE(kind, path); + CPE cpe = CPE.binary(path); cpEntries.add(cpe); break; } @@ -49,15 +49,15 @@ public class ClasspathUtil { IPath absoluteSourcePath = resolveWorkspacePath(sourcePath); //log("absoluteSourcePath =" + absoluteSourcePath); if (absoluteSourcePath!=null) { - CPE cpe = new CPE(kind, absoluteSourcePath.toString()); IPath of = entry.getOutputLocation(); //log("outputFolder =" + of); + IPath absoluteOutFolder; if (of!=null) { - IPath absoluteOutFolder = resolveWorkspacePath(of); - //log("absoluteOutFolder =" + absoluteOutFolder); - cpe.setOutputFolder(absoluteOutFolder.toString()); + absoluteOutFolder = resolveWorkspacePath(of); + } else { + absoluteOutFolder = resolveWorkspacePath(javaProject.getOutputLocation()); } - cpEntries.add(cpe); + cpEntries.add(CPE.source(absoluteSourcePath.toFile(), absoluteOutFolder.toFile())); } break; } @@ -66,8 +66,7 @@ public class ClasspathUtil { } } } - Classpath classpath = new Classpath(cpEntries, resolveWorkspacePath(javaProject.getOutputLocation()).toString()); - log("classpath.outputFolder=" + classpath.getDefaultOutputFolder()); + Classpath classpath = new Classpath(cpEntries); log("classpath=" + classpath.getEntries().size() + " entries"); return classpath; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java index ad4034d42..722ceef76 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaHoverProvider.java @@ -35,6 +35,7 @@ import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchyA import org.springframework.ide.vscode.boot.java.utils.ASTUtils; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; 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; @@ -267,8 +268,8 @@ public class BootJavaHoverProvider implements HoverHandler { try { IClasspath classpath = project.getClasspath(); if (classpath!=null) { - return classpath.getClasspathEntryPaths().stream().anyMatch(cpe -> { - String name = cpe.getFileName().toString(); + return IClasspathUtil.getBinaryRoots(classpath).stream().anyMatch(cpe -> { + String name = cpe.getName(); return name.startsWith("spring-boot-actuator-"); }); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java index 5ba18a169..b77d2a226 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaReferencesHandler.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.handlers; +import java.io.File; import java.nio.file.Path; import java.util.List; import java.util.Map; @@ -27,6 +28,7 @@ import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.ReferenceParams; import org.eclipse.lsp4j.TextDocumentIdentifier; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; 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.ReferencesHandler; @@ -125,10 +127,11 @@ public class BootJavaReferencesHandler implements ReferencesHandler { private String[] getClasspathEntries(IDocument doc) throws Exception { IJavaProject project = this.projectFinder.find(new TextDocumentIdentifier(doc.getUri())).get(); IClasspath classpath = project.getClasspath(); - Stream classpathEntries = classpath.getClasspathEntryPaths().stream(); + Stream classpathEntries = IClasspathUtil.getBinaryRoots(classpath).stream(); return classpathEntries - .filter(path -> path.toFile().exists()) - .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); + .filter(file -> file.exists()) + .map(file -> file.getAbsolutePath()) + .toArray(String[]::new); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AbstractSourceLinks.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AbstractSourceLinks.java index 86d97b4aa..3ab74121d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AbstractSourceLinks.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AbstractSourceLinks.java @@ -33,8 +33,9 @@ import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents; import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; -import org.springframework.ide.vscode.commons.javadoc.SourceUrlProviderFromSourceContainer; +import org.springframework.ide.vscode.commons.javadoc.TypeUrlProviderFromContainerUrl; import org.springframework.ide.vscode.commons.util.text.Region; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -59,7 +60,7 @@ public abstract class AbstractSourceLinks implements SourceLinks { @Override public Optional sourceLinkUrlForFQName(IJavaProject project, String fqName) { - Optional classpathResource = project.getClasspath().findClasspathResourceContainer(fqName); + Optional classpathResource = project.getIndex().findClasspathResourceContainer(fqName); if (classpathResource.isPresent()) { File file = classpathResource.get(); if (file.isDirectory()) { @@ -83,10 +84,10 @@ public abstract class AbstractSourceLinks implements SourceLinks { private Optional javaSourceLinkUrl(IJavaProject project, String fqName, File containerFolder) { IClasspath classpath = project.getClasspath(); - return project.getClasspath().getSourceFolders().stream() + return IClasspathUtil.getSourceFolders(classpath) .map(sourceFolder -> { try { - return Paths.get(sourceFolder).toUri().toURL(); + return sourceFolder.toURI().toURL(); } catch (MalformedURLException e) { LOG.get().warn("Failed to convert source folder " + sourceFolder + "to URI." + fqName, e); return null; @@ -94,7 +95,7 @@ public abstract class AbstractSourceLinks implements SourceLinks { }) .map(url -> { try { - return SourceUrlProviderFromSourceContainer.SOURCE_FOLDER_URL_SUPPLIER.sourceUrl(url, fqName); + return TypeUrlProviderFromContainerUrl.SOURCE_FOLDER_URL_SUPPLIER.url(url, fqName); } catch (Exception e) { LOG.get().warn("Failed to determine source URL from url=" + url + " fqName=" + fqName, e); return null; @@ -163,10 +164,10 @@ public abstract class AbstractSourceLinks implements SourceLinks { private Optional findCUForFQNameFromJar(IJavaProject project, File jarFile, String clientSourceUri, String fqName) { Optional cu = findCUfromCache(clientSourceUri); if (cu == null) { - cu = project.getClasspath().sourceContainer(jarFile) + cu = project.sourceContainer(jarFile) .map(url -> { try { - return SourceUrlProviderFromSourceContainer.JAR_SOURCE_URL_PROVIDER.sourceUrl(url, fqName); + return TypeUrlProviderFromContainerUrl.JAR_SOURCE_URL_PROVIDER.url(url, fqName); } catch (Exception e) { LOG.get().warn("Failed to determine source URL from url=" + url + " fqName=" + fqName, e); return null; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java index 88628a370..3b89aaff8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/CompilationUnitCache.java @@ -10,8 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; +import java.io.File; import java.net.URI; -import java.nio.file.Path; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -27,6 +27,7 @@ import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; @@ -172,10 +173,10 @@ public final class CompilationUnitCache { return new String[0]; } else { IClasspath classpath = project.getClasspath(); - Stream classpathEntries = classpath.getClasspathEntryPaths().stream(); + Stream classpathEntries = IClasspathUtil.getBinaryRoots(classpath).stream(); return classpathEntries - .filter(path -> path.toFile().exists()) - .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); + .filter(file -> file.exists()) + .map(file -> file.getAbsolutePath()).toArray(String[]::new); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java index bb2298081..82488a77a 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringIndexer.java @@ -63,6 +63,7 @@ import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformati import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation; import org.springframework.ide.vscode.boot.java.handlers.SymbolProvider; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; @@ -619,10 +620,11 @@ public class SpringIndexer { private String[] getClasspathEntries(IJavaProject project) throws Exception { IClasspath classpath = project.getClasspath(); - Stream classpathEntries = classpath.getClasspathEntryPaths().stream(); + Stream classpathEntries = IClasspathUtil.getBinaryRoots(classpath).stream(); return classpathEntries - .filter(path -> path.toFile().exists()) - .map(path -> path.toAbsolutePath().toString()).toArray(String[]::new); + .filter(file -> file.exists()) + .map(file -> file.getAbsolutePath()) + .toArray(String[]::new); } /** diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringResource.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringResource.java index 6424782cd..e20b406d6 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringResource.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/SpringResource.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.utils; +import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; @@ -18,6 +19,7 @@ import java.util.regex.Pattern; import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.util.Renderables; @@ -83,9 +85,12 @@ public class SpringResource { private String projectRelativePath(String pathStr) { Path path = Paths.get(pathStr); IClasspath classpath = project.getClasspath(); - Path outputFolder = classpath.getOutputFolder(); - if (path.startsWith(outputFolder)) { - return outputFolder.relativize(path).toString(); + Iterable ofs = () -> IClasspathUtil.getOutputFolders(classpath).iterator(); + for (File _outputFolder : ofs) { + Path outputFolder = _outputFolder.toPath(); + if (path.startsWith(outputFolder)) { + return outputFolder.relativize(path).toString(); + } } return pathStr; } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java index 2218d81ea..50b8025e7 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.boot.jdt.ls; import java.io.File; +import java.net.URI; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; @@ -32,6 +33,7 @@ import org.springframework.ide.vscode.commons.java.ClasspathData; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IJavadocProvider; +import org.springframework.ide.vscode.commons.java.JavaProject; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListener; @@ -39,6 +41,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage import org.springframework.ide.vscode.commons.util.Assert; import org.springframework.ide.vscode.commons.util.CollectorUtil; import org.springframework.ide.vscode.commons.util.ExceptionUtil; +import org.springframework.ide.vscode.commons.util.FileObserver; import org.springframework.ide.vscode.commons.util.UriUtil; import com.google.common.base.Supplier; @@ -53,7 +56,7 @@ public class JdtLsProjectCache implements JavaProjectsService { private CompletableFuture initialized = new CompletableFuture(); private SimpleLanguageServer server; - private Map table = new HashMap(); + private Map table = new HashMap(); private Logger log = LoggerFactory.getLogger(JdtLsProjectCache.class); private List listeners = new ArrayList<>(); @@ -70,20 +73,24 @@ public class JdtLsProjectCache implements JavaProjectsService { @Override public void changed(Event event) { initialized.thenRun(() -> { - synchronized (table) { - String uri = UriUtil.normalize(event.projectUri); - if (event.deleted) { - JdtLsProject deleted = table.remove(uri); - notifyDelete(deleted); - } else { - JdtLsProject newProject = new JdtLsProject(event.name, uri, event.classpath); - JdtLsProject oldProject = table.put(uri, newProject); - if (oldProject != null) { - notifyChanged(newProject); + try { + synchronized (table) { + String uri = UriUtil.normalize(event.projectUri); + if (event.deleted) { + JavaProject deleted = table.remove(uri); + notifyDelete(deleted); } else { - notifyCreated(newProject); + JavaProject newProject = new JavaProject(getFileObserver(), new URI(uri), new ClasspathData(event.name, event.classpath.getEntries())); + JavaProject oldProject = table.put(uri, newProject); + if (oldProject != null) { + notifyChanged(newProject); + } else { + notifyCreated(newProject); + } } } + } catch (Exception e) { + log.error("", e); } }); } @@ -106,6 +113,10 @@ public class JdtLsProjectCache implements JavaProjectsService { ); } + private FileObserver getFileObserver() { + return server.getWorkspaceService().getFileObserver(); + } + private boolean isOldJdt(Throwable e) { return ExceptionUtil.getMessage(e).contains("'sts.java.addClasspathListener' not supported"); } @@ -142,7 +153,7 @@ public class JdtLsProjectCache implements JavaProjectsService { }); } - private void notifyCreated(JdtLsProject newProject) { + private void notifyCreated(JavaProject newProject) { logEvent("Created", newProject); synchronized (listeners) { for (Listener listener : listeners) { @@ -151,16 +162,17 @@ public class JdtLsProjectCache implements JavaProjectsService { } } - private void notifyDelete(JdtLsProject deleted) { + private void notifyDelete(JavaProject deleted) { logEvent("Deleted", deleted); synchronized (listeners) { for (Listener listener : listeners) { listener.deleted(deleted); } } + deleted.dispose(); } - private void notifyChanged(JdtLsProject newProject) { + private void notifyChanged(JavaProject newProject) { logEvent("Changed", newProject); synchronized (listeners) { for (Listener listener : listeners) { @@ -169,7 +181,7 @@ public class JdtLsProjectCache implements JavaProjectsService { } } - private void logEvent(String type, JdtLsProject newProject) { + private void logEvent(String type, JavaProject newProject) { try { log.info("Project "+type+": " + newProject.getLocationUri()); log.info("Classpath has "+newProject.getClasspath().getClasspathEntries().size()+" entries"); @@ -190,7 +202,7 @@ public class JdtLsProjectCache implements JavaProjectsService { } synchronized (table) { - for (Entry e : table.entrySet()) { + for (Entry e : table.entrySet()) { String projectUri = e.getKey(); log.debug("projectUri = '{}'", projectUri); if (UriUtil.contains(projectUri, uri) ) { @@ -206,98 +218,4 @@ public class JdtLsProjectCache implements JavaProjectsService { return Optional.empty(); } - private class JdtLsProject implements IJavaProject { - - private final JdtClasspath classpath; - - public JdtLsProject(String name, String projectUri, Classpath classpath) { - this.classpath = new JdtClasspath(name, projectUri, classpath); - } - - @Override - public IClasspath getClasspath() { - return classpath; - } - - public String getLocationUri() { - return classpath.projectUri; - } - - } - - private class JdtClasspath extends JandexClasspath { - - private Classpath classpath; - private String name; - private String projectUri; - - - public JdtClasspath(String name, String uri, Classpath classpath) { - this.name = name; - this.projectUri = uri; - this.classpath = classpath; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public boolean exists() { - return new File(projectUri).exists(); - } - - @Override - public Path getOutputFolder() { - return Paths.get(classpath.getDefaultOutputFolder()); - } - - @Override - public Collection getClasspathEntries() throws Exception { - return classpath.getEntries(); - } - - @Override - public ImmutableList getClasspathResources() { - // TODO Auto-generated method stub - return null; - } - - @Override - public ImmutableList getSourceFolders() { - ImmutableList.Builder sourceEntries = ImmutableList.builder(); - try { - for (CPE e : getClasspathEntries()) { - if (Classpath.isSource(e)) { - sourceEntries.add(e.getPath()); - } - } - } catch (Exception e) { - log.error("", e); - } - return sourceEntries.build(); - - } - - @Override - public ClasspathData createClasspathData() throws Exception { - // We should not be needing this as we dont use DelegatingCachedClasspath - throw new UnsupportedOperationException("Not supported for JDT classpath: "); - } - - @Override - public Optional sourceContainer(File classpathResource) { - // TODO Auto-generated method stub - return null; - } - - @Override - protected IJavadocProvider createHtmlJavdocProvider(File classpathResource) { - // TODO Auto-generated method stub - return null; - } - - } - } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java index 411696a4c..56cab8310 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ClassReferenceProvider.java +++ b/headless-services/spring-boot-language-server/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.getClasspath().findType("java.lang.Object") : javaProject.getClasspath().findType(target); + IType targetType = target == null || target.isEmpty() ? javaProject.findType("java.lang.Object") : javaProject.findType(target); if (targetType == null) { return Flux.empty(); } - Set allSubclasses = javaProject.getClasspath() + Set allSubclasses = javaProject .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.getClasspath() + return javaProject .fuzzySearchTypes(query, type -> allSubclasses.contains(type)) .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) .flatMapIterable(l -> l) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java index 9e8957ee0..94ae452ea 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/LoggerNameProvider.java +++ b/headless-services/spring-boot-language-server/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.getClasspath() + javaProject.getIndex() .fuzzySearchPackages(query) .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())), - javaProject.getClasspath() + javaProject.getIndex() .fuzzySearchTypes(query, null) .map(t -> Tuples.of(StsValueHint.create(t.getT1()), t.getT2())) ) diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java index 5a24f36da..7bf77f26f 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/PropertiesLoader.java @@ -25,6 +25,7 @@ import java.util.zip.ZipEntry; import org.springframework.ide.vscode.boot.configurationmetadata.ConfigurationMetadataRepository; import org.springframework.ide.vscode.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder; import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IClasspathUtil; import org.springframework.ide.vscode.commons.util.Log; public class PropertiesLoader { @@ -55,14 +56,12 @@ public class PropertiesLoader { public ConfigurationMetadataRepository load(IClasspath classPath) { try { - classPath.getClasspathEntryPaths().forEach(entry -> { - //Log.info("Indexing "+entry); - File fileEntry = entry.toFile(); + IClasspathUtil.getBinaryRoots(classPath).forEach(fileEntry -> { if (fileEntry.exists()) { if (fileEntry.isDirectory()) { - loadFromOutputFolder(entry); + loadFromOutputFolder(fileEntry.toPath()); } else { - loadFromJar(entry); + loadFromJar(fileEntry.toPath()); } } }); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java index f256a3ec2..9c8cd3804 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/ResourceHintProvider.java @@ -63,7 +63,11 @@ public class ResourceHintProvider implements ValueProviderStrategy { private static class ClasspathHints extends CachingValueProvider { @Override protected Flux getValuesAsync(IJavaProject javaProject, String query) { - return Flux.fromStream(javaProject.getClasspath().getClasspathResources().stream().distinct().map(r -> r.replaceAll("\\\\", "/")).map(StsValueHint::create)); + return Flux.fromStream( + javaProject.getClasspathResources().stream() + .distinct().map(r -> r.replaceAll("\\\\", "/")) + .map(StsValueHint::create) + ); } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java index 8b5aa0ecd..0fe7deeab 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/hints/StsValueHint.java +++ b/headless-services/spring-boot-language-server/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.getClasspath().findType(fqName); + IType type = jp.findType(fqName); if (type!=null) { return create(type); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java index f8ba0024e..8862fdf3b 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/metadata/types/TypeUtil.java @@ -557,7 +557,7 @@ public class TypeUtil { private IType findType(String typeName) { try { if (javaProject!=null) { - return javaProject.getClasspath().findType(typeName); + return javaProject.findType(typeName); } } catch (Exception e) { Log.log(e); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java index ccaf6a06b..e7df5c677 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/autowired/test/AutowiredHoverProviderTest.java @@ -73,7 +73,7 @@ public class AutowiredHoverProviderTest { .build(); MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app", FOO_INTERFACE); - assertTrue(jp.getClasspath().findType("com.example.Foo").exists()); + assertTrue(jp.findType("com.example.Foo").exists()); harness.useProject(projects.mavenProject("empty-boot-15-web-app")); harness.intialize(null); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java index 7e9824f03..7b7caeff3 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/BeanInjectedIntoHoverProviderTest.java @@ -56,7 +56,7 @@ public class BeanInjectedIntoHoverProviderTest { .build(); MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app", FOO_INTERFACE); - assertTrue(jp.getClasspath().findType("hello.Foo").exists()); + assertTrue(jp.findType("hello.Foo").exists()); harness.useProject(jp); harness.intialize(null); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java index 85937f807..3106def93 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/livehover/test/ComponentInjectionsHoverProviderTest.java @@ -70,7 +70,7 @@ public class ComponentInjectionsHoverProviderTest { .build(); MavenJavaProject jp = projects.mavenProject("empty-boot-15-web-app", EXTRA_TYPES); - assertTrue(jp.getClasspath().findType("com.example.Foo").exists()); + assertTrue(jp.findType("com.example.Foo").exists()); harness.useProject(jp); harness.intialize(null); } diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/CompilationUnitCacheTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/CompilationUnitCacheTest.java index 786c75a65..1b71dc01c 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/CompilationUnitCacheTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/CompilationUnitCacheTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.springframework.ide.vscode.commons.java.DelegatingCachedClasspath; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.JavaProject; import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.commons.util.text.TextDocument; @@ -52,12 +53,7 @@ public class CompilationUnitCacheTest { public void cu_cached() throws Exception { harness = BootJavaLanguageServerHarness.builder() .mockDefaults().build(); - harness.useProject(new IJavaProject() { - @Override - public IClasspath getClasspath() { - return new DelegatingCachedClasspath<>(() -> null, null); - } - }); + harness.useProject(ProjectsHarness.dummyProject()); harness.intialize(null); TextDocument doc = new TextDocument(harness.createTempUri(), LanguageId.JAVA, 0, "package my.package\n" + @@ -94,12 +90,7 @@ public class CompilationUnitCacheTest { public void cu_cache_invalidated_by_doc_change() throws Exception { harness = BootJavaLanguageServerHarness.builder() .mockDefaults().build(); - harness.useProject(new IJavaProject() { - @Override - public IClasspath getClasspath() { - return new DelegatingCachedClasspath<>(() -> null, null); - } - }); + harness.useProject(ProjectsHarness.dummyProject()); harness.intialize(null); TextDocument doc = new TextDocument(harness.createTempUri(), LanguageId.JAVA, 0, "package my.package\n" + @@ -125,12 +116,7 @@ public class CompilationUnitCacheTest { public void cu_cache_invalidated_by_doc_close() throws Exception { harness = BootJavaLanguageServerHarness.builder() .mockDefaults().build(); - harness.useProject(new IJavaProject() { - @Override - public IClasspath getClasspath() { - return new DelegatingCachedClasspath<>(() -> null, null); - } - }); + harness.useProject(ProjectsHarness.dummyProject()); harness.intialize(null); TextDocument doc = new TextDocument(harness.createTempUri(), LanguageId.JAVA, 0, "package my.package\n" + diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java index 455ca32aa..3b048f37b 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java @@ -24,6 +24,8 @@ import org.springframework.ide.vscode.boot.java.links.VSCodeSourceLinks; import org.springframework.ide.vscode.commons.maven.MavenBuilder; import org.springframework.ide.vscode.commons.maven.MavenCore; import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; +import org.springframework.ide.vscode.commons.util.BasicFileObserver; +import org.springframework.ide.vscode.commons.util.FileObserver; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -43,7 +45,7 @@ public class VSCodeSourceLinksTest { public MavenJavaProject load(String projectName) throws Exception { Path testProjectPath = Paths.get(VSCodeSourceLinksTest.class.getResource("/test-projects/" + projectName).toURI()); MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); - return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return MavenJavaProject.create(new BasicFileObserver(), MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); } }); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/TypeUtilTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/TypeUtilTest.java index 40f29fe16..139e550e6 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/metadata/TypeUtilTest.java +++ b/headless-services/spring-boot-language-server/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.getClasspath().findType("demo.Color")); - assertNotNull(project.getClasspath().findType("demo.ColorData")); + assertNotNull(project.findType("demo.Color")); + assertNotNull(project.findType("demo.ColorData")); Type data = TypeParser.parse("demo.ColorData"); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java index 677fad81c..d91c9f204 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationPropertiesEditorTest.java @@ -40,10 +40,13 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguage import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject; import org.springframework.ide.vscode.commons.util.text.LanguageId; import org.springframework.ide.vscode.languageserver.testharness.Editor; +import org.springframework.ide.vscode.project.harness.ProjectsHarness; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; +import static org.springframework.ide.vscode.languageserver.testharness.ClasspathTestUtil.*; + /** * Boot App Properties Editor tests * @@ -220,7 +223,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.getClasspath().findType("demo.DemoApplication"); + IType type = p.findType("demo.DemoApplication"); assertNotNull(type); } @@ -229,7 +232,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.getClasspath().getOutputFolder().resolve(PropertiesLoader.PROJECT_META_DATA_LOCATIONS[0]); + Path metadataFile = getOutputFolder(p).resolve(PropertiesLoader.PROJECT_META_DATA_LOCATIONS[0]); assertTrue(metadataFile.toFile().isFile()); assertContains("\"name\": \"foo.counter\"", Files.toString(metadataFile.toFile(), Charset.forName("UTF8"))); } @@ -294,7 +297,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("boot-1.2.1-app-properties-list-of-pojo"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Foo")); + assertNotNull(p.findType("demo.Foo")); Editor editor = newEditor( "token.bad.guy=problem\n"+ @@ -319,7 +322,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("boot-1.2.1-app-properties-list-of-pojo"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Foo")); + assertNotNull(p.findType("demo.Foo")); assertCompletionsVariations("volder.foo.l<*>", "volder.foo.list[<*>"); assertCompletionsDisplayStringAndDetail("volder.foo.list[0].<*>", @@ -468,7 +471,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.findType("demo.Color")); data("foo.colors", "java.util.List", null, "A foonky list"); @@ -491,7 +494,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.findType("demo.Color")); data("foo.color", "demo.Color", null, "A foonky colour"); @@ -510,7 +513,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.findType("demo.Color")); data("foo.color", "demo.Color", null, "A foonky colour"); Editor editor = newEditor( @@ -533,7 +536,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.findType("demo.Color")); assertCompletionsVariations("foo.nam<*>", "foo.name-colors.<*>", @@ -552,7 +555,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { useProject(p); data("foo.name-colors", "java.util.Map", null, "Map with colors in its values"); - assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.findType("demo.Color")); Editor editor = newEditor( "foo.name-colors.jacket=BLUE\n" + @@ -571,8 +574,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.getClasspath().findType("demo.Color")); - assertNotNull(p.getClasspath().findType("demo.ColorData")); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); //Map Enum -> String: assertCompletionsVariations("foo.colnam<*>", "foo.color-names.<*>"); @@ -611,8 +614,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); - assertNotNull(p.getClasspath().findType("demo.ColorData")); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); Editor editor = newEditor( "foo.color-names.RED=Rood\n"+ @@ -631,8 +634,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); - assertNotNull(p.getClasspath().findType("demo.ColorData")); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); assertCompletion("foo.dat<*>", "foo.data.<*>"); @@ -666,8 +669,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); - assertNotNull(p.getClasspath().findType("demo.ColorData")); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); Editor editor = newEditor( "foo.data.bogus=Something\n" + @@ -701,8 +704,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); - assertNotNull(p.getClasspath().findType("demo.ColorData")); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.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)"); @@ -741,8 +744,8 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); - assertNotNull(p.getClasspath().findType("demo.ColorData")); + assertNotNull(p.findType("demo.Color")); + assertNotNull(p.findType("demo.ColorData")); Editor editor = newEditor( "foo.color-names.BLUE.dot=Blauw\n"+ @@ -767,7 +770,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.ClothingSize")); + assertNotNull(p.findType("demo.ClothingSize")); data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); @@ -811,7 +814,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.ClothingSize")); + assertNotNull(p.findType("demo.ClothingSize")); data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); @@ -1414,7 +1417,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { IJavaProject p = createPredefinedMavenProject("enums-boot-1.3.2-app"); useProject(p); - assertNotNull(p.getClasspath().findType("demo.Color")); + assertNotNull(p.findType("demo.Color")); data("my.colors", collectionType+"", null, "Ooh! nice colors!"); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java index 728a536a0..b2e901135 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/test/ApplicationYamlEditorTest.java @@ -585,7 +585,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.getClasspath().findType("demo.Foo")); + assertNotNull(p.findType("demo.Foo")); data("some-foo", "demo.Foo", null, "some Foo pojo property"); Editor editor = newEditor( "some-foo:\n" + @@ -617,7 +617,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.getClasspath().findType("demo.Foo")); + assertNotNull(p.findType("demo.Foo")); { Editor editor = newEditor( @@ -694,7 +694,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.getClasspath().findType("demo.Color")); + assertNotNull(p.findType("demo.Color")); data("foo.color", "demo.Color", null, "A foonky colour"); Editor editor = newEditor( @@ -1876,7 +1876,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.getClasspath().findType("demo.ClothingSize")); + assertNotNull(p.findType("demo.ClothingSize")); data("simple.pants.size", "demo.ClothingSize", null, "The simple pant's size"); diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/BootJavaLanguageServerHarness.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/BootJavaLanguageServerHarness.java index a019a1794..3b257775a 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/BootJavaLanguageServerHarness.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/BootJavaLanguageServerHarness.java @@ -11,6 +11,7 @@ package org.springframework.ide.vscode.project.harness; import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Duration; import org.junit.Assert; @@ -20,10 +21,13 @@ import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider; import org.springframework.ide.vscode.boot.metadata.types.TypeUtilProvider; +import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.languageserver.composable.ComposableLanguageServer; import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder; import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath; +import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE; import org.springframework.ide.vscode.commons.languageserver.util.LSFactory; import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness; @@ -135,9 +139,16 @@ public class BootJavaLanguageServerHarness extends LanguageServerHarness cache = CacheBuilder.newBuilder().concurrencyLevel(1).build(); + private final FileObserver fileObserver; + /** * A callback that is given a chance to make changes to test project contents before the test project * is created from it. @@ -81,8 +89,13 @@ public class ProjectsHarness { MAVEN // GRADLE? } + + public static final IJavaProject dummyProject() throws URISyntaxException { + return new JavaProject(new BasicFileObserver(), new URI("file:///someplace/nonexistent"), new DelegatingCachedClasspath(() -> null, null)); + } - private ProjectsHarness() { + private ProjectsHarness(FileObserver fileObserver) { + this.fileObserver = fileObserver; } public IJavaProject project(ProjectType type, String name, ProjectCustomizer customizer) throws Exception { @@ -100,7 +113,7 @@ public class ProjectsHarness { switch (type) { case MAVEN: MavenBuilder.newBuilder(testProjectPath).clean().pack().javadoc().skipTests().execute(); - return new MavenJavaProject(MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); + return MavenJavaProject.create(fileObserver, MavenCore.getDefault(), testProjectPath.resolve(MavenCore.POM_XML).toFile()); default: throw new IllegalStateException("Bug!!! Missing case"); } @@ -129,4 +142,5 @@ public class ProjectsHarness { public MavenJavaProject mavenProject(String name) throws Exception { return (MavenJavaProject) project(ProjectType.MAVEN, name); } + }