diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/META-INF/MANIFEST.MF b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/META-INF/MANIFEST.MF index 55709ab4..3ed752af 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/META-INF/MANIFEST.MF +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Eclipse LSP4Jakarta Plugin (Incubation) Bundle-Vendor: Eclipse LSP4Jakarta Bundle-SymbolicName: org.eclipse.lsp4jakarta.lsp4e.core;singleton:=true -Bundle-Version: 0.1.2.qualifier +Bundle-Version: 0.2.0.qualifier Bundle-Activator: org.eclipse.lsp4jakarta.lsp4e.Activator Require-Bundle: org.eclipse.lsp4e, diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/build.properties b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/build.properties index aa5b498e..c23fb760 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/build.properties +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/build.properties @@ -1,5 +1,5 @@ -source.. = src/ +source.. = src/main/java output.. = target/classes bin.includes = META-INF/,\ .,\ - /server/org.eclipse.lsp4jakarta.ls-jar-with-dependencies.jar + server/org.eclipse.lsp4jakarta.ls-jar-with-dependencies.jar diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/plugin.xml b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/plugin.xml index 97ff0a16..9ba01308 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/plugin.xml +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/plugin.xml @@ -39,6 +39,7 @@ id="org.jakartaee.lsp4e.jakartaserver"> + + diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/pom.xml b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/pom.xml index 0d7043c7..4ed2d68c 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/pom.xml +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/pom.xml @@ -1,105 +1,101 @@ - 4.0.0 - - org.eclipse.lsp4jakarta.lsp4e - org.eclipse.lsp4jakarta - 0.1.2-SNAPSHOT - - org.eclipse.lsp4jakarta.lsp4e.core - eclipse-plugin - - Eclipse LSP4Jakarta LSP4E Plugin - Eclipse LSP4Jakarta LSP4E Plugin - - - Eclipse LSP4Jakarta - https://github.com/eclipse/lsp4jakarta - - - - - EPL-2.0 - https://www.eclipse.org/legal/epl-2.0/ - Eclipse Public License 2.0 - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - get-server - - copy - - generate-resources - - ${basedir}/server/ - - - org.eclipse.lsp4jakarta - org.eclipse.lsp4jakarta.ls - 0.1.2-SNAPSHOT - jar-with-dependencies - - - - - - - false - true - - - - - org.apache.maven.plugins - maven-clean-plugin - - - - ${basedir}/server - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.apache.maven.plugins - maven-dependency-plugin - [2.7,) - - copy - - - - - - - - - - - - - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + org.eclipse.lsp4jakarta.lsp4e + org.eclipse.lsp4jakarta + 0.2.0-SNAPSHOT + + org.eclipse.lsp4jakarta.lsp4e.core + eclipse-plugin + Eclipse LSP4Jakarta LSP4E Plugin + Eclipse LSP4Jakarta LSP4E Plugin + + Eclipse LSP4Jakarta + https://github.com/eclipse/lsp4jakarta + + + + EPL-2.0 + https://www.eclipse.org/legal/epl-2.0/ + Eclipse Public License 2.0 + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + get-server + + copy + + generate-resources + + ${basedir}/server/ + + + org.eclipse.lsp4jakarta + org.eclipse.lsp4jakarta.ls + 0.2.0-SNAPSHOT + jar-with-dependencies + + + + + + + false + true + + + + + org.apache.maven.plugins + maven-clean-plugin + + + + ${basedir}/server + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-dependency-plugin + [2.7,) + + copy + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/Activator.java b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/Activator.java similarity index 95% rename from jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/Activator.java rename to jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/Activator.java index 56ca4fb0..80ac176f 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/Activator.java +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/Activator.java @@ -1,73 +1,72 @@ -/******************************************************************************* -* Copyright (c) 2020, 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.lsp4e; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.ui.plugin.AbstractUIPlugin; -import org.osgi.framework.BundleContext; - -/** - * The activator class controls the plug-in life cycle - */ -public class Activator extends AbstractUIPlugin { - - // The plug-in ID - public static final String PLUGIN_ID = "org.eclipse.lsp4jakarta.lsp4e.core"; //$NON-NLS-1$ - - // The shared instance - private static Activator plugin; - - public boolean started; - - /** - * The constructor - */ - public Activator() { - } - - @Override - public void start(BundleContext context) throws Exception { - super.start(context); - plugin = this; - getDefault().getLog().log(new Status(IStatus.INFO, PLUGIN_ID, "Starting activator class for lsp4e.core")); - started = true; - } - - @Override - public void stop(BundleContext context) throws Exception { - plugin = null; - started = false; - super.stop(context); - } - - /** - * Returns the shared instance - * - * @return the shared instance - */ - public static Activator getDefault() { - return plugin; - } - - public static void log(IStatus status) { - getDefault().getLog().log(status); - } - - public static void logException(String errMsg, Throwable ex) { - getDefault().getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, errMsg, ex)); - } - -} +/******************************************************************************* +* Copyright (c) 2020, 2022 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.lsp4e; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.lsp4jakarta.lsp4e.core"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + public boolean started; + + /** + * The constructor + */ + public Activator() {} + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + getDefault().getLog().log(new Status(IStatus.INFO, PLUGIN_ID, "Starting activator class for lsp4e.core")); + started = true; + } + + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + started = false; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + public static void log(IStatus status) { + getDefault().getLog().log(status); + } + + public static void logException(String errMsg, Throwable ex) { + getDefault().getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, errMsg, ex)); + } + +} diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/JakartaLSConnection.java b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/JakartaLSConnection.java similarity index 75% rename from jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/JakartaLSConnection.java rename to jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/JakartaLSConnection.java index 0210f48b..27b7618c 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/JakartaLSConnection.java +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/JakartaLSConnection.java @@ -1,12 +1,12 @@ -/******************************************************************************* - * Copyright (c) 2019, 2022 Red Hat, Inc. - * Distributed under license by Red Hat, Inc. All rights reserved. - * This program is 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: - * Red Hat, Inc. - initial API and implementation +/******************************************************************************* + * Copyright (c) 2019, 2022 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is 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: + * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.eclipse.lsp4jakarta.lsp4e; @@ -32,23 +32,27 @@ import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider; /** - * Connects to Jakarta Language Server - * - * Referenced: + * Connects to Jakarta Language Server Referenced: * https://github.com/jbosstools/jbosstools-quarkus/blob/master/plugins/org.jboss.tools.quarkus.lsp4e/src/org/jboss/tools/quarkus/lsp4e/QuarkusLanguageServer.java * Modified to fit the purposes of the Jakarta Language Server and client. - * - * @author kathrynkodama * + * @author kathrynkodama */ public class JakartaLSConnection extends ProcessStreamConnectionProvider { public JakartaLSConnection() { -// Locale.setDefault(new Locale("my", "TEST")); // testing code + // Locale.setDefault(new Locale("my", "TEST")); // testing code List commands = new ArrayList<>(); commands.add(computeJavaPath()); + + String debugPortString = System.getProperty(getClass().getName() + ".debugPort"); + if (debugPortString != null) { + commands.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=" + + debugPortString); + } + commands.add("-classpath"); try { commands.add(computeClasspath()); @@ -59,14 +63,13 @@ public JakartaLSConnection() { commands.add("-Duser.language=" + currentLocale.getLanguage()); commands.add("-Duser.country=" + currentLocale.getCountry()); - commands.add("org.eclipse.lsp4jakarta.JakartaLanguageServerLauncher"); + commands.add("org.eclipse.lsp4jakarta.ls.JakartaLanguageServerLauncher"); setCommands(commands); setWorkingDirectory(System.getProperty("user.dir")); } catch (IOException e) { Activator.getDefault().getLog().log( - new Status(IStatus.ERROR, Activator.getDefault().getBundle().getSymbolicName(), e.getMessage(), e)); + new Status(IStatus.ERROR, Activator.getDefault().getBundle().getSymbolicName(), e.getMessage(), e)); } - } private String computeClasspath() throws IOException { @@ -78,11 +81,9 @@ private String computeClasspath() throws IOException { private String computeJavaPath() { String javaPath = "java"; - boolean existsInPath = Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator))).map(Paths::get) - .anyMatch(path -> Files.exists(path.resolve("java"))); + boolean existsInPath = Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator))).map(Paths::get).anyMatch(path -> Files.exists(path.resolve("java"))); if (!existsInPath) { - File f = new File(System.getProperty("java.home"), - "bin/java" + (Platform.getOS().equals(Platform.OS_WIN32) ? ".exe" : "")); + File f = new File(System.getProperty("java.home"), "bin/java" + (Platform.getOS().equals(Platform.OS_WIN32) ? ".exe" : "")); javaPath = f.getAbsolutePath(); } return javaPath; @@ -103,5 +104,4 @@ public Object getInitializationOptions(URI rootUri) { return root; } - } diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/JakartaLanguageClient.java b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/JakartaLanguageClient.java new file mode 100755 index 00000000..da9bd5fd --- /dev/null +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/JakartaLanguageClient.java @@ -0,0 +1,142 @@ +/******************************************************************************* +* Copyright (c) 2020, 2022 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.lsp4e; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4e.LanguageClientImpl; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CompletionList; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionResult; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfo; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfoParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaProjectLabelsParams; +import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; +import org.eclipse.lsp4jakarta.commons.ProjectLabelInfoEntry; +import org.eclipse.lsp4jakarta.jdt.core.ProjectLabelManager; +import org.eclipse.lsp4jakarta.jdt.core.PropertiesManagerForJava; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; +import org.eclipse.lsp4jakarta.ls.api.JakartaLanguageClientAPI; + +public class JakartaLanguageClient extends LanguageClientImpl implements JakartaLanguageClientAPI { + + public JakartaLanguageClient() { + // do nothing + } + + private IProgressMonitor getProgressMonitor(CancelChecker cancelChecker) { + IProgressMonitor monitor = new NullProgressMonitor() { + public boolean isCanceled() { + cancelChecker.checkCanceled(); + return false; + }; + }; + return monitor; + } + + @Override + public CompletableFuture getJavaCompletion(JakartaJavaCompletionParams javaParams) { + return CompletableFutures.computeAsync(cancelChecker -> { + IProgressMonitor monitor = getProgressMonitor(cancelChecker); + CompletionList completionList; + try { + completionList = PropertiesManagerForJava.getInstance().completion(javaParams, + JDTUtilsLSImpl.getInstance(), monitor); + JavaCursorContextResult javaCursorContext = PropertiesManagerForJava.getInstance().javaCursorContext(javaParams, JDTUtilsLSImpl.getInstance(), monitor); + return new JakartaJavaCompletionResult(completionList, javaCursorContext); + } catch (JavaModelException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + }); + } + + @Override + public CompletableFuture> getAllJavaProjectLabels() { + return CompletableFutures.computeAsync((cancelChecker) -> { + IProgressMonitor monitor = getProgressMonitor(cancelChecker); + return ProjectLabelManager.getInstance().getProjectLabelInfo(); + }); + } + + @Override + public CompletableFuture getJavaProjectLabels(JakartaJavaProjectLabelsParams javaParams) { + return CompletableFutures.computeAsync((cancelChecker) -> { + IProgressMonitor monitor = getProgressMonitor(cancelChecker); + return ProjectLabelManager.getInstance().getProjectLabelInfo(javaParams, JDTUtilsLSImpl.getInstance(), + monitor); + }); + } + + @Override + public CompletableFuture getJavaFileInfo(JakartaJavaFileInfoParams javaParams) { + return CompletableFutures.computeAsync(cancelChecker -> { + IProgressMonitor monitor = getProgressMonitor(cancelChecker); + return PropertiesManagerForJava.getInstance().fileInfo(javaParams, JDTUtilsLSImpl.getInstance(), monitor); + }); + } + + @Override + public CompletableFuture> getJavaDiagnostics( + JakartaJavaDiagnosticsParams javaParams) { + return CompletableFutures.computeAsync((cancelChecker) -> { + IProgressMonitor monitor = getProgressMonitor(cancelChecker); + try { + return PropertiesManagerForJava.getInstance().diagnostics(javaParams, JDTUtilsLSImpl.getInstance(), + monitor); + } catch (JavaModelException e) { + return Collections.emptyList(); + } + }); + } + + @Override + public CompletableFuture> getJavaCodeAction(JakartaJavaCodeActionParams javaParams) { + return CompletableFutures.computeAsync((cancelChecker) -> { + IProgressMonitor monitor = getProgressMonitor(cancelChecker); + try { + return (List) PropertiesManagerForJava.getInstance().codeAction(javaParams, + JDTUtilsLSImpl.getInstance(), monitor); + } catch (JavaModelException e) { + return Collections.emptyList(); + } + }); + } + + @Override + public CompletableFuture resolveCodeAction(CodeAction unresolved) { + return CompletableFutures.computeAsync((cancelChecker) -> { + IProgressMonitor monitor = getProgressMonitor(cancelChecker); + try { + return (CodeAction) PropertiesManagerForJava.getInstance().resolveCodeAction(unresolved, + JDTUtilsLSImpl.getInstance(), monitor); + } catch (JavaModelException e) { + return null; + } + }); + } + +} diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/extensions/JakartaJavaCompletionProposalComputer.java b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/extensions/JakartaJavaCompletionProposalComputer.java similarity index 86% rename from jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/extensions/JakartaJavaCompletionProposalComputer.java rename to jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/extensions/JakartaJavaCompletionProposalComputer.java index 9c017094..1f910405 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/extensions/JakartaJavaCompletionProposalComputer.java +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/main/java/org/eclipse/lsp4jakarta/lsp4e/extensions/JakartaJavaCompletionProposalComputer.java @@ -38,15 +38,14 @@ public JakartaJavaCompletionProposalComputer() { } @Override - public void sessionStarted() { - } + public void sessionStarted() {} @Override public List computeCompletionProposals(ContentAssistInvocationContext context, - IProgressMonitor monitor) { + IProgressMonitor monitor) { CompletableFuture future = CompletableFuture.supplyAsync(() -> { return lsContentAssistProcessor.computeCompletionProposals(context.getViewer(), - context.getInvocationOffset()); + context.getInvocationOffset()); }); try { return Arrays.asList(future.get(TIMEOUT_LENGTH, TIMEOUT_UNIT)); @@ -58,9 +57,8 @@ public List computeCompletionProposals(ContentAssistInvocat @Override public List computeContextInformation(ContentAssistInvocationContext context, - IProgressMonitor monitor) { - IContextInformation[] contextInformation = lsContentAssistProcessor - .computeContextInformation(context.getViewer(), context.getInvocationOffset()); + IProgressMonitor monitor) { + IContextInformation[] contextInformation = lsContentAssistProcessor.computeContextInformation(context.getViewer(), context.getInvocationOffset()); return Arrays.asList(contextInformation); } diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/JakartaLanguageClient.java b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/JakartaLanguageClient.java deleted file mode 100755 index bd1fe475..00000000 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.core/src/org/eclipse/lsp4jakarta/lsp4e/JakartaLanguageClient.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.lsp4e; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4e.LanguageClientImpl; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.CodeActionParams; -import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.jsonrpc.CancelChecker; -import org.eclipse.lsp4j.jsonrpc.CompletableFutures; -import org.eclipse.lsp4jakarta.api.JakartaLanguageClientAPI; -import org.eclipse.lsp4jakarta.commons.JakartaClasspathParams; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextKind; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; -import org.eclipse.lsp4jakarta.jdt.core.JDTServicesManager; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; - -public class JakartaLanguageClient extends LanguageClientImpl implements JakartaLanguageClientAPI { - - public JakartaLanguageClient() { - // do nothing - } - - private IProgressMonitor getProgressMonitor(CancelChecker cancelChecker) { - IProgressMonitor monitor = new NullProgressMonitor() { - public boolean isCanceled() { - cancelChecker.checkCanceled(); - return false; - }; - }; - return monitor; - } - - @Override - public CompletableFuture> getJavaDiagnostics( - JakartaDiagnosticsParams jakartaParams) { - return CompletableFutures.computeAsync((cancelChecker) -> { - IProgressMonitor monitor = getProgressMonitor(cancelChecker); - - List publishDiagnostics = new ArrayList(); - publishDiagnostics = JDTServicesManager.getInstance().getJavaDiagnostics(jakartaParams.getUris(), monitor); - return publishDiagnostics; - }); - } - - @Override - public CompletableFuture> getContextBasedFilter(JakartaClasspathParams classpathParams) { - return CompletableFutures.computeAsync((cancelChecker) -> { - return JDTServicesManager.getInstance().getExistingContextsFromClassPath(classpathParams.getUri(), - classpathParams.getSnippetCtx()); - }); - } - - @Override - public CompletableFuture getJavaCursorContext(JakartaJavaCompletionParams params) { - JDTUtils utils = new JDTUtils(); - return CompletableFutures.computeAsync((cancelChecker) -> { - IProgressMonitor monitor = getProgressMonitor(cancelChecker); - try { - return JDTServicesManager.getInstance().javaCursorContext(params, utils, monitor); - } catch (JavaModelException e) { - return new JavaCursorContextResult(JavaCursorContextKind.IN_EMPTY_FILE, ""); - } - }); - } - - public CompletableFuture> getCodeAction(JakartaJavaCodeActionParams params) { - JDTUtils utils = new JDTUtils(); - - return CompletableFutures.computeAsync((cancelChecker) -> { - IProgressMonitor monitor = getProgressMonitor(cancelChecker); - try { - return (List) JDTServicesManager.getInstance().getCodeAction(params, utils, monitor); - } catch (JavaModelException e) { - return null; - } - }); - } - -} diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.feature/feature.xml b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.feature/feature.xml index 714ad538..3dace659 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.feature/feature.xml +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.feature/feature.xml @@ -2,7 +2,7 @@ + version="0.2.0.qualifier"> [Enter Feature Description here.] diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.feature/pom.xml b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.feature/pom.xml index 02537ac7..04718097 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.feature/pom.xml +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.feature/pom.xml @@ -1,27 +1,27 @@ - - 4.0.0 - - org.eclipse.lsp4jakarta.lsp4e - org.eclipse.lsp4jakarta - 0.1.2-SNAPSHOT - - org.eclipse.lsp4jakarta.lsp4e.feature - eclipse-feature - - Eclipse LSP4Jakarta LSP4E Feature - Eclipse LSP4Jakarta LSP4E Feature - - - Eclipse LSP4Jakarta - https://github.com/eclipse/lsp4jakarta - - - - - EPL-2.0 - https://www.eclipse.org/legal/epl-2.0/ - Eclipse Public License 2.0 - - - + + 4.0.0 + + org.eclipse.lsp4jakarta.lsp4e + org.eclipse.lsp4jakarta + 0.2.0-SNAPSHOT + + org.eclipse.lsp4jakarta.lsp4e.feature + eclipse-feature + Eclipse LSP4Jakarta LSP4E Feature + Eclipse LSP4Jakarta LSP4E Feature + + Eclipse LSP4Jakarta + https://github.com/eclipse/lsp4jakarta + + + + EPL-2.0 + https://www.eclipse.org/legal/epl-2.0/ + Eclipse Public License 2.0 + + + \ No newline at end of file diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.site/pom.xml b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.site/pom.xml index 80700ff8..392b1eca 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.site/pom.xml +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.site/pom.xml @@ -1,14 +1,16 @@ - - 4.0.0 - - org.eclipse.lsp4jakarta.lsp4e - org.eclipse.lsp4jakarta - 0.1.2-SNAPSHOT - - org.eclipse.lsp4jakarta.lsp4e.site - eclipse-repository - - Eclipse LSP4Jakarta JDT Plugin Update Site - Eclipse LSP4Jakarta JDT Plugin Update Site - + + 4.0.0 + + org.eclipse.lsp4jakarta.lsp4e + org.eclipse.lsp4jakarta + 0.2.0-SNAPSHOT + + org.eclipse.lsp4jakarta.lsp4e.site + eclipse-repository + Eclipse LSP4Jakarta JDT Plugin Update Site + Eclipse LSP4Jakarta JDT Plugin Update Site + \ No newline at end of file diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/META-INF/MANIFEST.MF b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/META-INF/MANIFEST.MF index 0d7428df..0b9ead9f 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/META-INF/MANIFEST.MF +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/META-INF/MANIFEST.MF @@ -3,8 +3,8 @@ Bundle-ManifestVersion: 2 Bundle-Name: Eclipse LSP4Jakarta Plugin Test Fragment (Incubation) Bundle-Vendor: Eclipse LSP4Jakarta Bundle-SymbolicName: org.eclipse.lsp4jakarta.lsp4e.test -Bundle-Version: 0.1.2.qualifier -Fragment-Host: org.eclipse.lsp4jakarta.lsp4e.core;bundle-version="0.1.2" +Bundle-Version: 0.2.0.qualifier +Fragment-Host: org.eclipse.lsp4jakarta.lsp4e.core;bundle-version="0.2.0" Bundle-RequiredExecutionEnvironment: JavaSE-17 Require-Bundle: org.eclipse.jdt.junit4.runtime;bundle-version="1.1.0", org.junit;bundle-version="4.11" diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/pom.xml b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/pom.xml index 368ebab9..e4da14e5 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/pom.xml +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/pom.xml @@ -1,28 +1,28 @@ - - - org.eclipse.lsp4jakarta.lsp4e - org.eclipse.lsp4jakarta - 0.1.2-SNAPSHOT - - 4.0.0 - org.eclipse.lsp4jakarta.lsp4e.test - eclipse-test-plugin - - Eclipse LSP4Jakarta LSP4E Test Plugin - Eclipse LSP4Jakarta LSP4E Test Plugin - - - Eclipse LSP4Jakarta - https://github.com/eclipse/lsp4jakarta - - - - - EPL-2.0 - https://www.eclipse.org/legal/epl-2.0/ - Eclipse Public License 2.0 - - - + + + org.eclipse.lsp4jakarta.lsp4e + org.eclipse.lsp4jakarta + 0.2.0-SNAPSHOT + + 4.0.0 + org.eclipse.lsp4jakarta.lsp4e.test + eclipse-test-plugin + Eclipse LSP4Jakarta LSP4E Test Plugin + Eclipse LSP4Jakarta LSP4E Test Plugin + + Eclipse LSP4Jakarta + https://github.com/eclipse/lsp4jakarta + + + + EPL-2.0 + https://www.eclipse.org/legal/epl-2.0/ + Eclipse Public License 2.0 + + diff --git a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/src/main/java/org/eclipse/lsp4jakarta/core/ActivatorTest.java b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/src/main/java/org/eclipse/lsp4jakarta/core/ActivatorTest.java index fb3fe204..d38b434d 100644 --- a/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/src/main/java/org/eclipse/lsp4jakarta/core/ActivatorTest.java +++ b/jakarta.eclipse/org.eclipse.lsp4jakarta.lsp4e.test/src/main/java/org/eclipse/lsp4jakarta/core/ActivatorTest.java @@ -7,14 +7,14 @@ import org.junit.Test; /** -* Sample integration test. In Eclipse, right-click > Run As > JUnit-Plugin.
-* In Maven CLI, run "mvn integration-test". -*/ + * Sample integration test. In Eclipse, right-click > Run As > JUnit-Plugin.
+ * In Maven CLI, run "mvn integration-test". + */ public class ActivatorTest { - @Test - public void veryStupidTest() { - assertEquals("org.eclipse.lsp4jakarta.lsp4e.core",Activator.PLUGIN_ID); - assertTrue("Plugin should be started", Activator.getDefault().started); - } + @Test + public void veryStupidTest() { + assertEquals("org.eclipse.lsp4jakarta.lsp4e.core", Activator.PLUGIN_ID); + assertTrue("Plugin should be started", Activator.getDefault().started); + } } \ No newline at end of file diff --git a/jakarta.eclipse/pom.xml b/jakarta.eclipse/pom.xml index d67d349b..47cc51b3 100644 --- a/jakarta.eclipse/pom.xml +++ b/jakarta.eclipse/pom.xml @@ -1,164 +1,179 @@ - - 4.0.0 - org.eclipse.lsp4jakarta - org.eclipse.lsp4jakarta.lsp4e - 0.1.2-SNAPSHOT - pom - - Eclipse LSP4Jakarta LSP4E Parent - Eclipse LSP4Jakarta LSP4E Parent - - - Eclipse LSP4Jakarta - https://github.com/eclipse/lsp4jakarta - - - - - EPL-2.0 - https://www.eclipse.org/legal/epl-2.0/ - Eclipse Public License 2.0 - - - - - UTF-8 - UTF-8 - 2.7.2 - ${tycho.version} - - -Xmx512m ${tycho.test.platformArgs} - - - - 2022-09 - p2 - http://download.eclipse.org/releases/2022-09 - - - - - cbi-release - https://repo.eclipse.org/content/repositories/cbi-releases/ - - - - - - org.eclipse.tycho - tycho-maven-plugin - ${tycho.version} - true - - - org.eclipse.tycho - target-platform-configuration - ${tycho.version} - - p2 - consider - true - - - - org.eclipse.tycho - tycho-source-plugin - ${tycho.version} - - - attach-source - - plugin-source - - - - - - - - - org.eclipse.tycho - tycho-packaging-plugin - ${tycho.version} - - yyyyMMdd-HHmm - - - - org.eclipse.tycho - tycho-surefire-plugin - ${tycho.version} - - true - ${tycho.test.jvmArgs} - - 60 - - - - - - - - macosx - - - mac - - - - -XstartOnFirstThread - - - - source-feature - - - feature.xml - - - - - - org.eclipse.tycho.extras - tycho-source-feature-plugin - ${tycho.extras.version} - - - source-feature - package - - source-feature - - - - - - org.eclipse.tycho - tycho-p2-plugin - ${tycho.version} - - - attach-p2-metadata - package - - p2-metadata - - - - - - - - - - org.eclipse.lsp4jakarta.lsp4e.core - org.eclipse.lsp4jakarta.lsp4e.test - org.eclipse.lsp4jakarta.lsp4e.feature - org.eclipse.lsp4jakarta.lsp4e.site - + + 4.0.0 + org.eclipse.lsp4jakarta + org.eclipse.lsp4jakarta.lsp4e + 0.2.0-SNAPSHOT + pom + Eclipse LSP4Jakarta LSP4E Parent + Eclipse LSP4Jakarta LSP4E Parent + + Eclipse LSP4Jakarta + https://github.com/eclipse/lsp4jakarta + + + + EPL-2.0 + https://www.eclipse.org/legal/epl-2.0/ + Eclipse Public License 2.0 + + + + UTF-8 + UTF-8 + 17 + 17 + 2.7.2 + ${tycho.version} + + -Xmx512m ${tycho.test.platformArgs} + + + + 2023-06 + p2 + http://download.eclipse.org/releases/2023-06 + + + + + cbi-release + https://repo.eclipse.org/content/repositories/cbi-releases/ + + + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho.version} + true + + + org.eclipse.tycho + target-platform-configuration + ${tycho.version} + + p2 + consider + true + + + + org.eclipse.tycho + tycho-source-plugin + ${tycho.version} + + + attach-source + + plugin-source + + + + + + net.revelc.code.formatter + formatter-maven-plugin + 2.23.0 + + ${project.basedir}/../../resources/ide/config/eclipse-formatter-config.xml + LF + + + + + format + + + + + + + + + org.eclipse.tycho + tycho-packaging-plugin + ${tycho.version} + + yyyyMMdd-HHmm + + + + org.eclipse.tycho + tycho-surefire-plugin + ${tycho.version} + + true + ${tycho.test.jvmArgs} + + 60 + + + + + + + + macosx + + + mac + + + + -XstartOnFirstThread + + + + source-feature + + + feature.xml + + + + + + org.eclipse.tycho.extras + tycho-source-feature-plugin + ${tycho.extras.version} + + + source-feature + package + + source-feature + + + + + + org.eclipse.tycho + tycho-p2-plugin + ${tycho.version} + + + attach-p2-metadata + package + + p2-metadata + + + + + + + + + + org.eclipse.lsp4jakarta.lsp4e.core + org.eclipse.lsp4jakarta.lsp4e.test + org.eclipse.lsp4jakarta.lsp4e.feature + org.eclipse.lsp4jakarta.lsp4e.site + \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/META-INF/MANIFEST.MF b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/META-INF/MANIFEST.MF index d83cd1a6..c86ec67b 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/META-INF/MANIFEST.MF +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Eclipse LSP4Jakarta JDT Extension (Incubation) Bundle-Vendor: Eclipse LSP4Jakarta Bundle-Localization: plugin Bundle-SymbolicName: org.eclipse.lsp4jakarta.jdt.core;singleton:=true -Bundle-Version: 0.1.2.qualifier +Bundle-Version: 0.2.0.qualifier Bundle-Activator: org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin Require-Bundle: org.eclipse.core.runtime, @@ -27,5 +27,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-ActivationPolicy: lazy Bundle-ClassPath: . Export-Package: - org.eclipse.lsp4jakarta.commons, - org.eclipse.lsp4jakarta.jdt.core + org.eclipse.lsp4jakarta.commons, + org.eclipse.lsp4jakarta.jdt.core, + org.eclipse.lsp4jakarta.jdt.core.utils, + org.eclipse.lsp4jakarta.jdt.internal.core.ls diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.properties b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.properties new file mode 100644 index 00000000..c8afe5e7 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.properties @@ -0,0 +1,17 @@ +############################################################################### +# Copyright (c) 2019 Red Hat Inc. and others. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +# which is available at https://www.apache.org/licenses/LICENSE-2.0. +# +# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +# +# Contributors: +# Red Hat Inc. - initial API and implementation +############################################################################### +pluginName=JDT Jakarta Extension +providerName=IBM +javaFeatureParticipants.name=Java features participants for Jakarta +projectLabelProviders.name=Project label providers extension \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.xml b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.xml index 27ebdbb7..26bbde03 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.xml +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/plugin.xml @@ -2,6 +2,13 @@ + + + @@ -9,10 +16,295 @@ - - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/pom.xml b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/pom.xml index 9e675264..292352be 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/pom.xml +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/pom.xml @@ -1,42 +1,41 @@ - - 4.0.0 - - org.eclipse.lsp4jakarta - jdt-parent - 0.1.2-SNAPSHOT - - org.eclipse.lsp4jakarta.jdt.core - eclipse-plugin - - Eclipse LSP4Jakarta JDT Plugin - Eclipse LSP4Jakarta JDT Plugin - - - Eclipse LSP4Jakarta - https://github.com/eclipse/lsp4jakarta - - - - - EPL-2.0 - https://www.eclipse.org/legal/epl-2.0/ - Eclipse Public License 2.0 - - - - - - com.google.code.gson - gson - 2.8.6 - - - - - - - src/main/resources - - - + + + 4.0.0 + + org.eclipse.lsp4jakarta + jdt-parent + 0.2.0-SNAPSHOT + + org.eclipse.lsp4jakarta.jdt.core + eclipse-plugin + Eclipse LSP4Jakarta JDT Plugin + Eclipse LSP4Jakarta JDT Plugin + + Eclipse LSP4Jakarta + https://github.com/eclipse/lsp4jakarta + + + + EPL-2.0 + https://www.eclipse.org/legal/epl-2.0/ + Eclipse Public License 2.0 + + + + + com.google.code.gson + gson + 2.9.0 + + + + + + src/main/resources + + + \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/schema/javaFeatureParticipants.exsd b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/schema/javaFeatureParticipants.exsd new file mode 100644 index 00000000..048d7b91 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/schema/javaFeatureParticipants.exsd @@ -0,0 +1,165 @@ + + + + + + + + + This extension point allows adding a Java feature (completion, diagnostics, codeAction) to consume it in the Java editor. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Java completion participant + + + + + + + Name of a class that implements IJavaCodeLensParticipant. + + + + + + + + + + + + + Java diagnostics participant. + + + + + + + Name of a class that implements IJavaDiagnosticsParticipant. + + + + + + + + + + + + + Java codeAction participant. + + + + + + + The code action kind top category. This kind is used to filter code action according the 'only' information of LSP CodeActionContext (see https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction). + + + + + + + + + + + + + The target diagnostic where the code action must be applied. This target can be a diagnostic code (ex: 'AddAnnotation') or the concatenatation of a diagnostic source, '#' and a diagnostic code (ex: 'jakarta-jaxrs#AddAnnotation") + + + + + + + Name of a class that implements IJavaCodeActionParticipant + + + + + + + + + + + + + + + 2.0 + + + + + + + + + The following is an example of a java feature participant extension: + +<pre> + <extension point="org.eclipse.lsp4mp.jdt.core.javaFeatureParticipants"> + <diagnostics + class="com.example.MyJavaDiagnosticsParticipant"> + </diagnostics> + <codeAction + kind="quickfix" + targetDiagnostic="sourceId#errorCode" + class="com.example.MyJavaCodeLensParticipant"> + </codeAction> + </extension> +</pre> + + + + diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/schema/projectLabelProviders.exsd b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/schema/projectLabelProviders.exsd new file mode 100644 index 00000000..e289b523 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/schema/projectLabelProviders.exsd @@ -0,0 +1,87 @@ + + + + + + + + + This extension point allows the addition of new project labels for Java projects. Labels are provided within a list, since Java projects could have more than one label. + +Some example labels are: "maven", "gradle" and "jakarta". + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of a class that implements IProjectLabelProvider. + + + + + + + + + + + + + + + + The following is an example of a properties provider extension: + +<pre> + <extension point="org.eclipse.lsp4jakarta.jdt.core.projectLabelProviders"> + <provider + class="com.example.MyProjectLabelProvider"/> + </extension> +</pre> + + + + + + + diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/ClasspathKind.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/ClasspathKind.java new file mode 100644 index 00000000..04ba9d3d --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/ClasspathKind.java @@ -0,0 +1,49 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +/** + * Classpath kind where application.properties is stored: + * + *
    + *
  • not in classpath
  • + *
  • in /java/main/src classpath
  • + *
  • in /java/main/test classpath
  • + *
+ * + * @author Angelo ZERR + * + */ +public enum ClasspathKind { + + NONE(1), SRC(2), TEST(3); + + private final int value; + + ClasspathKind(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static ClasspathKind forValue(int value) { + ClasspathKind[] allValues = ClasspathKind.values(); + if (value < 1 || value > allValues.length) + throw new IllegalArgumentException("Illegal enum value: " + value); + return allValues[value - 1]; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaClasspathParams.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaClasspathParams.java deleted file mode 100644 index e9508703..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaClasspathParams.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.commons; - -import java.util.List; - -public class JakartaClasspathParams { - - private String uri; - - private List snippetCtx; - - public JakartaClasspathParams() { - - } - - public JakartaClasspathParams(String uri, List snippetCtx) { - setUri(uri); - setSnippetCtx(snippetCtx); - } - - public void setUri(String uri) { - this.uri = uri; - } - - public String getUri() { - return this.uri; - } - - public void setSnippetCtx(List snippetCtx) { - this.snippetCtx = snippetCtx; - } - - public List getSnippetCtx() { - return this.snippetCtx; - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaDiagnosticsParams.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaDiagnosticsParams.java deleted file mode 100644 index 5bef7865..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaDiagnosticsParams.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.commons; - -import java.util.List; - -public class JakartaDiagnosticsParams { - - private List uris; - - private DocumentFormat documentFormat; - - public JakartaDiagnosticsParams() { - this(null); - } - - public JakartaDiagnosticsParams(List uris) { - setUris(uris); - } - - /** - * Returns the java file uris list. - * - * @return the java file uris list. - */ - public List getUris() { - return uris; - } - - /** - * Set the java file uris list. - * - * @param uris the java file uris list. - */ - public void setUris(List uris) { - this.uris = uris; - } - - public DocumentFormat getDocumentFormat() { - return documentFormat; - } - - public void setDocumentFormat(DocumentFormat documentFormat) { - this.documentFormat = documentFormat; - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCodeActionParams.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCodeActionParams.java index a1b7e077..5e67fd85 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCodeActionParams.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCodeActionParams.java @@ -11,7 +11,6 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ - package org.eclipse.lsp4jakarta.commons; import org.eclipse.lsp4j.CodeActionContext; @@ -20,29 +19,31 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; /** - * Java codeAction parameters. reused from - * https://github.com/eclipse/lsp4mp/blob/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/commons/MicroProfileJavaCodeActionParams.java + * Jakarta Java codeAction parameters. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/commons/MicroProfileJavaCodeActionParams.java * - * @author credit to Angelo ZERR + * @author Angelo ZERR * */ public class JakartaJavaCodeActionParams extends CodeActionParams { private boolean resourceOperationSupported; + private boolean commandConfigurationUpdateSupported; + + private boolean resolveSupported; + public JakartaJavaCodeActionParams() { super(); } public JakartaJavaCodeActionParams(final TextDocumentIdentifier textDocument, final Range range, - final CodeActionContext context) { + final CodeActionContext context) { super(textDocument, range, context); } - public JakartaJavaCodeActionParams(CodeActionParams params) { - super(params.getTextDocument(), params.getRange(), params.getContext()); - } - public String getUri() { return getTextDocument().getUri(); } @@ -55,4 +56,19 @@ public void setResourceOperationSupported(boolean resourceOperationSupported) { this.resourceOperationSupported = resourceOperationSupported; } -} \ No newline at end of file + public boolean isCommandConfigurationUpdateSupported() { + return commandConfigurationUpdateSupported; + } + + public void setCommandConfigurationUpdateSupported(boolean commandConfigurationUpdateSupported) { + this.commandConfigurationUpdateSupported = commandConfigurationUpdateSupported; + } + + public boolean isResolveSupported() { + return this.resolveSupported; + } + + public void setResolveSupported(boolean resolveSupported) { + this.resolveSupported = resolveSupported; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionParams.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionParams.java index be245048..3c741d9b 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionParams.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionParams.java @@ -14,63 +14,62 @@ *******************************************************************************/ package org.eclipse.lsp4jakarta.commons; - import org.eclipse.lsp4j.Position; /** - * Parameters that are passed on completion trigger for MicroProfile projects in Java files + * Parameters that are passed on completion trigger for Java projects. * * @author datho7561 */ public class JakartaJavaCompletionParams { - private String uri; - private Position position; + private String uri; + private Position position; - public JakartaJavaCompletionParams() { + public JakartaJavaCompletionParams() { - } + } - public JakartaJavaCompletionParams(String uri, Position position) { - this(); - setUri(uri); - setPosition(position); - } + public JakartaJavaCompletionParams(String uri, Position position) { + this(); + setUri(uri); + setPosition(position); + } - /** - * Returns the java file uri. - * - * @return the java file uri. - */ - public String getUri() { - return uri; - } + /** + * Returns the java file uri. + * + * @return the java file uri. + */ + public String getUri() { + return uri; + } - /** - * Set the java file uri. - * - * @param uri the java file uri. - */ - public void setUri(String uri) { - this.uri = uri; - } + /** + * Set the java file uri. + * + * @param uri the java file uri. + */ + public void setUri(String uri) { + this.uri = uri; + } - /** - * Returns the hover position - * - * @return the hover position - */ - public Position getPosition() { - return position; - } + /** + * Returns the hover position + * + * @return the hover position + */ + public Position getPosition() { + return position; + } - /** - * Sets the hover position - * - * @param position the hover position - */ - public void setPosition(Position position) { - this.position = position; - } + /** + * Sets the hover position + * + * @param position the hover position + */ + public void setPosition(Position position) { + this.position = position; + } } \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionResult.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionResult.java index 2579eaf6..d9bdb474 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionResult.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionResult.java @@ -20,39 +20,39 @@ * Represents completion information and context calculated by the Java language server component. */ public class JakartaJavaCompletionResult { - private CompletionList completionList; - private JavaCursorContextResult cursorContext; + private CompletionList completionList; + private JavaCursorContextResult cursorContext; - public JakartaJavaCompletionResult(CompletionList completionList, - JavaCursorContextResult cursorContext) { - this.completionList = completionList; - this.cursorContext = cursorContext; - } + public JakartaJavaCompletionResult(CompletionList completionList, + JavaCursorContextResult cursorContext) { + this.completionList = completionList; + this.cursorContext = cursorContext; + } - public JakartaJavaCompletionResult() { - this(null, null); - } + public JakartaJavaCompletionResult() { + this(null, null); + } - /** - * Returns the list of completion items contributed by the Java language server - * component. - * - * @return the list of completion items contributed by the Java language server - * component - */ - public CompletionList getCompletionList() { - return completionList; - } + /** + * Returns the list of completion items contributed by the Java language server + * component. + * + * @return the list of completion items contributed by the Java language server + * component + */ + public CompletionList getCompletionList() { + return completionList; + } - /** - * Returns information on the context of the cursor in the Java file, calculated - * by the Java language server component. - * - * @return information on the context of the cursor in the Java file, calculated - * by the Java language server component - */ - public JavaCursorContextResult getCursorContext() { - return cursorContext; - } + /** + * Returns information on the context of the cursor in the Java file, calculated + * by the Java language server component. + * + * @return information on the context of the cursor in the Java file, calculated + * by the Java language server component + */ + public JavaCursorContextResult getCursorContext() { + return cursorContext; + } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaDiagnosticsParams.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaDiagnosticsParams.java new file mode 100644 index 00000000..3936f72e --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaDiagnosticsParams.java @@ -0,0 +1,90 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +import java.util.List; + +/** + * Jakarta Java diagnostics parameters. + * + * Based on: https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/commons/MicroProfileJavaDiagnosticsParams.java + * + * @author Angelo ZERR + */ +public class JakartaJavaDiagnosticsParams { + + private List uris; + + private DocumentFormat documentFormat; + + private JakartaJavaDiagnosticsSettings settings; + + public JakartaJavaDiagnosticsParams() { + this(null); + } + + public JakartaJavaDiagnosticsParams(List uris) { + this(uris, null); + } + + public JakartaJavaDiagnosticsParams(List uris, JakartaJavaDiagnosticsSettings settings) { + setUris(uris); + this.settings = settings; + } + + /** + * Returns the java file uris list. + * + * @return the java file uris list. + */ + public List getUris() { + return uris; + } + + /** + * Set the java file uris list. + * + * @param uris the java file uris list. + */ + public void setUris(List uris) { + this.uris = uris; + } + + public DocumentFormat getDocumentFormat() { + return documentFormat; + } + + public void setDocumentFormat(DocumentFormat documentFormat) { + this.documentFormat = documentFormat; + } + + /** + * Returns the diagnostics settings. + * + * @return the diagnostics settings + */ + public JakartaJavaDiagnosticsSettings getSettings() { + return this.settings; + } + + /** + * Sets the diagnostics settings. + * + * @param settings the new value for the diagnostics settings + */ + public void setSettings(JakartaJavaDiagnosticsSettings settings) { + this.settings = settings; + } + +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaDiagnosticsSettings.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaDiagnosticsSettings.java new file mode 100644 index 00000000..7f4acda5 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaDiagnosticsSettings.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat Inc. and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +import java.util.Collections; +import java.util.List; + +/** + * Represents settings used by the Jakarta JDT component while validating Java + * class files. + * + * Based on: https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/commons/MicroProfileJavaDiagnosticsSettings.java + */ +public class JakartaJavaDiagnosticsSettings { + + public JakartaJavaDiagnosticsSettings(List patterns) { + this.patterns = patterns; + } + + private List patterns; + + /** + * Returns a list of patterns representing the properties to ignore validation + * when adding diagnostics for properties without + * values. + * + * @return a list of patterns representing the properties to ignore validation + * when adding diagnostics for properties without + * values. + */ + public List getPatterns() { + return patterns == null ? Collections.emptyList() : this.patterns; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfo.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfo.java new file mode 100644 index 00000000..8c470795 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfo.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +/** + * Java file information. + * + * @author Angelo ZERR + * + */ +public class JakartaJavaFileInfo { + + private String packageName; + + /** + * Returns the package name. + * + * @return the package name. + */ + public String getPackageName() { + return packageName; + } + + /** + * Set the package name. + * + * @param packageName the package name. + */ + public void setPackageName(String packageName) { + this.packageName = packageName; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfoParams.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfoParams.java new file mode 100644 index 00000000..567e86de --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfoParams.java @@ -0,0 +1,44 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +/** + * Jakarta Java file information parameters. + * + * @author Angelo ZERR + * + */ +public class JakartaJavaFileInfoParams { + + private String uri; + + /** + * Returns the Java file uri. + * + * @return the Java file uri + */ + public String getUri() { + return uri; + } + + /** + * Set the Java file uri. + * + * @param uri the Java file uri. + */ + public void setUri(String uri) { + this.uri = uri; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaProjectLabelsParams.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaProjectLabelsParams.java new file mode 100644 index 00000000..00228701 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaProjectLabelsParams.java @@ -0,0 +1,71 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +import java.util.List; + +/** + * Jakarta Java Project labels + * + * @author Angelo ZERR + * + */ +public class JakartaJavaProjectLabelsParams { + + private String uri; + + private List types; + + /** + * Returns the Java file uri. + * + * @return the Java file uri + */ + public String getUri() { + return uri; + } + + /** + * Set the Java file uri. + * + * @param uri the Java file uri. + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Returns the Java types list to check. + * + *

+ * If the owner Java project of the Java file URI contains some type in the + * classpath, it will return the type as label in + * {@link ProjectLabelInfoEntry#getLabels()} + *

+ * + * @return the Java types list to check + */ + public List getTypes() { + return types; + } + + /** + * Set the Java types list to check. + * + * @param types the Java types list to check. + */ + public void setTypes(List types) { + this.types = types; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextKind.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextKind.java index ecb10fa5..c5636d57 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextKind.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextKind.java @@ -18,216 +18,216 @@ */ public enum JavaCursorContextKind { - /** - * The cursor is in a file that does not have a root type declaration. - * - * eg. - *
- * |
- *
- */ - IN_EMPTY_FILE(1), + /** + * The cursor is in a file that does not have a root type declaration. + * + * eg. + *
+ * |
+ *
+ */ + IN_EMPTY_FILE(1), - /** - * The cursor is before a type declaration body, either at the root of a file or - * within another class. The cursor is before any annotations on the type. - * - * eg. - * - *
- * package org.acme;
- *
- * |
- * @Inject
- * public class MyClass {
- *
- * }
- *
- * - * eg. - * - *
- * package org.acme;
- *
- * @Inject
- * public class MyClass {
- *
- *     private int memberVariable;
- *     |
- *     @Inject
- *     public static class MyChildClass {
- *
- *     }
- * }
- *
- */ - BEFORE_CLASS(2), + /** + * The cursor is before a type declaration body, either at the root of a file or + * within another class. The cursor is before any annotations on the type. + * + * eg. + * + *
+ * package org.acme;
+ *
+ * |
+ * @Inject
+ * public class MyClass {
+ *
+ * }
+ *
+ * + * eg. + * + *
+ * package org.acme;
+ *
+ * @Inject
+ * public class MyClass {
+ *
+ *     private int memberVariable;
+ *     |
+ *     @Inject
+ *     public static class MyChildClass {
+ *
+ *     }
+ * }
+ *
+ */ + BEFORE_CLASS(2), - /** - * The cursor is in a type declaration body, and the next declaration in the - * body is a method declaration. The cursor is before any annotations on the - * method. - * - * eg. - * - *
- * package org.acme;
- *
- * public class MyClass {
- *     |
- *     @Deprecated
- *     public void myMethod() {
- *     }
- * }
- *
- */ - BEFORE_METHOD(3), + /** + * The cursor is in a type declaration body, and the next declaration in the + * body is a method declaration. The cursor is before any annotations on the + * method. + * + * eg. + * + *
+ * package org.acme;
+ *
+ * public class MyClass {
+ *     |
+ *     @Deprecated
+ *     public void myMethod() {
+ *     }
+ * }
+ *
+ */ + BEFORE_METHOD(3), - /** - * The cursor is in a type declaration body, and the next declaration in the - * body is a field declaration. The cursor is before any annotations on the - * field. - * - * eg. - * - *
- * package org.acme;
- *
- * public class MyClass {
- *     |
- *     @Deprecated
- *     public String myString;
- * }
- *
- */ - BEFORE_FIELD(4), + /** + * The cursor is in a type declaration body, and the next declaration in the + * body is a field declaration. The cursor is before any annotations on the + * field. + * + * eg. + * + *
+ * package org.acme;
+ *
+ * public class MyClass {
+ *     |
+ *     @Deprecated
+ *     public String myString;
+ * }
+ *
+ */ + BEFORE_FIELD(4), - /** - * The cursor is before a type declaration body, either at the root of a file or - * within another class. The cursor is somewhere within the annotation - * declarations on the class. - * - * eg. - * - *
- * package org.acme;
- *
- * @Inject|
- * public class MyClass {
- *
- * }
- *
- * - * eg. - * - *
- * package org.acme;
- *
- * @Inject
- * public class MyClass {
- *
- *     private int memberVariable;
- *
- *     @Inject|
- *     public static class MyChildClass {
- *
- *     }
- * }
- *
- */ - IN_CLASS_ANNOTATIONS(5), + /** + * The cursor is before a type declaration body, either at the root of a file or + * within another class. The cursor is somewhere within the annotation + * declarations on the class. + * + * eg. + * + *
+ * package org.acme;
+ *
+ * @Inject|
+ * public class MyClass {
+ *
+ * }
+ *
+ * + * eg. + * + *
+ * package org.acme;
+ *
+ * @Inject
+ * public class MyClass {
+ *
+ *     private int memberVariable;
+ *
+ *     @Inject|
+ *     public static class MyChildClass {
+ *
+ *     }
+ * }
+ *
+ */ + IN_CLASS_ANNOTATIONS(5), - /** - * The cursor is in a type declaration body, and the next declaration in the - * body is a method declaration. The cursor is somewhere within the annotation - * declarations on the method. - * - * eg. - * - *
- * package org.acme;
- *
- * public class MyClass {
- *
- *     @Deprecated|
- *     public void myMethod() {
- *     }
- * }
- *
- */ - IN_METHOD_ANNOTATIONS(6), + /** + * The cursor is in a type declaration body, and the next declaration in the + * body is a method declaration. The cursor is somewhere within the annotation + * declarations on the method. + * + * eg. + * + *
+ * package org.acme;
+ *
+ * public class MyClass {
+ *
+ *     @Deprecated|
+ *     public void myMethod() {
+ *     }
+ * }
+ *
+ */ + IN_METHOD_ANNOTATIONS(6), - /** - * The cursor is in a type declaration body, and the next declaration in the - * body is a field declaration. The cursor is somewhere within the annotation - * declarations on the field. - * - * eg. - * - *
- * package org.acme;
- *
- * public class MyClass {
- *
- *     @Deprecated|
- *     public String myString;
- * }
- *
- */ - IN_FIELD_ANNOTATIONS(7), + /** + * The cursor is in a type declaration body, and the next declaration in the + * body is a field declaration. The cursor is somewhere within the annotation + * declarations on the field. + * + * eg. + * + *
+ * package org.acme;
+ *
+ * public class MyClass {
+ *
+ *     @Deprecated|
+ *     public String myString;
+ * }
+ *
+ */ + IN_FIELD_ANNOTATIONS(7), - /** - * The cursor is in a type declaration body, after all the declarations for the - * type. - * - * eg. - * - *
- * package org.acme;
- *
- * public class MyClass {
- *
- *     @Deprecated
- *     public String myString;
- *     |
- * }
- *
- */ - IN_CLASS(8), + /** + * The cursor is in a type declaration body, after all the declarations for the + * type. + * + * eg. + * + *
+ * package org.acme;
+ *
+ * public class MyClass {
+ *
+ *     @Deprecated
+ *     public String myString;
+ *     |
+ * }
+ *
+ */ + IN_CLASS(8), - /** - * None of the above context apply. - * - * eg. - * - *
- * package org.acme;
- *
- * public class MyClass {
- *
- *     @Deprecated
- *     public void myMethod() {
- *         |
- *     }
- * }
- *
- */ - NONE(2000); + /** + * None of the above context apply. + * + * eg. + * + *
+ * package org.acme;
+ *
+ * public class MyClass {
+ *
+ *     @Deprecated
+ *     public void myMethod() {
+ *         |
+ *     }
+ * }
+ *
+ */ + NONE(2000); - private final int value; + private final int value; - private JavaCursorContextKind(int value) { - this.value = value; - } + private JavaCursorContextKind(int value) { + this.value = value; + } - public int getValue() { - return value; - } + public int getValue() { + return value; + } - public static JavaCursorContextKind forValue(int value) { - JavaCursorContextKind[] allValues = JavaCursorContextKind.values(); - if (value < 1 || value > allValues.length) - throw new IllegalArgumentException("Illegal enum value: " + value); - return allValues[value - 1]; - } + public static JavaCursorContextKind forValue(int value) { + JavaCursorContextKind[] allValues = JavaCursorContextKind.values(); + if (value < 1 || value > allValues.length) + throw new IllegalArgumentException("Illegal enum value: " + value); + return allValues[value - 1]; + } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextResult.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextResult.java index 62db4a6b..bde53b4b 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextResult.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextResult.java @@ -13,57 +13,56 @@ *******************************************************************************/ package org.eclipse.lsp4jakarta.commons; - /** * Represents context related to the cursor location in the given document. */ public class JavaCursorContextResult { - private JavaCursorContextKind kind; - private String prefix; + private JavaCursorContextKind kind; + private String prefix; - public JavaCursorContextResult(JavaCursorContextKind kind, String prefix) { - this.kind = kind; - this.prefix = prefix; - } + public JavaCursorContextResult(JavaCursorContextKind kind, String prefix) { + this.kind = kind; + this.prefix = prefix; + } - public JavaCursorContextResult() { - this(null, null); - } + public JavaCursorContextResult() { + this(null, null); + } - /** - * Returns the context of the cursor in the Java file or NONE if - * none of the contexts apply. - * - * For instance, it returns IN_METHOD_ANNOTATIONS if the cursor is - * in the list of annotations before a method declaration. - * - * @return the context of the cursor in the Java file - */ - public JavaCursorContextKind getKind() { - return kind; - } + /** + * Returns the context of the cursor in the Java file or NONE if + * none of the contexts apply. + * + * For instance, it returns IN_METHOD_ANNOTATIONS if the cursor is + * in the list of annotations before a method declaration. + * + * @return the context of the cursor in the Java file + */ + public JavaCursorContextKind getKind() { + return kind; + } - /** - * Returns the text content to the left of the cursor, up to the first whitespace. - * - * eg. - * - * - * public static| - * - * - * would return "static" - * - * - * | - * - * - * would return "" - * - *@return the text content to the left of the cursor, up to the first whitespace - */ - public String getPrefix() { - return prefix; - } + /** + * Returns the text content to the left of the cursor, up to the first whitespace. + * + * eg. + * + * + * public static| + * + * + * would return "static" + * + * + * | + * + * + * would return "" + * + * @return the text content to the left of the cursor, up to the first whitespace + */ + public String getPrefix() { + return prefix; + } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/ProjectLabelInfoEntry.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/ProjectLabelInfoEntry.java new file mode 100644 index 00000000..720abd62 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/ProjectLabelInfoEntry.java @@ -0,0 +1,77 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +import java.util.Collections; +import java.util.List; + +/** + * Stores labels for the project located at a specific project uri + * + * @author dakwon + * + */ +public class ProjectLabelInfoEntry { + public static final ProjectLabelInfoEntry EMPTY_PROJECT_INFO = new ProjectLabelInfoEntry("", "", Collections.emptyList()); + + private final String uri; + private final String name; + private final List labels; + + public ProjectLabelInfoEntry(String uri, String name, List labels) { + this.uri = uri; + this.name = name; + this.labels = labels; + } + + /** + * Returns the project uri + * + * @return the project uri + */ + public String getUri() { + return uri; + } + + /** + * Returns the name of the project + * + * @return The name of this project + */ + public String getName() { + return name; + } + + /** + * Returns the labels for the current project uri + * + * @return the labels for the current project uri + */ + public List getLabels() { + return labels; + } + + /** + * Returns true if the project has the given label and false otherwise. + * + * @param label the label. + * @return true if the project has the given label and false otherwise. + */ + public boolean hasLabel(String label) { + //boolean truth = true; + //return truth; + // right? + return labels != null && labels.contains(label); + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/CodeActionData.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/CodeActionData.java new file mode 100644 index 00000000..398ffca2 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/CodeActionData.java @@ -0,0 +1,76 @@ +/******************************************************************************* +* Copyright (c) 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons.codeaction; + +import java.util.Objects; + +/** + * Represents the data that all code actions have. + */ +public class CodeActionData { + + private String id; + + public CodeActionData() { + this(null); + } + + public CodeActionData(ICodeActionId id) { + setCodeActionId(id); + } + + /** + * Returns the id of this code action as a string. + * + * Each type of code action has an id that represents it, so that it's easy to + * associate a given code action to the code that generated it. + * + * @return the id of this code action + */ + public String getCodeActionId() { + return id; + } + + /** + * Sets the id of this code action. + * + * @param id the new value for the id of this code action + */ + public void setCodeActionId(ICodeActionId id) { + if (id != null) { + this.id = id.getId(); + } else { + this.id = null; + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof CodeActionData)) { + return false; + } + CodeActionData other = (CodeActionData) obj; + return Objects.equals(id, other.id); + } + + @Override + public String toString() { + return "CodeActionData [id=" + id + "]"; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/CodeActionResolveData.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/CodeActionResolveData.java new file mode 100644 index 00000000..22dbbb94 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/CodeActionResolveData.java @@ -0,0 +1,132 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons.codeaction; + +import java.util.Map; +import java.util.Objects; + +import org.eclipse.lsp4j.Range; + +/** + * Represents additional data that is needed to resolve a code action. + * + * @author datho7561 + */ +public class CodeActionResolveData extends CodeActionData { + + private String participantId; + private String documentUri; + + private Range range; + + private Map extendedData; + + private boolean resourceOperationSupported; + private boolean commandConfigurationUpdateSupported; + + /** + * Make code action data will all fields nulled/false + * + * Needed for Gson + */ + public CodeActionResolveData() { + this(null, null, null, null, false, false, null); + } + + public CodeActionResolveData(String documentUri, String participantId, Range range, + Map extendedData, boolean resourceOperationSupported, + boolean commandConfigurationUpdateSupported, ICodeActionId id) { + super(id); + this.documentUri = documentUri; + this.participantId = participantId; + this.range = range; + this.extendedData = extendedData; + this.resourceOperationSupported = resourceOperationSupported; + this.commandConfigurationUpdateSupported = commandConfigurationUpdateSupported; + } + + /** + * Returns the uri of the document that this code action applies to. + * + * @return the uri of the document that this code action applies to + */ + public String getDocumentUri() { + return this.documentUri; + } + + /** + * Returns the unique id of the IJavaCodeActionParticipant that can resolve the + * text edits for this code action. + * + * @return the unique id of the IJavaCodeActionParticipant that can resolve the + * text edits for this code action + */ + public String getParticipantId() { + return this.participantId; + } + + /** + * Returns the range for which this CodeAction is applicable for. + * + * @return the range for which this CodeAction is applicable for + */ + public Range getRange() { + return this.range; + } + + /** + * Returns the entry in the extended data for the given key + * + * @param key the key to get the entry for + * @return the entry in the extended data for the given key + */ + public Object getExtendedDataEntry(String key) { + return extendedData.get(key); + } + + /** + * Returns true if the client supports resource operations and false otherwise. + * + * @return true if the client supports resource operations and false otherwise + */ + public boolean isResourceOperationSupported() { + return this.resourceOperationSupported; + } + + /** + * Returns true if the client implements a command that allows the language + * server to update preferences, and false otherwise. + * + * @return true if the client implements a command that allows the language + * server to update preferences, and false otherwise + */ + public boolean isCommandConfigurationUpdateSupported() { + return this.commandConfigurationUpdateSupported; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof CodeActionResolveData)) { + return false; + } + CodeActionResolveData that = (CodeActionResolveData) other; + return Objects.equals(this.documentUri, that.documentUri) + && Objects.equals(this.participantId, that.participantId) && Objects.equals(this.range, that.range) + && Objects.equals(this.extendedData, that.extendedData) + && Objects.equals(this.resourceOperationSupported, that.resourceOperationSupported) + && Objects.equals(this.commandConfigurationUpdateSupported, that.commandConfigurationUpdateSupported); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/ICodeActionId.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/ICodeActionId.java new file mode 100644 index 00000000..c5a19ded --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/ICodeActionId.java @@ -0,0 +1,28 @@ +/******************************************************************************* +* Copyright (c) 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons.codeaction; + +/** + * Represents an id that identifies the type of code action. + */ +public interface ICodeActionId { + + /** + * Returns the id of this code action as a string. + * + * @return the id of this code action as a string + */ + String getId(); + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/JakartaCodeActionId.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/JakartaCodeActionId.java new file mode 100644 index 00000000..d8c987aa --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/commons/codeaction/JakartaCodeActionId.java @@ -0,0 +1,75 @@ +/******************************************************************************* +* Copyright (c) 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons.codeaction; + +/** + * Represents id of an lsp4mp code action. + */ +public enum JakartaCodeActionId implements ICodeActionId { + IgnoreUnknownProperty, + // JAXRS + jaxrsInsertPublicCtrtToClass, + MakeConstructorPublic, + MakeMethodPublic, + RemoveAllEntityParametersExcept, + // Annotations + ChangeReturnTypeToVoid, + InsertResourceAnnotationTypeAttribute, + InsertResourceAnnotationNameAttribute, + RemoveAllParameters, + RemoveAnnotationPreDestroy, + RemoveAnnotationPostConstruct, + AnnotationRemoveStaticModifier, + // Bean validation + RemoveConstraintAnnotation, + BBRemoveStaticModifier, + // Dependency injection + DIRemoveInjectAnnotation, + DIRemoveFinalModifier, + DIRemoveAbstractModifier, + DIRemoveStaticModifier, + // JSON-B + JSONBRemoveJsonbCreatorAnnotation, + JSONBRemoveJsonbTransientAnnotation, + JSONBRemoveAllButJsonbTransientAnnotation, + // Persistence + PersistenceRemoveFinalModifier, + PersistenceRemoveMapKeyAnnotation, + PersistenceInsertAttributesToMKJCAnnotation, + PersistenceInsertPublicCtrtToClass, + PersistenceInsertProtectedCtrtToClass, + // WebSockets + WBInsertPathParamAnnotationWithValueAttrib, + // Servlet + ServletCompleteWebFilterAnnotation, + ServletCompleteServletAnnotation, + ServletFilterImplementation, + ServletExtendClass, + ServletListenerImplementation, + // CDI + CDIRemoveProducesAndInjectAnnotations, + CDIInsertInjectAnnotation, + CDIInsertProtectedCtrtToClass, + CDIInsertPublicCtrtToClass, + CDIRemoveInvalidInjectAnnotations, + CDIRemoveProducesAnnotation, + CDIRemoveInjectAnnotation, + CDIRemoveScopeDeclarationAnnotationsButOne, + CDIReplaceScopeAnnotations; + + @Override + public String getId() { + return name(); + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/CodeActionHandler.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/CodeActionHandler.java deleted file mode 100644 index 8124ce84..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/CodeActionHandler.java +++ /dev/null @@ -1,281 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.IBuffer; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.manipulation.CoreASTProvider; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAbstractModifierQuickFix; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveFinalModifierQuickFix; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveInjectAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveMethodParametersQuickFix; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveStaticModifierQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; -import org.eclipse.lsp4jakarta.jdt.core.JsonRpcHelpers; -import org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants; -import org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.persistence.PersistenceAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.persistence.PersistenceConstants; -import org.eclipse.lsp4jakarta.jdt.core.persistence.PersistenceEntityQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.jax_rs.Jax_RSConstants; -import org.eclipse.lsp4jakarta.jdt.core.jax_rs.NoResourcePublicConstructorQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.jax_rs.NonPublicResourceMethodQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.jax_rs.ResourceMethodMultipleEntityParamsQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.jsonb.JsonbAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.jsonb.JsonbTransientAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.jsonb.JsonbConstants; -import org.eclipse.lsp4jakarta.jdt.core.cdi.ConflictProducesInjectQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants; -import org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstructorQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanNoArgConstructorQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.cdi.RemoveInvalidInjectParamAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.cdi.RemoveProduceAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.cdi.ScopeDeclarationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants; -import org.eclipse.lsp4jakarta.jdt.core.servlet.CompleteFilterAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.servlet.CompleteServletAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.servlet.FilterImplementationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.servlet.HttpServletQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.servlet.ListenerImplementationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.servlet.ServletConstants; -import org.eclipse.lsp4jakarta.jdt.core.persistence.DeleteConflictMapKeyQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.annotations.AddResourceMissingNameQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.annotations.AddResourceMissingTypeQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.annotations.AnnotationConstants; -import org.eclipse.lsp4jakarta.jdt.core.annotations.PostConstructReturnTypeQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.annotations.RemovePreDestroyAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.annotations.RemovePostConstructAnnotationQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.websocket.WebSocketConstants; -import org.eclipse.lsp4jakarta.jdt.core.websocket.AddPathParamQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; - -/** - * Code action handler. Partially reused from - * https://github.com/eclipse/lsp4mp/blob/b88710cc54170844717f655b9bff8bb4c4649a8d/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/codeaction/CodeActionHandler.java - * Modified to fit the purposes of the Jakarta Language Server with deletions of - * some unnecessary methods and modifications. - * - * @author credit to Angelo ZERR - * - */ -public class CodeActionHandler { - - public List codeAction(JakartaJavaCodeActionParams params, JDTUtils utils, IProgressMonitor monitor) { - String uri = params.getUri(); - ICompilationUnit unit = utils.resolveCompilationUnit(uri); - if (unit == null) { - return Collections.emptyList(); - } - try { - Range range = params.getRange(); - int startOffset = toOffset(unit.getBuffer(), range.getStart().getLine(), range.getStart().getCharacter()); - int endOffset = toOffset(unit.getBuffer(), range.getEnd().getLine(), range.getEnd().getCharacter()); - JavaCodeActionContext context = new JavaCodeActionContext(unit, startOffset, endOffset - startOffset, utils, - params); - context.setASTRoot(getASTRoot(unit, monitor)); - - List codeActions = new ArrayList<>(); - - HttpServletQuickFix HttpServletQuickFix = new HttpServletQuickFix(); - FilterImplementationQuickFix FilterImplementationQuickFix = new FilterImplementationQuickFix(); - ListenerImplementationQuickFix ListenerImplementationQuickFix = new ListenerImplementationQuickFix(); - CompleteServletAnnotationQuickFix CompleteServletAnnotationQuickFix = new CompleteServletAnnotationQuickFix(); - CompleteFilterAnnotationQuickFix CompleteFilterAnnotationQuickFix = new CompleteFilterAnnotationQuickFix(); - PersistenceAnnotationQuickFix PersistenceAnnotationQuickFix = new PersistenceAnnotationQuickFix(); - DeleteConflictMapKeyQuickFix DeleteConflictMapKeyQuickFix = new DeleteConflictMapKeyQuickFix(); - NonPublicResourceMethodQuickFix NonPublicResourceMethodQuickFix = new NonPublicResourceMethodQuickFix(); - ResourceMethodMultipleEntityParamsQuickFix ResourceMethodMultipleEntityParamsQuickFix = new ResourceMethodMultipleEntityParamsQuickFix(); - NoResourcePublicConstructorQuickFix NoResourcePublicConstructorQuickFix = new NoResourcePublicConstructorQuickFix(); - ManagedBeanQuickFix ManagedBeanQuickFix = new ManagedBeanQuickFix(); - PersistenceEntityQuickFix PersistenceEntityQuickFix = new PersistenceEntityQuickFix(); - ConflictProducesInjectQuickFix ConflictProducesInjectQuickFix = new ConflictProducesInjectQuickFix(); - BeanValidationQuickFix BeanValidationQuickFix = new BeanValidationQuickFix(); - ManagedBeanConstructorQuickFix ManagedBeanConstructorQuickFix = new ManagedBeanConstructorQuickFix(); - ManagedBeanNoArgConstructorQuickFix ManagedBeanNoArgConstructorQuickFix = new ManagedBeanNoArgConstructorQuickFix(); - JsonbAnnotationQuickFix JsonbAnnotationQuickFix = new JsonbAnnotationQuickFix(); - JsonbTransientAnnotationQuickFix JsonbTransientAnnotationQuickFix = new JsonbTransientAnnotationQuickFix(); - ScopeDeclarationQuickFix ScopeDeclarationQuickFix = new ScopeDeclarationQuickFix(); - RemovePreDestroyAnnotationQuickFix RemovePreDestroyAnnotationQuickFix=new RemovePreDestroyAnnotationQuickFix(); - RemovePostConstructAnnotationQuickFix RemovePostConstructAnnotationQuickFix=new RemovePostConstructAnnotationQuickFix(); - PostConstructReturnTypeQuickFix PostConstructReturnTypeQuickFix = new PostConstructReturnTypeQuickFix(); - RemoveFinalModifierQuickFix RemoveFinalModifierQuickFix = new RemoveFinalModifierQuickFix(); - RemoveStaticModifierQuickFix RemoveStaticModifierQuickFix = new RemoveStaticModifierQuickFix(); - RemoveMethodParametersQuickFix RemoveMethodParametersQuickFix =new RemoveMethodParametersQuickFix(); - AddResourceMissingNameQuickFix AddResourceMissingNameQuickFix=new AddResourceMissingNameQuickFix(); - AddResourceMissingTypeQuickFix AddResourceMissingTypeQuickFix=new AddResourceMissingTypeQuickFix(); - RemoveAbstractModifierQuickFix RemoveAbstractModifierQuickFix = new RemoveAbstractModifierQuickFix(); - RemoveInjectAnnotationQuickFix RemoveInjectAnnotationQuickFix = new RemoveInjectAnnotationQuickFix(); - RemoveProduceAnnotationQuickFix RemoveProduceAnnotationQuickFix = new RemoveProduceAnnotationQuickFix(); - RemoveInvalidInjectParamAnnotationQuickFix RemoveInvalidInjectParamAnnotationQuickFix = new RemoveInvalidInjectParamAnnotationQuickFix(); - AddPathParamQuickFix AddPathParamQuickFix = new AddPathParamQuickFix(); - - for (Diagnostic diagnostic : params.getContext().getDiagnostics()) { - try { - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE)) { - codeActions.addAll(HttpServletQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE_FILTER)) { - codeActions.addAll(FilterImplementationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE_LISTENER)) { - codeActions.addAll(ListenerImplementationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(AnnotationConstants.DIAGNOSTIC_CODE_MISSING_RESOURCE_NAME_ATTRIBUTE)) { - codeActions.addAll(AddResourceMissingNameQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(AnnotationConstants.DIAGNOSTIC_CODE_MISSING_RESOURCE_TYPE_ATTRIBUTE)) { - codeActions.addAll(AddResourceMissingTypeQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(AnnotationConstants.DIAGNOSTIC_CODE_POSTCONSTRUCT_RETURN_TYPE)) { - codeActions.addAll(PostConstructReturnTypeQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE_MISSING_ATTRIBUTE) - || diagnostic.getCode().getLeft() - .equals(ServletConstants.DIAGNOSTIC_CODE_DUPLICATE_ATTRIBUTES)) { - codeActions - .addAll(CompleteServletAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE_FILTER_MISSING_ATTRIBUTE) - || diagnostic.getCode().getLeft() - .equals(ServletConstants.DIAGNOSTIC_CODE_FILTER_DUPLICATE_ATTRIBUTES)) { - codeActions - .addAll(CompleteFilterAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(Jax_RSConstants.DIAGNOSTIC_CODE_NON_PUBLIC)) { - codeActions - .addAll(NonPublicResourceMethodQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(Jax_RSConstants.DIAGNOSTIC_CODE_MULTIPLE_ENTITY_PARAMS)) { - codeActions.addAll(ResourceMethodMultipleEntityParamsQuickFix.getCodeActions(context, - diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(Jax_RSConstants.DIAGNOSTIC_CODE_NO_PUBLIC_CONSTRUCTORS)) { - codeActions.addAll(NoResourcePublicConstructorQuickFix.getCodeActions(context, - diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft() - .equals(PersistenceConstants.DIAGNOSTIC_CODE_MISSING_ATTRIBUTES)) { - codeActions.addAll(PersistenceAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft() - .equals(PersistenceConstants.DIAGNOSTIC_CODE_INVALID_ANNOTATION)) { - codeActions.addAll(DeleteConflictMapKeyQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(PersistenceConstants.DIAGNOSTIC_CODE_MISSING_EMPTY_CONSTRUCTOR)) { - codeActions.addAll(PersistenceEntityQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(PersistenceConstants.DIAGNOSTIC_CODE_FINAL_METHODS) - || diagnostic.getCode().getLeft().equals(PersistenceConstants.DIAGNOSTIC_CODE_FINAL_VARIABLES) - || diagnostic.getCode().getLeft().equals(PersistenceConstants.DIAGNOSTIC_CODE_FINAL_CLASS)) { - codeActions.addAll(RemoveFinalModifierQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ManagedBeanConstants.DIAGNOSTIC_CODE)) { - codeActions.addAll(ManagedBeanQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ManagedBeanConstants.DIAGNOSTIC_CODE_PRODUCES_INJECT)) { - codeActions.addAll(ConflictProducesInjectQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ManagedBeanConstants.DIAGNOSTIC_CODE_INVALID_INJECT_PARAM)) { - codeActions.addAll(RemoveInjectAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(RemoveInvalidInjectParamAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ManagedBeanConstants.DIAGNOSTIC_CODE_INVALID_PRODUCES_PARAM)) { - codeActions.addAll(RemoveProduceAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(RemoveInvalidInjectParamAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(BeanValidationConstants.DIAGNOSTIC_CODE_STATIC) - || diagnostic.getCode().getLeft() - .equals(BeanValidationConstants.DIAGNOSTIC_CODE_INVALID_TYPE)) { - codeActions.addAll(BeanValidationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(ManagedBeanConstants.CONSTRUCTOR_DIAGNOSTIC_CODE)) { - codeActions.addAll(ManagedBeanConstructorQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(ManagedBeanNoArgConstructorQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION)) { - codeActions.addAll(JsonbAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if (diagnostic.getCode().getLeft().equals(JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_FIELD)) { - codeActions.addAll(JsonbTransientAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if(diagnostic.getCode().getLeft().equals(ManagedBeanConstants.DIAGNOSTIC_CODE_SCOPEDECL)) { - codeActions.addAll(ScopeDeclarationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if(diagnostic.getCode().getLeft().equals(DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_FINAL)) { - codeActions.addAll(RemoveInjectAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(RemoveFinalModifierQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if(diagnostic.getCode().getLeft().equals(DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_CONSTRUCTOR) || - diagnostic.getCode().getLeft().equals(DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_GENERIC)) { - codeActions.addAll(RemoveInjectAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if(diagnostic.getCode().getLeft().equals(DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_ABSTRACT)) { - codeActions.addAll(RemoveInjectAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(RemoveAbstractModifierQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if(diagnostic.getCode().getLeft().equals(DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_STATIC)) { - codeActions.addAll(RemoveInjectAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(RemoveStaticModifierQuickFix.getCodeActions(context, diagnostic, monitor)); - } - - if(diagnostic.getCode().getLeft().equals(AnnotationConstants.DIAGNOSTIC_CODE_POSTCONSTRUCT_PARAMS)) { - codeActions.addAll(RemovePostConstructAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(RemoveMethodParametersQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if(diagnostic.getCode().getLeft().equals(AnnotationConstants.DIAGNOSTIC_CODE_PREDESTROY_STATIC)) { - codeActions.addAll(RemovePreDestroyAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(RemoveStaticModifierQuickFix.getCodeActions(context, diagnostic, monitor)); - } - if(diagnostic.getCode().getLeft().equals(AnnotationConstants.DIAGNOSTIC_CODE_PREDESTROY_PARAMS)) { - codeActions.addAll(RemovePreDestroyAnnotationQuickFix.getCodeActions(context, diagnostic, monitor)); - codeActions.addAll(RemoveMethodParametersQuickFix.getCodeActions(context,diagnostic,monitor)); - } - if(diagnostic.getCode().getLeft().equals(WebSocketConstants.DIAGNOSTIC_CODE_PATH_PARAMS_ANNOT)) { - codeActions.addAll(AddPathParamQuickFix.getCodeActions(context, diagnostic, monitor)); - } - - } catch (CoreException e) { - e.printStackTrace(); - } - } - return codeActions; - - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Failed to retrieve Jakarta code action", e); - } - return null; - } - - private static CompilationUnit getASTRoot(ICompilationUnit unit, IProgressMonitor monitor) { - return CoreASTProvider.getInstance().getAST(unit, CoreASTProvider.WAIT_YES, monitor); - } - - public int toOffset(IBuffer buffer, int line, int column) { - return JsonRpcHelpers.toOffset(buffer, line, column); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ChangeCorrectionProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ChangeCorrectionProposal.java deleted file mode 100644 index d88ecc49..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ChangeCorrectionProposal.java +++ /dev/null @@ -1,262 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Copied from /org.eclipse.jdt.ui/src/org/eclipse/jdt/ui/text/java/correction/ChangeCorrectionProposal.java - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.Status; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; -import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; -import org.eclipse.jdt.core.manipulation.CUCorrectionProposalCore; -import org.eclipse.jdt.core.manipulation.ChangeCorrectionProposalCore; -import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration; -import org.eclipse.jface.text.IDocument; -import org.eclipse.ltk.core.refactoring.Change; -import org.eclipse.ltk.core.refactoring.IUndoManager; -import org.eclipse.ltk.core.refactoring.RefactoringCore; -import org.eclipse.ltk.core.refactoring.RefactoringStatus; -import org.eclipse.ltk.core.refactoring.TextChange; -import org.eclipse.text.edits.TextEdit; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; - -/** - * Adapted from - * /org.eclipse.jdt.ui/src/org/eclipse/jdt/ui/text/java/correction/ChangeCorrectionProposal.java - * and - * https://github.com/eclipse/lsp4mp/blob/55ac697e9382db279480d992a993f17bc7a92c60/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/corrections/proposal/ChangeCorrectionProposal.java - */ -public class ChangeCorrectionProposal extends ChangeCorrectionProposalCore { - - // LSP: Code Action Kind - private String fKind; - private ASTRewrite fRewrite; - private ImportRewrite fImportRewrite; - private CUCorrectionProposalCore fProposalCore; - - /** - * Constructs a change correction proposal. - * - * @param name the name that is displayed in the proposal selection dialog - * @param change the change that is executed when the proposal is applied or - * null if the change will be created by implementors - * of {@link #createChange()} - */ - public ChangeCorrectionProposal(String name, String kind, ICompilationUnit cu, ASTRewrite rewrite, int relevance) { - super(name, null, 0); - fKind = kind; - if (cu == null) { - throw new IllegalArgumentException("Compilation unit must not be null"); //$NON-NLS-1$ - } - - fRewrite = rewrite; - fProposalCore = new CUCorrectionProposalCore(name, cu, null, relevance); - } - - /** - * Performs the change associated with this proposal. - *

- * Subclasses may extend, but must call the super implementation. - * - * @throws CoreException when the invocation of the change failed - */ - @Override - protected void performChange() throws CoreException { - Change change = null; - try { - change = getChange(); - if (change != null) { - - change.initializeValidationData(new NullProgressMonitor()); - RefactoringStatus valid = change.isValid(new NullProgressMonitor()); - if (valid.hasFatalError()) { - IStatus status = new Status(IStatus.ERROR, JakartaCorePlugin.PLUGIN_ID, IStatus.ERROR, - valid.getMessageMatchingSeverity(RefactoringStatus.FATAL), null); - throw new CoreException(status); - } else { - IUndoManager manager = RefactoringCore.getUndoManager(); - Change undoChange; - boolean successful = false; - try { - manager.aboutToPerformChange(change); - undoChange = change.perform(new NullProgressMonitor()); - successful = true; - } finally { - manager.changePerformed(change, successful); - } - if (undoChange != null) { - undoChange.initializeValidationData(new NullProgressMonitor()); - manager.addUndo(getName(), undoChange); - } - } - } - } finally { - - if (change != null) { - change.dispose(); - } - } - } - - @Override - public Object getAdditionalProposalInfo(IProgressMonitor monitor) { - return fProposalCore.getAdditionalProposalInfo(monitor); - } - - @Override - public void apply() throws CoreException { - performChange(); - } - - @Override - protected final Change createChange() throws CoreException { - return createTextChange(); // make sure that only text changes are allowed here - } - - /** - * Returns the kind of the proposal. - * - * @return the kind of the proposal - */ - public String getKind() { - return fKind; - } - - /** - * @param codeActionKind the Code Action Kind to set - */ - public void setKind(String codeActionKind) { - this.fKind = codeActionKind; - } - - /** - * The compilation unit on which the change works. - * - * @return the compilation unit on which the change works - */ - public final ICompilationUnit getCompilationUnit() { - return fProposalCore.getCompilationUnit(); - } - - /** - * Returns the text change that is invoked when the change is applied. - * - * @return the text change that is invoked when the change is applied - * @throws CoreException if accessing the change failed - */ - public final TextChange getTextChange() throws CoreException { - return (TextChange) getChange(); - } - - /** - * Creates the text change for this proposal. This method is only called once - * and only when no text change has been passed in - * {@link #CUCorrectionProposal(String, ICompilationUnit, TextChange, int)}. - * - * @return the created text change - * @throws CoreException if the creation of the text change failed - */ - protected TextChange createTextChange() throws CoreException { - TextChange change = fProposalCore.getNewChange(); - // initialize text change - IDocument document = change.getCurrentDocument(new NullProgressMonitor()); - addEdits(document, change.getEdit()); - return change; - } - - /** - * Creates a preview of the content of the compilation unit after applying the - * change. - * - * @return the preview of the changed compilation unit - * @throws CoreException if the creation of the change failed - * - * @noreference This method is not intended to be referenced by clients. - */ - public String getPreviewContent() throws CoreException { - return getTextChange().getPreviewContent(new NullProgressMonitor()); - } - - /** - * Returns the import rewrite used for this compilation unit. - * - * @return the import rewrite or null if no import rewrite has been - * set - * @nooverride This method is not intended to be re-implemented or extended by - * clients. - */ - public ImportRewrite getImportRewrite() { - return fImportRewrite; - } - - /** - * Sets the import rewrite used for this compilation unit. - * - * @param rewrite the import rewrite - * @nooverride This method is not intended to be re-implemented or extended by - * clients. - */ - public void setImportRewrite(ImportRewrite rewrite) { - fImportRewrite = rewrite; - } - - /** - * Creates and sets the import rewrite used for this compilation unit. - * - * @param astRoot the AST for the current CU - * @return the created import rewrite - * @nooverride This method is not intended to be re-implemented or extended by - * clients. - */ - public ImportRewrite createImportRewrite(CompilationUnit astRoot) { - fImportRewrite = CodeStyleConfiguration.createImportRewrite(astRoot, true); - return fImportRewrite; - } - - protected void addEdits(IDocument document, TextEdit editRoot) throws CoreException { - ASTRewrite rewrite = getRewrite(); - if (rewrite != null) { - try { - TextEdit edit = rewrite.rewriteAST(); - editRoot.addChild(edit); - } catch (IllegalArgumentException e) { -// JakartaCorePlugin.log(new Status(IStatus.ERROR, JakartaCorePlugin.getDefault().getBundle().getSymbolicName(), -// "Invalid AST Rewriter", e)); - } - } - if (fImportRewrite != null) { - editRoot.addChild(fImportRewrite.rewriteImports(new NullProgressMonitor())); - } - } - - /** - * Returns the rewrite that has been passed in the constructor. Implementors can - * override this method to create the rewrite lazily. This method will only be - * called once. - * - * @return the rewrite to be used - * @throws CoreException when the rewrite could not be created - */ - protected ASTRewrite getRewrite() throws CoreException { - if (fRewrite == null) { -// JakartaCorePlugin.log(new Status(IStatus.ERROR, JakartaCorePlugin.getDefault().getBundle().getSymbolicName(), -// "Invalid AST Rewrite")); - } - return fRewrite; - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/RemoveMultipleAnnotations.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/RemoveMultipleAnnotations.java deleted file mode 100644 index 3956cd87..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/RemoveMultipleAnnotations.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Adit Rada - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -import com.google.gson.JsonArray; - -/** - * This class is used to provide options to remove multiple annotations - * at the same time. For example, "Remove @A, @B", "Remove @C, @D, @E". - * - * @author Adit Rada - * - */ -public abstract class RemoveMultipleAnnotations extends RemoveAnnotationConflictQuickFix { - - public RemoveMultipleAnnotations() { - // annotation list to be derived from the diagnostic passed to - // `getCodeActions()` - super(); - } - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - - JsonArray diagnosticData = (JsonArray) diagnostic.getData(); - - List annotations = IntStream.range(0, diagnosticData.size()) - .mapToObj(idx -> diagnosticData.get(idx).getAsString()).collect(Collectors.toList()); - - if (parentType != null) { - - List> annotationsListsToRemove = getMultipleRemoveAnnotations(annotations); - for (List annotationList : annotationsListsToRemove) { - String[] annotationsToRemove = annotationList.toArray(new String[annotationList.size()]); - removeAnnotation(diagnostic, context, parentType, codeActions, annotationsToRemove); - } - } - return codeActions; - } - - /** - * Each List in the returned List of Lists should be a set of annotations that - * will be removed at one go. For example, to provide the user with the option to remove - * "@A, @B" and "@C". The return should be [[A, B], [C]] - * - * @param All the annotations present on the member. - * @return A List of Lists, with each list containing the annotations that must be - * removed at the same time. - * @author Adit Rada - * - */ - protected abstract List> getMultipleRemoveAnnotations(List annotations); -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ReplaceAnnotationProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ReplaceAnnotationProposal.java deleted file mode 100644 index 6631cfda..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ReplaceAnnotationProposal.java +++ /dev/null @@ -1,112 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2016 IBM Corporation and others. -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 - * - * Copied from /org.eclipse.jdt.ui/src/org/eclipse/jdt/internal/ui/text/correction/proposals/NewAnnotationMemberProposal.java - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; - -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.FieldDeclaration; -import org.eclipse.jdt.core.dom.IAnnotationBinding; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.TypeDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; -import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; -import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; -import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; -import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; - -/** - * Similar functionality as NewAnnotationProposal. The main difference is that - * first removes specified annotations before adding a new annotation. - * - * Note: This class only accepts one new annotation to add. - * - * @author Kathryn Kodama - */ -public class ReplaceAnnotationProposal extends NewAnnotationProposal { - - private final String[] removeAnnotations; - - public ReplaceAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, String annotation, String... removeAnnotations) { - super(label, targetCU, invocationNode, binding, relevance, annotation); - this.removeAnnotations = removeAnnotations; - } - - @Override - protected ASTRewrite getRewrite() throws CoreException { - CompilationUnit fInvocationNode = getInvocationNode(); - IBinding fBinding = getBinding(); - String[] annotations = getAnnotations(); - - ASTNode declNode = null; - ASTNode boundNode = fInvocationNode.findDeclaringNode(fBinding); - CompilationUnit newRoot = fInvocationNode; - if (boundNode != null) { - declNode = boundNode; // is same CU - } else { - newRoot = ASTResolving.createQuickFixAST(getCompilationUnit(), null); - declNode = newRoot.findDeclaringNode(fBinding.getKey()); - } - ImportRewrite imports = createImportRewrite(newRoot); - - boolean isField = declNode instanceof VariableDeclarationFragment; - if (isField) { - declNode = declNode.getParent(); - } - if (declNode instanceof TypeDeclaration || isField) { - AST ast = declNode.getAST(); - ASTRewrite rewrite = ASTRewrite.create(ast); - - ImportRewriteContext importRewriteContext = new ContextSensitiveImportRewriteContext(declNode, imports); - - // remove annotations in the removeAnnotations list - @SuppressWarnings("unchecked") - List children = (List) (declNode.getParent() instanceof CompilationUnit ? declNode : declNode.getParent()) - .getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY); - for (ASTNode child : children) { - if (child instanceof Annotation) { - Annotation annotation = (Annotation) child; - IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding(); - boolean containsAnnotation = Arrays.stream(removeAnnotations) - .anyMatch(annotationBinding.getName()::contains); - if (containsAnnotation) { - rewrite.remove(child, null); - } - } - } - for (String annotation : annotations) { - Annotation marker = ast.newMarkerAnnotation(); - marker.setTypeName(ast.newName(imports.addImport(annotation, importRewriteContext))); // $NON-NLS-1$ - rewrite.getListRewrite(declNode, - isField ? FieldDeclaration.MODIFIERS2_PROPERTY : TypeDeclaration.MODIFIERS2_PROPERTY) - .insertFirst(marker, null); - } - - return rewrite; - } - return null; - } - - - -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationAttributesQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationAttributesQuickFix.java deleted file mode 100644 index 5765c26b..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationAttributesQuickFix.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Lidia Ataupillco Ramos - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * Quickfix for adding attributes to existing annotations - * - * @author Lidia Ataupillco Ramos - * - */ -public class InsertAnnotationAttributesQuickFix extends InsertAnnotationQuickFix { - public InsertAnnotationAttributesQuickFix(String annotation, String... attributes) { - this(annotation, false, attributes); - } - - public InsertAnnotationAttributesQuickFix(String annotation, boolean generateOnlyOneCodeAction, String... attributes) { - super(annotation, generateOnlyOneCodeAction, attributes); - } - - @Override - protected String getLabel(String annotation, String... attributes) { - return Messages.getMessage("AddAtoB", attributes[0], annotation); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationMissingQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationMissingQuickFix.java deleted file mode 100644 index 6f752a41..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationMissingQuickFix.java +++ /dev/null @@ -1,140 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.NewAnnotationProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * QuickFix for inserting annotations. - * Reused from https://github.com/eclipse/lsp4mp/blob/6f2d700a88a3262e39cc2ba04beedb429e162246/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/codeaction/InsertAnnotationMissingQuickFix.java - * - * @author Angelo ZERR - * - */ -public class InsertAnnotationMissingQuickFix implements IJavaCodeActionParticipant { - - private final String[] annotations; - - private final boolean generateOnlyOneCodeAction; - - /** - * Constructor for insert annotation quick fix. - * - *

- * The participant will generate a CodeAction per annotation. - *

- * - * @param annotations list of annotation to insert. - */ - public InsertAnnotationMissingQuickFix(String... annotations) { - this(false, annotations); - } - - /** - * Constructor for insert annotation quick fix. - * - * @param generateOnlyOneCodeAction true if the participant must generate a - * CodeAction which insert the list of - * annotation and false otherwise. - * @param annotations list of annotation to insert. - */ - public InsertAnnotationMissingQuickFix(boolean generateOnlyOneCodeAction, String... annotations) { - this.generateOnlyOneCodeAction = generateOnlyOneCodeAction; - this.annotations = annotations; - } - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - if (parentType != null) { - insertAnnotations(diagnostic, context, parentType, codeActions); - } - return codeActions; - } - - protected static IBinding getBinding(ASTNode node) { - if (node.getParent() instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment = (VariableDeclarationFragment) node.getParent(); - return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); - } - if (node.getParent() instanceof MethodDeclaration) { - MethodDeclaration methodDecl = (MethodDeclaration) node.getParent(); - return methodDecl.resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } - - protected String[] getAnnotations() { - return this.annotations; - } - - protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions) throws CoreException { - if (generateOnlyOneCodeAction) { - insertAnnotation(diagnostic, context, parentType, codeActions, annotations); - } else { - for (String annotation : annotations) { - insertAnnotation(diagnostic, context, parentType, codeActions, annotation); - } - } - } - - protected static void insertAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions, String... annotations) throws CoreException { - // Insert the annotation and the proper import by using JDT Core Manipulation - // API - String name = getLabel(annotations); - ChangeCorrectionProposal proposal = new NewAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, annotations); - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - if (codeAction != null) { - codeActions.add(codeAction); - } - } - - private static String getLabel(String[] annotations) { - StringBuilder list = new StringBuilder(); - for (int i = 0; i < annotations.length; i++) { - String annotation = annotations[i]; - String annotationName = annotation.substring(annotation.lastIndexOf('.') + 1, annotation.length()); - if (i > 0) { - list.append(", "); // assume comma list is ok: @A, @B, @C - } - list.append("@"); // Java syntax - list.append(annotationName); - } - return Messages.getMessage("InsertItem", list.toString()); - } - -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationQuickFix.java deleted file mode 100644 index 37ecc01b..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/InsertAnnotationQuickFix.java +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -* Lidia Ataupillco Ramos -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.core.dom.SingleVariableDeclaration; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyAnnotationProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * Quickfix for adding new annotations with or without attributes - * - * @author Zijian Pei - * @author Lidia Ataupillco Ramos - * - */ -public class InsertAnnotationQuickFix implements IJavaCodeActionParticipant { - - private final String[] attributes; - - private final String annotation; - - protected final boolean generateOnlyOneCodeAction; - - public InsertAnnotationQuickFix(String annotation, String... attributes) { - this(annotation, false, attributes); - } - - /** - * Constructor for add missing attributes quick fix. - * - * @param generateOnlyOneCodeAction true if the participant must generate a - * CodeAction which add the list of attributes - * and false otherwise. - * @param attributes list of attributes to add. - */ - public InsertAnnotationQuickFix(String annotation, boolean generateOnlyOneCodeAction, - String... attributes) { - this.annotation = annotation; - this.generateOnlyOneCodeAction = generateOnlyOneCodeAction; - this.attributes = attributes; - } - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - - List codeActions = new ArrayList<>(); - addAttributes(diagnostic, context, parentType, codeActions, annotation); - - return codeActions; - } - - protected void addAttributes(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions, String annotation) throws CoreException { - if (generateOnlyOneCodeAction) { - addAttribute(diagnostic, context, parentType, codeActions, annotation, attributes); - } else { - for (String attribute : attributes) { - addAttribute(diagnostic, context, parentType, codeActions, annotation, attribute); - } - } - } - - /** - * use setData() API with diagnostic to pass in ElementType in diagnostic - * collector class. - * - */ - private void addAttribute(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions, String annotation, String... attributes) throws CoreException { - // Remove the modifier and the proper import by using JDT Core Manipulation - // API - ASTNode coveredNode = context.getCoveredNode().getParent(); - String name = getLabel(annotation, attributes); - ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, annotation, Arrays.asList(attributes)); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - - if (codeAction != null) { - codeActions.add(codeAction); - } - } - - protected IBinding getBinding(ASTNode node) { - // handle annotation insertions for a single variable declaration - if (node.getParent() instanceof SingleVariableDeclaration) { - return ((SingleVariableDeclaration) node.getParent()).resolveBinding(); - } - - if (node.getParent() instanceof VariableDeclarationFragment) { - return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } - - protected String getLabel(String annotation, String... attributes) { - return Messages.getMessage("InsertItem", "@" + annotation); // uses Java syntax - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveAbstractModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveAbstractModifierQuickFix.java deleted file mode 100644 index cf7b04c5..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveAbstractModifierQuickFix.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0 -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Himanshu Chotwani - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -/** - * - * Quick fix for removing abstract when it is used for method with @Inject - * - * @author Himanshu Chotwani - * - */ -public class RemoveAbstractModifierQuickFix extends RemoveModifierConflictQuickFix{ - public RemoveAbstractModifierQuickFix() { - super (false, "abstract"); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveAnnotationConflictQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveAnnotationConflictQuickFix.java deleted file mode 100644 index d923bf8b..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveAnnotationConflictQuickFix.java +++ /dev/null @@ -1,134 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.DeleteAnnotationProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * QuickFix for removing annotations. Modified from - * https://github.com/eclipse/lsp4mp/blob/6f2d700a88a3262e39cc2ba04beedb429e162246/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/codeaction/InsertAnnotationMissingQuickFix.java - * - * @author Angelo ZERR - * - */ -public class RemoveAnnotationConflictQuickFix implements IJavaCodeActionParticipant { - - private final String[] annotations; - - protected final boolean generateOnlyOneCodeAction; - - /** - * Constructor for insert annotation quick fix. - * - *

- * The participant will generate a CodeAction per annotation. - *

- * - * @param annotations list of annotation to insert. - */ - public RemoveAnnotationConflictQuickFix(String... annotations) { - this(false, annotations); - } - - /** - * Constructor for insert annotation quick fix. - * - * @param generateOnlyOneCodeAction true if the participant must generate a - * CodeAction which insert the list of - * annotation and false otherwise. - * @param annotations list of annotation to insert. - */ - public RemoveAnnotationConflictQuickFix(boolean generateOnlyOneCodeAction, String... annotations) { - this.generateOnlyOneCodeAction = generateOnlyOneCodeAction; - this.annotations = annotations; - } - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - if (parentType != null) { - removeAnnotations(diagnostic, context, parentType, codeActions); - } - return codeActions; - } - - protected void removeAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions) throws CoreException { - if (generateOnlyOneCodeAction) { - removeAnnotation(diagnostic, context, parentType, codeActions, annotations); - } else { - for (String annotation : annotations) { - removeAnnotation(diagnostic, context, parentType, codeActions, annotation); - } - } - } - - protected static void removeAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions, String... annotations) throws CoreException { - // Remove the annotation and the proper import by using JDT Core Manipulation - // API - String name = getLabel(annotations); - ChangeCorrectionProposal proposal = new DeleteAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, context.getCoveredNode().getParent(), annotations); - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - if (codeAction != null) { - codeActions.add(codeAction); - } - } - - protected IBinding getBinding(ASTNode node) { - if (node.getParent() instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment = (VariableDeclarationFragment) node.getParent(); - return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } - - protected String[] getAnnotations() { - return this.annotations; - } - - private static String getLabel(String[] annotations) { - StringBuilder list = new StringBuilder(); - for (int i = 0; i < annotations.length; i++) { - String annotation = annotations[i]; - String annotationName = annotation.substring(annotation.lastIndexOf('.') + 1, annotation.length()); - if (i > 0) { - list.append(", "); // assume comma list is ok: @A, @B, @C - } - list.append("@"); // Java syntax - list.append(annotationName); - } - return Messages.getMessage("RemoveItem", list.toString()); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveFinalModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveFinalModifierQuickFix.java deleted file mode 100644 index c40682fc..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveFinalModifierQuickFix.java +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0 -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Himanshu Chotwani - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - - -/** - * - * Quick fix for removing final when it is used for @Inject field - * - * @author Himanshu Chotwani - * - */ -public class RemoveFinalModifierQuickFix extends RemoveModifierConflictQuickFix { - - public RemoveFinalModifierQuickFix() { - super(false, "final"); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveInjectAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveInjectAnnotationQuickFix.java deleted file mode 100644 index ef02d1d0..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveInjectAnnotationQuickFix.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Himanshu Chotwani - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -/** - * - * Quick fix for removing @Inject when it is used for final field - * - * @author Himanshu Chotwani - * - */ - -public class RemoveInjectAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { - - public RemoveInjectAnnotationQuickFix() { - super(false, "jakarta.inject.Inject"); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveMethodParametersQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveMethodParametersQuickFix.java deleted file mode 100644 index 270a0414..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveMethodParametersQuickFix.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.SingleVariableDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyModifiersProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.RemoveParamsProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; -import org.eclipse.lsp4jakarta.jdt.core.annotations.AnnotationConstants; -import org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants; - -/** - * Quickfix for removing all parameters from a method - * - * @author Zijian Pei - * - */ -public class RemoveMethodParametersQuickFix implements IJavaCodeActionParticipant { - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - ASTNode node = context.getCoveredNode(); - MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); - IMethodBinding parentMethod = parentNode.resolveBinding(); - List codeActions = new ArrayList<>(); - List parameters = (List) parentNode.parameters(); - String name = Messages.getMessage("RemoveAllParameters"); - ChangeCorrectionProposal proposal = new RemoveParamsProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentMethod, 0, parameters, null); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeActions.add(codeAction); - return codeActions; - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveModifierConflictQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveModifierConflictQuickFix.java deleted file mode 100644 index c8f94ff6..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveModifierConflictQuickFix.java +++ /dev/null @@ -1,161 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Himanshu Chotwani - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyModifiersProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - - -/** - * QuickFix for removing modifiers. - * - * @author Himanshu Chotwani - * - */ -public class RemoveModifierConflictQuickFix implements IJavaCodeActionParticipant { - - private final String[] modifiers; - - protected final boolean generateOnlyOneCodeAction; - - - /** - * Constructor for remove modifier quick fix. - * - *

- * The participant will generate a CodeAction per modifier. - *

- * - * @param modifiers list of modifiers to remove. - */ - public RemoveModifierConflictQuickFix(String... modifiers) { - this(false, modifiers); - } - - /** - * Constructor for remove modifiers quick fix. - * - * @param generateOnlyOneCodeAction true if the participant must generate a - * CodeAction which remove the list of - * modifiers and false otherwise. - * @param modifiers list of modifiers to remove. - */ - public RemoveModifierConflictQuickFix(boolean generateOnlyOneCodeAction, String... modifiers) { - this.generateOnlyOneCodeAction = generateOnlyOneCodeAction; - this.modifiers = modifiers; - } - - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - - List codeActions = new ArrayList<>(); - removeModifiers(diagnostic, context, parentType, codeActions); - - return codeActions; - } - - protected void removeModifiers(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions) throws CoreException { - if (generateOnlyOneCodeAction) { - removeModifier(diagnostic, context, parentType, codeActions, modifiers); - } else { - for (String modifier : modifiers) { - removeModifier(diagnostic, context, parentType, codeActions, modifier); - } - } - } - - /** - * use setData() API with diagnostic to pass in ElementType in diagnostic collector class. - * - */ - private void removeModifier(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions, String... modifier) throws CoreException { - // Remove the modifier and the proper import by using JDT Core Manipulation - // API - ASTNode coveredNode = context.getCoveredNode().getParent(); - String label = getLabel(diagnostic, modifier); - - ModifyModifiersProposal proposal = new ModifyModifiersProposal(label, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, coveredNode, new ArrayList<>(), Arrays.asList(modifier)); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - - if (codeAction != null) { - codeActions.add(codeAction); - } - } - - private String getLabel(Diagnostic diagnostic, String... modifier) { - String label; - if (diagnostic.getData().toString().equals(String.valueOf(IJavaElement.LOCAL_VARIABLE))){ - label = Messages.getMessage("RemoveTheModifierFromThisVariable", modifier[0]); - } else if (diagnostic.getData().toString().equals(String.valueOf(IJavaElement.FIELD))) { - label = Messages.getMessage("RemoveTheModifierFromThisField", modifier[0]); - } else if (diagnostic.getData().toString().equals(String.valueOf(IJavaElement.METHOD))) { - label = Messages.getMessage("RemoveTheModifierFromThisMethod", modifier[0]); - } else if (diagnostic.getData().toString().equals(String.valueOf(IJavaElement.CLASS_FILE)) || - diagnostic.getData().toString().equals(String.valueOf(IJavaElement.TYPE))) { - label = Messages.getMessage("RemoveTheModifierFromThisClass", modifier[0]); - } else { - label = Messages.getMessage("RemoveTheModifierFromThis", modifier[0], ""); - } - return label; - } - - /** - * Removes a set of modifiers from a given ASTNode with a given code action label - */ - protected void removeModifier(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions, ASTNode coveredNode, String label, String... modifier) throws CoreException { - - ModifyModifiersProposal proposal = new ModifyModifiersProposal(label, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, coveredNode, new ArrayList<>(), Arrays.asList(modifiers)); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - - if (codeAction != null) { - codeActions.add(codeAction); - } - } - - protected IBinding getBinding(ASTNode node) { - ASTNode parentNode = node.getParent(); - if (node.getParent() instanceof VariableDeclarationFragment) { - return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); - } else if (node.getParent() instanceof MethodDeclaration) { - return ((MethodDeclaration) node.getParent()).resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveParamAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveParamAnnotationQuickFix.java deleted file mode 100644 index ff26409e..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveParamAnnotationQuickFix.java +++ /dev/null @@ -1,83 +0,0 @@ - /******************************************************************************* - * Copyright (c) 2021, 2023 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.MarkerAnnotation; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.Name; -import org.eclipse.jdt.core.dom.SingleVariableDeclaration; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * QuickFix for removing parameter annotations - */ -public class RemoveParamAnnotationQuickFix extends RemoveModifierConflictQuickFix { - - String[] annotations; - - public RemoveParamAnnotationQuickFix(String ...annotations) { - super(annotations); - this.annotations = annotations; - } - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - - ASTNode node = context.getCoveredNode(); - MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); - IMethodBinding parentMethod = parentNode.resolveBinding(); - - List codeActions = new ArrayList<>(); - - List parameters = (List) parentNode.parameters(); - - for (SingleVariableDeclaration parameter : parameters) { - - List modifiers = (List) parameter.getStructuralProperty(SingleVariableDeclaration.MODIFIERS2_PROPERTY); - ArrayList annotationsToRemove = new ArrayList<>(); - - for(ASTNode modifier : modifiers) { - Name markAnnotationTypeName = ((MarkerAnnotation) modifier).getTypeName(); - if (Arrays.asList(this.annotations).stream().anyMatch(m -> m.equals(markAnnotationTypeName.toString()))) { - annotationsToRemove.add(markAnnotationTypeName.toString()); - } - } - - StringBuilder sb = new StringBuilder(); - // Java annotations in comma delimited list, assume that is ok. - sb.append("'@").append(annotationsToRemove.get(0)).append("'"); - for (int i = 1; i < annotationsToRemove.size();i++) { - sb.append(", '@").append(annotationsToRemove.get(i)).append("'"); - } - String label = Messages.getMessage("RemoveTheModifierFromParameter", sb.toString(), parameter.getName().toString()); - - removeModifier(diagnostic, context, parentMethod, codeActions, parameter, label, - (String []) annotationsToRemove.toArray(new String[annotationsToRemove.size()])); - } - - return codeActions; - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveStaticModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveStaticModifierQuickFix.java deleted file mode 100644 index b68e5613..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/quickfix/RemoveStaticModifierQuickFix.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0 -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Himanshu Chotwani - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix; - -/** - * - * Quick fix for removing static modifier when it is used for a method - * with @Inject - * - * @author Himanshu Chotwani - * - */ -public class RemoveStaticModifierQuickFix extends RemoveModifierConflictQuickFix { - - public RemoveStaticModifierQuickFix() { - super(false, "static"); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ASTNodeUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ASTNodeUtils.java index dce60779..ebbe93a4 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ASTNodeUtils.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ASTNodeUtils.java @@ -20,19 +20,18 @@ */ public class ASTNodeUtils { - private ASTNodeUtils() { - } + private ASTNodeUtils() {} - /** - * Returns true if the given ASTNode represents an annotation, and false otherwise. - * - * @param node the ast node to check, assumed to be non-null - * @return true if the given ASTNode represents an annotation, and false otherwise - */ - public static boolean isAnnotation(ASTNode node) { - int nodeType = node.getNodeType(); - return nodeType == ASTNode.MARKER_ANNOTATION || nodeType == ASTNode.SINGLE_MEMBER_ANNOTATION - || nodeType == ASTNode.NORMAL_ANNOTATION; - } + /** + * Returns true if the given ASTNode represents an annotation, and false otherwise. + * + * @param node the ast node to check, assumed to be non-null + * @return true if the given ASTNode represents an annotation, and false otherwise + */ + public static boolean isAnnotation(ASTNode node) { + int nodeType = node.getNodeType(); + return nodeType == ASTNode.MARKER_ANNOTATION || nodeType == ASTNode.SINGLE_MEMBER_ANNOTATION + || nodeType == ASTNode.NORMAL_ANNOTATION; + } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ASTUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ASTUtils.java index f2734478..fb23178e 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ASTUtils.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ASTUtils.java @@ -28,7 +28,7 @@ public class ASTUtils { /** * Converts a given compilation unit to an ASTNode. - * + * * @param unit * @return ASTNode parsed from the compilation unit */ @@ -42,7 +42,7 @@ public static ASTNode getASTNode(ICompilationUnit unit) { /** * Given a compilation unit returns a list of all method invocations. - * + * * @param unit * @return list of method invocations */ diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/AnnotationUtil.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/AnnotationUtil.java deleted file mode 100644 index e760697b..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/AnnotationUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation, Lidia Ataupillco Ramos and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core; - -import org.eclipse.jdt.core.IAnnotatable; - -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.Collections; - -public class AnnotationUtil { - /** - * Returns the list of recognised defining annotations applied to a - * class. - * - * @param type the type representing the class - * @param scopes list of defining annotations - * @return list of recognised defining annotations applied to a class - */ - public static List getScopeAnnotations(IAnnotatable type, Set scopes) { - try { - // Construct a stream of only the annotations applied to the type that are also - // recognised annotations found in scopes. - return Arrays.stream(type.getAnnotations()).map(annotation -> annotation.getElementName()) - .filter(scopes::contains).distinct().collect(Collectors.toList()); - - } catch (Exception e) { - return Collections.emptyList(); - } - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/DiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/DiagnosticsCollector.java deleted file mode 100644 index 3c8337fb..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/DiagnosticsCollector.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020 IBM Corporation, Pengyu Xiong and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Pengyu Xiong - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core; - -import java.util.List; - -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.lsp4j.Diagnostic; - -/** - * Diagnostics Collector interface - * @author Pengyu Xiong - * - */ -public interface DiagnosticsCollector { - public void completeDiagnostic(Diagnostic diagnostic); - - public void collectDiagnostics(ICompilationUnit unit, List diagnostics); -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/IProjectLabelProvider.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/IProjectLabelProvider.java new file mode 100644 index 00000000..45836b93 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/IProjectLabelProvider.java @@ -0,0 +1,39 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core; + +import java.util.List; + +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaModelException; + +/** + * Project label provider API + * + * Based on: https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/IProjectLabelProvider.java + * + * @author dakwon + */ +public interface IProjectLabelProvider { + + /** + * Returns a list of project labels ("maven", "jakarta", etc.) for the given + * project + * + * @param project the project to get labels for + * @return a list of project labels for the given project + * @throws JavaModelException + */ + List getProjectLabels(IJavaProject project) throws JavaModelException; +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JDTServicesManager.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JDTServicesManager.java deleted file mode 100644 index 30be18ca..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JDTServicesManager.java +++ /dev/null @@ -1,532 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2019, 2022 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core; - -import java.net.URI; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.core.IBuffer; -import org.eclipse.jdt.core.IClassFile; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.ITypeRoot; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.ASTVisitor; -import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; -import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; -import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; -import org.eclipse.jdt.core.dom.BodyDeclaration; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.EnumConstantDeclaration; -import org.eclipse.jdt.core.dom.EnumDeclaration; -import org.eclipse.jdt.core.dom.FieldDeclaration; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.NodeFinder; -import org.eclipse.jdt.core.dom.RecordDeclaration; -import org.eclipse.jdt.core.dom.TypeDeclaration; -import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; -import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.jsonrpc.validation.NonNull; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextKind; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; -import org.eclipse.lsp4jakarta.jdt.codeAction.CodeActionHandler; -import org.eclipse.lsp4jakarta.jdt.core.annotations.AnnotationDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.jax_rs.Jax_RSClassDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.jax_rs.ResourceMethodDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.jsonb.JsonbDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.jsonp.JsonpDiagnosticCollector; -import org.eclipse.lsp4jakarta.jdt.core.persistence.PersistenceEntityDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.persistence.PersistenceMapKeyDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.servlet.FilterDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.servlet.ListenerDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.servlet.ServletDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.websocket.WebSocketDiagnosticsCollector; - -/** - * JDT manager for Java files Modified from - * https://github.com/eclipse/lsp4mp/blob/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/PropertiesManagerForJava.java - * with methods modified and removed to fit the purposes of the Jakarta Language - * Server - * - */ -public class JDTServicesManager { - - private List diagnosticsCollectors = new ArrayList<>(); - - private static final JDTServicesManager INSTANCE = new JDTServicesManager(); - - private final CodeActionHandler codeActionHandler; - - public static JDTServicesManager getInstance() { - return INSTANCE; - } - - private JDTServicesManager() { - diagnosticsCollectors.add(new ServletDiagnosticsCollector()); - diagnosticsCollectors.add(new AnnotationDiagnosticsCollector()); - diagnosticsCollectors.add(new FilterDiagnosticsCollector()); - diagnosticsCollectors.add(new ListenerDiagnosticsCollector()); - diagnosticsCollectors.add(new BeanValidationDiagnosticsCollector()); - diagnosticsCollectors.add(new PersistenceEntityDiagnosticsCollector()); - diagnosticsCollectors.add(new PersistenceMapKeyDiagnosticsCollector()); - diagnosticsCollectors.add(new ResourceMethodDiagnosticsCollector()); - diagnosticsCollectors.add(new Jax_RSClassDiagnosticsCollector()); - diagnosticsCollectors.add(new JsonbDiagnosticsCollector()); - diagnosticsCollectors.add(new ManagedBeanDiagnosticsCollector()); - diagnosticsCollectors.add(new DependencyInjectionDiagnosticsCollector()); - diagnosticsCollectors.add(new JsonpDiagnosticCollector()); - diagnosticsCollectors.add(new WebSocketDiagnosticsCollector()); - // comment it out as the collector is doing nothing - // diagnosticsCollectors.add(new TransactionsDiagnosticsCollector()); - this.codeActionHandler = new CodeActionHandler(); - } - - /** - * Returns diagnostics for the given uris from the JakartaDiagnosticsParams. - * - * @param javaParams the diagnostics parameters - * @return diagnostics - */ - public List getJavaDiagnostics(JakartaDiagnosticsParams javaParams) { - return getJavaDiagnostics(javaParams.getUris(), new NullProgressMonitor()); - } - - /** - * Returns diagnostics for the given uris - * - * @param uris the list of uris to collect diagnostics for - * @return diagnostics - */ - public List getJavaDiagnostics(List uris, - IProgressMonitor monitor) { - if (uris == null) { - return Collections.emptyList(); - } - - List publishDiagnostics = new ArrayList(); - for (String uri : uris) { - List diagnostics = new ArrayList<>(); - URI u = JDTUtils.toURI(uri); - ICompilationUnit unit = JDTUtils.resolveCompilationUnit(u); - for (DiagnosticsCollector d : diagnosticsCollectors) { - if (monitor.isCanceled()) { - break; - } - d.collectDiagnostics(unit, diagnostics); - } - PublishDiagnosticsParams publishDiagnostic = new PublishDiagnosticsParams(uri, diagnostics); - publishDiagnostics.add(publishDiagnostic); - if (monitor.isCanceled()) { - return Collections.emptyList(); - } - } - return publishDiagnostics; - } - - /** - * @author ankushsharma - * @brief Gets all snippet contexts that exist in the current project classpath - * @param uri - String representing file from which to derive project - * classpath - * @param snippetContext - get all the context fields from the snippets and - * check if they exist in this method - * @return List - */ - public List getExistingContextsFromClassPath(String uri, List snippetContexts) { - // Initialize the list that will hold the classpath - List classpath = new ArrayList<>(); - // Convert URI into a compilation unit - ICompilationUnit unit = JDTUtils.resolveCompilationUnit(JDTUtils.toURI(uri)); - // Get Java Project - IJavaProject project = unit.getJavaProject(); - // Get Java Project - if (project != null) { - snippetContexts.forEach(ctx -> { - IType classPathctx = null; - try { - classPathctx = project.findType(ctx); - if (classPathctx != null) { - classpath.add(ctx); - } else { - classpath.add(null); - } - } catch (JavaModelException e) { - JavaLanguageServerPlugin.logException("Failed to retrieve projectContext from JDT...", e); - classpath.add(null); - } - }); - } else { - // Populate the Array with nulls up to length of snippetContext - snippetContexts.forEach(ctx -> { - classpath.add(null); - }); - } - - // FOR NOW, append package name and class name to the list in order for LS to - // resolve ${packagename} and ${classname} variables - String className = unit.getElementName(); - if (className.endsWith(".java") == true) { - className = className.substring(0, className.length() - 5); - } - String packageName = unit.getParent() != null ? unit.getParent().getElementName() : ""; - classpath.add(packageName); - classpath.add(className); - - return classpath; - } - - public List getCodeAction(JakartaJavaCodeActionParams params, JDTUtils utils, IProgressMonitor monitor) - throws JavaModelException { - return codeActionHandler.codeAction(params, utils, monitor); - } - - /** - * Returns the cursor context for the given file and cursor position. - * - * @param params the completion params that provide the file and cursor - * position to get the context for - * @param utils the jdt utils - * @param monitor the progress monitor - * @return the cursor context for the given file and cursor position - * @throws JavaModelException when the buffer for the file cannot be accessed or - * the Java model cannot be accessed - */ - public JavaCursorContextResult javaCursorContext(JakartaJavaCompletionParams params, JDTUtils utils, - IProgressMonitor monitor) throws JavaModelException { - String uri = params.getUri(); - ITypeRoot typeRoot = resolveTypeRoot(uri, utils, monitor); - - if (typeRoot == null) { - return new JavaCursorContextResult(JavaCursorContextKind.IN_EMPTY_FILE, ""); - } - CompilationUnit ast = ASTResolving.createQuickFixAST((ICompilationUnit) typeRoot, monitor); - - JavaCursorContextKind kind = getJavaCursorContextKind(params, typeRoot, ast, utils, monitor); - String prefix = getJavaCursorPrefix(params, typeRoot, ast, utils, monitor); - - return new JavaCursorContextResult(kind, prefix); - } - - private static JavaCursorContextKind getJavaCursorContextKind(JakartaJavaCompletionParams params, - ITypeRoot typeRoot, CompilationUnit ast, JDTUtils utils, IProgressMonitor monitor) - throws JavaModelException { - - if (typeRoot.findPrimaryType() == null) { - return JavaCursorContextKind.IN_EMPTY_FILE; - } - - Position completionPosition = params.getPosition(); - int completionOffset = utils.toOffset(typeRoot.getBuffer(), completionPosition.getLine(), - completionPosition.getCharacter()); - - NodeFinder nodeFinder = new NodeFinder(ast, completionOffset, 0); - ASTNode node = nodeFinder.getCoveringNode(); - ASTNode oldNode = node; - while (node != null && (!(node instanceof AbstractTypeDeclaration) - || offsetOfFirstNonAnnotationModifier((BodyDeclaration) node) >= completionOffset)) { - if (node.getParent() != null) { - switch (node.getParent().getNodeType()) { - case ASTNode.METHOD_DECLARATION: - case ASTNode.FIELD_DECLARATION: - case ASTNode.ENUM_CONSTANT_DECLARATION: - case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: - if (!ASTNodeUtils.isAnnotation(node) && node.getStartPosition() < completionOffset) { - return JavaCursorContextKind.NONE; - } - break; - } - } - oldNode = node; - node = node.getParent(); - } - - if (node == null) { - // we are likely before or after the type root class declaration - FindWhatsBeingAnnotatedASTVisitor visitor = new FindWhatsBeingAnnotatedASTVisitor(completionOffset, false); - oldNode.accept(visitor); - switch (visitor.getAnnotatedNodeType()) { - case ASTNode.TYPE_DECLARATION: - case ASTNode.ANNOTATION_TYPE_DECLARATION: - case ASTNode.ENUM_DECLARATION: - case ASTNode.RECORD_DECLARATION: { - if (visitor.isInAnnotations()) { - return JavaCursorContextKind.IN_CLASS_ANNOTATIONS; - } - return JavaCursorContextKind.BEFORE_CLASS; - } - default: - return JavaCursorContextKind.NONE; - } - } - - AbstractTypeDeclaration typeDeclaration = (AbstractTypeDeclaration) node; - FindWhatsBeingAnnotatedASTVisitor visitor = new FindWhatsBeingAnnotatedASTVisitor(completionOffset); - typeDeclaration.accept(visitor); - switch (visitor.getAnnotatedNodeType()) { - case ASTNode.TYPE_DECLARATION: - case ASTNode.ANNOTATION_TYPE_DECLARATION: - case ASTNode.ENUM_DECLARATION: - case ASTNode.RECORD_DECLARATION: - return visitor.isInAnnotations() ? JavaCursorContextKind.IN_CLASS_ANNOTATIONS - : JavaCursorContextKind.BEFORE_CLASS; - case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: - case ASTNode.METHOD_DECLARATION: - return visitor.isInAnnotations() ? JavaCursorContextKind.IN_METHOD_ANNOTATIONS - : JavaCursorContextKind.BEFORE_METHOD; - case ASTNode.FIELD_DECLARATION: - case ASTNode.ENUM_CONSTANT_DECLARATION: - return visitor.isInAnnotations() ? JavaCursorContextKind.IN_FIELD_ANNOTATIONS - : JavaCursorContextKind.BEFORE_FIELD; - default: - return JavaCursorContextKind.IN_CLASS; - } - } - - private static @NonNull String getJavaCursorPrefix(JakartaJavaCompletionParams params, ITypeRoot typeRoot, - CompilationUnit ast, JDTUtils utils, IProgressMonitor monitor) throws JavaModelException { - Position completionPosition = params.getPosition(); - int completionOffset = utils.toOffset(typeRoot.getBuffer(), completionPosition.getLine(), - completionPosition.getCharacter()); - - String fileContents = null; - try { - IBuffer buffer = typeRoot.getBuffer(); - if (buffer == null) { - return null; - } - fileContents = buffer.getContents(); - } catch (JavaModelException e) { - return ""; - } - if (fileContents == null) { - return ""; - } - int i; - for (i = completionOffset; i > 0 && !Character.isWhitespace(fileContents.charAt(i - 1)); i--) { - } - return fileContents.substring(i, completionOffset); - } - - /** - * Given the uri returns a {@link ITypeRoot}. May return null if it can not - * associate the uri with a Java file or class file. - * - * @param uri - * @param utils JDT LS utilities - * @param monitor the progress monitor - * @return compilation unit - */ - private static ITypeRoot resolveTypeRoot(String uri, JDTUtils utils, IProgressMonitor monitor) { - utils.waitForLifecycleJobs(monitor); - final ICompilationUnit unit = utils.resolveCompilationUnit(uri); - IClassFile classFile = null; - if (unit == null) { - classFile = utils.resolveClassFile(uri); - if (classFile == null) { - return null; - } - } else { - if (!unit.getResource().exists() || monitor.isCanceled()) { - return null; - } - } - return unit != null ? unit : classFile; - } - - /** - * Searches through the AST to figure out the following: - *
    - *
  • If an annotation were to be placed at the completionOffset, what type of - * node would it be annotating?
  • - *
  • Is the completionOffset within the list of annotations before a - * member?
  • - *
- */ - private static class FindWhatsBeingAnnotatedASTVisitor extends ASTVisitor { - - private int completionOffset; - private int closest = Integer.MAX_VALUE; - private int annotatedNode = 0; - private boolean visitedParentType; - private boolean inAnnotations = false; - - public FindWhatsBeingAnnotatedASTVisitor(int completionOffset, boolean startingInParent) { - this.completionOffset = completionOffset; - this.visitedParentType = !startingInParent; - } - - public FindWhatsBeingAnnotatedASTVisitor(int completionOffset) { - this(completionOffset, true); - } - - @Override - public boolean visit(MethodDeclaration node) { - return visitNode(node); - } - - @Override - public boolean visit(FieldDeclaration node) { - return visitNode(node); - } - - @Override - public boolean visit(EnumConstantDeclaration node) { - return visitNode(node); - } - - @Override - public boolean visit(AnnotationTypeMemberDeclaration node) { - return visitNode(node); - } - - @Override - public boolean visit(TypeDeclaration node) { - return visitAbstractType(node); - } - - @Override - public boolean visit(EnumDeclaration node) { - return visitAbstractType(node); - } - - @Override - public boolean visit(AnnotationTypeDeclaration node) { - return visitAbstractType(node); - } - - @Override - public boolean visit(RecordDeclaration node) { - return visitAbstractType(node); - } - - private boolean visitAbstractType(AbstractTypeDeclaration node) { - // we need to visit the children of the first type declaration, - // since the visitor start visiting from the supplied node. - if (!visitedParentType) { - visitedParentType = true; - return true; - } - return visitNode(node); - } - - private boolean visitNode(BodyDeclaration node) { - // ignore generated nodes - if (isGenerated(node)) { - return false; - } - // consider the start of the declaration to be after the annotations - int start = node.modifiers().isEmpty() ? node.getStartPosition() : offsetOfFirstNonAnnotationModifier(node); - if (start < closest && completionOffset <= start) { - closest = node.getStartPosition(); - annotatedNode = node.getNodeType(); - inAnnotations = node.getStartPosition() < completionOffset && completionOffset <= start; - } - // We don't want to enter nested classes - return false; - } - - /** - * Returns the type of the node that an annotation placed at the completion - * offset would be annotating. - * - * @see org.eclipse.jdt.core.dom.ASTNode#getNodeType() - * @return the type of the node that an annotation placed at the completion - * offset would be annotating - */ - public int getAnnotatedNodeType() { - return annotatedNode; - } - - /** - * Returns true if the completion offset is within the list of annotations - * preceding a body declaration (field, method, class declaration) or false - * otherwise. - * - * @return true if the completion offset is within the list of annotations - * preceding a body declaration (field, method, class declaration) or - * false otherwise - */ - public boolean isInAnnotations() { - return inAnnotations; - } - - } - - private static int offsetOfFirstNonAnnotationModifier(BodyDeclaration node) { - List modifiers = node.modifiers(); - for (int i = 0; i < modifiers.size(); i++) { - ASTNode modifier = (ASTNode) modifiers.get(i); - if (!ASTNodeUtils.isAnnotation(modifier)) { - return modifier.getStartPosition(); - } - } - if (node instanceof MethodDeclaration method) { - if (method.getReturnType2() != null) { - return method.getReturnType2().getStartPosition(); - } - // package protected constructor - return method.getName().getStartPosition(); - } else if (node instanceof FieldDeclaration field) { - return field.getType().getStartPosition(); - } else { - var type = (AbstractTypeDeclaration) node; - int nameOffset = type.getName().getStartPosition(); - int keywordLength = (switch (type.getNodeType()) { - case ASTNode.TYPE_DECLARATION -> ((TypeDeclaration) type).isInterface() ? "interface" : "class"; - case ASTNode.ENUM_DECLARATION -> "enum"; - case ASTNode.ANNOTATION_TYPE_DECLARATION -> "@interface"; - case ASTNode.RECORD_DECLARATION -> "record"; - default -> ""; - }).length(); - - // HACK: this assumes the code contains one space between the keyword and the - // type name, which isn't always the case - return nameOffset - (keywordLength + 1); - } - } - - /** - * Returns true if the given node was generated by Project Lombok and false - * otherwise. - * - * @param node the node to check if it's generated - * @return true if the given node was generated by Project Lombok and false - * otherwise - */ - private static boolean isGenerated(ASTNode node) { - try { - return ((Boolean) node.getClass().getField("$isGenerated").get(node)).booleanValue(); - } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { - return false; - } - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JDTUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JDTUtils.java deleted file mode 100644 index 9f60a33c..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JDTUtils.java +++ /dev/null @@ -1,694 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016-2017 Red Hat, Inc. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Red Hat Inc. - initial API and implementation and/or initial documentation - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.function.Function; - -import org.eclipse.core.internal.utils.FileUtil; -import org.eclipse.core.resources.IContainer; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Platform; -import org.eclipse.core.runtime.URIUtil; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jdt.core.IAnnotatable; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.IBuffer; -import org.eclipse.jdt.core.IClassFile; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IField; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.ILocalVariable; -import org.eclipse.jdt.core.IMember; -import org.eclipse.jdt.core.IMemberValuePair; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IOpenable; -import org.eclipse.jdt.core.ISourceRange; -import org.eclipse.jdt.core.ISourceReference; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.ITypeParameter; -import org.eclipse.jdt.core.ITypeRoot; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.SourceRange; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.PackageDeclaration; -import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; -import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; -import org.eclipse.jdt.ls.core.internal.handlers.DocumentLifeCycleHandler; -import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.Position; -import org.eclipse.lsp4j.Range; - -import com.google.common.base.Charsets; - -/** - * This class is a copy/paste of JDT LS - * https://github.com/eclipse/eclipse.jdt.ls/blob/master/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTUtils.java - * with deletions of some unnecessary methods and modifications to logging for - * Jakarta LS project. - * - */ -public class JDTUtils { - - public static final String PATH_SEPARATOR = "/"; - public static final String PERIOD = "."; - public static final String SRC = "src"; - - public static final String FILE_UNC_PREFIX = "file:////"; - private static final String JDT_SCHEME = "jdt"; - // Code generators known to cause problems - private static Set SILENCED_CODEGENS = Collections.singleton("lombok"); - - public static final String DEFAULT_PROJECT_NAME = "jdt.java-project"; - - private static final int COMPILATION_UNIT_UPDATE_TIMEOUT = 3000; - // Percent encoding obtained from: https://en.wikipedia.org/wiki/Percent-encoding#Reserved_characters - private static final String LEVEL1_URI_REGEX = "(?:\\/(?:(?:\\{(\\w|-|%20|%21|%23|%24|%25|%26|%27|%28|%29|%2A|%2B|%2C|%2F|%3A|%3B|%3D|%3F|%40|%5B|%5D)+\\})|(?:(\\w|%20|%21|%23|%24|%25|%26|%27|%28|%29|%2A|%2B|%2C|%2F|%3A|%3B|%3D|%3F|%40|%5B|%5D)+)))*\\/?"; - - /** - * Given the uri returns a {@link ICompilationUnit}. May return null if it can - * not associate the uri with a Java file. - * - * @param uriString - * @return compilation unit - */ - public static ICompilationUnit resolveCompilationUnit(String uriString) { - return resolveCompilationUnit(toURI(uriString)); - } - - /** - * Given the uri returns a {@link ICompilationUnit}. May return null if it can - * not associate the uri with a Java file. - * - * @param uri - * @return compilation unit - */ - public static ICompilationUnit resolveCompilationUnit(URI uri) { - if (uri == null || JDT_SCHEME.equals(uri.getScheme()) || !uri.isAbsolute()) { - return null; - } - - IFile resource = (IFile) findResource(uri, ResourcesPlugin.getWorkspace().getRoot()::findFilesForLocationURI); - if (resource != null) { - if (!ProjectUtils.isJavaProject(resource.getProject())) { - return null; - } - if (resource.getFileExtension() != null) { - String name = resource.getName(); - if (JavaCore.isJavaLikeFileName(name)) { - ICompilationUnit unit = JavaCore.createCompilationUnitFrom(resource); - try { - // Give underlying resource time to catch up - // (timeout at COMPILATION_UNIT_UPDATE_TIMEOUT milliseconds). - long endTime = System.currentTimeMillis() + COMPILATION_UNIT_UPDATE_TIMEOUT; - while (!unit.isConsistent() && System.currentTimeMillis() < endTime) { - } - } catch (JavaModelException e) { - } - return unit; - } - } - return null; - } - return getFakeCompilationUnit(uri, new NullProgressMonitor()); - - } - - static ICompilationUnit getFakeCompilationUnit(URI uri, IProgressMonitor monitor) { - if (uri == null || !"file".equals(uri.getScheme()) || !uri.getPath().endsWith(".java")) { - return null; - } - java.nio.file.Path path = Paths.get(uri); - // Only support existing standalone java files - if (!Files.isReadable(path)) { - return null; - } - - IProject project = getDefaultProject(); - if (project == null || !project.isAccessible()) { - return null; - } - IJavaProject javaProject = JavaCore.create(project); - - String packageName = getPackageName(javaProject, uri); - String fileName = path.getName(path.getNameCount() - 1).toString(); - String packagePath = packageName.replace(PERIOD, PATH_SEPARATOR); - - IPath filePath = new Path(SRC).append(packagePath).append(fileName); - final IFile file = project.getFile(filePath); - if (!file.isLinked()) { - try { - createFolders(file.getParent(), monitor); - file.createLink(uri, IResource.REPLACE, monitor); - } catch (CoreException e) { - String errMsg = "Failed to create linked resource from " + uri + " to " + project.getName(); - JakartaCorePlugin.logException(errMsg, e); - } - } - if (file.isLinked()) { - return (ICompilationUnit) JavaCore.create(file, javaProject); - } - return null; - } - - public static void createFolders(IContainer folder, IProgressMonitor monitor) throws CoreException { - if (!folder.exists() && folder instanceof IFolder) { - IContainer parent = folder.getParent(); - createFolders(parent, monitor); - folder.refreshLocal(IResource.DEPTH_ZERO, monitor); - if (!folder.exists()) { - ((IFolder) folder).create(true, true, monitor); - } - } - } - - public static String getPackageName(IJavaProject javaProject, URI uri) { - try { - File file = ResourceUtils.toFile(uri); - // FIXME need to determine actual charset from file - String content = com.google.common.io.Files.toString(file, Charsets.UTF_8); - if (content.isEmpty() && javaProject != null - && DEFAULT_PROJECT_NAME.equals(javaProject.getProject().getName())) { - java.nio.file.Path path = Paths.get(uri); - java.nio.file.Path parent = path; - while (parent.getParent() != null && parent.getParent().getNameCount() > 0) { - parent = parent.getParent(); - String name = parent.getName(parent.getNameCount() - 1).toString(); - if (SRC.equals(name)) { - String pathStr = path.getParent().toString(); - if (pathStr.length() > parent.toString().length()) { - pathStr = pathStr.substring(parent.toString().length() + 1); - pathStr = pathStr.replace(PATH_SEPARATOR, PERIOD); - return pathStr; - } - } - } - } else { - return getPackageName(javaProject, content); - } - } catch (IOException e) { - JakartaCorePlugin.logException("Failed to read package name from " + uri, e); - } - return ""; - } - - public static String getPackageName(IJavaProject javaProject, String fileContent) { - if (fileContent == null) { - return ""; - } - // TODO probably not the most efficient way to get the package name as this - // reads the whole file; - char[] source = fileContent.toCharArray(); - ASTParser parser = ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); - parser.setProject(javaProject); - parser.setIgnoreMethodBodies(true); - parser.setSource(source); - CompilationUnit ast = (CompilationUnit) parser.createAST(null); - PackageDeclaration pkg = ast.getPackage(); - return (pkg == null || pkg.getName() == null) ? "" : pkg.getName().getFullyQualifiedName(); - } - - /** - * Given the uri returns a {@link IClassFile}. May return null if it can not - * resolve the uri to a library. - * - * @see #toLocation(IClassFile, int, int) - * @param uri with 'jdt' scheme - * @return class file - */ - public static IClassFile resolveClassFile(String uriString) { - return resolveClassFile(toURI(uriString)); - } - - /** - * Given the uri returns a {@link IClassFile}. May return null if it can not - * resolve the uri to a library. - * - * @see #toLocation(IClassFile, int, int) - * @param uri with 'jdt' scheme - * @return class file - */ - public static IClassFile resolveClassFile(URI uri) { - if (uri != null && JDT_SCHEME.equals(uri.getScheme()) && "contents".equals(uri.getAuthority())) { - String handleId = uri.getQuery(); - IJavaElement element = JavaCore.create(handleId); - IClassFile cf = (IClassFile) element.getAncestor(IJavaElement.CLASS_FILE); - return cf; - } - return null; - } - - public static IProject getDefaultProject() { - return getWorkspaceRoot().getProject(DEFAULT_PROJECT_NAME); - } - - private static IWorkspaceRoot getWorkspaceRoot() { - return ResourcesPlugin.getWorkspace().getRoot(); - } - - public static IFile findFile(String uriString) { - return (IFile) findResource(toURI(uriString), - ResourcesPlugin.getWorkspace().getRoot()::findFilesForLocationURI); - } - - public static IResource findResource(URI uri, Function resourceFinder) { - if (uri == null || !"file".equals(uri.getScheme())) { - return null; - } - IResource[] resources = resourceFinder.apply(uri); - if (resources.length == 0) { - //On Mac, Linked resources are referenced via the "real" URI, i.e file://USERS/username/... - //instead of file://Users/username/..., so we check against that real URI. - URI realUri = FileUtil.realURI(uri); - if (!uri.equals(realUri)) { - uri = realUri; - resources = resourceFinder.apply(uri); - } - } - if (resources.length == 0 && Platform.OS_WIN32.equals(Platform.getOS()) - && uri.toString().startsWith(FILE_UNC_PREFIX)) { - String uriString = uri.toString(); - int index = uriString.indexOf(PATH_SEPARATOR, FILE_UNC_PREFIX.length()); - if (index > 0) { - String server = uriString.substring(FILE_UNC_PREFIX.length(), index); - uriString = uriString.replace(server, server.toUpperCase()); - try { - uri = new URI(uriString); - } catch (URISyntaxException e) { - // JavaLanguageServerPlugin.logException(e.getMessage(), e); - } - resources = resourceFinder.apply(uri); - } - } - switch (resources.length) { - case 0: - return null; - case 1: - return resources[0]; - default:// several candidates if a linked resource was created before the real project - // was configured - IResource resource = null; - for (IResource f : resources) { - // find closest project containing that file, in case of nested projects - if (resource == null || f.getProjectRelativePath().segmentCount() < resource.getProjectRelativePath() - .segmentCount()) { - resource = f; - } - } - return resource; - } - } - - public static URI toURI(String uriString) { - if (uriString == null || uriString.isEmpty()) { - return null; - } - try { - URI uri = new URI(uriString); - if (Platform.OS_WIN32.equals(Platform.getOS()) && URIUtil.isFileURI(uri)) { - uri = URIUtil.toFile(uri).toURI(); - } - return uri; - } catch (URISyntaxException e) { - JakartaCorePlugin.logException("Failed to resolve " + uriString, e); - return null; - } - } - - public static String toUri(IClassFile classFile) { - String packageName = classFile.getParent().getElementName(); - String jarName = classFile.getParent().getParent().getElementName(); - String uriString = null; - try { - uriString = new URI(JDT_SCHEME, "contents", PATH_SEPARATOR + jarName + PATH_SEPARATOR + packageName - + PATH_SEPARATOR + classFile.getElementName(), classFile.getHandleIdentifier(), null) - .toASCIIString(); - } catch (URISyntaxException e) { - JakartaCorePlugin.logException("Error generating URI for class ", e); - } - return uriString; - } - - public static String toUri(ITypeRoot typeRoot) { - if (typeRoot instanceof ICompilationUnit) { - return toURI((ICompilationUnit) typeRoot); - } - if (typeRoot instanceof IClassFile) { - return toUri((IClassFile) typeRoot); - } - return null; - } - - /** - * Creates a range for the given offset and length for an {@link IOpenable} - * - * @param openable - * @param offset - * @param length - * @return - * @throws JavaModelException - */ - public static Range toRange(IOpenable openable, int offset, int length) throws JavaModelException { - Range range = newRange(); - if (offset > 0 || length > 0) { - int[] loc = null; - int[] endLoc = null; - IBuffer buffer = openable.getBuffer(); - if (buffer != null) { - loc = JsonRpcHelpers.toLine(buffer, offset); - endLoc = JsonRpcHelpers.toLine(buffer, offset + length); - } - if (loc == null) { - loc = new int[2]; - } - if (endLoc == null) { - endLoc = new int[2]; - } - setPosition(range.getStart(), loc); - setPosition(range.getEnd(), endLoc); - } - return range; - } - - /** - * Creates a new {@link Range} with its start and end {@link Position}s set to - * line=0, character=0 - * - * @return a new {@link Range}; - */ - public static Range newRange() { - return new Range(new Position(), new Position()); - } - - private static void setPosition(Position position, int[] coords) { - assert coords.length == 2; - position.setLine(coords[0]); - position.setCharacter(coords[1]); - } - - /** - * Returns uri for a compilation unit - * - * @param cu - * @return - */ - public static String toURI(ICompilationUnit cu) { - return getFileURI(cu.getResource()); - } - - /** - * Returns uri for a resource - * - * @param resource - * @return - */ - public static String getFileURI(IResource resource) { - return ResourceUtils.fixURI( - resource.getRawLocationURI() == null ? resource.getLocationURI() : resource.getRawLocationURI()); - } - - public static boolean isHiddenGeneratedElement(IJavaElement element) { - // generated elements are annotated with @Generated and they need to be filtered - // out - if (element instanceof IAnnotatable) { - try { - IAnnotation[] annotations = ((IAnnotatable) element).getAnnotations(); - if (annotations.length != 0) { - for (IAnnotation annotation : annotations) { - if (isSilencedGeneratedAnnotation(annotation)) { - return true; - } - } - } - } catch (JavaModelException e) { - // ignore - } - } - return false; - } - - private static boolean isSilencedGeneratedAnnotation(IAnnotation annotation) throws JavaModelException { - if ("javax.annotation.Generated".equals(annotation.getElementName()) - || "javax.annotation.processing.Generated".equals(annotation.getElementName())) { - IMemberValuePair[] memberValuePairs = annotation.getMemberValuePairs(); - for (IMemberValuePair m : memberValuePairs) { - if ("value".equals(m.getMemberName()) && IMemberValuePair.K_STRING == m.getValueKind()) { - if (m.getValue() instanceof String) { - return SILENCED_CODEGENS.contains(m.getValue()); - } else if (m.getValue() instanceof Object[]) { - for (Object val : (Object[]) m.getValue()) { - if (SILENCED_CODEGENS.contains(val)) { - return true; - } - } - } - } - } - } - return false; - } - - /** - * Enumeration for determining the location of a Java element. Either returns - * with the name range only, or the extended source range around the name of the - * element. - */ - public enum LocationType { - /** - * This is range encapsulating only the name of the Java element. - */ - NAME_RANGE { - - @Override - ISourceRange getRange(IJavaElement element) throws JavaModelException { - return getNameRange(element); - } - - }, - /** - * The range enclosing this element not including leading/trailing whitespace - * but everything else like comments. This information is typically used to - * determine if the client's cursor is inside the element. - */ - FULL_RANGE { - - @Override - ISourceRange getRange(IJavaElement element) throws JavaModelException { - return getSourceRange(element); - } - - }; - - /* default */ abstract ISourceRange getRange(IJavaElement element) throws JavaModelException; - } - - public static Location toLocation(IJavaElement element) throws JavaModelException { - return toLocation(element, LocationType.NAME_RANGE); - } - - /** - * Creates a location for a given java element. Unlike {@link #toLocation} this - * method can be called to return with a range that contains surrounding - * comments (method body), not just the name of the Java element. Element can be - * a {@link ICompilationUnit} or {@link IClassFile} - * - * @param element - * @param type the range type. The {@link LocationType#NAME_RANGE name} or - * {@link LocationType#FULL_RANGE full} range. - * @return location or null - * @throws JavaModelException - */ - public static Location toLocation(IJavaElement element, LocationType type) throws JavaModelException { - ICompilationUnit unit = (ICompilationUnit) element.getAncestor(IJavaElement.COMPILATION_UNIT); - IClassFile cf = (IClassFile) element.getAncestor(IJavaElement.CLASS_FILE); - if (unit == null && cf == null) { - return null; - } - if (element instanceof ISourceReference) { - ISourceRange nameRange = type.getRange(element); - if (SourceRange.isAvailable(nameRange)) { - if (cf == null) { - return toLocation(unit, nameRange.getOffset(), nameRange.getLength()); - } else { - return toLocation(cf, nameRange.getOffset(), nameRange.getLength()); - } - } - } - return null; - } - - public static ISourceRange getNameRange(IJavaElement element) throws JavaModelException { - ISourceRange nameRange = null; - if (element instanceof IMember) { - IMember member = (IMember) element; - nameRange = member.getNameRange(); - if ((!SourceRange.isAvailable(nameRange))) { - nameRange = member.getSourceRange(); - } - } else if (element instanceof ITypeParameter || element instanceof ILocalVariable) { - nameRange = ((ISourceReference) element).getNameRange(); - } else if (element instanceof ISourceReference) { - nameRange = ((ISourceReference) element).getSourceRange(); - } - if (!SourceRange.isAvailable(nameRange) && element.getParent() != null) { - nameRange = getNameRange(element.getParent()); - } - return nameRange; - } - - private static ISourceRange getSourceRange(IJavaElement element) throws JavaModelException { - ISourceRange sourceRange = null; - if (element instanceof IMember) { - IMember member = (IMember) element; - sourceRange = member.getSourceRange(); - } else if (element instanceof ITypeParameter || element instanceof ILocalVariable) { - sourceRange = ((ISourceReference) element).getSourceRange(); - } else if (element instanceof ISourceReference) { - sourceRange = ((ISourceReference) element).getSourceRange(); - } - if (!SourceRange.isAvailable(sourceRange) && element.getParent() != null) { - sourceRange = getSourceRange(element.getParent()); - } - return sourceRange; - } - - /** - * Creates location to the given offset and length for the compilation unit - * - * @param unit - * @param offset - * @param length - * @return location or null - * @throws JavaModelException - */ - public static Location toLocation(ICompilationUnit unit, int offset, int length) throws JavaModelException { - return new Location(ResourceUtils.toClientUri(toURI(unit)), toRange(unit, offset, length)); - } - - /** - * Creates a default location for the class file. - * - * @param classFile - * @return location - * @throws JavaModelException - */ - public static Location toLocation(IClassFile classFile) throws JavaModelException { - return toLocation(classFile, 0, 0); - } - - /** - * Creates location to the given offset and length for the class file. - * - * @param unit - * @param offset - * @param length - * @return location - * @throws JavaModelException - */ - public static Location toLocation(IClassFile classFile, int offset, int length) throws JavaModelException { - String uriString = toUri(classFile); - if (uriString != null) { - Range range = toRange(classFile, offset, length); - return new Location(uriString, range); - } - return null; - } - - /** - * Check if a URI starts with a leading slash. - * - * @param uriString - * @return boolean - */ - public static boolean hasLeadingSlash(String uriString) { - return uriString.startsWith("/"); - } - - /** - * Check if a URI follows a valid URI-template (level-1) specified by - * RFC 6570. - * - * @param uriString - * @return boolean - */ - public static boolean isValidLevel1URI(String uriString) { - return uriString.matches(LEVEL1_URI_REGEX); - } - - /** - * Returns a list of all accessors (getter and setter) of the given field. - * Note that for boolean fields the accessor of the form "isField" is retuned - * "getField" is not present. - * - * @param unit the compilation unit the field belongs to - * @param field the accesors of this field are returned - * @return a list of accessor methods - * @throws JavaModelException - */ - public static List getFieldAccessors(ICompilationUnit unit, IField field) throws JavaModelException { - List accessors = new ArrayList(); - String fieldName = field.getElementName(); - fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); - List accessorNames = new ArrayList(); - accessorNames.add("get" + fieldName); - accessorNames.add("set" + fieldName); - accessorNames.add("is" + fieldName); - - for (IType type : unit.getAllTypes()) { - for (IMethod method : type.getMethods()) { - String methodName = method.getElementName(); - if (accessorNames.contains(methodName)) - accessors.add(method); - } - } - return accessors; - } - - public static void waitForLifecycleJobs(IProgressMonitor monitor) { - try { - Job.getJobManager().join(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor); - } catch (OperationCanceledException ignorable) { - // No need to pollute logs when query is cancelled - } catch (Exception e) { - JavaLanguageServerPlugin.logException(e.getMessage(), e); - } - } - - public static int toOffset(IBuffer buffer, int line, int column) { - return JsonRpcHelpers.toOffset(buffer, line, column); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JakartaCorePlugin.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JakartaCorePlugin.java index 0a4e59a0..115431dd 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JakartaCorePlugin.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JakartaCorePlugin.java @@ -32,38 +32,38 @@ public class JakartaCorePlugin implements BundleActivator { public void start(BundleContext context) throws Exception { // super.start(context); - plugin = this; - } + plugin = this; + } - public void stop(BundleContext context) throws Exception { - plugin = null; + public void stop(BundleContext context) throws Exception { + plugin = null; // super.stop(context); - } + } - /** - * Returns the shared instance - * - * @return the shared instance - */ - public static JakartaCorePlugin getDefault() { - return plugin; - } + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static JakartaCorePlugin getDefault() { + return plugin; + } - /** - * Add the given Jakarta properties changed listener. - * - * @param listener the listener to add - */ - public static void log(IStatus status) { + /** + * Add the given Jakarta properties changed listener. + * + * @param listener the listener to add + */ + public static void log(IStatus status) { // getDefault().getLog().log(status); } - /** - * Remove the given Jakarta properties changed listener. - * - * @param listener the listener to remove - */ - public static void logException(String errMsg, Throwable ex) { + /** + * Remove the given Jakarta properties changed listener. + * + * @param listener the listener to remove + */ + public static void logException(String errMsg, Throwable ex) { // getDefault().getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, errMsg, ex)); } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JsonRpcHelpers.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JsonRpcHelpers.java index c83679c6..819bd4bb 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JsonRpcHelpers.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JsonRpcHelpers.java @@ -29,7 +29,7 @@ public class JsonRpcHelpers { /** * Convert line, column to a document offset. - * + * * @param buffer * @param line * @param column @@ -44,7 +44,7 @@ public static int toOffset(IBuffer buffer, int line, int column) { /** * Convert line, column to a document offset. - * + * * @param document * @param line * @param column @@ -54,14 +54,14 @@ public static int toOffset(IDocument document, int line, int column) { try { return document.getLineOffset(line) + column; } catch (BadLocationException e) { - JakartaCorePlugin.logException(e.getMessage(), e); + JakartaCorePlugin.logException(e.getMessage(), e); } return -1; } /** * Convert offset to line number and column. - * + * * @param buffer * @param line * @param column @@ -84,7 +84,7 @@ public static int[] toLine(IDocument document, int offset) { int column = offset - document.getLineOffset(line); return new int[] { line, column }; } catch (BadLocationException e) { - JakartaCorePlugin.logException(e.getMessage(), e); + JakartaCorePlugin.logException(e.getMessage(), e); } return null; } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ProjectLabelDefinition.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ProjectLabelDefinition.java new file mode 100644 index 00000000..afcd1d2c --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ProjectLabelDefinition.java @@ -0,0 +1,61 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core; + +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaModelException; + +/** + * Wrapper class around IProjectLabelProvider + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/ProjectLabelDefinition.java + */ +public class ProjectLabelDefinition { + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(ProjectLabelDefinition.class.getName()); + + /** Project label provider instance. */ + private final IProjectLabelProvider projectLabelProvider; + + /** + * Constructor. + * + * @param projectLabelProvider the project label provider instance. + */ + public ProjectLabelDefinition(IProjectLabelProvider projectLabelProvider) { + this.projectLabelProvider = projectLabelProvider; + } + + /** + * Returns a list of project labels ("maven", "jakarta", etc.) for the + * given project + * + * @param project the Java project + * @return a list of project labels for the given project + */ + public List getProjectLabels(IJavaProject project) { + try { + return projectLabelProvider.getProjectLabels(project); + } catch (JavaModelException e) { + LOGGER.log(Level.SEVERE, "Error while getting project labels", e); + return Collections.emptyList(); + } + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ProjectLabelManager.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ProjectLabelManager.java new file mode 100644 index 00000000..2f3525ad --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ProjectLabelManager.java @@ -0,0 +1,152 @@ +/******************************************************************************* +* Copyright (c) 2019-2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.lsp4jakarta.commons.JakartaJavaProjectLabelsParams; +import org.eclipse.lsp4jakarta.commons.ProjectLabelInfoEntry; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.JDTJakartaUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.JDTTypeUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ProjectLabelRegistry; + +/** + * Project label manager which provides ProjectLabelInfo containing + * project labels for all projects in the workspace + * + */ +public class ProjectLabelManager { + private static final ProjectLabelManager INSTANCE = new ProjectLabelManager(); + + public static ProjectLabelManager getInstance() { + return INSTANCE; + } + + private ProjectLabelManager() { + + } + + /** + * Returns project label results for all projects in the workspace + * + * @return project label results for all projects in the workspace + */ + public List getProjectLabelInfo() { + List results = new ArrayList<>(); + IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + + for (IProject project : projects) { + ProjectLabelInfoEntry info = getProjectLabelInfo(project, null); + if (info != null) { + results.add(info); + } + } + return results; + } + + /** + * Returns project label results for the given Eclipse project. + * + * @param project Eclipse project. + * @param types the Java type list to check. + * @return project label results for the given Eclipse project. + */ + private ProjectLabelInfoEntry getProjectLabelInfo(IProject project, List types) { + String uri = JDTJakartaUtils.getProjectURI(project); + if (uri != null) { + return new ProjectLabelInfoEntry(uri, project.getName(), getProjectLabels(project, types)); + } + return null; + } + + /** + * Returns project label results for the given Java file uri parameter. + * + * @param params the Java file uri parameter. + * @param utils the JDT utilities. + * @param monitor the progress monitor. + * @return project label results for the given Java file uri parameter. + */ + public ProjectLabelInfoEntry getProjectLabelInfo(JakartaJavaProjectLabelsParams params, IJDTUtils utils, + IProgressMonitor monitor) { + IProject project = findProject(params.getUri(), utils); + if (project == null) { + // The uri doesn't belong to an Eclipse project + return ProjectLabelInfoEntry.EMPTY_PROJECT_INFO; + } + return getProjectLabelInfo(project, params.getTypes()); + } + + private static IProject findProject(String uri, IJDTUtils utils) { + if (uri.startsWith("jdt://jarentry")) { + URI jarEntryUri = URI.create(uri); + String rootId = jarEntryUri.getQuery(); + IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) JavaCore.create(rootId); + if (packageRoot != null) { + IJavaProject javaProject = packageRoot.getJavaProject(); + return javaProject != null ? javaProject.getProject() : null; + } + return null; + } + IFile file = utils.findFile(uri); + return file != null ? file.getProject() : null; + } + + /** + * Returns the project labels for the given project. + * + * @param project the Eclipse project. + * @param types the Java type list to check. + * @return the project labels for the given project. + */ + private List getProjectLabels(IProject project, List types) { + IJavaProject javaProject = JavaCore.create(project); + + if (javaProject == null) { + return Collections.emptyList(); + } + + // Update labels by using the + // "org.eclipse.lsp4mp.jdt.core.projectLabelProviders" extension point (ex + // : "maven", "gradle", "quarkus", "jakarta"). + List projectLabels = new ArrayList<>(); + List definitions = ProjectLabelRegistry.getInstance().getProjectLabelDefinitions(); + for (ProjectLabelDefinition definition : definitions) { + projectLabels.addAll(definition.getProjectLabels(javaProject)); + } + // Update labels by checking if some Java types are in the classpath of the Java + // project. + if (types != null) { + for (String type : types) { + if (JDTTypeUtils.findType(javaProject, type) != null) { + projectLabels.add(type); + } + } + } + + return projectLabels; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/PropertiesManagerForJava.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/PropertiesManagerForJava.java new file mode 100644 index 00000000..fd925245 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/PropertiesManagerForJava.java @@ -0,0 +1,553 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; +import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; +import org.eclipse.jdt.core.dom.BodyDeclaration; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.EnumConstantDeclaration; +import org.eclipse.jdt.core.dom.EnumDeclaration; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.NodeFinder; +import org.eclipse.jdt.core.dom.RecordDeclaration; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.CompletionList; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; +import org.eclipse.lsp4jakarta.commons.DocumentFormat; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsSettings; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfo; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfoParams; +import org.eclipse.lsp4jakarta.commons.JavaCursorContextKind; +import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; +import org.eclipse.lsp4jakarta.jdt.core.java.completion.JavaCompletionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.JavaFeaturesRegistry; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.codeaction.CodeActionHandler; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.completion.JavaCompletionDefinition; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.diagnostics.JavaDiagnosticsDefinition; + +/** + * JDT Jakarta manager for Java files. + * + * Based on: https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/PropertiesManagerForJava.java + * + * @author Angelo ZERR + * + */ +public class PropertiesManagerForJava { + + private static final PropertiesManagerForJava INSTANCE = new PropertiesManagerForJava(); + + private final CodeActionHandler codeActionHandler; + + public static PropertiesManagerForJava getInstance() { + return INSTANCE; + } + + private PropertiesManagerForJava() { + this.codeActionHandler = new CodeActionHandler(); + } + + /** + * Returns the CompletionItems given the completion item params + * + * @param params the completion item params + * @param utils the IJDTUtils + * @param monitor the progress monitors + * @return the CompletionItems for the given the completion item params + * @throws JavaModelException + */ + public CompletionList completion(JakartaJavaCompletionParams params, IJDTUtils utils, IProgressMonitor monitor) throws JavaModelException { + String uri = params.getUri(); + ITypeRoot typeRoot = resolveTypeRoot(uri, utils, monitor); + if (typeRoot == null) { + return null; + } + + Position completionPosition = params.getPosition(); + int completionOffset = utils.toOffset(typeRoot.getBuffer(), completionPosition.getLine(), + completionPosition.getCharacter()); + + List completionItems = new ArrayList<>(); + JavaCompletionContext completionContext = new JavaCompletionContext(uri, typeRoot, utils, completionOffset); + + List completions = JavaFeaturesRegistry.getInstance().getJavaCompletionDefinitions().stream().filter(completion -> completion.isAdaptedForCompletion(completionContext, + monitor)).collect(Collectors.toList()); + + if (completions.isEmpty()) { + return null; + } + + completions.forEach(completion -> { + List collectedCompletionItems = completion.collectCompletionItems(completionContext, monitor); + if (collectedCompletionItems != null) { + completionItems.addAll(collectedCompletionItems); + } + }); + + if (monitor.isCanceled()) { + return null; + } + CompletionList completionList = new CompletionList(); + completionList.setItems(completionItems); + return completionList; + } + + /** + * Returns the cursor context for the given file and cursor position. + * + * @param params the completion params that provide the file and cursor + * position to get the context for + * @param utils the jdt utils + * @param monitor the progress monitor + * @return the cursor context for the given file and cursor position + * @throws JavaModelException when the buffer for the file cannot be accessed or + * the Java model cannot be accessed + */ + public JavaCursorContextResult javaCursorContext(JakartaJavaCompletionParams params, IJDTUtils utils, + IProgressMonitor monitor) throws JavaModelException { + String uri = params.getUri(); + ITypeRoot typeRoot = resolveTypeRoot(uri, utils, monitor); + + if (typeRoot == null) { + return new JavaCursorContextResult(JavaCursorContextKind.IN_EMPTY_FILE, ""); + } + CompilationUnit ast = ASTResolving.createQuickFixAST((ICompilationUnit) typeRoot, monitor); + + JavaCursorContextKind kind = getJavaCursorContextKind(params, typeRoot, ast, utils, monitor); + String prefix = getJavaCursorPrefix(params, typeRoot, ast, utils, monitor); + + return new JavaCursorContextResult(kind, prefix); + } + + private static JavaCursorContextKind getJavaCursorContextKind(JakartaJavaCompletionParams params, + ITypeRoot typeRoot, CompilationUnit ast, IJDTUtils utils, IProgressMonitor monitor) throws JavaModelException { + + if (typeRoot.findPrimaryType() == null) { + return JavaCursorContextKind.IN_EMPTY_FILE; + } + + Position completionPosition = params.getPosition(); + int completionOffset = utils.toOffset(typeRoot.getBuffer(), completionPosition.getLine(), + completionPosition.getCharacter()); + + NodeFinder nodeFinder = new NodeFinder(ast, completionOffset, 0); + ASTNode node = nodeFinder.getCoveringNode(); + ASTNode oldNode = node; + while (node != null && (!(node instanceof AbstractTypeDeclaration) + || offsetOfFirstNonAnnotationModifier((BodyDeclaration) node) >= completionOffset)) { + if (node.getParent() != null) { + switch (node.getParent().getNodeType()) { + case ASTNode.METHOD_DECLARATION: + case ASTNode.FIELD_DECLARATION: + case ASTNode.ENUM_CONSTANT_DECLARATION: + case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: + if (!ASTNodeUtils.isAnnotation(node) && node.getStartPosition() < completionOffset) { + return JavaCursorContextKind.NONE; + } + break; + } + } + oldNode = node; + node = node.getParent(); + } + + if (node == null) { + // we are likely before or after the type root class declaration + FindWhatsBeingAnnotatedASTVisitor visitor = new FindWhatsBeingAnnotatedASTVisitor(completionOffset, false); + oldNode.accept(visitor); + switch (visitor.getAnnotatedNodeType()) { + case ASTNode.TYPE_DECLARATION: + case ASTNode.ANNOTATION_TYPE_DECLARATION: + case ASTNode.ENUM_DECLARATION: + case ASTNode.RECORD_DECLARATION: { + if (visitor.isInAnnotations()) { + return JavaCursorContextKind.IN_CLASS_ANNOTATIONS; + } + return JavaCursorContextKind.BEFORE_CLASS; + } + default: + return JavaCursorContextKind.NONE; + } + } + + AbstractTypeDeclaration typeDeclaration = (AbstractTypeDeclaration) node; + FindWhatsBeingAnnotatedASTVisitor visitor = new FindWhatsBeingAnnotatedASTVisitor(completionOffset); + typeDeclaration.accept(visitor); + switch (visitor.getAnnotatedNodeType()) { + case ASTNode.TYPE_DECLARATION: + case ASTNode.ANNOTATION_TYPE_DECLARATION: + case ASTNode.ENUM_DECLARATION: + case ASTNode.RECORD_DECLARATION: + return visitor.isInAnnotations() ? JavaCursorContextKind.IN_CLASS_ANNOTATIONS : JavaCursorContextKind.BEFORE_CLASS; + case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: + case ASTNode.METHOD_DECLARATION: + return visitor.isInAnnotations() ? JavaCursorContextKind.IN_METHOD_ANNOTATIONS : JavaCursorContextKind.BEFORE_METHOD; + case ASTNode.FIELD_DECLARATION: + case ASTNode.ENUM_CONSTANT_DECLARATION: + return visitor.isInAnnotations() ? JavaCursorContextKind.IN_FIELD_ANNOTATIONS : JavaCursorContextKind.BEFORE_FIELD; + default: + return JavaCursorContextKind.IN_CLASS; + } + } + + private static @NonNull String getJavaCursorPrefix(JakartaJavaCompletionParams params, ITypeRoot typeRoot, + CompilationUnit ast, IJDTUtils utils, IProgressMonitor monitor) throws JavaModelException { + Position completionPosition = params.getPosition(); + int completionOffset = utils.toOffset(typeRoot.getBuffer(), completionPosition.getLine(), + completionPosition.getCharacter()); + + String fileContents = null; + try { + IBuffer buffer = typeRoot.getBuffer(); + if (buffer == null) { + return null; + } + fileContents = buffer.getContents(); + } catch (JavaModelException e) { + return ""; + } + if (fileContents == null) { + return ""; + } + int i; + for (i = completionOffset; i > 0 && !Character.isWhitespace(fileContents.charAt(i - 1)); i--) { + } + return fileContents.substring(i, completionOffset); + } + + /** + * Given the uri returns a {@link ITypeRoot}. May return null if it can not + * associate the uri with a Java file or class file. + * + * @param uri + * @param utils JDT LS utilities + * @param monitor the progress monitor + * @return compilation unit + */ + private static ITypeRoot resolveTypeRoot(String uri, IJDTUtils utils, IProgressMonitor monitor) { + utils.waitForLifecycleJobs(monitor); + final ICompilationUnit unit = utils.resolveCompilationUnit(uri); + IClassFile classFile = null; + if (unit == null) { + classFile = utils.resolveClassFile(uri); + if (classFile == null) { + return null; + } + } else { + if (!unit.getResource().exists() || monitor.isCanceled()) { + return null; + } + } + return unit != null ? unit : classFile; + } + + /** + * Returns the codeAction list according the given codeAction parameters. + * + * @param params the codeAction parameters + * @param utils the utilities class + * @param monitor the monitor + * @return the codeAction list according the given codeAction parameters. + * @throws JavaModelException + */ + public List codeAction(JakartaJavaCodeActionParams params, IJDTUtils utils, + IProgressMonitor monitor) throws JavaModelException { + return codeActionHandler.codeAction(params, utils, monitor); + } + + /** + * Returns the codeAction list according the given codeAction parameters. + * + * @param unresolved the CodeAction to resolve + * @param utils the utilities class + * @param monitor the monitor + * @return the codeAction list according the given codeAction parameters. + * @throws JavaModelException + */ + public CodeAction resolveCodeAction(CodeAction unresolved, IJDTUtils utils, IProgressMonitor monitor) throws JavaModelException { + return codeActionHandler.resolveCodeAction(unresolved, utils, monitor); + } + + /** + * Returns diagnostics for the given uris list. + * + * @param params the diagnostics parameters + * @param utils the utilities class + * @return diagnostics for the given uris list. + * @throws JavaModelException + */ + public List diagnostics(JakartaJavaDiagnosticsParams params, IJDTUtils utils, + IProgressMonitor monitor) throws JavaModelException { + List uris = params.getUris(); + if (uris == null) { + return Collections.emptyList(); + } + DocumentFormat documentFormat = params.getDocumentFormat(); + List publishDiagnostics = new ArrayList(); + for (String uri : uris) { + List diagnostics = new ArrayList<>(); + PublishDiagnosticsParams publishDiagnostic = new PublishDiagnosticsParams(uri, diagnostics); + publishDiagnostics.add(publishDiagnostic); + collectDiagnostics(uri, utils, documentFormat, params.getSettings(), diagnostics, monitor); + } + if (monitor.isCanceled()) { + return Collections.emptyList(); + } + return publishDiagnostics; + } + + private void collectDiagnostics(String uri, IJDTUtils utils, DocumentFormat documentFormat, + JakartaJavaDiagnosticsSettings settings, List diagnostics, IProgressMonitor monitor) { + ITypeRoot typeRoot = resolveTypeRoot(uri, utils, monitor); + if (typeRoot == null) { + return; + } + + // Collect all adapted diagnostics participant + JavaDiagnosticsContext context = new JavaDiagnosticsContext(uri, typeRoot, utils, documentFormat, settings); + List definitions = JavaFeaturesRegistry.getInstance().getJavaDiagnosticsDefinitions().stream().filter(definition -> definition.isAdaptedForDiagnostics(context, + monitor)).collect(Collectors.toList()); + if (definitions.isEmpty()) { + return; + } + + // Begin, collect, end participants + definitions.forEach(definition -> definition.beginDiagnostics(context, monitor)); + definitions.forEach(definition -> { + List collectedDiagnostics = definition.collectDiagnostics(context, monitor); + if (collectedDiagnostics != null && !collectedDiagnostics.isEmpty()) { + diagnostics.addAll(collectedDiagnostics); + } + }); + definitions.forEach(definition -> definition.endDiagnostics(context, monitor)); + } + + /** + * Searches through the AST to figure out the following: + *
    + *
  • If an annotation were to be placed at the completionOffset, what type of + * node would it be annotating?
  • + *
  • Is the completionOffset within the list of annotations before a + * member?
  • + *
+ */ + private static class FindWhatsBeingAnnotatedASTVisitor extends ASTVisitor { + + private int completionOffset; + private int closest = Integer.MAX_VALUE; + private int annotatedNode = 0; + private boolean visitedParentType; + private boolean inAnnotations = false; + + public FindWhatsBeingAnnotatedASTVisitor(int completionOffset, boolean startingInParent) { + this.completionOffset = completionOffset; + this.visitedParentType = !startingInParent; + } + + public FindWhatsBeingAnnotatedASTVisitor(int completionOffset) { + this(completionOffset, true); + } + + @Override + public boolean visit(MethodDeclaration node) { + return visitNode(node); + } + + @Override + public boolean visit(FieldDeclaration node) { + return visitNode(node); + } + + @Override + public boolean visit(EnumConstantDeclaration node) { + return visitNode(node); + } + + @Override + public boolean visit(AnnotationTypeMemberDeclaration node) { + return visitNode(node); + } + + @Override + public boolean visit(TypeDeclaration node) { + return visitAbstractType(node); + } + + @Override + public boolean visit(EnumDeclaration node) { + return visitAbstractType(node); + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + return visitAbstractType(node); + } + + @Override + public boolean visit(RecordDeclaration node) { + return visitAbstractType(node); + } + + private boolean visitAbstractType(AbstractTypeDeclaration node) { + // we need to visit the children of the first type declaration, + // since the visitor start visiting from the supplied node. + if (!visitedParentType) { + visitedParentType = true; + return true; + } + return visitNode(node); + } + + private boolean visitNode(BodyDeclaration node) { + // ignore generated nodes + if (isGenerated(node)) { + return false; + } + // consider the start of the declaration to be after the annotations + int start = node.modifiers().isEmpty() ? node.getStartPosition() : offsetOfFirstNonAnnotationModifier(node); + if (start < closest && completionOffset <= start) { + closest = node.getStartPosition(); + annotatedNode = node.getNodeType(); + inAnnotations = node.getStartPosition() < completionOffset && completionOffset <= start; + } + // We don't want to enter nested classes + return false; + } + + /** + * Returns the type of the node that an annotation placed at the completion + * offset would be annotating. + * + * @see org.eclipse.jdt.core.dom.ASTNode#getNodeType() + * @return the type of the node that an annotation placed at the completion + * offset would be annotating + */ + public int getAnnotatedNodeType() { + return annotatedNode; + } + + /** + * Returns true if the completion offset is within the list of annotations + * preceding a body declaration (field, method, class declaration) or false + * otherwise. + * + * @return true if the completion offset is within the list of annotations + * preceding a body declaration (field, method, class declaration) or + * false otherwise + */ + public boolean isInAnnotations() { + return inAnnotations; + } + + } + + private static int offsetOfFirstNonAnnotationModifier(BodyDeclaration node) { + List modifiers = node.modifiers(); + for (int i = 0; i < modifiers.size(); i++) { + ASTNode modifier = (ASTNode) modifiers.get(i); + if (!ASTNodeUtils.isAnnotation(modifier)) { + return modifier.getStartPosition(); + } + } + if (node instanceof MethodDeclaration method) { + if (method.getReturnType2() != null) { + return method.getReturnType2().getStartPosition(); + } + // package protected constructor + return method.getName().getStartPosition(); + } else if (node instanceof FieldDeclaration field) { + return field.getType().getStartPosition(); + } else { + var type = (AbstractTypeDeclaration) node; + int nameOffset = type.getName().getStartPosition(); + int keywordLength = (switch (type.getNodeType()) { + case ASTNode.TYPE_DECLARATION -> ((TypeDeclaration) type).isInterface() ? "interface" : "class"; + case ASTNode.ENUM_DECLARATION -> "enum"; + case ASTNode.ANNOTATION_TYPE_DECLARATION -> "@interface"; + case ASTNode.RECORD_DECLARATION -> "record"; + default -> ""; + }).length(); + + // HACK: this assumes the code contains one space between the keyword and the + // type name, which isn't always the case + return nameOffset - (keywordLength + 1); + } + } + + /** + * Returns true if the given node was generated by Project Lombok and false + * otherwise. + * + * @param node the node to check if it's generated + * @return true if the given node was generated by Project Lombok and false + * otherwise + */ + private static boolean isGenerated(ASTNode node) { + try { + return ((Boolean) node.getClass().getField("$isGenerated").get(node)).booleanValue(); + } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { + return false; + } + } + + /** + * Returns the Java file information (ex : package name) from the given file URI + * and null otherwise. + * + * @param params the file information parameters. + * @param utils the utilities class + * @param monitor the monitor + * @return the Java file information (ex : package name) from the given file URI + * and null otherwise. + */ + public JakartaJavaFileInfo fileInfo(JakartaJavaFileInfoParams params, IJDTUtils utils, IProgressMonitor monitor) { + String uri = params.getUri(); + final ICompilationUnit unit = utils.resolveCompilationUnit(uri); + if (unit != null && unit.exists()) { + JakartaJavaFileInfo fileInfo = new JakartaJavaFileInfo(); + String packageName = unit.getParent() != null ? unit.getParent().getElementName() : ""; + fileInfo.setPackageName(packageName); + return fileInfo; + } + return null; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AddResourceMissingNameQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AddResourceMissingNameQuickFix.java deleted file mode 100644 index d35083f7..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AddResourceMissingNameQuickFix.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.annotations; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.InsertAnnotationAttributesQuickFix; - -/** - * Quickfix for adding missing name to @Resource - * - * @author Zijian Pei - * - */ -public class AddResourceMissingNameQuickFix extends InsertAnnotationAttributesQuickFix { - - public AddResourceMissingNameQuickFix() { - super("jakarta.annotation.Resource", false, "name"); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AddResourceMissingTypeQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AddResourceMissingTypeQuickFix.java deleted file mode 100644 index 4c26c472..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AddResourceMissingTypeQuickFix.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.annotations; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.InsertAnnotationAttributesQuickFix; - -/** - * Quickfix for adding missing type to @Resource - * - * - * @author Zijian Pei - * - */ -public class AddResourceMissingTypeQuickFix extends InsertAnnotationAttributesQuickFix { - - public AddResourceMissingTypeQuickFix() { - super("jakarta.annotation.Resource", false, "type"); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AnnotationDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AnnotationDiagnosticsCollector.java deleted file mode 100644 index 3cd3c4b1..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AnnotationDiagnosticsCollector.java +++ /dev/null @@ -1,254 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.annotations; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IAnnotatable; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IField; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.ILocalVariable; -import org.eclipse.jdt.core.IMemberValuePair; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IPackageDeclaration; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4j.jsonrpc.messages.Tuple; -import org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * - * jararta.annotation Diagnostics - * - *
  • Diagnostic 1: @Generated 'date' attribute does not follow ISO 8601.
  • - *
  • Diagnostic 2: @Resource 'name' attribute missing (when annotation is used - * on a class).
  • - *
  • Diagnostic 3: @Resource 'type' attribute missing (when annotation is used - * on a class).
  • - *
  • Diagnostic 4: @PostConstruct method has parameters.
  • - *
  • Diagnostic 5: @PostConstruct method is not void.
  • - *
  • Diagnostic 6: @PostConstruct method throws checked exception(s).
  • - *
  • Diagnostic 7: @PreDestroy method has parameters.
  • - *
  • Diagnostic 8: @PreDestroy method is static.
  • - *
  • Diagnostic 9: @PreDestroy method throws checked exception(s).
  • - * - * @see https://jakarta.ee/specifications/annotations/2.0/annotations-spec-2.0.html#annotations - * - */ -public class AnnotationDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public AnnotationDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return AnnotationConstants.DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit != null) { - try { - ArrayList> annotatables = new ArrayList>(); - String[] validAnnotations = { AnnotationConstants.GENERATED_FQ_NAME }; - String[] validTypeAnnotations = { AnnotationConstants.GENERATED_FQ_NAME, - AnnotationConstants.RESOURCE_FQ_NAME }; - String[] validMethodAnnotations = { AnnotationConstants.GENERATED_FQ_NAME, - AnnotationConstants.POST_CONSTRUCT_FQ_NAME, AnnotationConstants.PRE_DESTROY_FQ_NAME, - AnnotationConstants.RESOURCE_FQ_NAME }; - - IPackageDeclaration[] packages = unit.getPackageDeclarations(); - for (IPackageDeclaration p : packages) { - IAnnotation[] annotations = p.getAnnotations(); - for (IAnnotation annotation : annotations) { - if (isValidAnnotation(annotation.getElementName(), validAnnotations)) - annotatables.add(new Tuple.Two<>(annotation, p)); - } - } - - IType[] types = unit.getAllTypes(); - for (IType type : types) { - // Type - IAnnotation[] annotations = type.getAnnotations(); - for (IAnnotation annotation : annotations) { - if (isValidAnnotation(annotation.getElementName(), validTypeAnnotations)) - annotatables.add(new Tuple.Two<>(annotation, type)); - } - // Method - IMethod[] methods = type.getMethods(); - for (IMethod method : methods) { - annotations = method.getAnnotations(); - for (IAnnotation annotation : annotations) { - if (isValidAnnotation(annotation.getElementName(), validMethodAnnotations)) - annotatables.add(new Tuple.Two<>(annotation, method)); - } - // method parameters - ILocalVariable[] parameters = method.getParameters(); - for (ILocalVariable parameter : parameters) { - annotations = parameter.getAnnotations(); - for (IAnnotation annotation : annotations) { - if (isValidAnnotation(annotation.getElementName(), validAnnotations)) - annotatables.add(new Tuple.Two<>(annotation, parameter)); - } - } - } - // Field - IField[] fields = type.getFields(); - for (IField field : fields) { - annotations = field.getAnnotations(); - for (IAnnotation annotation : annotations) { - if (isValidAnnotation(annotation.getElementName(), validTypeAnnotations)) - annotatables.add(new Tuple.Two<>(annotation, field)); - } - } - } - - for (Tuple.Two annotatable : annotatables) { - IAnnotation annotation = annotatable.getFirst(); - IAnnotatable element = annotatable.getSecond(); - - if (isMatchedAnnotation(unit, annotation, AnnotationConstants.GENERATED_FQ_NAME)) { - for (IMemberValuePair pair : annotation.getMemberValuePairs()) { - // If date element exists and is non-empty, it must follow ISO 8601 format. - if (pair.getMemberName().equals("date")) { - if (pair.getValue() instanceof String) { - String date = (String) pair.getValue(); - if (!date.equals("")) { - if (!Pattern.matches(AnnotationConstants.ISO_8601_REGEX, date)) { - - String diagnosticMessage = Messages.getMessage( - "AnnotationMustDefineAttributeFollowing8601", "@Generated", "date"); - diagnostics.add(createDiagnostic(annotation, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_DATE_FORMAT, null, - DiagnosticSeverity.Error)); - } - } - } - } - } - } else if (isMatchedAnnotation(unit, annotation, AnnotationConstants.RESOURCE_FQ_NAME)) { - if (element instanceof IType) { - IType type = (IType) element; - if (type.getElementType() == IJavaElement.TYPE && ((IType) type).isClass()) { - Boolean nameEmpty = true; - Boolean typeEmpty = true; - for (IMemberValuePair pair : annotation.getMemberValuePairs()) { - if (pair.getMemberName().equals("name")) { - nameEmpty = false; - } - if (pair.getMemberName().equals("type")) { - typeEmpty = false; - } - } - String diagnosticMessage; - if (nameEmpty) { - diagnosticMessage = Messages.getMessage("AnnotationMustDefineAttribute", - "@Resource", "name"); - diagnostics.add(createDiagnostic(annotation, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_MISSING_RESOURCE_NAME_ATTRIBUTE, null, - DiagnosticSeverity.Error)); - } - - if (typeEmpty) { - diagnosticMessage = Messages.getMessage("AnnotationMustDefineAttribute", - "@Resource", "type"); - diagnostics.add(createDiagnostic(annotation, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_MISSING_RESOURCE_TYPE_ATTRIBUTE, null, - DiagnosticSeverity.Error)); - } - } - } - } - if (isMatchedAnnotation(unit, annotation, AnnotationConstants.POST_CONSTRUCT_FQ_NAME)) { - if (element instanceof IMethod) { - IMethod method = (IMethod) element; - if (method.getNumberOfParameters() != 0) { - String diagnosticMessage = Messages.getMessage("MethodMustNotHaveParameters", - "@PostConstruct"); - diagnostics.add(createDiagnostic(method, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_POSTCONSTRUCT_PARAMS, null, - DiagnosticSeverity.Error)); - } - - if (!method.getReturnType().equals("V")) { - String diagnosticMessage = Messages.getMessage("MethodMustBeVoid", - "@PostConstruct"); - diagnostics.add(createDiagnostic(method, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_POSTCONSTRUCT_RETURN_TYPE, null, - DiagnosticSeverity.Error)); - } - - if (method.getExceptionTypes().length != 0) { - String diagnosticMessage = Messages.getMessage("MethodMustNotThrow", - "@PostConstruct"); - diagnostics.add(createDiagnostic(method, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_POSTCONSTRUCT_EXCEPTION, null, - DiagnosticSeverity.Warning)); - } - } - } else if (isMatchedAnnotation(unit, annotation, AnnotationConstants.PRE_DESTROY_FQ_NAME)) { - if (element instanceof IMethod) { - IMethod method = (IMethod) element; - if (method.getNumberOfParameters() != 0) { - String diagnosticMessage = Messages.getMessage("MethodMustNotHaveParameters", - "@PreDestroy"); - diagnostics.add(createDiagnostic(method, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_PREDESTROY_PARAMS, null, - DiagnosticSeverity.Error)); - } - - if (Flags.isStatic(method.getFlags())) { - String diagnosticMessage = Messages.getMessage("MethodMustNotBeStatic", - "@PreDestroy"); - diagnostics.add(createDiagnostic(method, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_PREDESTROY_STATIC, method.getElementType(), - DiagnosticSeverity.Error)); - } - - if (method.getExceptionTypes().length != 0) { - String diagnosticMessage = Messages.getMessage("MethodMustNotThrow", - "@PreDestroy"); - diagnostics.add(createDiagnostic(method, unit, diagnosticMessage, - AnnotationConstants.DIAGNOSTIC_CODE_PREDESTROY_EXCEPTION, null, - DiagnosticSeverity.Warning)); - } - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate diagnostics", e); - } - } - } - - private static boolean isValidAnnotation(String annotationName, String[] validAnnotations) { - for (String fqName : validAnnotations) { - if (fqName.endsWith(annotationName)) { - return true; - } - } - return false; - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/PostConstructReturnTypeQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/PostConstructReturnTypeQuickFix.java deleted file mode 100644 index 2d8a3bb5..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/PostConstructReturnTypeQuickFix.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022, 2023 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Yijia Jing - *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.annotations; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.PrimitiveType; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyReturnTypeProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * Quick fix for AnnotationDiagnosticsCollector that changes the return type of a method to void. - * Uses ModifyReturnTypeProposal. - * - * @author Yijia Jing - * - */ -public class PostConstructReturnTypeQuickFix implements IJavaCodeActionParticipant { - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - String name = Messages.getMessage("ChangeReturnTypeToVoid"); - ChangeCorrectionProposal proposal = new ModifyReturnTypeProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, node.getAST().newPrimitiveType(PrimitiveType.VOID)); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeActions.add(codeAction); - return codeActions; - } - - protected IBinding getBinding(ASTNode node) { - if (node.getParent() instanceof MethodDeclaration) { - return ((MethodDeclaration) node.getParent()).resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/RemovePostConstructAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/RemovePostConstructAnnotationQuickFix.java deleted file mode 100644 index b9e239eb..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/RemovePostConstructAnnotationQuickFix.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.annotations; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -/** - * Quickfix for removing @PostConstruct - * - * @author Zijian Pei - * - */ -public class RemovePostConstructAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { - - public RemovePostConstructAnnotationQuickFix() { - super(false, "jakarta.annotation.PostConstruct"); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/RemovePreDestroyAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/RemovePreDestroyAnnotationQuickFix.java deleted file mode 100644 index 18304758..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/RemovePreDestroyAnnotationQuickFix.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.annotations; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -/** - * Quickfix for removing @PreDestory - * - * @author Zijian Pei - * - */ -public class RemovePreDestroyAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { - - public RemovePreDestroyAnnotationQuickFix() { - super(false, "jakarta.annotation.PreDestroy"); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationDiagnosticsCollector.java deleted file mode 100644 index 53ec9547..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationDiagnosticsCollector.java +++ /dev/null @@ -1,276 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation, Reza Akhavan and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Reza Akhavan - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.beanvalidation; - -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.ASSERT_FALSE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.ASSERT_TRUE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.BIG_DECIMAL; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.BIG_INTEGER; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.BOOLEAN; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.BYTE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.CHAR_SEQUENCE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.DECIMAL_MAX; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.DECIMAL_MIN; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.DIAGNOSTIC_CODE_INVALID_TYPE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.DIAGNOSTIC_CODE_STATIC; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.DIAGNOSTIC_SOURCE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.DIGITS; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.DOUBLE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.EMAIL; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.FLOAT; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.FUTURE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.FUTURE_OR_PRESENT; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.INTEGER; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.LONG; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.MAX; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.MIN; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.NEGATIVE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.NEGATIVE_OR_ZERO; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.NOT_BLANK; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.PAST; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.PAST_OR_PRESENT; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.PATTERN; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.POSITIVE; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.POSTIVE_OR_ZERO; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.SET_OF_ANNOTATIONS; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.SET_OF_DATE_TYPES; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.SHORT; -import static org.eclipse.lsp4jakarta.jdt.core.beanvalidation.BeanValidationConstants.STRING; - -import java.util.List; - -import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IField; -import org.eclipse.jdt.core.IMember; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.Signature; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -public class BeanValidationDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public BeanValidationDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return DIAGNOSTIC_SOURCE; - } - - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - - if (unit != null) { - IType[] alltypes; - IField[] allFields; - IAnnotation[] annotations; - IMethod[] allMethods; - - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - allFields = type.getFields(); - for (IField field : allFields) { - annotations = field.getAnnotations(); - for (IAnnotation annotation : annotations) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), - SET_OF_ANNOTATIONS.toArray(new String[0])); - if (matchedAnnotation != null) { - validAnnotation(field, annotation, matchedAnnotation, diagnostics); - } - } - } - allMethods = type.getMethods(); - for (IMethod method : allMethods) { - annotations = method.getAnnotations(); - for (IAnnotation annotation : annotations) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), - SET_OF_ANNOTATIONS.toArray(new String[0])); - if (matchedAnnotation != null) { - validAnnotation(method, annotation, matchedAnnotation, diagnostics); - } - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate diagnostics", e); - } - } - - } - - private void validAnnotation(IMember element, IAnnotation annotation, String matchedAnnotation, - List diagnostics) throws JavaModelException { - IType declaringType = element.getDeclaringType(); - if (declaringType != null) { - String annotationName = annotation.getElementName(); - boolean isMethod = element instanceof IMethod; - - if (Flags.isStatic(element.getFlags())) { - String source = isMethod ? - Messages.getMessage("ConstraintAnnotationsMethod") : - Messages.getMessage("ConstraintAnnotationsField"); - diagnostics.add(createDiagnostic(element, declaringType.getCompilationUnit(), - source, DIAGNOSTIC_CODE_STATIC, - annotationName, DiagnosticSeverity.Error)); - } else { - String type = (isMethod) ? ((IMethod) element).getReturnType() : ((IField) element).getTypeSignature(); - - if (matchedAnnotation.equals(ASSERT_FALSE) || matchedAnnotation.equals(ASSERT_TRUE)) { - String source = isMethod ? - Messages.getMessage("AnnotationBooleanMethods", "@" + annotationName) : - Messages.getMessage("AnnotationBooleanFields", "@" + annotationName); - if (!type.equals(getSignatureFormatOfType(BOOLEAN)) && !type.equals(Signature.SIG_BOOLEAN)) { - diagnostics.add(createDiagnostic(element, declaringType.getCompilationUnit(), - source, DIAGNOSTIC_CODE_INVALID_TYPE, annotationName, DiagnosticSeverity.Error)); - } - } else if (matchedAnnotation.equals(DECIMAL_MAX) || matchedAnnotation.equals(DECIMAL_MIN) - || matchedAnnotation.equals(DIGITS)) { - if (!type.equals(getSignatureFormatOfType(BIG_DECIMAL)) - && !type.equals(getSignatureFormatOfType(BIG_INTEGER)) - && !type.equals(getSignatureFormatOfType(CHAR_SEQUENCE)) - && !type.equals(getSignatureFormatOfType(BYTE)) - && !type.equals(getSignatureFormatOfType(SHORT)) - && !type.equals(getSignatureFormatOfType(INTEGER)) - && !type.equals(getSignatureFormatOfType(LONG)) && !type.equals(Signature.SIG_BYTE) - && !type.equals(Signature.SIG_SHORT) && !type.equals(Signature.SIG_INT) - && !type.equals(Signature.SIG_LONG)) { - String source = isMethod ? - Messages.getMessage("AnnotationBigDecimalMethods", "@" + annotationName) : - Messages.getMessage("AnnotationBigDecimalFields", "@" + annotationName); - diagnostics.add(createDiagnostic(element, declaringType.getCompilationUnit(), source, - DIAGNOSTIC_CODE_INVALID_TYPE, annotationName, DiagnosticSeverity.Error)); - } - } else if (matchedAnnotation.equals(EMAIL)) { - checkStringOnly(element, declaringType, diagnostics, annotationName, isMethod, type); - } else if (matchedAnnotation.equals(NOT_BLANK)) { - checkStringOnly(element, declaringType, diagnostics, annotationName, isMethod, type); - } else if (matchedAnnotation.equals(PATTERN)) { - checkStringOnly(element, declaringType, diagnostics, annotationName, isMethod, type); - } else if (matchedAnnotation.equals(FUTURE) || matchedAnnotation.equals(FUTURE_OR_PRESENT) - || matchedAnnotation.equals(PAST) || matchedAnnotation.equals(PAST_OR_PRESENT)) { - String dataType = getDataTypeName(type); - String dataTypeFQName = getMatchedJavaElementName(declaringType, dataType, - SET_OF_DATE_TYPES.toArray(new String[0])); - if (dataTypeFQName == null) { - String source = isMethod ? - Messages.getMessage("AnnotationDateMethods", "@" + annotationName) : - Messages.getMessage("AnnotationDateFields", "@" + annotationName); - diagnostics.add(createDiagnostic(element, declaringType.getCompilationUnit(), - source, DIAGNOSTIC_CODE_INVALID_TYPE, annotationName, DiagnosticSeverity.Error)); - } - } else if (matchedAnnotation.equals(MIN) || matchedAnnotation.equals(MAX)) { - if (!type.equals(getSignatureFormatOfType(BIG_DECIMAL)) - && !type.equals(getSignatureFormatOfType(BIG_INTEGER)) - && !type.equals(getSignatureFormatOfType(BYTE)) - && !type.equals(getSignatureFormatOfType(SHORT)) - && !type.equals(getSignatureFormatOfType(INTEGER)) - && !type.equals(getSignatureFormatOfType(LONG)) && !type.equals(Signature.SIG_BYTE) - && !type.equals(Signature.SIG_SHORT) && !type.equals(Signature.SIG_INT) - && !type.equals(Signature.SIG_LONG)) { - String source = isMethod ? - Messages.getMessage("AnnotationMinMaxMethods", "@" + annotationName) : - Messages.getMessage("AnnotationMinMaxFields", "@" + annotationName); - diagnostics.add(createDiagnostic(element, declaringType.getCompilationUnit(), - source, DIAGNOSTIC_CODE_INVALID_TYPE, annotationName, DiagnosticSeverity.Error)); - } - } else if (matchedAnnotation.equals(NEGATIVE) || matchedAnnotation.equals(NEGATIVE_OR_ZERO) - || matchedAnnotation.equals(POSITIVE) || matchedAnnotation.equals(POSTIVE_OR_ZERO)) { - if (!type.equals(getSignatureFormatOfType(BIG_DECIMAL)) - && !type.equals(getSignatureFormatOfType(BIG_INTEGER)) - && !type.equals(getSignatureFormatOfType(BYTE)) - && !type.equals(getSignatureFormatOfType(SHORT)) - && !type.equals(getSignatureFormatOfType(INTEGER)) - && !type.equals(getSignatureFormatOfType(LONG)) - && !type.equals(getSignatureFormatOfType(FLOAT)) - && !type.equals(getSignatureFormatOfType(DOUBLE)) && !type.equals(Signature.SIG_BYTE) - && !type.equals(Signature.SIG_SHORT) && !type.equals(Signature.SIG_INT) - && !type.equals(Signature.SIG_LONG) && !type.equals(Signature.SIG_FLOAT) - && !type.equals(Signature.SIG_DOUBLE)) { - String source = isMethod ? - Messages.getMessage("AnnotationPositiveMethods", "@" + annotationName) : - Messages.getMessage("AnnotationPositiveFields", "@" + annotationName); - diagnostics.add(createDiagnostic(element, declaringType.getCompilationUnit(), - source, DIAGNOSTIC_CODE_INVALID_TYPE, annotationName, DiagnosticSeverity.Error)); - } - } - - // These ones contains check on all collection types which requires resolving - // the String of the type somehow - // This will also require us to check if the field type was a custom collection - // subtype which means we - // have to resolve it and get the super interfaces and check to see if - // Collection, Map or Array was implemented - // for that custom type (which could as well be a user made subtype) - -// else if (annotation.getElementName().equals(NOT_EMPTY) || annotation.getElementName().equals(SIZE)) { -// -// System.out.println("--Field name: " + Signature.getTypeSignatureKind(fieldType)); -// System.out.println("--Field name: " + Signature.getParameterTypes(fieldType)); -// if ( !fieldType.equals(getSignatureFormatOfType(CHAR_SEQUENCE)) && -// !fieldType.contains("List") && -// !fieldType.contains("Set") && -// !fieldType.contains("Collection") && -// !fieldType.contains("Array") && -// !fieldType.contains("Vector") && -// !fieldType.contains("Stack") && -// !fieldType.contains("Queue") && -// !fieldType.contains("Deque") && -// !fieldType.contains("Map")) { -// -// diagnostics.add(new Diagnostic(fieldAnnotationrange, -// "This annotation can only be used on CharSequence, Collection, Array, " -// + "Map type fields.")); -// } -// } - } - } - } - - private void checkStringOnly(IMember element, IType declaringType, List diagnostics, - String annotationName, boolean isMethod, String type) throws JavaModelException { - if (!type.equals(getSignatureFormatOfType(STRING)) - && !type.equals(getSignatureFormatOfType(CHAR_SEQUENCE))) { - String source = isMethod ? - Messages.getMessage("AnnotationStringMethods", "@" + annotationName) : - Messages.getMessage("AnnotationStringFields", "@" + annotationName); - diagnostics.add(createDiagnostic(element, declaringType.getCompilationUnit(), - source, DIAGNOSTIC_CODE_INVALID_TYPE, annotationName, DiagnosticSeverity.Error)); - } - } - - /* - * Refer to Class signature documentation for the formating - * https://www.ibm.com/support/knowledgecenter/sl/SS5JSH_9.5.0/org.eclipse.jdt. - * doc.isv/reference/api/org/eclipse/jdt/core/Signature.html - */ - private static String getSignatureFormatOfType(String type) { - return "Q" + type + ";"; - } - - private static String getDataTypeName(String type) { - int length = type.length(); - if (length > 0 && type.charAt(0) == 'Q' && type.charAt(length - 1) == ';') { - return type.substring(1, length - 1); - } - return type; - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationQuickFix.java deleted file mode 100644 index 2bed96b0..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationQuickFix.java +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.beanvalidation; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.DeleteAnnotationProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyModifiersProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * Quickfix for fixing {@link BeanValidationConstants#DIAGNOSTIC_CODE_Static} error by either action - * 1. Removing constraint annotation on static field or method - * 2. Removing static modifier from field or method - * - * @author Leslie Dawson (lamminade) - * - */ -public class BeanValidationQuickFix implements IJavaCodeActionParticipant { - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - - List codeActions = new ArrayList<>(); - codeActions.add(removeConstraintAnnotations(diagnostic, context, parentType)); - - if (diagnostic.getCode().getLeft().equals(BeanValidationConstants.DIAGNOSTIC_CODE_STATIC)) { - codeActions.add(removeStaticModifier(diagnostic, context, parentType)); - } - - return codeActions; - } - - private CodeAction removeConstraintAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType) throws CoreException { - String annotationName = diagnostic.getData().toString().replace("\"", ""); - String name = Messages.getMessage("RemoveConstraintAnnotation", annotationName); - ChangeCorrectionProposal proposal = new DeleteAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, context.getCoveredNode().getParent(), annotationName); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - if (codeAction != null) { - return codeAction; - } - return null; - } - - private CodeAction removeStaticModifier(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType) throws CoreException { - String name = Messages.getMessage("RemoveStaticModifier"); - ModifyModifiersProposal proposal = new ModifyModifiersProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, context.getCoveredNode().getParent(), new ArrayList<>(), Arrays.asList("static")); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - - if (codeAction != null) { - return codeAction; - } - return null; - } - - protected IBinding getBinding(ASTNode node) { - if (node.getParent() instanceof VariableDeclarationFragment) { - return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ConflictProducesInjectQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ConflictProducesInjectQuickFix.java deleted file mode 100644 index faedd629..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ConflictProducesInjectQuickFix.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* IBM Corporation, Jianing Xu - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -/** - * - * Quick fix for removing @Produces/@Inject when they are used for the same field - * or property - * - * @author Jianing Xu - * - */ -public class ConflictProducesInjectQuickFix extends RemoveAnnotationConflictQuickFix { - - public ConflictProducesInjectQuickFix() { - super(false, "jakarta.enterprise.inject.Produces", "jakarta.inject.Inject"); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanConstructorQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanConstructorQuickFix.java deleted file mode 100644 index 537b9681..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanConstructorQuickFix.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Hani Damlaj -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.InsertAnnotationMissingQuickFix; - -public class ManagedBeanConstructorQuickFix extends InsertAnnotationMissingQuickFix { - public ManagedBeanConstructorQuickFix() { - super("jakarta.inject.Inject"); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanDiagnosticsCollector.java deleted file mode 100644 index 9d439934..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanDiagnosticsCollector.java +++ /dev/null @@ -1,395 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 IBM Corporation. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Hani Damlaj, Jianing Xu - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.CONSTRUCTOR_DIAGNOSTIC_CODE; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.DEPENDENT; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.DEPENDENT_FQ_NAME; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.DIAGNOSTIC_CODE; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.DIAGNOSTIC_CODE_SCOPEDECL; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.DIAGNOSTIC_SOURCE; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.DISPOSES; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.DISPOSES_FQ_NAME; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.INJECT; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.INJECT_FQ_NAME; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.INVALID_INJECT_PARAMS_FQ; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.OBSERVES_ASYNC_FQ_NAME; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.OBSERVES_FQ_NAME; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.PRODUCES; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.PRODUCES_FQ_NAME; -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.SCOPE_FQ_NAMES; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IField; -import org.eclipse.jdt.core.ILocalVariable; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; - -public class ManagedBeanDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public ManagedBeanDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit == null) - return; - - try { - IType[] types = unit.getAllTypes(); - String[] scopeFQNames = SCOPE_FQ_NAMES.toArray(String[]::new); - for (IType type : types) { - List managedBeanAnnotations = getMatchedJavaElementNames(type, Stream.of(type.getAnnotations()) - .map(annotation -> annotation.getElementName()).toArray(String[]::new), - scopeFQNames); - boolean isManagedBean = managedBeanAnnotations.size() > 0; - - if (managedBeanAnnotations.size() > 1) { - // convert to simple name - List diagnosticData = managedBeanAnnotations.stream() - .map(annotation -> getSimpleName(annotation)).collect(Collectors.toList()); - diagnostics.add(createDiagnostic(type, unit, - Messages.getMessage("ScopeTypeAnnotationsManagedBean"), - DIAGNOSTIC_CODE_SCOPEDECL, (JsonArray) (new Gson().toJsonTree(diagnosticData)), - DiagnosticSeverity.Error)); - } - - String[] injectAnnotations = { PRODUCES_FQ_NAME, INJECT_FQ_NAME }; - IField fields[] = type.getFields(); - for (IField field : fields) { - int fieldFlags = field.getFlags(); - String[] annotationNames = Stream.of(field.getAnnotations()) - .map(annotation -> annotation.getElementName()).toArray(String[]::new); - List fieldScopes = getMatchedJavaElementNames(type, annotationNames, scopeFQNames); - - /** - * If a managed bean has a non-static public field, it must have - * scope @Dependent. If a managed bean with a non-static public field declares - * any scope other than @Dependent, the container automatically detects the - * problem and treats it as a definition error. - * - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#managed_beans - */ - if (isManagedBean && Flags.isPublic(fieldFlags) && !Flags.isStatic(fieldFlags) - && (fieldScopes.size() != 1 || !fieldScopes.get(0).equals(DEPENDENT_FQ_NAME))) { - diagnostics.add(createDiagnostic(field, unit, - Messages.getMessage("ManagedBeanWithNonStaticPublicField"), - DIAGNOSTIC_CODE, null, - DiagnosticSeverity.Error)); - } - - /** - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_bean_scope - * A bean class or producer method or field may specify at most one scope type - * annotation. If a bean class or producer method or field specifies multiple - * scope type annotations, the container automatically detects the problem and - * treats it as a definition error. - * - * Here we only look at the fields. - */ - List fieldInjects = getMatchedJavaElementNames(type, annotationNames, injectAnnotations); - boolean isProducerField = false, isInjectField = false; - for (String annotation : fieldInjects) { - if (PRODUCES_FQ_NAME.equals(annotation)) - isProducerField = true; - else if (INJECT_FQ_NAME.equals(annotation)) - isInjectField = true; - } - if (isProducerField && fieldScopes.size() > 1) { - List diagnosticData = fieldScopes.stream().map(annotation -> getSimpleName(annotation)) - .collect(Collectors.toList()); // convert to simple name - diagnosticData.add(PRODUCES); - diagnostics.add(createDiagnostic(field, unit, - Messages.getMessage("ScopeTypeAnnotationsProducerField"), - DIAGNOSTIC_CODE_SCOPEDECL, (JsonArray) (new Gson().toJsonTree(diagnosticData)), - DiagnosticSeverity.Error)); - } - - if (isProducerField && isInjectField) { - /* - * ========= Produces and Inject Annotations Checks ========= - * - * go through each field and method to make sure @Produces and @Inject are not used together - * - * see: https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_producer_field - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_producer_method - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_injected_field - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_initializer - */ - - // A single field cannot have the same - diagnostics.add(createDiagnostic(field, unit, - Messages.getMessage("ManagedBeanProducesAndInject"), - ManagedBeanConstants.DIAGNOSTIC_CODE_PRODUCES_INJECT, null, DiagnosticSeverity.Error)); - } - - } - - IMethod[] methods = type.getMethods(); - List constructorMethods = new ArrayList(); - for (IMethod method : methods) { - - // Find all methods on the type that are constructors. - if (isConstructorMethod(method)) - constructorMethods.add(method); - - /** - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_bean_scope - * A bean class or producer method or field may specify at most one scope type - * annotation. If a bean class or producer method or field specifies multiple - * scope type annotations, the container automatically detects the problem and - * treats it as a definition error. - * - * Here we only look at the methods. - */ - String[] annotationNames = Stream.of(method.getAnnotations()) - .map(annotation -> annotation.getElementName()).toArray(String[]::new); - List methodScopes = getMatchedJavaElementNames(type, annotationNames, scopeFQNames); - List methodInjects = getMatchedJavaElementNames(type, annotationNames, injectAnnotations); - boolean isProducerMethod = false, isInjectMethod = false; - for (String annotation : methodInjects) { - if (PRODUCES_FQ_NAME.equals(annotation)) - isProducerMethod = true; - else if (INJECT_FQ_NAME.equals(annotation)) - isInjectMethod = true; - } - - if (isProducerMethod && methodScopes.size() > 1) { - List diagnosticData = methodScopes.stream().map(annotation -> getSimpleName(annotation)) - .collect(Collectors.toList()); // convert to simple name - diagnosticData.add(PRODUCES); - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("ScopeTypeAnnotationsProducerMethod"), - DIAGNOSTIC_CODE_SCOPEDECL, (JsonArray) (new Gson().toJsonTree(diagnosticData)), - DiagnosticSeverity.Error)); - } - - if (isProducerMethod && isInjectMethod) { - /* - * ========= Produces and Inject Annotations Checks ========= - * - * go through each field and method to make sure @Produces and @Inject are not used together - * - * see: https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_producer_field - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_producer_method - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_injected_field - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_initializer - */ - - // A single method cannot have the same - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("ManagedBeanProducesAndInject"), - ManagedBeanConstants.DIAGNOSTIC_CODE_PRODUCES_INJECT, null, DiagnosticSeverity.Error)); - } - - } - - if (isManagedBean && constructorMethods.size() > 0) { - /** - * If the managed bean does not have a constructor that takes no parameters, it - * must have a constructor annotated @Inject. No additional special annotations - * are required. - */ - - // If there are no constructor methods, there is an implicit empty constructor - // generated by the compiler. - List methodsNeedingDiagnostics = new ArrayList(); - for (IMethod m : constructorMethods) { - if (m.getNumberOfParameters() == 0) { - methodsNeedingDiagnostics.clear(); - break; - } - IAnnotation[] annotations = m.getAnnotations(); - boolean hasParameterizedInjectConstructor = false; - // look up '@Inject' annotation - for (IAnnotation annotation : annotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), INJECT_FQ_NAME)) { - hasParameterizedInjectConstructor = true; - break; - } - } - if (hasParameterizedInjectConstructor) { - methodsNeedingDiagnostics.clear(); - break; - } else - methodsNeedingDiagnostics.add(m); - } - - // Deliver a diagnostic on all parameterized constructors that they must add an - // @Inject annotation - for (IMethod m : methodsNeedingDiagnostics) { - diagnostics.add(createDiagnostic(m, unit, Messages.getMessage("ManagedBeanConstructorWithParameters"), - CONSTRUCTOR_DIAGNOSTIC_CODE, null, DiagnosticSeverity.Error)); - } - } - - /** - * If a managed bean class is of generic type, it must be annotated with @Dependent - */ - if (isManagedBean) { - boolean isClassGeneric = type.getTypeParameters().length != 0; - boolean isDependent = managedBeanAnnotations.stream() - .anyMatch(annotation -> DEPENDENT_FQ_NAME.equals(annotation)); - - if (isClassGeneric && !isDependent) { - diagnostics.add(createDiagnostic(type, unit, Messages.getMessage("ManagedBeanGenericType"), - DIAGNOSTIC_CODE, null, DiagnosticSeverity.Error)); - } - } - - /* - * ========= Inject and Disposes, Observes, ObservesAsync Annotations Checks========= - */ - /* - * go through each method to make sure @Inject - * and @Disposes, @Observes, @ObservesAsync are not used together - * - * see: https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_bean_constructor - * https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_initializer - * - */ - invalidParamsCheck(unit, diagnostics, type, INJECT_FQ_NAME, - ManagedBeanConstants.DIAGNOSTIC_CODE_INVALID_INJECT_PARAM); - - if (isManagedBean) { - /* - * ========= Produces and Disposes, Observes, ObservesAsync Annotations Checks========= - */ - /* - * go through each method to make sure @Produces - * and @Disposes, @Observes, @ObservesAsync are not used together - * - * see: https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_producer_method - * - * note: - * we need to check for bean defining annotations first to make sure the managed bean is discovered. - * - */ - invalidParamsCheck(unit, diagnostics, type, PRODUCES_FQ_NAME, - ManagedBeanConstants.DIAGNOSTIC_CODE_INVALID_PRODUCES_PARAM); - - for (IMethod method : methods) { - int numDisposes = 0; - Set invalidAnnotations = new TreeSet<>(); - ILocalVariable[] params = method.getParameters(); - - for (ILocalVariable param : params) { - IAnnotation[] annotations = param.getAnnotations(); - for (IAnnotation annotation : annotations) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), - INVALID_INJECT_PARAMS_FQ); - if (DISPOSES_FQ_NAME.equals(matchedAnnotation)) { - numDisposes++; - } else if (OBSERVES_FQ_NAME.equals(matchedAnnotation) - || OBSERVES_ASYNC_FQ_NAME.equals(matchedAnnotation)) { - invalidAnnotations.add("@" + annotation.getElementName()); - } - } - } - - if(numDisposes == 0) continue; - if(numDisposes > 1) { - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("ManagedBeanDisposeOneParameter"), - ManagedBeanConstants.DIAGNOSTIC_CODE_REDUNDANT_DISPOSES, null, - DiagnosticSeverity.Error)); - } - - if(!invalidAnnotations.isEmpty()) { - diagnostics.add(createDiagnostic(method, unit, - createInvalidDisposesLabel(invalidAnnotations), - ManagedBeanConstants.DIAGNOSTIC_CODE_INVALID_DISPOSES_PARAM, null, - DiagnosticSeverity.Error)); - } - } - } - } - - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate diagnostics", e); - } - } - - private void invalidParamsCheck(ICompilationUnit unit, List diagnostics, IType type, String target, - String diagnosticCode) throws JavaModelException { - for (IMethod method : type.getMethods()) { - IAnnotation targetAnnotation = null; - - for (IAnnotation annotation : method.getAnnotations()) { - if (isMatchedJavaElement(type, annotation.getElementName(), target)) { - targetAnnotation = annotation; - break; - } - } - - if (targetAnnotation == null) - continue; - - Set invalidAnnotations = new TreeSet<>(); - ILocalVariable[] params = method.getParameters(); - for (ILocalVariable param : params) { - List paramScopes = getMatchedJavaElementNames(type, Stream.of(param.getAnnotations()) - .map(annotation -> annotation.getElementName()).toArray(String[]::new), - INVALID_INJECT_PARAMS_FQ); - for (String annotation : paramScopes) { - invalidAnnotations.add("@" + getSimpleName(annotation)); - } - } - - if (!invalidAnnotations.isEmpty()) { - String label = PRODUCES_FQ_NAME.equals(target) ? - createInvalidProducesLabel(invalidAnnotations) : - createInvalidInjectLabel(invalidAnnotations); - diagnostics.add(createDiagnostic(method, unit, label, diagnosticCode, null, DiagnosticSeverity.Error)); - } - - } - } - - private String createInvalidInjectLabel(Set invalidAnnotations) { - return Messages.getMessage("ManagedBeanInvalidInject", String.join(", ", invalidAnnotations)); // assuming comma delimited list is ok - } - - private String createInvalidProducesLabel(Set invalidAnnotations) { - return Messages.getMessage("ManagedBeanInvalidProduces", String.join(", ", invalidAnnotations)); // assuming comma delimited list is ok - } - - private String createInvalidDisposesLabel(Set invalidAnnotations) { - return Messages.getMessage("ManagedBeanInvalidDisposer", String.join(", ", invalidAnnotations)); // assuming comma delimited list is ok - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanNoArgConstructorQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanNoArgConstructorQuickFix.java deleted file mode 100644 index dbd322aa..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanNoArgConstructorQuickFix.java +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Hani Damlaj -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.AddConstructorProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * - * Quick fix for adding a `protected`/`public` no argument constructor - * for a managed bean that do not have: - * - a no argument constructor - * - a constructor annotated with `@Inject` - * - */ - -public class ManagedBeanNoArgConstructorQuickFix implements IJavaCodeActionParticipant { - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - if (parentType != null) { - codeActions.addAll(addConstructor(diagnostic, context, parentType)); - } - return codeActions; - } - - protected static IBinding getBinding(ASTNode node) { - if (node.getParent() instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment = (VariableDeclarationFragment) node.getParent(); - return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } - - private List addConstructor(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType) throws CoreException { - List codeActions = new ArrayList<>(); - - // option for protected constructor - String name = Messages.getMessage("AddProtectedConstructor"); - ChangeCorrectionProposal proposal = new AddConstructorProposal(name, - context.getCompilationUnit(), context.getASTRoot(), parentType, 0); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - - if (codeAction != null) { - codeActions.add(codeAction); - } - - // option for public constructor - name = Messages.getMessage("AddPublicConstructor"); - proposal = new AddConstructorProposal(name, - context.getCompilationUnit(), context.getASTRoot(), parentType, 0, "public"); - codeAction = context.convertToCodeAction(proposal, diagnostic); - - if (codeAction != null) { - codeActions.add(codeAction); - } - - return codeActions; - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanQuickFix.java deleted file mode 100644 index a7da0d80..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanQuickFix.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Hani Damlaj -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ReplaceAnnotationProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.InsertAnnotationMissingQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.*; - -public class ManagedBeanQuickFix extends InsertAnnotationMissingQuickFix { - public ManagedBeanQuickFix() { - super("jakarta.enterprise.context.Dependent"); - } - - private static final String[] REMOVE_ANNOTATION_NAMES = new ArrayList<>(SCOPES).toArray(new String[SCOPES.size()]); - - @Override - protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions) throws CoreException { - String[] annotations = getAnnotations(); - for (String annotation : annotations) { - insertAndReplaceAnnotation(diagnostic, context, parentType, codeActions, annotation); - } - } - - private static void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, - IBinding parentType, List codeActions, String annotation) throws CoreException { - // Diagnostic is reported on the variable declaration, however the - // annotations that need to be replaced are on the type declaration (class - // definition) containing the variable declaration. We retrieve the type - // declaration container here. - ASTNode parentNode = context.getASTRoot().findDeclaringNode(parentType); - IBinding classBinding = getBinding(parentNode); - - // Insert the annotation and the proper import by using JDT Core Manipulation - // API - String name = getLabel(annotation); - ChangeCorrectionProposal proposal = new ReplaceAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), classBinding, 0, annotation, REMOVE_ANNOTATION_NAMES); - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - if (codeAction != null) { - codeActions.add(codeAction); - } - } - - private static String getLabel(String annotation) { - String annotationName = annotation.substring(annotation.lastIndexOf('.') + 1, annotation.length()); - return Messages.getMessage("ReplaceCurrentScope", "@" + annotationName); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/RemoveInvalidInjectParamAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/RemoveInvalidInjectParamAnnotationQuickFix.java deleted file mode 100644 index a24ff0e1..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/RemoveInvalidInjectParamAnnotationQuickFix.java +++ /dev/null @@ -1,26 +0,0 @@ - /******************************************************************************* - * Copyright (c) 2021 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveParamAnnotationQuickFix; - -/** - * QuickFix for deleting any of @Disposes, @Observes and @ObservesAsync annotation for parameters - */ -public class RemoveInvalidInjectParamAnnotationQuickFix extends RemoveParamAnnotationQuickFix { - - public RemoveInvalidInjectParamAnnotationQuickFix() { - super(ManagedBeanConstants.INVALID_INJECT_PARAMS.toArray((String[]::new))); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/RemoveProduceAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/RemoveProduceAnnotationQuickFix.java deleted file mode 100644 index 7f40c750..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/RemoveProduceAnnotationQuickFix.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0 -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Himanshu Chotwani - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -/** - * - * Quick fix for removing @Produces annotation - * - */ -public class RemoveProduceAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { - - public RemoveProduceAnnotationQuickFix() { - super(false, "jakarta.enterprise.inject.Produces"); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ScopeDeclarationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ScopeDeclarationQuickFix.java deleted file mode 100644 index f83d7130..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ScopeDeclarationQuickFix.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -import com.google.gson.JsonArray; - -public class ScopeDeclarationQuickFix extends RemoveAnnotationConflictQuickFix { - public ScopeDeclarationQuickFix() { - // annotation list to be derived from the diagnostic passed to - // `getCodeActions()` - super(); - } - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - - JsonArray diagnosticData = (JsonArray) diagnostic.getData(); - - List annotations = IntStream.range(0, diagnosticData.size()) - .mapToObj(idx -> diagnosticData.get(idx).getAsString()).collect(Collectors.toList()); - - annotations.remove(ManagedBeanConstants.PRODUCES); - - if (parentType != null) { - /** - * for each annotation, choose the current annotation to keep and remove the - * rest since we can have at most one scope annotation. - */ - for (String annotation : annotations) { - List resultingAnnotations = new ArrayList<>(annotations); - resultingAnnotations.remove(annotation); - - removeAnnotation(diagnostic, context, parentType, codeActions, - resultingAnnotations.toArray(new String[] {})); - } - - } - return codeActions; - - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/Utils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/Utils.java deleted file mode 100644 index e72d8e4e..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/Utils.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2022 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.cdi; - -import org.eclipse.jdt.core.IType; - -import static org.eclipse.lsp4jakarta.jdt.core.cdi.ManagedBeanConstants.*; -import static org.eclipse.lsp4jakarta.jdt.core.AnnotationUtil.getScopeAnnotations; - -public class Utils { - /** - * Detects if a class is a managed bean by looking for a bean defining - * annotation. - * - * @param type the type representing the potential bean class - * @return true if the class has a bean defining annotation. - */ - static boolean isManagedBean(IType type) { - return getScopeAnnotations(type, SCOPES).size() > 0; - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/ConflictInjectMultipleConstructorQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/ConflictInjectMultipleConstructorQuickFix.java deleted file mode 100644 index 95a0bb8e..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/ConflictInjectMultipleConstructorQuickFix.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0 -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Ananya Rao -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.di; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -/** - * - * Quick fix for removing @Inject when it is used with more than one constructor. - * - * @author Ananya Rao - * - */ - -public class ConflictInjectMultipleConstructorQuickFix extends RemoveAnnotationConflictQuickFix { - public ConflictInjectMultipleConstructorQuickFix(){ - super(false, "jakarta.inject.Inject"); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/DependencyInjectionDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/DependencyInjectionDiagnosticsCollector.java deleted file mode 100644 index 22cfcf8b..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/DependencyInjectionDiagnosticsCollector.java +++ /dev/null @@ -1,153 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0 -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Himanshu Chotwani - initial API and implementation -* Ananya Rao - Diagnostic Collection for multiple constructors annotated with inject -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.di; - -import static org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_ABSTRACT; -import static org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_CONSTRUCTOR; -import static org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_FINAL; -import static org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_GENERIC; -import static org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants.DIAGNOSTIC_CODE_INJECT_STATIC; -import static org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants.DIAGNOSTIC_SOURCE; -import static org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants.INJECT; -import static org.eclipse.lsp4jakarta.jdt.core.di.DependencyInjectionConstants.INJECT_FQ_NAME; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; - -import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IField; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * - * jararta.annotation Diagnostics - * - *
  • Diagnostic 1: @Inject fields cannot be final.
  • - *
  • Diagnostic 2: @Inject methods cannot be final.
  • - *
  • Diagnostic 3: @Inject methods cannot be abstract.
  • - *
  • Diagnostic 4: @Inject methods cannot be static.
  • - *
  • Diagnostic 5: @Inject methods cannot be generic.
  • - * - * @see https://jakarta.ee/specifications/dependency-injection/2.0/jakarta-injection-spec-2.0.html - * - */ - -public class DependencyInjectionDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public DependencyInjectionDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit == null) - return; - - IType[] alltypes; - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - IField[] allFields = type.getFields(); - for (IField field : allFields) { - if (Flags.isFinal(field.getFlags()) - && containsAnnotation(type, field.getAnnotations(), INJECT_FQ_NAME)) { - String msg = Messages.getMessage("InjectNoFinalField"); - diagnostics.add(createDiagnostic(field, unit, msg, - DIAGNOSTIC_CODE_INJECT_FINAL, field.getElementType(), - DiagnosticSeverity.Error)); - } - } - - List injectedConstructors = new ArrayList(); - IMethod[] allMethods = type.getMethods(); - for (IMethod method : allMethods) { - int methodFlag = method.getFlags(); - boolean isFinal = Flags.isFinal(methodFlag); - boolean isAbstract = Flags.isAbstract(methodFlag); - boolean isStatic = Flags.isStatic(methodFlag); - boolean isGeneric = method.getTypeParameters().length != 0; - - if (containsAnnotation(type, method.getAnnotations(), INJECT_FQ_NAME)) { - if (isConstructorMethod(method)) - injectedConstructors.add(method); - if (isFinal) { - String msg = Messages.getMessage("InjectNoFinalMethod"); - diagnostics.add(createDiagnostic(method, unit, msg, - DIAGNOSTIC_CODE_INJECT_FINAL, method.getElementType(), - DiagnosticSeverity.Error)); - } - if (isAbstract) { - String msg = Messages.getMessage("InjectNoAbstractMethod"); - diagnostics.add(createDiagnostic(method, unit, msg, - DIAGNOSTIC_CODE_INJECT_ABSTRACT, method.getElementType(), - DiagnosticSeverity.Error)); - } - if (isStatic) { - String msg = Messages.getMessage("InjectNoStaticMethod"); - diagnostics.add(createDiagnostic(method, unit, msg, - DIAGNOSTIC_CODE_INJECT_STATIC, method.getElementType(), - DiagnosticSeverity.Error)); - } - - if (isGeneric) { - String msg = Messages.getMessage("InjectNoGenericMethod"); - diagnostics.add(createDiagnostic(method, unit, msg, - DIAGNOSTIC_CODE_INJECT_GENERIC, method.getElementType(), - DiagnosticSeverity.Error)); - } - } - } - - // if more than one 'inject' constructor, add diagnostic to all constructors - if (injectedConstructors.size() > 1) { - String msg = Messages.getMessage("InjectMoreThanOneConstructor"); - for (IMethod m : injectedConstructors) { - diagnostics.add(createDiagnostic(m, unit,msg, - DIAGNOSTIC_CODE_INJECT_CONSTRUCTOR, null, DiagnosticSeverity.Error)); - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate diagnostics", e); - } - } - - private boolean containsAnnotation(IType type, IAnnotation[] annotations, String annotationFQName) { - return Stream.of(annotations).anyMatch(annotation -> { - try { - return isMatchedJavaElement(type, annotation.getElementName(), annotationFQName); - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot validate annotations", e); - return false; - } - }); - } -} - diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/AbstractJavaContext.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/AbstractJavaContext.java similarity index 84% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/AbstractJavaContext.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/AbstractJavaContext.java index 1c0df323..a7d195f5 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/AbstractJavaContext.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/AbstractJavaContext.java @@ -11,8 +11,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction; +package org.eclipse.lsp4jakarta.jdt.core.java; import java.util.HashMap; import java.util.Map; @@ -22,14 +21,12 @@ import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; - -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; /** - * Abstract class for Java context for a given compilation unit. Reused from - * https://github.com/eclipse/lsp4mp/blob/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/AbtractJavaContext.java + * Abstract class for Java context for a given compilation unit. * - * @author credit to Angelo ZERR + * @author Angelo ZERR * */ public abstract class AbstractJavaContext { @@ -38,13 +35,13 @@ public abstract class AbstractJavaContext { private final ITypeRoot typeRoot; - private final JDTUtils utils; + private final IJDTUtils utils; private Map cache; private CompilationUnit fASTRoot; - public AbstractJavaContext(String uri, ITypeRoot typeRoot, JDTUtils utils) { + public AbstractJavaContext(String uri, ITypeRoot typeRoot, IJDTUtils utils) { this.uri = uri; this.typeRoot = typeRoot; this.utils = utils; @@ -63,14 +60,14 @@ public IJavaProject getJavaProject() { return getTypeRoot().getJavaProject(); } - public JDTUtils getUtils() { + public IJDTUtils getUtils() { return utils; } /** * Associates the specified value with the specified key in the cache. * - * @param key the key. + * @param key the key. * @param value the value. */ public void put(String key, Object value) { @@ -109,4 +106,4 @@ public void setASTRoot(CompilationUnit root) { fASTRoot = root; } -} \ No newline at end of file +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/ExtendedCodeAction.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/ExtendedCodeAction.java new file mode 100644 index 00000000..5dd2e45d --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/ExtendedCodeAction.java @@ -0,0 +1,88 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.lsp4j.CodeAction; + +/** + * Extends LSP CodeAction to store relevance information used to sort the code + * actions. + * + * @author Angelo ZERR + * + */ +public class ExtendedCodeAction extends CodeAction { + + private static class CodeActionComparator implements Comparator { + + @Override + public int compare(CodeAction ca1, CodeAction ca2) { + String k1 = ca1.getKind(); + String k2 = ca2.getKind(); + if (!StringUtils.isBlank(k1) && !StringUtils.isBlank(k2) && !k1.equals(k2)) { + return k1.compareTo(k2); + } + + if (ca1 instanceof ExtendedCodeAction && ca2 instanceof ExtendedCodeAction) { + int r1 = ((ExtendedCodeAction) ca1).getRelevance(); + int r2 = ((ExtendedCodeAction) ca2).getRelevance(); + int relevanceDif = r2 - r1; + if (relevanceDif != 0) { + return relevanceDif; + } + } + return ca1.getTitle().compareToIgnoreCase(ca2.getTitle()); + } + } + + private static final CodeActionComparator CODE_ACTION_COMPARATOR = new CodeActionComparator(); + + private transient int relevance; + + public ExtendedCodeAction(String name) { + super(name); + } + + /** + * Returns the relevance. + * + * @return the relevance. + */ + public int getRelevance() { + return relevance; + } + + /** + * Sets the relevance + * + * @param relevance the relevance + */ + public void setRelevance(int relevance) { + this.relevance = relevance; + } + + /** + * Sort the given code actions list by using relevance information. + * + * @param codeActions code actions to sort + */ + public static void sort(List codeActions) { + Collections.sort(codeActions, CODE_ACTION_COMPARATOR); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/IInvocationContext.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/IInvocationContext.java similarity index 91% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/IInvocationContext.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/IInvocationContext.java index 1fd625c6..53aeede5 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/IInvocationContext.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/IInvocationContext.java @@ -12,16 +12,13 @@ * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction; +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.CompilationUnit; /** - * Reused from - * https://github.com/eclipse/lsp4mp/blob/1fb9b326d89774f4404b30eabdba0e73d9c40c07/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/codeaction/IInvocationContext.java * Context information for quick fix and quick assist processors. *

    * Note: this interface is not intended to be implemented. diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/IJavaCodeActionParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/IJavaCodeActionParticipant.java similarity index 68% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/IJavaCodeActionParticipant.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/IJavaCodeActionParticipant.java index dab95cc6..93129446 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/IJavaCodeActionParticipant.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/IJavaCodeActionParticipant.java @@ -11,8 +11,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction; +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; import java.util.List; @@ -22,13 +21,20 @@ import org.eclipse.lsp4j.Diagnostic; /** - * Java codeAction participants API. reused from - * https://github.com/eclipse/lsp4mp/blob/b88710cc54170844717f655b9bff8bb4c4649a8d/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/codeaction/IJavaCodeActionParticipant.java + * Java codeAction participants API. * - * @author credit to Angelo ZERR + * @author Angelo ZERR * */ public interface IJavaCodeActionParticipant { + + /** + * Returns the unique identifier of this code action participant. + * + * @return the unique identifier of this code action participant + */ + String getParticipantId(); + /** * Returns true if the code actions are adaptable for the given context and * false otherwise. @@ -43,20 +49,27 @@ public interface IJavaCodeActionParticipant { * @return true if adaptable and false otherwise. * */ - default boolean isAdaptedForCodeAction(JavaCodeActionContext context, IProgressMonitor monitor) - throws CoreException { + default boolean isAdaptedForCodeAction(JavaCodeActionContext context, IProgressMonitor monitor) throws CoreException { return true; } /** * Return the code action list for a given compilation unit and null otherwise. * - * @param context the java code action context. + * @param context the java code action context. * @param diagnostic the diagnostic which must be fixed and null otherwise. - * @param monitor the progress monitor + * @param monitor the progress monitor * @return the code action list for a given compilation unit and null otherwise. * @throws CoreException */ List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException; + IProgressMonitor monitor) throws CoreException; + + /** + * Returns the code action with the TextEdits filled in. + * + * @param unresolved the code action to resolve + * @return the code action with the TextEdits filled in + */ + CodeAction resolveCodeAction(JavaCodeActionResolveContext context); } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationAttributeQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationAttributeQuickFix.java new file mode 100644 index 00000000..939a97d1 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationAttributeQuickFix.java @@ -0,0 +1,95 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.InsertAnnotationAttributeProposal; + +/** + * QuickFix for inserting attribute of a given annotation. + * + * @author Angelo ZERR + * + */ +public abstract class InsertAnnotationAttributeQuickFix implements IJavaCodeActionParticipant { + + private static final Logger LOGGER = Logger.getLogger(InsertAnnotationAttributeQuickFix.class.getName()); + + private static final String CODE_ACTION_LABEL = "Insert ''{0}'' attribute"; + + private final String attributeName; + + /** + * Constructor for inserting attribute annotation quick fix. + * + * @param attribute name list of annotation to insert. + */ + public InsertAnnotationAttributeQuickFix(String attributeName) { + this.attributeName = attributeName; + } + + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel(attributeName)); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), getCodeActionId())); + return Collections.singletonList(codeAction); + } + + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode selectedNode = context.getCoveringNode(); + Annotation annotation = (Annotation) selectedNode.getParent().getParent(); + String name = getLabel(attributeName); + ChangeCorrectionProposal proposal = new InsertAnnotationAttributeProposal(name, context.getCompilationUnit(), annotation, 0, attributeName); + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action edit for inserting an attribute value", e); + } + return toResolve; + } + + /** + * Returns the id for this code action. + * + * @return the id for this code action + */ + protected abstract ICodeActionId getCodeActionId(); + + private static String getLabel(String memberName) { + return MessageFormat.format(CODE_ACTION_LABEL, memberName); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationAttributesQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationAttributesQuickFix.java new file mode 100644 index 00000000..d4569fdd --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationAttributesQuickFix.java @@ -0,0 +1,149 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ModifyAnnotationProposal; + +/** + * Inserts the specified set of attributes the the specified annotation. + */ +public abstract class InsertAnnotationAttributesQuickFix implements IJavaCodeActionParticipant { + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(InsertAnnotationAttributesQuickFix.class.getName()); + + /** Code action label template. */ + private static final String CODE_ACTION_LABEL = "Insert ''{0}'' attribute{1} to @{2}"; + + /** The annotation to which attributes are added. */ + private final String annotation; + + /** The attributes the add to the annotation. */ + private final String[] attributes; + + /** + * Constructor. + * + * @param annotation The fully qualified annotation to modify. + * @param attributes The attribute names to add to given annotation. + */ + public InsertAnnotationAttributesQuickFix(String annotation, String... attributes) { + this.annotation = annotation; + this.attributes = attributes; + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertAnnotationAttributesQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + String name = getLabel(annotation, attributes); + ExtendedCodeAction codeAction = new ExtendedCodeAction(name); + codeAction.setRelevance(0); + codeAction.setDiagnostics(Collections.singletonList(diagnostic)); + codeAction.setKind(CodeActionKind.QuickFix); + + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), getCodeActionId())); + codeActions.add(codeAction); + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + String label = getLabel(annotation, attributes); + ASTNode node = context.getCoveringNode(); + IBinding parentType = getBinding(node); + ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, Arrays.asList(attributes), annotation); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, + "Unable to resolve code action edit for inserting an anotation with attributes", + e); + } + + return toResolve; + } + + /** + * Returns the id for this code action. + * + * @return The id for this code action. + */ + protected abstract ICodeActionId getCodeActionId(); + + /** + * Returns the code action label. + * + * @param annotaiton The annotation name. + * @param attributes The attribute names. + * + * @return The code action label. + */ + protected String getLabel(String annotation, String[] attributes) { + String[] parts = annotation.split("\\."); + String AnnotationName = (parts.length > 1) ? parts[parts.length - 1] : annotation; + String atributeNames = String.join(",", attributes); + String pluralSuffix = (attributes.length > 1) ? "s" : ""; + return MessageFormat.format(CODE_ACTION_LABEL, atributeNames, pluralSuffix, AnnotationName); + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationMissingQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationMissingQuickFix.java new file mode 100644 index 00000000..9638edc0 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertAnnotationMissingQuickFix.java @@ -0,0 +1,182 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.jdt.internal.corext.dom.Bindings; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.InsertAnnotationProposal; + +/** + * QuickFix for inserting annotations. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/codeaction/InsertAnnotationMissingQuickFix.java + * + * @author Angelo ZERR + * + */ +public abstract class InsertAnnotationMissingQuickFix implements IJavaCodeActionParticipant { + + private static final Logger LOGGER = Logger.getLogger(InsertAnnotationMissingQuickFix.class.getName()); + + protected static final String ANNOTATION_KEY = "annotation"; + + private final String[] annotations; + + private final boolean generateOnlyOneCodeAction; + + /** + * Constructor for insert annotation quick fix. + * + *

    + * The participant will generate a CodeAction per annotation. + *

    + * + * @param annotations list of annotation to insert. + */ + public InsertAnnotationMissingQuickFix(String... annotations) { + this(false, annotations); + } + + /** + * Constructor for insert annotation quick fix. + * + * @param generateOnlyOneCodeAction true if the participant must generate a + * CodeAction which insert the list of + * annotation and false otherwise. + * @param annotations list of annotation to insert. + */ + public InsertAnnotationMissingQuickFix(boolean generateOnlyOneCodeAction, String... annotations) { + this.generateOnlyOneCodeAction = generateOnlyOneCodeAction; + this.annotations = annotations; + } + + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + insertAnnotations(diagnostic, context, codeActions); + return codeActions; + } + + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + CodeActionResolveData data = (CodeActionResolveData) toResolve.getData(); + List resolveAnnotations = (List) data.getExtendedDataEntry(ANNOTATION_KEY); + String[] resolveAnnotationsArray = resolveAnnotations.toArray(String[]::new); + String name = getLabel(resolveAnnotationsArray); + ASTNode node = context.getCoveringNode(); + IBinding parentType = getBinding(node); + + ChangeCorrectionProposal proposal = new InsertAnnotationProposal(name, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, resolveAnnotationsArray); + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to create workspace edit for code action to insert missing annotation", e); + } + + return toResolve; + } + + protected IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + return Bindings.getBindingOfParentType(node); + } + + protected String[] getAnnotations() { + return this.annotations; + } + + protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, List codeActions) throws CoreException { + if (generateOnlyOneCodeAction) { + insertAnnotation(diagnostic, context, codeActions, annotations); + } else { + for (String annotation : annotations) { + insertAnnotation(diagnostic, context, codeActions, annotation); + } + } + } + + protected void insertAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, List codeActions, + String... annotations) throws CoreException { + String name = getLabel(annotations); + ExtendedCodeAction codeAction = new ExtendedCodeAction(name); + codeAction.setRelevance(0); + codeAction.setDiagnostics(Collections.singletonList(diagnostic)); + codeAction.setKind(CodeActionKind.QuickFix); + + Map extendedData = new HashMap<>(); + extendedData.put(ANNOTATION_KEY, Arrays.asList(annotations)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), extendedData, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), getCodeActionId())); + + codeActions.add(codeAction); + } + + private static String getLabel(String[] annotations) { + StringBuilder name = new StringBuilder("Insert "); + for (int i = 0; i < annotations.length; i++) { + String annotation = annotations[i]; + String annotationName = annotation.substring(annotation.lastIndexOf('.') + 1, annotation.length()); + if (i > 0) { + name.append(", "); + } + name.append("@"); + name.append(annotationName); + } + return name.toString(); + } + + /** + * Returns the id for this code action. + * + * @return the id for this code action + */ + protected abstract ICodeActionId getCodeActionId(); + + /** + * Returns true if all the listed annotations should be added in one code + * action, and false if separate code actions should be generated for each + * annotation. + * + * @return true if all the listed annotations should be added in one code + * action, and false if separate code actions should be generated for + * each annotation + */ + protected boolean isGenerateOnlyOneCodeAction() { + return this.generateOnlyOneCodeAction; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertDefaultConstructorToClassQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertDefaultConstructorToClassQuickFix.java new file mode 100644 index 00000000..c721a7fb --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/InsertDefaultConstructorToClassQuickFix.java @@ -0,0 +1,130 @@ +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.AddConstructorProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; + +/** + * Inserts default constructor to the active class. + */ +public abstract class InsertDefaultConstructorToClassQuickFix implements IJavaCodeActionParticipant { + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(InsertDefaultConstructorToClassQuickFix.class.getName()); + + /** Code action label template. */ + private static final String CODE_ACTION_LABEL = "Add a default ''{0}'' constructor to this class"; + + /** + * Access modifier for the new constructor. + */ + private final String accessModifier; + + /** + * Constructor. + * + * @param accessModifier The access modifier to use. + */ + public InsertDefaultConstructorToClassQuickFix(String accessModifier) { + this.accessModifier = accessModifier; + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertDefaultConstructorToClassQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + List codeActions = new ArrayList<>(); + if (parentType != null) { + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel(accessModifier)); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), getCodeActionId())); + codeActions.add(codeAction); + } + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + String label = getLabel(accessModifier); + + ChangeCorrectionProposal proposal = new AddConstructorProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, accessModifier); + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action to insert a default constructor to class", + e); + } + + return toResolve; + } + + /** + * Returns the id for this code action. + * + * @return the id for this code action + */ + protected abstract ICodeActionId getCodeActionId(); + + /** + * Returns the code action label. + * + * @param am The access modifier name. + * + * @return The code action label. + */ + protected String getLabel(String am) { + return MessageFormat.format(CODE_ACTION_LABEL, am); + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/JavaCodeActionContext.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/JavaCodeActionContext.java similarity index 70% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/JavaCodeActionContext.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/JavaCodeActionContext.java index f0d5e004..439ae6f2 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/JavaCodeActionContext.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/JavaCodeActionContext.java @@ -11,8 +11,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.codeAction; +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; import java.util.Arrays; @@ -25,15 +24,15 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.core.ChangeUtil; +import org.eclipse.lsp4jakarta.jdt.core.java.AbstractJavaContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.ChangeUtil; /** - * Java codeAction context for a given compilation unit. Reused from - * https://github.com/eclipse/lsp4mp/blob/b88710cc54170844717f655b9bff8bb4c4649a8d/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/codeaction/JavaCodeActionContext.java - * - * @author credit to Angelo ZERR + * Java codeAction context for a given compilation unit. + * + * @author Angelo ZERR * */ public class JavaCodeActionContext extends AbstractJavaContext implements IInvocationContext { @@ -44,8 +43,8 @@ public class JavaCodeActionContext extends AbstractJavaContext implements IInvoc private final JakartaJavaCodeActionParams params; private NodeFinder fNodeFinder; - public JavaCodeActionContext(ITypeRoot typeRoot, int selectionOffset, int selectionLength, JDTUtils utils, - JakartaJavaCodeActionParams params) { + public JavaCodeActionContext(ITypeRoot typeRoot, int selectionOffset, int selectionLength, IJDTUtils utils, + JakartaJavaCodeActionParams params) { super(params.getUri(), typeRoot, utils); this.selectionOffset = selectionOffset; this.selectionLength = selectionLength; @@ -97,20 +96,28 @@ public ASTNode getCoveredNode() { return fNodeFinder.getCoveredNode(); } - public CodeAction convertToCodeAction(ChangeCorrectionProposal proposal, Diagnostic... diagnostics) - throws CoreException { + public CodeAction convertToCodeAction(ChangeCorrectionProposal proposal, Diagnostic... diagnostics) throws CoreException { String name = proposal.getName(); WorkspaceEdit edit = ChangeUtil.convertToWorkspaceEdit(proposal.getChange(), getUri(), getUtils(), - params.isResourceOperationSupported()); + params.isResourceOperationSupported()); if (!ChangeUtil.hasChanges(edit)) { return null; } - CodeAction codeAction = new CodeAction(); - codeAction.setTitle(name); + ExtendedCodeAction codeAction = new ExtendedCodeAction(name); + codeAction.setRelevance(proposal.getRelevance()); codeAction.setKind(proposal.getKind()); codeAction.setEdit(edit); codeAction.setDiagnostics(Arrays.asList(diagnostics)); return codeAction; } + public WorkspaceEdit convertToWorkspaceEdit(ChangeCorrectionProposal proposal) throws CoreException { + WorkspaceEdit edit = ChangeUtil.convertToWorkspaceEdit(proposal.getChange(), getUri(), getUtils(), + params.isResourceOperationSupported()); + if (!ChangeUtil.hasChanges(edit)) { + return null; + } + return edit; + } + } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/JavaCodeActionResolveContext.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/JavaCodeActionResolveContext.java new file mode 100644 index 00000000..a99c6a0f --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/JavaCodeActionResolveContext.java @@ -0,0 +1,39 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; + +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; + +/** + * Similar to {@see JavaCodeActionContext}, but includes additional information + * needed for code action resolve. + * + * @auhtod datho7561 + */ +public class JavaCodeActionResolveContext extends JavaCodeActionContext { + + private final CodeAction unresolved; + + public JavaCodeActionResolveContext(ITypeRoot typeRoot, int selectionOffset, int selectionLength, IJDTUtils utils, + JakartaJavaCodeActionParams params, CodeAction unresolved) { + super(typeRoot, selectionOffset, selectionLength, utils, params); + this.unresolved = unresolved; + } + + public CodeAction getUnresolved() { + return this.unresolved; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/RemoveAnnotationConflictQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/RemoveAnnotationConflictQuickFix.java new file mode 100644 index 00000000..fb765655 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/RemoveAnnotationConflictQuickFix.java @@ -0,0 +1,213 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.RemoveAnnotationProposal; + +/** + * Removes annotations. + */ +public abstract class RemoveAnnotationConflictQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(RemoveAnnotationConflictQuickFix.class.getName()); + + /** Annotations to be removed. */ + private final String[] annotations; + + /** + * Indicator to generate a single action for a list of annotations or one for + * each annotation in the list. + */ + protected final boolean generateOnlyOneCodeAction; + + /** Map key to retrieve a list of annotations. */ + public static final String ANNOTATIONS_KEY = "annotations"; + + /** + * Constructor. + * + * @param annotations The list of annotations to be removed. + */ + public RemoveAnnotationConflictQuickFix(String... annotations) { + this(false, annotations); + } + + /** + * Constructor. + * + * @param generateOnlyOneCodeAction The single action creation indicator. If + * true, a single code action is created to + * remove the specified set of annotations; + * otherwise, a code action is created per + * annotation to delete. + */ + public RemoveAnnotationConflictQuickFix(boolean generateOnlyOneCodeAction, String... annotations) { + this.generateOnlyOneCodeAction = generateOnlyOneCodeAction; + this.annotations = annotations; + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + if (parentType != null) { + createCodeActions(diagnostic, context, parentType, codeActions); + } + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + CodeActionResolveData data = (CodeActionResolveData) toResolve.getData(); + List annotationToRemoveList = (List) data.getExtendedDataEntry(ANNOTATIONS_KEY); + String[] annotationToRemove = annotationToRemoveList.toArray(String[]::new); + String label = getLabel(annotationToRemove); + ChangeCorrectionProposal proposal = new RemoveAnnotationProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, context.getCoveredNode().getParent(), annotationToRemove); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action to remove annotation", e); + } + + return toResolve; + } + + /** + * Creates one or more code actions for the given annotations. + * + * @param diagnostic The code diagnostic associated with the action to be + * created. + * @param context The context. + * @param parentType The parent type. + * @param codeActions The list of code actions. + * + * @throws CoreException + */ + protected void createCodeActions(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, + List codeActions) throws CoreException { + if (generateOnlyOneCodeAction) { + createCodeAction(diagnostic, context, parentType, codeActions, annotations); + } else { + for (String annotation : annotations) { + createCodeAction(diagnostic, context, parentType, codeActions, annotation); + } + } + } + + /** + * Creates a code action to remove the input annotations. + * + * @param diagnostic The code diagnostic associated with the action to be + * created. + * @param context The context. + * @param parentType The parent type. + * @param codeActions The list of code actions. + * @param annotations The annotations to remove. + * + * + * @throws CoreException + */ + protected void createCodeAction(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, + List codeActions, String... annotations) throws CoreException { + String label = getLabel(annotations); + + ExtendedCodeAction codeAction = new ExtendedCodeAction(label); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + Map extendedData = new HashMap(); + extendedData.put(ANNOTATIONS_KEY, Arrays.asList(annotations)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), extendedData, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), getCodeActionId())); + + codeActions.add(codeAction); + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } + + protected String[] getAnnotations() { + return this.annotations; + } + + /** + * Returns the label associated with the input annotations. + * + * @param annotations The annotations to remove. + * @return The label associated with the input annotations. + */ + protected String getLabel(String[] annotations) { + StringBuilder name = new StringBuilder("Remove "); + for (int i = 0; i < annotations.length; i++) { + String annotation = annotations[i]; + String annotationName = annotation.substring(annotation.lastIndexOf('.') + 1, annotation.length()); + if (i > 0) { + name.append(", "); // assume comma list is ok: @A, @B, @C + } + name.append("@"); // Java syntax + name.append(annotationName); + } + return name.toString(); + } + + /** + * Returns the id for this code action. + * + * @return the id for this code action + */ + protected abstract ICodeActionId getCodeActionId(); +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/RemoveModifierConflictQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/RemoveModifierConflictQuickFix.java new file mode 100644 index 00000000..d3ec9bf1 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/codeaction/RemoveModifierConflictQuickFix.java @@ -0,0 +1,207 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Himanshu Chotwani - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.core.java.codeaction; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ModifyModifiersProposal; + +/** + * Removes modifiers from the declaring element. + */ +public abstract class RemoveModifierConflictQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(RemoveAnnotationConflictQuickFix.class.getName()); + + /** Map key to retrieve a list of modifiers. */ + public static final String MODIFIERS_KEY = "modifiers"; + + /** Code action label template. */ + private static final String CODE_ACTION_LABEL = "Remove the ''{0}'' modifier"; + + /** + * Array of modifiers to remove. + */ + private final String[] modifiers; + + /** + * Single action creation indicator. If true, a single code action is created to + * remove the specified set of modifiers; otherwise, a code action is created + * per modifier to delete. + */ + protected final boolean generateOnlyOneCodeAction; + + /** + * Constructor. + * + * @param modifiers The modifiers to remove. + */ + public RemoveModifierConflictQuickFix(String... modifiers) { + this(false, modifiers); + } + + /** + * Constructor. + * + * @param generateOnlyOneCodeAction The single action creation indicator. If + * true, a single code action is created to + * remove the specified set of modifiers; + * otherwise, a code action is created per + * modifier to delete. + * @param modifiers list of modifiers to remove. + */ + public RemoveModifierConflictQuickFix(boolean generateOnlyOneCodeAction, String... modifiers) { + this.generateOnlyOneCodeAction = generateOnlyOneCodeAction; + this.modifiers = modifiers; + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + if (parentType != null) { + createCodeActions(diagnostic, context, codeActions); + } + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode coveredParentNode = context.getCoveredNode().getParent(); + IBinding parentType = getBinding(context.getCoveredNode()); + CodeActionResolveData data = (CodeActionResolveData) toResolve.getData(); + List modifiersToDeleteList = (List) data.getExtendedDataEntry(MODIFIERS_KEY); + String[] modifiersToDelete = modifiersToDeleteList.toArray(String[]::new); + String label = getLabel(modifiersToDelete); + ModifyModifiersProposal proposal = new ModifyModifiersProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, coveredParentNode, new ArrayList<>(), Arrays.asList(modifiers)); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action to remove annotation", e); + } + + return toResolve; + } + + /** + * Creates one or more code actions to remove one or more modifiers. + * + * @param diagnostic The code diagnostic associated with the action to be + * created. + * @param context The context. + * @param codeActions The list of code action to update. + * + * @throws CoreException + */ + protected void createCodeActions(Diagnostic diagnostic, JavaCodeActionContext context, + List codeActions) throws CoreException { + if (generateOnlyOneCodeAction) { + createCodeAction(diagnostic, context, codeActions, modifiers); + } else { + for (String modifier : modifiers) { + createCodeAction(diagnostic, context, codeActions, modifier); + } + } + } + + /** + * Creates a code action to remove the input modifiers. + * + * @param diagnostic The code diagnostic associated with the action to be + * created. + * @param context The context. + * @param codeActions The list of code actions. + * @param modifiers The modifiers to remove. + * + * + * @throws CoreException + */ + protected void createCodeAction(Diagnostic diagnostic, JavaCodeActionContext context, + List codeActions, String... modifiers) throws CoreException { + String label = getLabel(modifiers); + ExtendedCodeAction codeAction = new ExtendedCodeAction(label); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + Map extendedData = new HashMap(); + extendedData.put(MODIFIERS_KEY, Arrays.asList(modifiers)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), extendedData, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), getCodeActionId())); + + codeActions.add(codeAction); + } + + /** + * Returns the label associated with the input modifier. + * + * @param modifier The modifier to remove. + * @return The label associated with the input modifier. + */ + protected String getLabel(String... modifier) { + return MessageFormat.format(CODE_ACTION_LABEL, modifier[0]); + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } else if (node.getParent() instanceof MethodDeclaration) { + return ((MethodDeclaration) node.getParent()).resolveBinding(); + } + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } + + /** + * Returns the id for this code action. + * + * @return the id for this code action + */ + protected abstract ICodeActionId getCodeActionId(); +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/completion/IJavaCompletionParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/completion/IJavaCompletionParticipant.java new file mode 100644 index 00000000..e2f4d71f --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/completion/IJavaCompletionParticipant.java @@ -0,0 +1,51 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.completion; + +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.lsp4j.CompletionItem; + +/** + * The API for a completion feature + * + * @author datho7561 + */ +public interface IJavaCompletionParticipant { + + /** + * Returns true if this completion feature should be active in this context, and false otherwise + * + * @param context the context of where completion is triggered + * @param monitor the progress monitor + * @return true if this completion feature should be active in this context, and false otherwise + * @throws CoreException + */ + default boolean isAdaptedForCompletion(JavaCompletionContext context, IProgressMonitor monitor) throws CoreException { + return true; + } + + /** + * Returns the completion items for the given completion context + * + * @param context the completion context + * @param monitor the progress monitor + * @return the completion items for the given completion context + * @throws CoreException + */ + List collectCompletionItems(JavaCompletionContext context, IProgressMonitor monitor) throws CoreException; + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/completion/JavaCompletionContext.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/completion/JavaCompletionContext.java new file mode 100644 index 00000000..2c7e7dbf --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/completion/JavaCompletionContext.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2021 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.completion; + +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.lsp4jakarta.jdt.core.java.AbstractJavaContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; + +/** + * Represents the context of where completion was triggered + * + * @author datho7561 + */ +public class JavaCompletionContext extends AbstractJavaContext { + + private int offset; + + public JavaCompletionContext(String uri, ITypeRoot typeRoot, IJDTUtils utils, int offset) { + super(uri, typeRoot, utils); + this.offset = offset; + } + + /** + * Returns the offset from the beginning of the document where completion was + * triggered + * + * @return the offset from the beginning of the document where completion was + * triggered + */ + public int getOffset() { + return offset; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ASTRewriteCorrectionProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ASTRewriteCorrectionProposal.java new file mode 100644 index 00000000..5ed1ee1b --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ASTRewriteCorrectionProposal.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2000, 2014 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copied from /org.eclipse.jdt.ui/src/org/eclipse/jdt/ui/text/java/correction/ASTRewriteCorrectionProposal.java + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; +import org.eclipse.jdt.core.manipulation.CodeStyleConfiguration; +import org.eclipse.jdt.ls.core.internal.StatusFactory; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.TextEdit; + +/** + * A proposal for quick fixes and quick assists that works on an AST rewrite. + * Either a rewrite is directly passed in the constructor or the method + * {@link #getRewrite()} is overridden to provide the AST rewrite that is + * evaluated on the document when the proposal is applied. + */ +public class ASTRewriteCorrectionProposal extends CUCorrectionProposal { + + private ASTRewrite fRewrite; + private ImportRewrite fImportRewrite; + + /** + * Constructs an AST rewrite correction proposal. + * + * @param name the display name of the proposal + * @param cu the compilation unit that is modified + * @param rewrite the AST rewrite that is invoked when the proposal is applied + * or null if {@link #getRewrite()} is overridden + * @param relevance the relevance of this proposal + */ + public ASTRewriteCorrectionProposal(String name, String kind, ICompilationUnit cu, ASTRewrite rewrite, + int relevance) { + super(name, kind, cu, null, relevance); + fRewrite = rewrite; + } + + /** + * Returns the import rewrite used for this compilation unit. + * + * @return the import rewrite or null if no import rewrite has been + * set + * @nooverride This method is not intended to be re-implemented or extended by + * clients. + */ + public ImportRewrite getImportRewrite() { + return fImportRewrite; + } + + /** + * Sets the import rewrite used for this compilation unit. + * + * @param rewrite the import rewrite + * @nooverride This method is not intended to be re-implemented or extended by + * clients. + */ + public void setImportRewrite(ImportRewrite rewrite) { + fImportRewrite = rewrite; + } + + /** + * Creates and sets the import rewrite used for this compilation unit. + * + * @param astRoot the AST for the current CU + * @return the created import rewrite + * @nooverride This method is not intended to be re-implemented or extended by + * clients. + */ + public ImportRewrite createImportRewrite(CompilationUnit astRoot) { + fImportRewrite = CodeStyleConfiguration.createImportRewrite(astRoot, true); + return fImportRewrite; + } + + @Override + protected void addEdits(IDocument document, TextEdit editRoot) throws CoreException { + super.addEdits(document, editRoot); + ASTRewrite rewrite = getRewrite(); + if (rewrite != null) { + try { + TextEdit edit = rewrite.rewriteAST(); + editRoot.addChild(edit); + } catch (IllegalArgumentException e) { + throw new CoreException(StatusFactory.newErrorStatus("Invalid AST rewriter", e)); + } + } + if (fImportRewrite != null) { + editRoot.addChild(fImportRewrite.rewriteImports(new NullProgressMonitor())); + } + } + + /** + * Returns the rewrite that has been passed in the constructor. Implementors can + * override this method to create the rewrite lazily. This method will only be + * called once. + * + * @return the rewrite to be used + * @throws CoreException when the rewrite could not be created + */ + protected ASTRewrite getRewrite() throws CoreException { + if (fRewrite == null) { + IStatus status = StatusFactory.newErrorStatus("Rewrite not initialized", null); //$NON-NLS-1$ + throw new CoreException(status); + } + return fRewrite; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/AddConstructorProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/AddConstructorProposal.java similarity index 86% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/AddConstructorProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/AddConstructorProposal.java index 2d0b8f5a..054e372f 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/AddConstructorProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/AddConstructorProposal.java @@ -10,8 +10,7 @@ * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; - +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; import java.util.List; @@ -24,7 +23,6 @@ import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Modifier; -import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; @@ -34,36 +32,37 @@ /** * Code action proposal for adding a no-arg constructor to a class - * - * @author Leslie Dawson - * @see PersistenceEntityQuickFix - * + * + * @author Leslie Dawson + * @see PersistenceEntityQuickFix + * */ -public class AddConstructorProposal extends ChangeCorrectionProposal { +public class AddConstructorProposal extends ASTRewriteCorrectionProposal { private final CompilationUnit invocationNode; private final IBinding binding; private final String visibility; - + /** * Constructor for AddMethodProposal - * + * */ public AddConstructorProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance) { + IBinding binding, int relevance) { super(label, CodeActionKind.QuickFix, targetCU, null, relevance); this.invocationNode = invocationNode; this.binding = binding; this.visibility = "protected"; } - + /** * Constructor for AddMethodProposal - * - * @param visibility a valid visibility modifier for the constructor, defaults to protected + * + * @param visibility a valid visibility modifier for the constructor, defaults + * to protected */ public AddConstructorProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, String visibility) { + IBinding binding, int relevance, String visibility) { super(label, CodeActionKind.QuickFix, targetCU, null, relevance); this.invocationNode = invocationNode; this.binding = binding; @@ -76,14 +75,14 @@ protected ASTRewrite getRewrite() throws CoreException { ASTNode declNode = null; ASTNode boundNode = invocationNode.findDeclaringNode(binding); CompilationUnit newRoot = invocationNode; - + if (boundNode != null) { declNode = boundNode; } else { newRoot = ASTResolving.createQuickFixAST(getCompilationUnit(), null); declNode = newRoot.findDeclaringNode(binding.getKey()); - } - + } + AST ast = declNode.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast); ListRewrite list = rewrite.getListRewrite(declNode, TypeDeclaration.BODY_DECLARATIONS_PROPERTY); @@ -93,12 +92,12 @@ protected ASTRewrite getRewrite() throws CoreException { SimpleName name = ast.newSimpleName(declNode.getStructuralProperty(TypeDeclaration.NAME_PROPERTY).toString()); Block methodBody = ast.newBlock(); List modifiers = md.modifiers(); - + modifiers.add(ast.newModifier(Modifier.ModifierKeyword.toKeyword(visibility))); md.setName(name); md.setConstructor(true); md.setBody(methodBody); - + // insert method list.insertFirst(md, null); return rewrite; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/CUCorrectionProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/CUCorrectionProposal.java new file mode 100644 index 00000000..78d54264 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/CUCorrectionProposal.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2000, 2012 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copied from /org.eclipse.jdt.ui/src/org/eclipse/jdt/ui/text/java/correction/CUCorrectionProposal.java + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.manipulation.CUCorrectionProposalCore; +import org.eclipse.jdt.core.manipulation.ICUCorrectionProposal; +import org.eclipse.jdt.core.refactoring.CompilationUnitChange; +import org.eclipse.jface.text.IDocument; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.TextChange; +import org.eclipse.text.edits.TextEdit; + +/** + * A proposal for quick fixes and quick assists that work on a single + * compilation unit. Either a {@link TextChange text change} is directly passed + * in the constructor or method {@link #addEdits(IDocument, TextEdit)} is + * overridden to provide the text edits that are applied to the document when + * the proposal is evaluated. + *

    + * The proposal takes care of the preview of the changes as proposal + * information. + *

    + */ +public class CUCorrectionProposal extends ChangeCorrectionProposal implements ICUCorrectionProposal { + + private CUCorrectionProposalCore fProposalCore; + + /** + * Constructs a correction proposal working on a compilation unit with a given + * text change. + * + * @param name the name that is displayed in the proposal selection dialog + * @param kind the kind of the correction type that the proposal performs + * @param cu the compilation unit to which the change can be applied + * @param change the change that is executed when the proposal is applied or + * null if implementors override + * {@link #addEdits(IDocument, TextEdit)} to provide the text + * edits or {@link #createTextChange()} to provide a text + * change + * @param relevance the relevance of this proposal + */ + public CUCorrectionProposal(String name, String kind, ICompilationUnit cu, TextChange change, int relevance) { + super(name, kind, change, relevance); + if (cu == null) { + throw new IllegalArgumentException("Compilation unit must not be null"); //$NON-NLS-1$ + } + fProposalCore = new CUCorrectionProposalCore(name, cu, change, relevance); + } + + /** + * Called when the {@link CompilationUnitChange} is initialized. Subclasses can + * override to add text edits to the root edit of the change. Implementors must + * not access the proposal, e.g. not call {@link #getChange()}. + *

    + * The default implementation does not add any edits + *

    + * + * @param document content of the underlying compilation unit. To be accessed + * read only. + * @param editRoot The root edit to add all edits to + * @throws CoreException can be thrown if adding the edits is failing. + */ + protected void addEdits(IDocument document, TextEdit editRoot) throws CoreException { + // empty default implementation + } + + @Override + public Object getAdditionalProposalInfo(IProgressMonitor monitor) { + return fProposalCore.getAdditionalProposalInfo(monitor); + } + + @Override + public void apply() throws CoreException { + performChange(); + } + + /** + * Creates the text change for this proposal. This method is only called once + * and only when no text change has been passed in + * {@link #CUCorrectionProposal(String, ICompilationUnit, TextChange, int)}. + * + * @return the created text change + * @throws CoreException if the creation of the text change failed + */ + protected TextChange createTextChange() throws CoreException { + TextChange change = fProposalCore.getNewChange(); + // initialize text change + IDocument document = change.getCurrentDocument(new NullProgressMonitor()); + addEdits(document, change.getEdit()); + return change; + } + + @Override + protected final Change createChange() throws CoreException { + return createTextChange(); // make sure that only text changes are allowed here + } + + /** + * Returns the text change that is invoked when the change is applied. + * + * @return the text change that is invoked when the change is applied + * @throws CoreException if accessing the change failed + */ + @Override + public final TextChange getTextChange() throws CoreException { + return (TextChange) getChange(); + } + + /** + * The compilation unit on which the change works. + * + * @return the compilation unit on which the change works + */ + public final ICompilationUnit getCompilationUnit() { + return fProposalCore.getCompilationUnit(); + } + + /** + * Creates a preview of the content of the compilation unit after applying the + * change. + * + * @return the preview of the changed compilation unit + * @throws CoreException if the creation of the change failed + * + * @noreference This method is not intended to be referenced by clients. + */ + public String getPreviewContent() throws CoreException { + return getTextChange().getPreviewContent(new NullProgressMonitor()); + } + + @Override + public String toString() { + try { + return getPreviewContent(); + } catch (CoreException e) { + // didn't work out + } + return super.toString(); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ChangeCorrectionProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ChangeCorrectionProposal.java new file mode 100644 index 00000000..c7fa62c4 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ChangeCorrectionProposal.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2000, 2014 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copied from /org.eclipse.jdt.ui/src/org/eclipse/jdt/ui/text/java/correction/ChangeCorrectionProposal.java + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.manipulation.ChangeCorrectionProposalCore; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.IUndoManager; +import org.eclipse.ltk.core.refactoring.RefactoringCore; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; + +public class ChangeCorrectionProposal extends ChangeCorrectionProposalCore { + // LSP: Code Action Kind + private String fKind; + + /** + * Constructs a change correction proposal. + * + * @param name the name that is displayed in the proposal selection dialog + * @param change the change that is executed when the proposal is applied or + * null if the change will be created by + * implementors of {@link #createChange()} + * @param relevance the relevance of this proposal + */ + public ChangeCorrectionProposal(String name, String kind, Change change, int relevance) { + super(name, change, relevance); + fKind = kind; + } + + /** + * Performs the change associated with this proposal. + *

    + * Subclasses may extend, but must call the super implementation. + * + * @throws CoreException when the invocation of the change failed + */ + @Override + protected void performChange() throws CoreException { + + Change change = null; + try { + change = getChange(); + if (change != null) { + + change.initializeValidationData(new NullProgressMonitor()); + RefactoringStatus valid = change.isValid(new NullProgressMonitor()); + if (valid.hasFatalError()) { + IStatus status = new Status(IStatus.ERROR, JakartaCorePlugin.PLUGIN_ID, IStatus.ERROR, valid.getMessageMatchingSeverity(RefactoringStatus.FATAL), null); + throw new CoreException(status); + } else { + IUndoManager manager = RefactoringCore.getUndoManager(); + Change undoChange; + boolean successful = false; + try { + manager.aboutToPerformChange(change); + undoChange = change.perform(new NullProgressMonitor()); + successful = true; + } finally { + manager.changePerformed(change, successful); + } + if (undoChange != null) { + undoChange.initializeValidationData(new NullProgressMonitor()); + manager.addUndo(getName(), undoChange); + } + } + } + } finally { + + if (change != null) { + change.dispose(); + } + } + } + + /** + * Returns the kind of the proposal. + * + * @return the kind of the proposal + */ + public String getKind() { + return fKind; + } + + /** + * @param codeActionKind the Code Action Kind to set + */ + public void setKind(String codeActionKind) { + this.fKind = codeActionKind; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ExtendClassProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ExtendClassProposal.java similarity index 92% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ExtendClassProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ExtendClassProposal.java index 7613f0be..112c5f83 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ExtendClassProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ExtendClassProposal.java @@ -12,7 +12,7 @@ * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; @@ -28,19 +28,18 @@ import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; -import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; import org.eclipse.jdt.internal.corext.dom.Bindings; import org.eclipse.lsp4j.CodeActionKind; -public class ExtendClassProposal extends ChangeCorrectionProposal { +public class ExtendClassProposal extends ASTRewriteCorrectionProposal { private IBinding fBinding; private CompilationUnit fAstRoot; private String interfaceType; public ExtendClassProposal(String name, ICompilationUnit targetCU, ITypeBinding binding, CompilationUnit astRoot, - String interfaceType, int relevance) { + String interfaceType, int relevance) { super(name, CodeActionKind.QuickFix, targetCU, null, relevance); Assert.isTrue(binding != null && Bindings.isDeclarationBinding(binding)); diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ImplementInterfaceProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ImplementInterfaceProposal.java similarity index 73% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ImplementInterfaceProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ImplementInterfaceProposal.java index a2512882..049978f7 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ImplementInterfaceProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ImplementInterfaceProposal.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2023 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at @@ -12,7 +12,9 @@ * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; + +import java.text.MessageFormat; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; @@ -34,29 +36,42 @@ import org.eclipse.jdt.internal.corext.dom.Bindings; import org.eclipse.lsp4j.CodeActionKind; -/** - * QuickFix for implementing interface - * - * copied from - * /org.eclipse.jdt.ui/src/org/eclipse/jdt/internal/ui/text/correction/proposals/ImplementInterfaceProposal.java - */ -public class ImplementInterfaceProposal extends ChangeCorrectionProposal { +public class ImplementInterfaceProposal extends ASTRewriteCorrectionProposal { + + private static final String TITLE_MESSAGE = "Let ''{0}'' implement ''{1}''"; private IBinding fBinding; private CompilationUnit fAstRoot; private String interfaceType; public ImplementInterfaceProposal(String name, ICompilationUnit targetCU, ITypeBinding binding, - CompilationUnit astRoot, String interfaceType, int relevance) { - super(name, CodeActionKind.QuickFix, targetCU, null, relevance); + CompilationUnit astRoot, + String interfaceType, int relevance) { + super(name, CodeActionKind.QuickFix, targetCU, null, relevance); // $NON-NLS-1$ + + Assert.isTrue(binding != null && Bindings.isDeclarationBinding(binding)); + + fBinding = binding; + fAstRoot = astRoot; + this.interfaceType = interfaceType; + } + + public ImplementInterfaceProposal(ICompilationUnit targetCU, ITypeBinding binding, CompilationUnit astRoot, + String interfaceType, int relevance) { + super("", CodeActionKind.QuickFix, targetCU, null, relevance); //$NON-NLS-1$ Assert.isTrue(binding != null && Bindings.isDeclarationBinding(binding)); fBinding = binding; fAstRoot = astRoot; this.interfaceType = interfaceType; + + String[] args = { BasicElementLabels.getJavaElementName(binding.getName()), + BasicElementLabels.getJavaElementName(interfaceType) }; + setDisplayName(MessageFormat.format(TITLE_MESSAGE, args)); } + @Override protected ASTRewrite getRewrite() throws CoreException { ASTNode boundNode = fAstRoot.findDeclaringNode(fBinding); ASTNode declNode = null; @@ -77,13 +92,11 @@ protected ASTRewrite getRewrite() throws CoreException { Type newInterface = ast.newSimpleType(ast.newName(name)); ASTRewrite rewrite = ASTRewrite.create(ast); - ListRewrite listRewrite = rewrite.getListRewrite(declNode, TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY); listRewrite.insertLast(newInterface, null); - return rewrite; } return null; } -} \ No newline at end of file +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/InsertAnnotationAttributeProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/InsertAnnotationAttributeProposal.java new file mode 100644 index 00000000..0c4effc4 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/InsertAnnotationAttributeProposal.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2000, 2016 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copied from /org.eclipse.jdt.ui/src/org/eclipse/jdt/internal/ui/text/correction/proposals/MissingAnnotationAttributesProposal.java + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.ArrayInitializer; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MarkerAnnotation; +import org.eclipse.jdt.core.dom.MemberValuePair; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.SingleMemberAnnotation; +import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; +import org.eclipse.jdt.core.dom.rewrite.ListRewrite; +import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; +import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; +import org.eclipse.lsp4j.CodeActionKind; + +public class InsertAnnotationAttributeProposal extends LinkedCorrectionProposal { + + private final Annotation fAnnotation; + private final Set attributes; + + public InsertAnnotationAttributeProposal(String label, ICompilationUnit targetCU, Annotation annotation, + int relevance, String... attributes) { + super(label, CodeActionKind.QuickFix, targetCU, null, relevance); + this.fAnnotation = annotation; + this.attributes = new HashSet<>(Arrays.asList(attributes)); + } + + @Override + protected ASTRewrite getRewrite() throws CoreException { + AST ast = fAnnotation.getAST(); + + ASTRewrite rewrite = ASTRewrite.create(ast); + createImportRewrite((CompilationUnit) fAnnotation.getRoot()); + + ListRewrite listRewrite; + if (fAnnotation instanceof NormalAnnotation) { + listRewrite = rewrite.getListRewrite(fAnnotation, NormalAnnotation.VALUES_PROPERTY); + } else { + NormalAnnotation newAnnotation = ast.newNormalAnnotation(); + newAnnotation.setTypeName((Name) rewrite.createMoveTarget(fAnnotation.getTypeName())); + rewrite.replace(fAnnotation, newAnnotation, null); + + listRewrite = rewrite.getListRewrite(newAnnotation, NormalAnnotation.VALUES_PROPERTY); + } + addDefinedAtributes(fAnnotation.resolveTypeBinding(), listRewrite); + + return rewrite; + } + + private void addDefinedAtributes(ITypeBinding binding, ListRewrite listRewriter) { + Set implementedAttribs = new HashSet(); + if (fAnnotation instanceof NormalAnnotation) { + List list = ((NormalAnnotation) fAnnotation).values(); + for (int i = 0; i < list.size(); i++) { + MemberValuePair curr = list.get(i); + implementedAttribs.add(curr.getName().getIdentifier()); + } + } else if (fAnnotation instanceof SingleMemberAnnotation) { + implementedAttribs.add("value"); //$NON-NLS-1$ + } + ASTRewrite rewriter = listRewriter.getASTRewrite(); + AST ast = rewriter.getAST(); + ImportRewriteContext context = null; + ASTNode bodyDeclaration = ASTResolving.findParentBodyDeclaration(listRewriter.getParent()); + if (bodyDeclaration != null) { + context = new ContextSensitiveImportRewriteContext(bodyDeclaration, getImportRewrite()); + } + + IMethodBinding[] declaredMethods = binding.getDeclaredMethods(); + for (int i = 0; i < declaredMethods.length; i++) { + IMethodBinding curr = declaredMethods[i]; + if (!implementedAttribs.contains(curr.getName()) && attributes.contains(curr.getName())) { + MemberValuePair pair = ast.newMemberValuePair(); + pair.setName(ast.newSimpleName(curr.getName())); + pair.setValue(newDefaultExpression(ast, curr.getReturnType(), context)); + listRewriter.insertLast(pair, null); + } + } + } + + private Expression newDefaultExpression(AST ast, ITypeBinding type, ImportRewriteContext context) { + if (type.isPrimitive()) { + String name = type.getName(); + if ("boolean".equals(name)) { //$NON-NLS-1$ + return ast.newBooleanLiteral(false); + } else { + return ast.newNumberLiteral("0"); //$NON-NLS-1$ + } + } + if (type == ast.resolveWellKnownType("java.lang.String")) { //$NON-NLS-1$ + return ast.newStringLiteral(); + } + if (type.isArray()) { + ArrayInitializer initializer = ast.newArrayInitializer(); + initializer.expressions().add(newDefaultExpression(ast, type.getElementType(), context)); + return initializer; + } + if (type.isAnnotation()) { + MarkerAnnotation annotation = ast.newMarkerAnnotation(); + annotation.setTypeName(ast.newName(getImportRewrite().addImport(type, context))); + return annotation; + } + return ast.newNullLiteral(); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/NewAnnotationProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/InsertAnnotationProposal.java similarity index 80% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/NewAnnotationProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/InsertAnnotationProposal.java index 789e3aef..b5a351dc 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/NewAnnotationProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/InsertAnnotationProposal.java @@ -7,12 +7,12 @@ * * SPDX-License-Identifier: EPL-2.0 * - * Copied from https://github.com/eclipse/lsp4mp/blob/6f2d700a88a3262e39cc2ba04beedb429e162246/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/corrections/proposal/NewAnnotationProposal.java + * Copied from /org.eclipse.jdt.ui/src/org/eclipse/jdt/internal/ui/text/correction/proposals/NewAnnotationMemberProposal.java * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.ICompilationUnit; @@ -32,19 +32,15 @@ import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; import org.eclipse.lsp4j.CodeActionKind; -/** - * Code action proposal for annotation reused from - * https://github.com/eclipse/lsp4mp/blob/6f2d700a88a3262e39cc2ba04beedb429e162246/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/java/corrections/proposal/NewAnnotationProposal.java - */ -public class NewAnnotationProposal extends ChangeCorrectionProposal { +public class InsertAnnotationProposal extends ASTRewriteCorrectionProposal { private final CompilationUnit fInvocationNode; private final IBinding fBinding; - protected final String[] annotations; + private final String[] annotations; - public NewAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, String... annotations) { + public InsertAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, + IBinding binding, int relevance, String... annotations) { super(label, CodeActionKind.QuickFix, targetCU, null, relevance); fInvocationNode = invocationNode; fBinding = binding; @@ -82,7 +78,7 @@ protected ASTRewrite getRewrite() throws CoreException { } else if (declNode instanceof MethodDeclaration) { rewrite.getListRewrite(declNode, MethodDeclaration.MODIFIERS2_PROPERTY).insertFirst(marker, null); } else if (declNode instanceof TypeDeclaration) { - rewrite.getListRewrite(declNode,TypeDeclaration.MODIFIERS2_PROPERTY).insertFirst(marker, null); + rewrite.getListRewrite(declNode, TypeDeclaration.MODIFIERS2_PROPERTY).insertFirst(marker, null); } } @@ -93,7 +89,7 @@ protected ASTRewrite getRewrite() throws CoreException { /** * Returns the Compilation Unit node - * + * * @return the invocation node for the Compilation Unit */ protected CompilationUnit getInvocationNode() { @@ -102,7 +98,7 @@ protected CompilationUnit getInvocationNode() { /** * Returns the Binding object associated with the new annotation change - * + * * @return the binding object */ protected IBinding getBinding() { @@ -111,10 +107,10 @@ protected IBinding getBinding() { /** * Returns the annotations list - * + * * @return the list of new annotations to add */ protected String[] getAnnotations() { return this.annotations; } -} \ No newline at end of file +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/LinkedCorrectionProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/LinkedCorrectionProposal.java new file mode 100644 index 00000000..e44fb44f --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/LinkedCorrectionProposal.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2000, 2012 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Originally copied from org.eclipse.jdt.internal.corrections.proposals.LinkedCorrectionProposal; + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.jdt.core.dom.rewrite.ITrackedNodePosition; +import org.eclipse.jdt.internal.corext.fix.LinkedProposalModelCore; + +/** + * A proposal for quick fixes and quick assists that works on a AST rewriter and + * enters the linked mode when the proposal is set up. Either a rewriter is + * directly passed in the constructor or method {@link #getRewrite()} is + * overridden to provide the AST rewriter that is evaluated to the document when + * the proposal is applied. + * + * @since 3.0 + */ +public class LinkedCorrectionProposal extends ASTRewriteCorrectionProposal { + + private LinkedProposalModelCore fLinkedProposalModel; + + /** + * Constructs a linked correction proposal. + * + * @param name The display name of the proposal. + * @param cu The compilation unit that is modified. + * @param rewrite The AST rewrite that is invoked when the proposal is applied + * null can be passed if {@link #getRewrite()} is + * overridden. + * @param relevance The relevance of this proposal. + * @param image The image that is displayed for this proposal or + * null if no image is desired. + */ + public LinkedCorrectionProposal(String name, String kind, ICompilationUnit cu, ASTRewrite rewrite, int relevance) { + super(name, kind, cu, rewrite, relevance); + fLinkedProposalModel = null; + } + + public LinkedProposalModelCore getLinkedProposalModel() { + if (fLinkedProposalModel == null) { + fLinkedProposalModel = new LinkedProposalModelCore(); + } + return fLinkedProposalModel; + } + + public void setLinkedProposalModel(LinkedProposalModelCore model) { + fLinkedProposalModel = model; + } + + /** + * Adds a linked position to be shown when the proposal is applied. All + * positions with the same group id are linked. + * + * @param position The position to add. + * @param isFirst If set, the proposal is jumped to first. + * @param groupID The id of the group the proposal belongs to. All proposals in + * the same group are linked. + */ + public void addLinkedPosition(ITrackedNodePosition position, boolean isFirst, String groupID) { + getLinkedProposalModel().getPositionGroup(groupID, true).addPosition(position, isFirst); + } + + /** + * Adds a linked position to be shown when the proposal is applied. All + * positions with the same group id are linked. + * + * @param position The position to add. + * @param sequenceRank The sequence rank, see TODO. + * @param groupID The id of the group the proposal belongs to. All + * proposals in the same group are linked. + */ + public void addLinkedPosition(ITrackedNodePosition position, int sequenceRank, String groupID) { + getLinkedProposalModel().getPositionGroup(groupID, true).addPosition(position, sequenceRank); + } + + /** + * Sets the end position of the linked mode to the end of the passed range. + * + * @param position The position that describes the end position of the linked + * mode. + */ + public void setEndPosition(ITrackedNodePosition position) { + getLinkedProposalModel().setEndPosition(position); + } + + /** + * Adds a linked position proposal to the group with the given id. + * + * @param groupID The id of the group that should present the proposal + * @param proposal The string to propose. + * @param image The image to show for the position proposal or + * null if no image is desired. + */ + public void addLinkedPositionProposal(String groupID, String proposal) { + getLinkedProposalModel().getPositionGroup(groupID, true).addProposal(proposal, 10); + } + + // /** + // * Adds a linked position proposal to the group with the given id. + // * + // * @param groupID + // * The id of the group that should present the proposal + // * @param displayString + // * The name of the proposal + // * @param proposal + // * The string to insert. + // * @param image + // * The image to show for the position proposal or + // * null if no image is desired. + // * @deprecated use {@link #addLinkedPositionProposal(String, String, Image)} + // * instead + // */ + // @Deprecated + // public void addLinkedPositionProposal(String groupID, String displayString, + // String proposal, Image image) { + // addLinkedPositionProposal(groupID, proposal, image); + // } + + /** + * Adds a linked position proposal to the group with the given id. + * + * @param groupID The id of the group that should present the proposal + * @param type The binding to use as type name proposal. + */ + public void addLinkedPositionProposal(String groupID, ITypeBinding type) { + getLinkedProposalModel().getPositionGroup(groupID, true).addProposal(type, getCompilationUnit(), 10); + } + + // @Override + // protected void performChange(IEditorPart part, IDocument document) throws + // CoreException { + // try { + // super.performChange(part, document); + // if (part == null) { + // return; + // } + // + // if (fLinkedProposalModel != null) { + // if (fLinkedProposalModel.hasLinkedPositions() && part instanceof JavaEditor) + // { + // // enter linked mode + // ITextViewer viewer = ((JavaEditor) part).getViewer(); + // new LinkedProposalModelPresenter().enterLinkedMode(viewer, part, + // didOpenEditor(), fLinkedProposalModel); + // } else if (part instanceof ITextEditor) { + // LinkedProposalPositionGroup.PositionInformation endPosition = + // fLinkedProposalModel.getEndPosition(); + // if (endPosition != null) { + // // select a result + // int pos = endPosition.getOffset() + endPosition.getLength(); + // ((ITextEditor) part).selectAndReveal(pos, 0); + // } + // } + // } + // } catch (BadLocationException e) { + // throw new CoreException(JavaUIStatus.createError(IStatus.ERROR, e)); + // } + // } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyAnnotationProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyAnnotationProposal.java similarity index 84% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyAnnotationProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyAnnotationProposal.java index fef7f91f..8d8b40dd 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyAnnotationProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyAnnotationProposal.java @@ -1,16 +1,16 @@ -/******************************************************************************* - * Copyright (c) 2021, 2022 IBM Corporation and others. - * +/******************************************************************************* + * Copyright (c) 2021, 2022 IBM Corporation and others. + * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: + * + * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; import static java.util.stream.Collectors.toList; @@ -29,10 +29,10 @@ import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.MemberValuePair; import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; @@ -40,15 +40,15 @@ import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; /** - * + * * Code action proposal for modifying an existing annotation. Option for adding * additional string attributes and option for removing string attributes from * specified annotation. - * + * * @author Kathryn Kodama * */ -public class ModifyAnnotationProposal extends NewAnnotationProposal { +public class ModifyAnnotationProposal extends InsertAnnotationProposal { // list of attributes to add to the annotations private final List attributesToAdd; @@ -57,20 +57,22 @@ public class ModifyAnnotationProposal extends NewAnnotationProposal { private final List attributesToRemove; public ModifyAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, String annotation, List attributesToAdd, - List attributesToRemove) { + IBinding binding, int relevance, String annotation, List attributesToAdd, + List attributesToRemove) { super(label, targetCU, invocationNode, binding, relevance, annotation); this.attributesToAdd = attributesToAdd; this.attributesToRemove = attributesToRemove; } + public ModifyAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, String annotation, List attributesToAdd) { + IBinding binding, int relevance, String annotation, List attributesToAdd) { super(label, targetCU, invocationNode, binding, relevance, annotation); this.attributesToAdd = attributesToAdd; this.attributesToRemove = new ArrayList<>(); } + public ModifyAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, List attributesToAdd, String... annotations) { + IBinding binding, int relevance, List attributesToAdd, String... annotations) { super(label, targetCU, invocationNode, binding, relevance, annotations); this.attributesToAdd = attributesToAdd; this.attributesToRemove = new ArrayList<>(); @@ -115,26 +117,22 @@ protected ASTRewrite getRewrite() throws CoreException { ImportRewriteContext importRewriteContext = new ContextSensitiveImportRewriteContext(declNode, imports); List existingAnnotations = new ArrayList(); - List children = (List) declNode - .getStructuralProperty(FieldDeclaration.MODIFIERS2_PROPERTY); + List children = (List) declNode.getStructuralProperty(FieldDeclaration.MODIFIERS2_PROPERTY); // for all existing annotations (that are the annotation we want) for (ASTNode child : children) { if (child instanceof Annotation) { Annotation annotation = (Annotation) child; - boolean containsAnnotation = Arrays.stream(annotationShortNames) - .anyMatch(annotation.getTypeName().toString()::contains); - - // check if current child annotation has all attributes to add already or any to remove + boolean containsAnnotation = Arrays.stream(annotationShortNames).anyMatch(annotation.getTypeName().toString()::contains); + + // check if current child annotation has all attributes to add already or any to + // remove if (containsAnnotation && child instanceof NormalAnnotation) { - List existingValues = (List) ((NormalAnnotation) child).values().stream() - .map(mvp -> ((MemberValuePair) mvp).getName().toString()).collect(toList()); - - boolean containsAllToAdd = this.attributesToAdd.stream() - .allMatch(attr -> existingValues.stream().anyMatch(v -> v.equals(attr))); - boolean containsAnyToRemove = this.attributesToRemove.stream() - .anyMatch(attr -> existingValues.stream().anyMatch(v -> v.equals(attr))); - + List existingValues = (List) ((NormalAnnotation) child).values().stream().map(mvp -> ((MemberValuePair) mvp).getName().toString()).collect(toList()); + + boolean containsAllToAdd = this.attributesToAdd.stream().allMatch(attr -> existingValues.stream().anyMatch(v -> v.equals(attr))); + boolean containsAnyToRemove = this.attributesToRemove.stream().anyMatch(attr -> existingValues.stream().anyMatch(v -> v.equals(attr))); + if (!containsAllToAdd || containsAnyToRemove) { existingAnnotations.add(annotation); rewrite.remove(child, null); @@ -143,20 +141,19 @@ protected ASTRewrite getRewrite() throws CoreException { } } - // add new annotations to proposal (restoring those that were removed) for (Annotation a : existingAnnotations) { if (a instanceof NormalAnnotation) { NormalAnnotation marker = ast.newNormalAnnotation(); - marker.setTypeName(ast.newName(imports.addImport(a.getTypeName().toString(), importRewriteContext))); + marker.setTypeName( + ast.newName(imports.addImport(a.getTypeName().toString(), importRewriteContext))); List values = marker.values(); - + // add existing attributes to annotation List existingValues = ((NormalAnnotation) a).values(); for (MemberValuePair mvp : existingValues) { - boolean removeAttribute = this.attributesToRemove - .contains(mvp.getName().getFullyQualifiedName()); - + boolean removeAttribute = this.attributesToRemove.contains(mvp.getName().getFullyQualifiedName()); + // do not add attributes to be removed if (!removeAttribute) { MemberValuePair memberValuePair = ast.newMemberValuePair(); @@ -173,7 +170,7 @@ protected ASTRewrite getRewrite() throws CoreException { values.add(memberValuePair); } } - + // add new attributes for (String newAttr : this.attributesToAdd) { // dont add duplicate attributes to an annotation @@ -186,50 +183,46 @@ protected ASTRewrite getRewrite() throws CoreException { values.add(memberValuePair); } } - + rewrite.getListRewrite(declNode, - isField ? FieldDeclaration.MODIFIERS2_PROPERTY : TypeDeclaration.MODIFIERS2_PROPERTY) - .insertFirst(marker, null); - } + isField ? FieldDeclaration.MODIFIERS2_PROPERTY : TypeDeclaration.MODIFIERS2_PROPERTY).insertFirst(marker, null); + } } - + return rewrite; } else if (declNode instanceof TypeDeclaration || isField || isSingleVarDecl) { AST ast = declNode.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast); - + ImportRewriteContext importRewriteContext = new ContextSensitiveImportRewriteContext(declNode, imports); List existingAnnotations = new ArrayList(); ChildListPropertyDescriptor property = isSingleVarDecl ? SingleVariableDeclaration.MODIFIERS2_PROPERTY : TypeDeclaration.MODIFIERS2_PROPERTY; - List children = (List) declNode - .getStructuralProperty(property); + List children = (List) declNode.getStructuralProperty(property); // find and save existing annotation, then remove it from ast for (ASTNode child : children) { if (child instanceof Annotation) { Annotation annotation = (Annotation) child; - boolean containsAnnotation = Arrays.stream(annotationShortNames) - .anyMatch(annotation.getTypeName().toString()::contains); + boolean containsAnnotation = Arrays.stream(annotationShortNames).anyMatch(annotation.getTypeName().toString()::contains); if (containsAnnotation) { existingAnnotations.add(annotation); rewrite.remove(child, null); } } } - + // add new annotation with fields from existing annotation for (String annotation : annotations) { NormalAnnotation marker = ast.newNormalAnnotation(); marker.setTypeName(ast.newName(imports.addImport(annotation, importRewriteContext))); List values = marker.values(); - + if (!existingAnnotations.isEmpty()) { for (Annotation a : existingAnnotations) { if (a instanceof NormalAnnotation) { List existingValues = ((NormalAnnotation) a).values(); for (MemberValuePair mvp : existingValues) { - boolean removeAttribute = this.attributesToRemove - .contains(mvp.getName().getFullyQualifiedName()); + boolean removeAttribute = this.attributesToRemove.contains(mvp.getName().getFullyQualifiedName()); // do not add attribute to be removed if (!removeAttribute) { @@ -260,7 +253,7 @@ protected ASTRewrite getRewrite() throws CoreException { memberValuePair.setValue(stringValue); values.add(memberValuePair); } - + ChildListPropertyDescriptor newRewrite; if (isSingleVarDecl) { newRewrite = SingleVariableDeclaration.MODIFIERS2_PROPERTY; @@ -270,8 +263,7 @@ protected ASTRewrite getRewrite() throws CoreException { newRewrite = TypeDeclaration.MODIFIERS2_PROPERTY; } - rewrite.getListRewrite(declNode, newRewrite) - .insertFirst(marker, null); + rewrite.getListRewrite(declNode, newRewrite).insertFirst(marker, null); } return rewrite; } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyModifiersProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyModifiersProposal.java similarity index 59% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyModifiersProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyModifiersProposal.java index a54635d3..982b0e94 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyModifiersProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyModifiersProposal.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021 IBM Corporation, Matthew Shocrylas and others. +* Copyright (c) 2021, 2023 IBM Corporation and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,17 +11,15 @@ * IBM Corporation, Matthew Shocrylas - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.IBinding; @@ -33,30 +31,28 @@ import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; -import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; -import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; -import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4jakarta.jdt.internal.jaxrs.InsertDefaultPublicConstructorQuickFix; /** - * Code action proposal for changing the visibility modifier of a method, field, or type. - * Used by JAX-RS NonPublicResourceMethodQuickFix. - * - * @author Matthew Shocrylas - * @author Leslie Dawson - * @see CodeActionHandler - * @see NonPublicResourceMethodQuickFix - * @see PersistenceEntityQuickFix - * + * Code action proposal for changing the visibility modifier of a method, field, + * or type. Used by JAX-RS NonPublicResourceMethodQuickFix. + * + * @author Matthew Shocrylas + * @author Leslie Dawson + * @see CodeActionHandler + * @see InsertDefaultPublicConstructorQuickFix + * @see PersistenceEntityQuickFix + * */ -public class ModifyModifiersProposal extends ChangeCorrectionProposal { +public class ModifyModifiersProposal extends ASTRewriteCorrectionProposal { private final CompilationUnit invocationNode; private final IBinding binding; private final ASTNode coveredNode; - + // list of modifiers to add private final List modifiersToAdd; @@ -64,43 +60,46 @@ public class ModifyModifiersProposal extends ChangeCorrectionProposal { private final List modifiersToRemove; /** - * Constructor for ModifyModifiersProposal that accepts both a list of modifiers to remove as well as to add - * - * @param modifiersToAdd list of valid modifiers as strings to be added - * @param modifiersToRemove list of modifiers as strings to be removed + * Constructor for ModifyModifiersProposal that accepts both a list of modifiers + * to remove as well as to add + * + * @param modifiersToAdd list of valid modifiers as strings to be added + * @param modifiersToRemove list of modifiers as strings to be removed */ public ModifyModifiersProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, ASTNode coveredNode, List modifiersToAdd, List modifiersToRemove) { + IBinding binding, int relevance, ASTNode coveredNode, List modifiersToAdd, + List modifiersToRemove) { super(label, CodeActionKind.QuickFix, targetCU, null, relevance); this.invocationNode = invocationNode; this.binding = binding; this.coveredNode = coveredNode; - this.modifiersToAdd = modifiersToAdd; + this.modifiersToAdd = modifiersToAdd; this.modifiersToRemove = modifiersToRemove; } - + /** - * Constructor for ModifyModifiersProposal that accepts only a list of modifiers to add - * If a visibility modifier is specified to be added, existing visibility modifiers will be removed + * Constructor for ModifyModifiersProposal that accepts only a list of modifiers + * to add If a visibility modifier is specified to be added, existing visibility + * modifiers will be removed * - * @param modifiersToAdd list of valid modifiers as strings to be added + * @param modifiersToAdd list of valid modifiers as strings to be added */ public ModifyModifiersProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, ASTNode coveredNode, List modifiersToAdd) { + IBinding binding, int relevance, ASTNode coveredNode, List modifiersToAdd) { super(label, CodeActionKind.QuickFix, targetCU, null, relevance); this.invocationNode = invocationNode; this.binding = binding; this.coveredNode = coveredNode; - this.modifiersToAdd = modifiersToAdd; + this.modifiersToAdd = modifiersToAdd; this.modifiersToRemove = new ArrayList<>(); } - + @Override protected ASTRewrite getRewrite() throws CoreException { ASTNode declNode = null; ASTNode boundNode = invocationNode.findDeclaringNode(binding); CompilationUnit newRoot = invocationNode; - + if (boundNode != null) { declNode = boundNode; } else { @@ -116,33 +115,32 @@ protected ASTRewrite getRewrite() throws CoreException { declNode = declNode.getParent(); } - AST ast = declNode.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast); ListRewrite modifiersList = null; List modifiers = new ArrayList(); - switch(declNode.getNodeType()) { - case ASTNode.METHOD_DECLARATION: - modifiersList = rewrite.getListRewrite(declNode, MethodDeclaration.MODIFIERS2_PROPERTY); - modifiers = (List) declNode.getStructuralProperty(MethodDeclaration.MODIFIERS2_PROPERTY); - break; - case ASTNode.FIELD_DECLARATION: - modifiersList = rewrite.getListRewrite(declNode, FieldDeclaration.MODIFIERS2_PROPERTY); - modifiers = (List) declNode.getStructuralProperty(FieldDeclaration.MODIFIERS2_PROPERTY); - break; - case ASTNode.TYPE_DECLARATION: - modifiersList = rewrite.getListRewrite(declNode, TypeDeclaration.MODIFIERS2_PROPERTY); - modifiers = (List) declNode.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY); - break; - case ASTNode.SINGLE_VARIABLE_DECLARATION: - modifiersList = rewrite.getListRewrite(declNode, SingleVariableDeclaration.MODIFIERS2_PROPERTY); - modifiers = (List) declNode.getStructuralProperty(SingleVariableDeclaration.MODIFIERS2_PROPERTY); - break; - default: - modifiersList = null; - } + switch (declNode.getNodeType()) { + case ASTNode.METHOD_DECLARATION: + modifiersList = rewrite.getListRewrite(declNode, MethodDeclaration.MODIFIERS2_PROPERTY); + modifiers = (List) declNode.getStructuralProperty(MethodDeclaration.MODIFIERS2_PROPERTY); + break; + case ASTNode.FIELD_DECLARATION: + modifiersList = rewrite.getListRewrite(declNode, FieldDeclaration.MODIFIERS2_PROPERTY); + modifiers = (List) declNode.getStructuralProperty(FieldDeclaration.MODIFIERS2_PROPERTY); + break; + case ASTNode.TYPE_DECLARATION: + modifiersList = rewrite.getListRewrite(declNode, TypeDeclaration.MODIFIERS2_PROPERTY); + modifiers = (List) declNode.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY); + break; + case ASTNode.SINGLE_VARIABLE_DECLARATION: + modifiersList = rewrite.getListRewrite(declNode, SingleVariableDeclaration.MODIFIERS2_PROPERTY); + modifiers = (List) declNode.getStructuralProperty(SingleVariableDeclaration.MODIFIERS2_PROPERTY); + break; + default: + modifiersList = null; + } for (ASTNode modifier : modifiers) { if (modifier instanceof MarkerAnnotation) { @@ -153,8 +151,7 @@ protected ASTRewrite getRewrite() throws CoreException { modifiersList.remove(markAnno, null); continue; } - } - else if (modifier instanceof Modifier) { + } else if (modifier instanceof Modifier) { Modifier.ModifierKeyword modKeyword = ((Modifier) modifier).getKeyword(); // Check if modifier is in toRemove list @@ -164,14 +161,14 @@ else if (modifier instanceof Modifier) { } // Otherwise check if the existing modifier is private, public or protected - if (modKeyword.equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD) || - modKeyword.equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD) || - modKeyword.equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD)) { + if (modKeyword.equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD) + || modKeyword.equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD) + || modKeyword.equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD)) { // if adding a visibility modifier, need to remove the existing one - if (modifiersToAdd.stream().anyMatch(m -> (m.equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD.toString()) || - m.equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD.toString()) || - m.equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD.toString())))) { + if (modifiersToAdd.stream().anyMatch(m -> (m.equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD.toString()) + || m.equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD.toString()) + || m.equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD.toString())))) { modifiersList.remove(modifier, null); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyReturnTypeProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyReturnTypeProposal.java similarity index 83% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyReturnTypeProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyReturnTypeProposal.java index ac94b9fd..1f43095f 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/ModifyReturnTypeProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ModifyReturnTypeProposal.java @@ -10,7 +10,7 @@ * Contributors: * Yijia Jing *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.dom.AST; @@ -22,48 +22,50 @@ import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4jakarta.jdt.internal.annotations.ModifyConstructReturnTypeQuickFix; /** * Code action proposal for changing the return type of a method. - * + * * @author Yijia Jing * @see CodeActionHandler - * @see PostConstructReturnTypeQuickFix + * @see ModifyConstructReturnTypeQuickFix * */ -public class ModifyReturnTypeProposal extends ChangeCorrectionProposal { - +public class ModifyReturnTypeProposal extends ASTRewriteCorrectionProposal { + private final CompilationUnit invocationNode; private final IBinding binding; private final Type newReturnType; /** - * Constructor for ModifyReturnTypeProposal that accepts the new return type of a method. - * + * Constructor for ModifyReturnTypeProposal that accepts the new return type of + * a method. + * * @param newReturnType the new return type to change to */ - public ModifyReturnTypeProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, Type newReturnType) { + public ModifyReturnTypeProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, + IBinding binding, int relevance, Type newReturnType) { super(label, CodeActionKind.QuickFix, targetCU, null, relevance); this.invocationNode = invocationNode; this.binding = binding; this.newReturnType = newReturnType; } - + @SuppressWarnings("restriction") @Override protected ASTRewrite getRewrite() { ASTNode declNode = null; ASTNode boundNode = invocationNode.findDeclaringNode(binding); CompilationUnit newRoot = invocationNode; - + if (boundNode != null) { declNode = boundNode; } else { newRoot = ASTResolving.createQuickFixAST(getCompilationUnit(), null); declNode = newRoot.findDeclaringNode(binding.getKey()); } - + if (declNode.getNodeType() == ASTNode.METHOD_DECLARATION) { AST ast = declNode.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast); diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/DeleteAnnotationProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/RemoveAnnotationProposal.java similarity index 82% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/DeleteAnnotationProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/RemoveAnnotationProposal.java index bfe265eb..35efd8ab 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/DeleteAnnotationProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/RemoveAnnotationProposal.java @@ -11,7 +11,7 @@ * Contributors: * IBM Corporation, Jianing Xu - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; import java.util.Arrays; import java.util.List; @@ -23,7 +23,6 @@ import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.FieldDeclaration; -import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.TypeDeclaration; @@ -36,14 +35,14 @@ import org.eclipse.lsp4j.CodeActionKind; /** - * + * * Code action proposal for deleting an existing annotation for * MethodDeclaration/Field. - * + * * Author: Jianing Xu * */ -public class DeleteAnnotationProposal extends ChangeCorrectionProposal { +public class RemoveAnnotationProposal extends ASTRewriteCorrectionProposal { private final CompilationUnit fInvocationNode; private final IBinding fBinding; @@ -52,18 +51,18 @@ public class DeleteAnnotationProposal extends ChangeCorrectionProposal { /** * Constructor for DeleteAnnotationProposal - * - * @param label - annotation label - * @param targetCU - the entire Java compilation unit + * + * @param label - annotation label + * @param targetCU - the entire Java compilation unit * @param invocationNode * @param binding * @param relevance - * @param declaringNode - declaringNode covered node of diagnostic + * @param declaringNode - declaringNode covered node of diagnostic * @param annotations - * + * */ - public DeleteAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, ASTNode declaringNode, String... annotations) { + public RemoveAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, + IBinding binding, int relevance, ASTNode declaringNode, String... annotations) { super(label, CodeActionKind.QuickFix, targetCU, null, relevance); this.fInvocationNode = invocationNode; this.fBinding = binding; @@ -107,14 +106,11 @@ protected ASTRewrite getRewrite() throws CoreException { @SuppressWarnings("unchecked") List children; if (isMethod) { - children = (List) declNode - .getStructuralProperty(MethodDeclaration.MODIFIERS2_PROPERTY); + children = (List) declNode.getStructuralProperty(MethodDeclaration.MODIFIERS2_PROPERTY); } else if (isType) { - children = (List) declNode - .getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY); + children = (List) declNode.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY); } else { - children = (List) declNode - .getStructuralProperty(FieldDeclaration.MODIFIERS2_PROPERTY); + children = (List) declNode.getStructuralProperty(FieldDeclaration.MODIFIERS2_PROPERTY); } // find and save existing annotation, then remove it from ast @@ -123,8 +119,7 @@ protected ASTRewrite getRewrite() throws CoreException { Annotation annotation = (Annotation) child; // IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding(); - boolean containsAnnotation = Arrays.stream(annotationShortNames) - .anyMatch(annotation.getTypeName().toString()::equals); + boolean containsAnnotation = Arrays.stream(annotationShortNames).anyMatch(annotation.getTypeName().toString()::equals); if (containsAnnotation) { rewrite.remove(child, null); } @@ -148,7 +143,7 @@ protected CompilationUnit getInvocationNode() { /** * Returns the Binding object associated with the new annotation change - * + * * @return the binding object */ protected IBinding getBinding() { @@ -157,7 +152,7 @@ protected IBinding getBinding() { /** * Returns the annotations list - * + * * @return the list of new annotations to add */ protected String[] getAnnotations() { diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/RemoveParamsProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/RemoveParamsProposal.java similarity index 84% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/RemoveParamsProposal.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/RemoveParamsProposal.java index 5766a387..05f5fda2 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/codeAction/proposal/RemoveParamsProposal.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/RemoveParamsProposal.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021 IBM Corporation, Bera Sogut and others. +* Copyright (c) 2021, 2023 IBM Corporation and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,7 +11,7 @@ * IBM Corporation, Bera Sogut - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.codeAction.proposal; +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; import java.util.List; @@ -27,6 +27,7 @@ import org.eclipse.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4jakarta.jdt.internal.jaxrs.RemoveMethodEntityParamsWithExclusionQuickFix; /** * Code action proposal for removing parameters of a method except one. Used by @@ -34,10 +35,10 @@ * * @author Bera Sogut * @see CodeActionHandler - * @see ResourceMethodMultipleEntityParamsQuickFix + * @see RemoveMethodEntityParamsWithExclusionQuickFix * */ -public class RemoveParamsProposal extends ChangeCorrectionProposal { +public class RemoveParamsProposal extends ASTRewriteCorrectionProposal { private final CompilationUnit invocationNode; private final IBinding binding; @@ -52,12 +53,12 @@ public class RemoveParamsProposal extends ChangeCorrectionProposal { * Constructor for RemoveParamsProposal that accepts parameters to remove and a * parameter to keep. * - * @param params the parameters of the function to remove + * @param params the parameters of the function to remove * @param paramToKeep the parameter of the function to keep */ public RemoveParamsProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, - IBinding binding, int relevance, List params, - SingleVariableDeclaration paramToKeep) { + IBinding binding, int relevance, List params, + SingleVariableDeclaration paramToKeep) { super(label, CodeActionKind.QuickFix, targetCU, null, relevance); this.invocationNode = invocationNode; this.binding = binding; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ReplaceAnnotationProposal.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ReplaceAnnotationProposal.java new file mode 100644 index 00000000..a16d45f0 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/corrections/proposal/ReplaceAnnotationProposal.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2000, 2016 IBM Corporation and others. +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + * + * Copied from /org.eclipse.jdt.ui/src/org/eclipse/jdt/internal/ui/text/correction/proposals/NewAnnotationMemberProposal.java + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.IAnnotationBinding; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.TypeDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; +import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; +import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving; +import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; + +/** + * Similar functionality as NewAnnotationProposal. The main difference is that + * first removes specified annotations before adding a new annotation. + * + * Note: This class only accepts one new annotation to add. + * + * @author Kathryn Kodama + */ +public class ReplaceAnnotationProposal extends InsertAnnotationProposal { + + private final String[] removeAnnotations; + + public ReplaceAnnotationProposal(String label, ICompilationUnit targetCU, CompilationUnit invocationNode, + IBinding binding, int relevance, String annotation, String... removeAnnotations) { + super(label, targetCU, invocationNode, binding, relevance, annotation); + this.removeAnnotations = removeAnnotations; + } + + @Override + protected ASTRewrite getRewrite() throws CoreException { + CompilationUnit fInvocationNode = getInvocationNode(); + IBinding fBinding = getBinding(); + String[] annotations = getAnnotations(); + + ASTNode declNode = null; + ASTNode boundNode = fInvocationNode.findDeclaringNode(fBinding); + CompilationUnit newRoot = fInvocationNode; + if (boundNode != null) { + declNode = boundNode; // is same CU + } else { + newRoot = ASTResolving.createQuickFixAST(getCompilationUnit(), null); + declNode = newRoot.findDeclaringNode(fBinding.getKey()); + } + ImportRewrite imports = createImportRewrite(newRoot); + + boolean isField = declNode instanceof VariableDeclarationFragment; + if (isField) { + declNode = declNode.getParent(); + } + if (declNode instanceof TypeDeclaration || isField) { + AST ast = declNode.getAST(); + ASTRewrite rewrite = ASTRewrite.create(ast); + + ImportRewriteContext importRewriteContext = new ContextSensitiveImportRewriteContext(declNode, imports); + + // remove annotations in the removeAnnotations list + @SuppressWarnings("unchecked") + List children = (List) declNode.getStructuralProperty(TypeDeclaration.MODIFIERS2_PROPERTY); + for (ASTNode child : children) { + if (child instanceof Annotation) { + Annotation annotation = (Annotation) child; + IAnnotationBinding annotationBinding = annotation.resolveAnnotationBinding(); + boolean containsAnnotation = Arrays.stream(removeAnnotations).anyMatch(annotationBinding.getName()::contains); + if (containsAnnotation) { + rewrite.remove(child, null); + } + } + } + for (String annotation : annotations) { + Annotation marker = ast.newMarkerAnnotation(); + marker.setTypeName(ast.newName(imports.addImport(annotation, importRewriteContext))); // $NON-NLS-1$ + rewrite.getListRewrite(declNode, + isField ? FieldDeclaration.MODIFIERS2_PROPERTY : TypeDeclaration.MODIFIERS2_PROPERTY).insertFirst(marker, null); + } + + return rewrite; + } + return null; + } + +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/definition/JavaDefinitionContext.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/definition/JavaDefinitionContext.java new file mode 100644 index 00000000..6a8c25cf --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/definition/JavaDefinitionContext.java @@ -0,0 +1,59 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.definition; + +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4jakarta.jdt.core.java.AbstractJavaContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; + +/** + * Java definition context for a given compilation unit. + * + * @author Angelo ZERR + * + */ +public class JavaDefinitionContext extends AbstractJavaContext { + + private final IJavaElement hyperlinkedElement; + + private final Position hyperlinkedPosition; + + public JavaDefinitionContext(String uri, ITypeRoot typeRoot, IJDTUtils utils, IJavaElement hyperlinkeElement, + Position hyperlinkePosition) { + super(uri, typeRoot, utils); + this.hyperlinkedElement = hyperlinkeElement; + this.hyperlinkedPosition = hyperlinkePosition; + } + + /** + * Returns the hyperlinked Java element. + * + * @return the hyperlinked Java element. + */ + public IJavaElement getHyperlinkedElement() { + return hyperlinkedElement; + } + + /** + * Returns the hyperlinked position. + * + * @return the hyperlinked position. + */ + public Position getHyperlinkedPosition() { + return hyperlinkedPosition; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/IJavaDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/IJavaDiagnosticsParticipant.java new file mode 100644 index 00000000..fa3c07cf --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/IJavaDiagnosticsParticipant.java @@ -0,0 +1,84 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.diagnostics; + +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.lsp4j.Diagnostic; + +/** + * Java diagnostics participants API. + * + * @author Angelo ZERR + * + */ +public interface IJavaDiagnosticsParticipant { + + /** + * Returns true if diagnostics must be collected for the given context and false + * otherwise. + * + *

    + * Collection is done by default. Participants can override this to check if + * some classes are on the classpath before deciding to process the collection. + *

    + * + * @param the java diagnostics context + * @param monitor the progress monitor + * @return true if diagnostics must be collected for the given context and false + * otherwise. + * + */ + default boolean isAdaptedForDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + return true; + } + + /** + * Begin diagnostics collection. + * + * @param context the java diagnostics context + * @param monitor the progress monitor + * + * @throws CoreException + */ + default void beginDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + + } + + /** + * Collect diagnostics according to the context. + * + * @param context the java diagnostics context + * @param monitor the progress monitor + * + * @return diagnostics list and null otherwise. + * + * @throws CoreException + */ + List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException; + + /** + * End diagnostics collection. + * + * @param context the java diagnostics context + * @param monitor the progress monitor + * + * @throws CoreException + */ + default void endDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/IJavaErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/IJavaErrorCode.java new file mode 100644 index 00000000..de5af0ce --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/IJavaErrorCode.java @@ -0,0 +1,25 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.diagnostics; + +/** + * Diagnostics Java error code API. + * + * @author Angelo ZERR + * + */ +public interface IJavaErrorCode { + + String getCode(); +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/JavaDiagnosticsContext.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/JavaDiagnosticsContext.java new file mode 100644 index 00000000..4f017f79 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/java/diagnostics/JavaDiagnosticsContext.java @@ -0,0 +1,92 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.java.diagnostics; + +import java.util.Collections; + +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.commons.DocumentFormat; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsSettings; +import org.eclipse.lsp4jakarta.jdt.core.java.AbstractJavaContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; + +/** + * Java diagnostics context for a given compilation unit. + * + * @author Angelo ZERR + * + */ +public class JavaDiagnosticsContext extends AbstractJavaContext { + + private final DocumentFormat documentFormat; + + private final JakartaJavaDiagnosticsSettings settings; + + public JavaDiagnosticsContext(String uri, ITypeRoot typeRoot, IJDTUtils utils, DocumentFormat documentFormat, + JakartaJavaDiagnosticsSettings settings) { + super(uri, typeRoot, utils); + this.documentFormat = documentFormat; + if (settings == null) { + this.settings = new JakartaJavaDiagnosticsSettings(Collections.emptyList()); + } else { + this.settings = settings; + } + } + + public DocumentFormat getDocumentFormat() { + return documentFormat; + } + + /** + * Returns the JakartaJavaDiagnosticsSettings. + * + * Should not be null. + * + * @return the JakartaJavaDiagnosticsSettings + */ + public JakartaJavaDiagnosticsSettings getSettings() { + return this.settings; + } + + public Diagnostic createDiagnostic(String uri, String message, Range range, String source, IJavaErrorCode code) { + return createDiagnostic(uri, message, range, source, code, DiagnosticSeverity.Warning); + } + + public Diagnostic createDiagnostic(String uri, String message, Range range, String source, IJavaErrorCode code, + DiagnosticSeverity severity) { + return createDiagnostic(uri, message, range, source, null, code, severity); + + } + + public Diagnostic createDiagnostic(String uri, String message, Range range, String source, Object data, + IJavaErrorCode code, + DiagnosticSeverity severity) { + Diagnostic diagnostic = new Diagnostic(); + diagnostic.setSource(source); + diagnostic.setMessage(message); + diagnostic.setSeverity(severity); + diagnostic.setRange(range); + if (code != null) { + diagnostic.setCode(code.getCode()); + } + if (data != null) { + diagnostic.setData(data); + } + return diagnostic; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/Jax_RSClassDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/Jax_RSClassDiagnosticsCollector.java deleted file mode 100644 index a1be3f2d..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/Jax_RSClassDiagnosticsCollector.java +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 IBM Corporation, Matthew Shocrylas and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation, Matthew Shocrylas - initial API and implementation - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.jax_rs; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * Diagnostic collector for root resource classes with multiple constructors - * - * @author Matthew Shocrylas - * - */ -public class Jax_RSClassDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public Jax_RSClassDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return Jax_RSConstants.DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - - if (unit != null) { - IType[] alltypes; - - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - boolean isRootResource = false; - boolean isProviderResource = false; - IAnnotation[] annotationList = type.getAnnotations(); - - for (IAnnotation annotation : annotationList) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), - Jax_RSConstants.SET_OF_JAXRS_ANNOTATIONS1); - if (matchedAnnotation != null) { - if (Jax_RSConstants.PATH_ANNOTATION.equals(matchedAnnotation)) { - isRootResource = true; - } else if (Jax_RSConstants.PROVIDER_ANNOTATION.equals(matchedAnnotation)) { - isProviderResource = true; - } - } - } - - if (isRootResource || isProviderResource) { // annotated class - List nonPublicConstructors = new ArrayList(); - boolean hasPublicConstructor = false; - int maxParams = 0; - Map constructorParamsMap = new HashMap(); - IMethod[] methods = type.getMethods(); - for (IMethod method : methods) { - if (isConstructorMethod(method)) { - if (Flags.isPublic(method.getFlags())) { - hasPublicConstructor = true; - nonPublicConstructors.clear(); // ignore all non-public constructors - if (isRootResource) { - int numParams = method.getNumberOfParameters(); - if (numParams > maxParams) { - maxParams = numParams; - } - constructorParamsMap.put(method, numParams); - } - } else if (!hasPublicConstructor) { - nonPublicConstructors.add(method); - } - } - } - // no public constructor defined - if (nonPublicConstructors.size() > 0) { - String diagnosticMessage = isRootResource ? - Messages.getMessage("RootResourceClasses") : - Messages.getMessage("ProviderClasses"); - for (IMethod constructor : nonPublicConstructors) { - diagnostics.add(createDiagnostic(constructor, unit, diagnosticMessage, - Jax_RSConstants.DIAGNOSTIC_CODE_NO_PUBLIC_CONSTRUCTORS, null, - DiagnosticSeverity.Error)); - } - } - // check public constructors' parameters - ArrayList equalMaxParamMethods = new ArrayList(); - for (Map.Entry entry : constructorParamsMap.entrySet()) { - if (entry.getValue() == maxParams) { - equalMaxParamMethods.add(entry.getKey()); - } else if (entry.getValue() < maxParams) { - IMethod method = entry.getKey(); - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("ConstructorIsUnused"), - Jax_RSConstants.DIAGNOSTIC_CODE_UNUSED_CONSTRUCTOR, null, - DiagnosticSeverity.Warning)); - } - } - if (equalMaxParamMethods.size() > 1) { // more than one - for (IMethod method : equalMaxParamMethods) { - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("MultipleConstructorsNumberOfParameters"), - Jax_RSConstants.DIAGNOSTIC_CODE_AMBIGUOUS_CONSTRUCTORS, null, - DiagnosticSeverity.Warning)); - } - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate JAX-RS diagnostics", e); - } - } - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/Jax_RSConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/Jax_RSConstants.java deleted file mode 100644 index c5e2136b..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/Jax_RSConstants.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2022 IBM Corporation, Matthew Shocrylas and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation, Matthew Shocrylas - initial API and implementation, Bera Sogut - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.jax_rs; - -import java.util.ArrayList; -import java.util.List; - -public class Jax_RSConstants { - - public static final String RESOURCE_METHOD = "ResourceMethod"; - - /* Annotation Constants */ - public static final ArrayList METHOD_DESIGNATORS = new ArrayList( - List.of("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS")); - - /* Annotations which make a resource method parameter a non entity parameter. */ - public static final ArrayList NON_ENTITY_PARAM_ANNOTATIONS = new ArrayList( - List.of("FormParam", "MatrixParam", "QueryParam", "PathParam", "CookieParam", "HeaderParam", "Context")); - - public static final String PATH_ANNOTATION = "jakarta.ws.rs.Path"; - public static final String PROVIDER_ANNOTATION = "jakarta.ws.rs.ext.Provider"; - - /* Source */ - public static final String DIAGNOSTIC_SOURCE = "jakarta-jax_rs"; - - /* Diagnostics fields constants */ - public static final String DIAGNOSTIC_CODE_NON_PUBLIC = "NonPublicResourceMethod"; - public static final String DIAGNOSTIC_CODE_MULTIPLE_ENTITY_PARAMS = "ResourceMethodMultipleEntityParams"; - public static final String DIAGNOSTIC_CODE_UNUSED_CONSTRUCTOR = "UnusedConstructor"; - public static final String DIAGNOSTIC_CODE_AMBIGUOUS_CONSTRUCTORS = "AmbiguousConstructors"; - public static final String DIAGNOSTIC_CODE_NO_PUBLIC_CONSTRUCTORS = "NoPublicConstructors"; - - public final static String[] SET_OF_METHOD_DESIGNATORS_ANNOTATIONS = { "jakarta.ws.rs.GET", "jakarta.ws.rs.POST", - "jakarta.ws.rs.PUT", "jakarta.ws.rs.DELETE", "jakarta.ws.rs.PATCH", "jakarta.ws.rs.HEAD", - "jakarta.ws.rs.OPTIONS" }; - public final static String[] SET_OF_NON_ENTITY_PARAM_ANNOTATIONS = { "jakarta.ws.rs.FormParam", - "jakarta.ws.rs.MatrixParam", "jakarta.ws.rs.QueryParam", "jakarta.ws.rs.PathParam", - "jakarta.ws.rs.CookieParam", "jakarta.ws.rs.HeaderParam", "jakarta.ws.rs.core.Context" }; - public final static String[] SET_OF_JAXRS_ANNOTATIONS1 = { PATH_ANNOTATION, PROVIDER_ANNOTATION }; - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/NoResourcePublicConstructorQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/NoResourcePublicConstructorQuickFix.java deleted file mode 100644 index 01c86902..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/NoResourcePublicConstructorQuickFix.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 IBM Corporation, Shaunak Tulshibagwale and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation, Shaunak Tulshibagwale - *******************************************************************************/ - - -package org.eclipse.lsp4jakarta.jdt.core.jax_rs; - - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.AddConstructorProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyModifiersProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * Quick fix for NoResourcePublicConstructorQuickFix that uses - * ModifyModifiersProposal. - * - * @author Shaunak Tulshibagwale - * - */ -public class NoResourcePublicConstructorQuickFix implements IJavaCodeActionParticipant { - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); - IMethodBinding parentMethod = parentNode.resolveBinding(); - IBinding parentType = getBinding(node); - - if (parentMethod != null) { - - final String TITLE_MESSAGE = Messages.getMessage("MakeConstructorPublic"); - - ChangeCorrectionProposal proposal = new ModifyModifiersProposal(TITLE_MESSAGE, context.getCompilationUnit(), - context.getASTRoot(), parentMethod, 0, null, Arrays.asList("public")); - - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(TITLE_MESSAGE); - codeActions.add(codeAction); - - // option for public constructor - String name = Messages.getMessage("NoargPublicConstructor"); - proposal = new AddConstructorProposal(name, - context.getCompilationUnit(), context.getASTRoot(), parentType, 0, "public"); - codeAction = context.convertToCodeAction(proposal, diagnostic); - codeActions.add(codeAction); - - } - return codeActions; - } - - protected static IBinding getBinding(ASTNode node) { - if (node.getParent() instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment = (VariableDeclarationFragment) node.getParent(); - return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/NonPublicResourceMethodQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/NonPublicResourceMethodQuickFix.java deleted file mode 100644 index 4a5d4d84..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/NonPublicResourceMethodQuickFix.java +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 IBM Corporation, Matthew Shocrylas and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation, Matthew Shocrylas - initial API and implementation - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.jax_rs; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyModifiersProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * Quick fix for ResourceMethodDiagnosticsCollector that uses - * ModifyModifiersProposal. - * - * @author Matthew Shocrylas - * - */ -public class NonPublicResourceMethodQuickFix implements IJavaCodeActionParticipant { - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); - IMethodBinding parentMethod = parentNode.resolveBinding(); - - if (parentMethod != null) { - - final String TITLE_MESSAGE = Messages.getMessage("MakeMethodPublic"); - - ChangeCorrectionProposal proposal = new ModifyModifiersProposal(TITLE_MESSAGE, context.getCompilationUnit(), - context.getASTRoot(), parentMethod, 0, null, Arrays.asList("public")); - - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(TITLE_MESSAGE); - codeActions.add(codeAction); - } - return codeActions; - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/ResourceMethodDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/ResourceMethodDiagnosticsCollector.java deleted file mode 100644 index 703f021f..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/ResourceMethodDiagnosticsCollector.java +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 IBM Corporation, Matthew Shocrylas and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation, Matthew Shocrylas - initial API and implementation, Bera Sogut - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.jax_rs; - -import java.util.List; - -import org.apache.commons.lang3.ArrayUtils; -import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.ILocalVariable; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -public class ResourceMethodDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public ResourceMethodDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return Jax_RSConstants.DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - - if (unit != null) { - String[] methodDesignators = ArrayUtils.addAll(Jax_RSConstants.SET_OF_METHOD_DESIGNATORS_ANNOTATIONS, - Jax_RSConstants.PATH_ANNOTATION); - IType[] alltypes; - IMethod[] methods; - - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - methods = type.getMethods(); - boolean isInterface = type.isInterface(); - - for (IMethod method : methods) { - IAnnotation[] methodAnnotations = method.getAnnotations(); - boolean isResourceMethod = false; - boolean isValid = true; - boolean isPublic = Flags.isPublic(method.getFlags()); - boolean usesDfltAccessModifier = Flags.isPackageDefault(method.getFlags()); - - for (IAnnotation annotation : methodAnnotations) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), - methodDesignators); - if (matchedAnnotation != null) { - if (isValid && !isPublic && !(usesDfltAccessModifier && isInterface) ) - isValid = false; - if (!Jax_RSConstants.PATH_ANNOTATION.equals(matchedAnnotation)) { - isResourceMethod = true; - break; - } - } - } - if (!isValid) { - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("OnlyPublicMethods"), - Jax_RSConstants.DIAGNOSTIC_CODE_NON_PUBLIC, null, DiagnosticSeverity.Error)); - } - if (isResourceMethod) { - int numEntityParams = 0; - ILocalVariable[] parameters = method.getParameters(); - for (ILocalVariable param : parameters) { - boolean isEntityParam = true; - IAnnotation[] annotations = param.getAnnotations(); - for (IAnnotation annotation : annotations) { - String matchedAnnotation = getMatchedJavaElementName(type, - annotation.getElementName(), - Jax_RSConstants.SET_OF_NON_ENTITY_PARAM_ANNOTATIONS); - if (matchedAnnotation != null) { - isEntityParam = false; - break; - } - } - if (isEntityParam) - numEntityParams++; - } - if (numEntityParams > 1) { - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("ResourceMethodsEntityParameter"), - Jax_RSConstants.DIAGNOSTIC_CODE_MULTIPLE_ENTITY_PARAMS, null, - DiagnosticSeverity.Error)); - } - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate JAX-RS diagnostics", e); - } - } - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/ResourceMethodMultipleEntityParamsQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/ResourceMethodMultipleEntityParamsQuickFix.java deleted file mode 100644 index 4346620e..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jax_rs/ResourceMethodMultipleEntityParamsQuickFix.java +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 IBM Corporation, Bera Sogut and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation, Bera Sogut - initial API and implementation - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.jax_rs; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.Annotation; -import org.eclipse.jdt.core.dom.IExtendedModifier; -import org.eclipse.jdt.core.dom.IMethodBinding; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.Name; -import org.eclipse.jdt.core.dom.SingleVariableDeclaration; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.RemoveParamsProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * Quick fix for the ResourceMethodMultipleEntityParams diagnostic in - * ResourceMethodDiagnosticsCollector. This class adds a quick fix for each - * entity parameter which removes all entity parameters except the chosen one. - * - * @author Bera Sogut - * - */ -public class ResourceMethodMultipleEntityParamsQuickFix implements IJavaCodeActionParticipant { - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); - IMethodBinding parentMethod = parentNode.resolveBinding(); - - if (parentMethod != null) { - - List params = (List) parentNode.parameters(); - - List entityParams = new ArrayList<>(); - - for (SingleVariableDeclaration param : params) { - if (isEntityParam(param)) { - entityParams.add(param); - } - } - - for (SingleVariableDeclaration entityParam : entityParams) { - final String TITLE_MESSAGE = Messages.getMessage("RemoveAllEntityParametersExcept", - entityParam.getName().getIdentifier()); - - // Remove all entity parameters except the current chosen one - ChangeCorrectionProposal proposal = new RemoveParamsProposal(TITLE_MESSAGE, - context.getCompilationUnit(), context.getASTRoot(), parentMethod, 0, entityParams, entityParam); - - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(TITLE_MESSAGE); - codeActions.add(codeAction); - } - - } - return codeActions; - } - - /** - * Returns a boolean variable that indicates whether the given parameter is an - * entity parameter or not. - * - * @param param the parameter to check whether it is an entity parameter or not - * @return true if the given parameter is an entity parameter, false otherwise - */ - private boolean isEntityParam(SingleVariableDeclaration param) { - ArrayList nonEntityParamAnnotations = Jax_RSConstants.NON_ENTITY_PARAM_ANNOTATIONS; - - boolean isEntityParam = true; - for (IExtendedModifier modifier : (List) param.modifiers()) { - if (modifier.isAnnotation()) { - Name typeName = ((Annotation) modifier).getTypeName(); - if (nonEntityParamAnnotations.contains(typeName.toString())) { - isEntityParam = false; - break; - } - } - } - - return isEntityParam; - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbAnnotationQuickFix.java deleted file mode 100644 index 4bab78eb..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbAnnotationQuickFix.java +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.jsonb; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -/** - * Quick fix for removing additional @JsonbCreator annotations when more than - * one occur in a class - * - * @author Leslie Dawson - * - */ -public class JsonbAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { - public JsonbAnnotationQuickFix() { - super("jakarta.json.bind.annotation.JsonbCreator"); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbDiagnosticsCollector.java deleted file mode 100644 index d7a2466e..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbDiagnosticsCollector.java +++ /dev/null @@ -1,181 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation, Matheus Cruz and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Matheus Cruz, Yijia Jing - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.jsonb; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IAnnotatable; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IField; -import org.eclipse.jdt.core.IMember; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; - -/** - * This class contains logic for Jsonb diagnostics: - * 1) Multiple JsonbCreator annotations on constructors will cause a diagnostic. - * 2) JsonbTransient not being a mutually exclusive Jsonb annotation will cause a diagnostic. - */ -public class JsonbDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public JsonbDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return JsonbConstants.DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit == null) - return; - try { - IType[] types = unit.getAllTypes(); - IMethod[] methods; - IAnnotation[] allAnnotations; - - for (IType type : types) { - methods = type.getMethods(); - List jonbMethods = new ArrayList(); - // methods - for (IMethod method : type.getMethods()) { - if (isConstructorMethod(method) || Flags.isStatic(method.getFlags())) { - allAnnotations = method.getAnnotations(); - for (IAnnotation annotation : allAnnotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), JsonbConstants.JSONB_CREATOR)) - jonbMethods.add(method); - } - } - } - if (jonbMethods.size() > JsonbConstants.MAX_METHOD_WITH_JSONBCREATOR) { - for (IMethod method : methods) { - diagnostics.add(createDiagnostic(method, unit, Messages.getMessage("ErrorMessageJsonbCreator"), - JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION, null, DiagnosticSeverity.Error)); - } - } - // fields - for (IField field : type.getFields()) { - collectJsonbTransientFieldDiagnostics(unit, type, diagnostics, field); - collectJsonbTransientAccessorDiagnostics(unit, type, diagnostics, field); - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate jakarta-jsonb diagnostics", e); - } - } - - private void collectJsonbTransientFieldDiagnostics(ICompilationUnit unit, IType type, List diagnostics, IField field) throws JavaModelException { - List jsonbAnnotationsForField = getJsonbAnnotationNames(type, field); - if (jsonbAnnotationsForField.contains(JsonbConstants.JSONB_TRANSIENT_FQ_NAME)) { - boolean hasAccessorConflict = false; - // Diagnostics on the accessors of the field are created when they are - // annotated with Jsonb annotations other than JsonbTransient. - List accessors = JDTUtils.getFieldAccessors(unit, field); - for (IMethod accessor : accessors) { - List jsonbAnnotationsForAccessor = getJsonbAnnotationNames(type, accessor); - if (hasJsonbAnnotationOtherThanTransient(jsonbAnnotationsForAccessor)) { - createJsonbTransientDiagnostic(unit, diagnostics, accessor, jsonbAnnotationsForAccessor, - JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_FIELD); - hasAccessorConflict = true; - } - } - // Diagnostic is created on the field if @JsonbTransient is not mutually - // exclusive or - // accessor has annotations other than JsonbTransient - if (hasAccessorConflict || hasJsonbAnnotationOtherThanTransient(jsonbAnnotationsForField)) - createJsonbTransientDiagnostic(unit, diagnostics, field, jsonbAnnotationsForField, - JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_FIELD); - } - } - - private void collectJsonbTransientAccessorDiagnostics(ICompilationUnit unit, IType type, List diagnostics, IField field) throws JavaModelException { - boolean createDiagnosticForField = false; - List jsonbAnnotationsForField = getJsonbAnnotationNames(type, field); - List accessors = JDTUtils.getFieldAccessors(unit, field); - for (IMethod accessor : accessors) { - List jsonbAnnotationsForAccessor = getJsonbAnnotationNames(type, accessor); - boolean hasFieldConflict = false; - if (jsonbAnnotationsForAccessor.contains(JsonbConstants.JSONB_TRANSIENT_FQ_NAME)) { - // Diagnostic is created if the field of this accessor has a annotation other - // then JsonbTransient - if (hasJsonbAnnotationOtherThanTransient(jsonbAnnotationsForField)) { - createDiagnosticForField = true; - hasFieldConflict = true; - } - - // Diagnostic is created on the accessor if field has annotation other than - // JsonbTransient - // or if @JsonbTransient is not mutually exclusive - if (hasFieldConflict || hasJsonbAnnotationOtherThanTransient(jsonbAnnotationsForAccessor)) - createJsonbTransientDiagnostic(unit, diagnostics, accessor, jsonbAnnotationsForAccessor, - JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_ACCESSOR); - - } - } - if (createDiagnosticForField) - createJsonbTransientDiagnostic(unit, diagnostics, field, jsonbAnnotationsForField, - JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_ACCESSOR); - } - - private boolean createJsonbTransientDiagnostic(ICompilationUnit unit, List diagnostics, IMember member, - List jsonbAnnotations, String code) throws JavaModelException { - String diagnosticErrorMessage = null; - if (code.equals(JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_FIELD)) - diagnosticErrorMessage = Messages.getMessage("ErrorMessageJsonbTransientOnField"); - else if (code.equals(JsonbConstants.DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_ACCESSOR)) - diagnosticErrorMessage = Messages.getMessage("ErrorMessageJsonbTransientOnAccessor"); - // convert to simple name for current tests - List diagnosticData = jsonbAnnotations.stream().map(annotation -> getSimpleName(annotation)) - .collect(Collectors.toList()); - diagnostics.add(createDiagnostic(member, unit, diagnosticErrorMessage, code, - (JsonArray) (new Gson().toJsonTree(diagnosticData)), DiagnosticSeverity.Error)); - return true; - } - - private List getJsonbAnnotationNames(IType type, IAnnotatable annotable) throws JavaModelException { - List jsonbAnnotationNames = new ArrayList(); - IAnnotation annotations[] = annotable.getAnnotations(); - for (IAnnotation annotation : annotations) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), JsonbConstants.JSONB_ANNOTATIONS.toArray(String[]::new)); - if (matchedAnnotation != null) { - jsonbAnnotationNames.add(matchedAnnotation); - } - } - return jsonbAnnotationNames; - } - - private boolean hasJsonbAnnotationOtherThanTransient(List jsonbAnnotations) throws JavaModelException { - for (String annotationName : jsonbAnnotations) - if (JsonbConstants.JSONB_ANNOTATIONS.contains(annotationName) - && !annotationName.equals(JsonbConstants.JSONB_TRANSIENT_FQ_NAME)) - return true; - return false; - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbTransientAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbTransientAnnotationQuickFix.java deleted file mode 100644 index 76beda07..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbTransientAnnotationQuickFix.java +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Adit Rada - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.jsonb; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.RemoveMultipleAnnotations; - -/** - * Quick fix for removing @JsonbTransient annotations when more than - * one occur in a class. - * The getCodeActions method is overridden in order to make sure that - * we return our custom quick fixes. There will be two quick fixes given - * to the user: (1) either remove @JsonbTransient or (2) remove all other - * Jsonb annotations. - * - * @author Adit Rada - * - */ -public class JsonbTransientAnnotationQuickFix extends RemoveMultipleAnnotations { - @Override - protected List> getMultipleRemoveAnnotations(List annotations) { - List> annotationsListsToRemove = new ArrayList>(); - - if (annotations.contains(JsonbConstants.JSONB_TRANSIENT)) { - // Provide as one option: Remove JsonbTransient - annotationsListsToRemove.add(Arrays.asList(JsonbConstants.JSONB_TRANSIENT_FQ_NAME)); - } - - // Provide as another option: Remove all other JsonbAnnotations - annotations.remove(JsonbConstants.JSONB_TRANSIENT); - if (annotations.size() > 0) { - annotationsListsToRemove.add(annotations); - } - - return annotationsListsToRemove; - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonp/JsonpDiagnosticCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonp/JsonpDiagnosticCollector.java deleted file mode 100644 index 9b7624b9..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonp/JsonpDiagnosticCollector.java +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022, 2023 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Yijia Jing - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.jsonp; - -import java.util.List; -import java.util.stream.Collectors; - -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.dom.Expression; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.MethodInvocation; -import org.eclipse.jdt.core.dom.StringLiteral; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4jakarta.jdt.core.ASTUtils; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - - -public class JsonpDiagnosticCollector extends AbstractDiagnosticsCollector { - - public JsonpDiagnosticCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return JsonpConstants.DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit == null) { - return; - } - List allMethodInvocations = ASTUtils.getMethodInvocations(unit); - List createPointerInvocations = allMethodInvocations.stream() - .filter(mi -> { - try { - return isMatchedJsonCreatePointer(unit, mi); - } catch (JavaModelException e) { - return false; - } - }).collect(Collectors.toList()); - for (MethodInvocation m: createPointerInvocations) { - Expression arg = (Expression) m.arguments().get(0); - if (isInvalidArgument(arg)) { - // If the argument supplied to a createPointer invocation is a String literal and is neither an empty String - // or a sequence of '/' prefixed tokens, a diagnostic highlighting the invalid argument is created. - try { - Range range = JDTUtils.toRange(unit, arg.getStartPosition(), arg.getLength()); - Diagnostic diagnostic = new Diagnostic(range, Messages.getMessage("CreatePointerErrorMessage")); - completeDiagnostic(diagnostic, JsonpConstants.DIAGNOSTIC_CODE_CREATE_POINTER); - diagnostics.add(diagnostic); - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate diagnostics", e); - } - } - } - } - - private boolean isInvalidArgument(Expression arg) { - if (arg instanceof StringLiteral) { - String argValue = ((StringLiteral)arg).getLiteralValue(); - if (!(argValue.isEmpty() || argValue.matches("^(\\/[^\\/]+)+$"))) { - return true; - } - } - return false; - } - - private boolean isMatchedJsonCreatePointer(ICompilationUnit unit, MethodInvocation mi) - throws JavaModelException { - if (mi.arguments().size() == 1 && JsonpConstants.CREATE_POINTER.equals(mi.getName().getIdentifier()) - && mi.getExpression() != null) { - Expression ex = mi.getExpression(); - String qualifier = ex.toString(); - if (JsonpConstants.JSON_FQ_NAME.endsWith(qualifier)) { - // For performance reason, we check if the import of Java element name is - // declared - if (isImportedJavaElement(unit, JsonpConstants.JSON_FQ_NAME) == true) - return true; - // only check fully qualified java element - if (JsonpConstants.JSON_FQ_NAME.equals(qualifier)) { - ITypeBinding itb = ex.resolveTypeBinding(); - return itb != null && qualifier.equals(itb.getQualifiedName()); - } - } - } - return false; - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/DeleteConflictMapKeyQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/DeleteConflictMapKeyQuickFix.java deleted file mode 100644 index 7816ea6b..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/DeleteConflictMapKeyQuickFix.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* IBM Corporation, Jianing Xu - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.persistence; - -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.RemoveAnnotationConflictQuickFix; - -/** - * - * Quick fix for removing @MapKey/@MapKeyClass when they are used for the same field - * or property - * - * @author Jianing Xu - * - */ -public class DeleteConflictMapKeyQuickFix extends RemoveAnnotationConflictQuickFix { - - public DeleteConflictMapKeyQuickFix() { - super(false, "jakarta.persistence.annotation.MapKeyClass", "jakarta.persistence.annotation.MapKey"); - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceAnnotationQuickFix.java deleted file mode 100644 index 7697f2e3..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceAnnotationQuickFix.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.persistence; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyAnnotationProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.InsertAnnotationMissingQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - - -/** - * QuickFix for fixing {@link PersistenceConstants#DIAGNOSTIC_CODE_MISSING_ATTRIBUTES} error - * by providing several code actions to add the missing elements to the existing annotations: - * - * {@link PersistenceConstants#DIAGNOSTIC_CODE_MISSING_ATTRIBUTES} - *
      - *
    • Add the `name` attribute to the `@MapKeyJoinColumn` annotation - *
    • Add the `referencedColumnName` attribute to the `@MapKeyJoinColumn` annotation - *
    - * - * @author Leslie Dawson (lamminade) - * - */ -public class PersistenceAnnotationQuickFix extends InsertAnnotationMissingQuickFix { - - public PersistenceAnnotationQuickFix() { - super("jakarta.persistence.MapKeyJoinColumn"); - } - - @Override - protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions) throws CoreException { - String[] annotations = getAnnotations(); - insertAndReplaceAnnotation(diagnostic, context, parentType, codeActions, annotations); - } - - private static void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, - IBinding parentType, List codeActions, String... annotations) throws CoreException { - ArrayList attributes = new ArrayList<>(); - attributes.add("name"); - attributes.add("referencedColumnName"); - String name = Messages.getMessage("AddTheMissingAttributes"); - - ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, attributes, annotations); - - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(name); - if (codeAction != null) { - codeActions.add(codeAction); - } - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceEntityDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceEntityDiagnosticsCollector.java deleted file mode 100644 index 7b32d767..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceEntityDiagnosticsCollector.java +++ /dev/null @@ -1,179 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation, Ankush Sharma and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Ankush Sharma - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.persistence; - -import java.util.List; - -import org.eclipse.jdt.core.Flags; - -/** - * @author ankushsharma - * @brief Diagnostics implementation for Jakarta Persistence 3.0 - */ - -// Imports -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IField; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -public class PersistenceEntityDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public PersistenceEntityDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return PersistenceConstants.DIAGNOSTIC_SOURCE; - } - - /** - * check if the modifier provided is static - * - * @param flag - * @return - * @note modifier flags are an addition of all flags combined - */ - private boolean isStatic(int flag) { - // If a field is static, we do not care about it, we care about all other field - Integer isPublicStatic = flag - Flags.AccPublic; - Integer isPrivateStatic = flag - Flags.AccPrivate; - Integer isFinalStatic = flag - Flags.AccFinal; - Integer isProtectedStatic = flag - Flags.AccProtected; - Integer isStatic = flag; - if (isPublicStatic.equals(Flags.AccStatic) || isPrivateStatic.equals(Flags.AccStatic) - || isStatic.equals(Flags.AccStatic) || isFinalStatic.equals(Flags.AccStatic) - || isProtectedStatic.equals(Flags.AccStatic)) { - return true; - } - return false; - } - - /** - * check if the modifier provided is final - * - * @param flag - * @return - * @note modifier flags are an addition of all flags combined - */ - private boolean isFinal(int flag) { - Integer isPublicFinal = flag - Flags.AccPublic; - Integer isPrivateFinal = flag - Flags.AccPrivate; - Integer isProtectedFinal = flag - Flags.AccProtected; - Integer isFinal = flag; - if (isPublicFinal.equals(Flags.AccFinal) || isPrivateFinal.equals(Flags.AccFinal) - || isProtectedFinal.equals(Flags.AccFinal) || isFinal.equals(Flags.AccFinal)) { - return true; - } - return false; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit != null) { - IType[] alltypes; - IAnnotation[] allAnnotations; - - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - allAnnotations = type.getAnnotations(); - - /* ============ Entity Annotation Diagnostics =========== */ - IAnnotation EntityAnnotation = null; - for (IAnnotation annotation : allAnnotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), PersistenceConstants.ENTITY)) { - EntityAnnotation = annotation; - } - } - - if (EntityAnnotation != null) { - // Define boolean requirements for the diagnostics - boolean hasPublicOrProtectedNoArgConstructor = false; - boolean hasArgConstructor = false; - boolean isEntityClassFinal = false; - - // Get the Methods of the annotated Class - for (IMethod method : type.getMethods()) { - if (isConstructorMethod(method)) { - // We have found a method that is a constructor - if (method.getNumberOfParameters() > 0) { - hasArgConstructor = true; - continue; - } - // Don't need to perform subtractions to check flags because eclipse notifies on - // illegal constructor modifiers - if (method.getFlags() != Flags.AccPublic && method.getFlags() != Flags.AccProtected) - continue; - hasPublicOrProtectedNoArgConstructor = true; - } - // All Methods of this class should not be final - if (isFinal(method.getFlags())) { - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("EntityNoFinalMethods"), - PersistenceConstants.DIAGNOSTIC_CODE_FINAL_METHODS, method.getElementType(), - DiagnosticSeverity.Error)); - } - } - - // Go through the instance variables and make sure no instance vars are final - for (IField field : type.getFields()) { - // If a field is static, we do not care about it, we care about all other field - if (isStatic(field.getFlags())) { - continue; - } - // If we find a non-static variable that is final, this is a problem - if (isFinal(field.getFlags())) { - diagnostics.add(createDiagnostic(field, unit, - Messages.getMessage("EntityNoFinalVariables"), - PersistenceConstants.DIAGNOSTIC_CODE_FINAL_VARIABLES, field.getElementType(), - DiagnosticSeverity.Error)); - } - } - - // Ensure that the Entity class is not given a final modifier - if (isFinal(type.getFlags())) - isEntityClassFinal = true; - - // Create Diagnostics if needed - if (!hasPublicOrProtectedNoArgConstructor && hasArgConstructor) { - diagnostics.add(createDiagnostic(type, unit, - Messages.getMessage("EntityNoArgConstructor"), - PersistenceConstants.DIAGNOSTIC_CODE_MISSING_EMPTY_CONSTRUCTOR, null, - DiagnosticSeverity.Error)); - } - - if (isEntityClassFinal) { - diagnostics.add(createDiagnostic(type, unit, - Messages.getMessage("EntityNoFinalClass"), - PersistenceConstants.DIAGNOSTIC_CODE_FINAL_CLASS, type.getElementType(), - DiagnosticSeverity.Error)); - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate persistence diagnostics", e); - } - } - // We do not do anything if the found unit is null - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceEntityQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceEntityQuickFix.java deleted file mode 100644 index de610330..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceEntityQuickFix.java +++ /dev/null @@ -1,115 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2021, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.persistence; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.AddConstructorProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyModifiersProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * QuickFix for fixing {@link PersistenceConstants#DIAGNOSTIC_CODE_MISSING_ATTRIBUTES} error - * by providing several code actions to remove incorrect modifiers or add missing constructor: - * - * {@link PersistenceConstants#DIAGNOSTIC_CODE_MISSING_EMPTY_CONSTRUCTOR} - *
      - *
    • Add a (no-arg) void constructor to this class if the class has other constructors - * which do not conform to this - *
    - * - * {@link PersistenceConstants#DIAGNOSTIC_CODE_FINAL_METHODS} - *
      - *
    • Remove the FINAL modifier from all methods in this class - *
    - * - * {@link PersistenceConstants#DIAGNOSTIC_CODE_FINAL_VARIABLES} - *
      - *
    • Remove the FINAL modifier from all variables in this class - *
    - * - * {@link PersistenceConstants#DIAGNOSTIC_CODE_FINAL_CLASS} - *
      - *
    • Remove the FINAL modifier from this class - *
    - * - * @author Leslie Dawson (lamminade) - * - */ -public class PersistenceEntityQuickFix implements IJavaCodeActionParticipant { - - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - IBinding parentType = getBinding(node); - if (parentType != null) { - - // add constructor - if (diagnostic.getCode().getLeft().equals(PersistenceConstants.DIAGNOSTIC_CODE_MISSING_EMPTY_CONSTRUCTOR)) { - codeActions.addAll(addConstructor(diagnostic, context, parentType)); - } - } - return codeActions; - } - - protected static IBinding getBinding(ASTNode node) { - if (node.getParent() instanceof VariableDeclarationFragment) { - VariableDeclarationFragment fragment = (VariableDeclarationFragment) node.getParent(); - return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); - } - return Bindings.getBindingOfParentType(node); - } - - private List addConstructor(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType) throws CoreException { - List codeActions = new ArrayList<>(); - - // option for protected constructor - String name = Messages.getMessage("AddNoArgProtectedConstructor"); - ChangeCorrectionProposal proposal = new AddConstructorProposal(name, - context.getCompilationUnit(), context.getASTRoot(), parentType, 0); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - - if (codeAction != null) { - codeActions.add(codeAction); - } - - // option for public constructor - name = Messages.getMessage("AddNoArgPublicConstructor"); - proposal = new AddConstructorProposal(name, - context.getCompilationUnit(), context.getASTRoot(), parentType, 0, "public"); - codeAction = context.convertToCodeAction(proposal, diagnostic); - - if (codeAction != null) { - codeActions.add(codeAction); - } - - return codeActions; - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceMapKeyDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceMapKeyDiagnosticsCollector.java deleted file mode 100644 index 9768e130..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceMapKeyDiagnosticsCollector.java +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation, Ankush Sharma and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Ankush Sharma - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.persistence; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IField; -import org.eclipse.jdt.core.IMember; -import org.eclipse.jdt.core.IMemberValuePair; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -public class PersistenceMapKeyDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public PersistenceMapKeyDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return PersistenceConstants.DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit != null) { - IType[] alltypes; - IAnnotation[] allAnnotations; - - try { - alltypes = unit.getAllTypes(); - IMethod[] methods; - IField[] fields; - - for (IType type : alltypes) { - methods = type.getMethods(); - for (IMethod method : methods) { - List mapKeyJoinCols = new ArrayList(); - boolean hasMapKeyAnnotation = false; - boolean hasMapKeyClassAnnotation = false; - allAnnotations = method.getAnnotations(); - for (IAnnotation annotation : allAnnotations) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), - PersistenceConstants.SET_OF_PERSISTENCE_ANNOTATIONS); - if (matchedAnnotation != null) { - if (PersistenceConstants.MAPKEY.equals(matchedAnnotation)) - hasMapKeyAnnotation = true; - else if (PersistenceConstants.MAPKEYCLASS.equals(matchedAnnotation)) - hasMapKeyClassAnnotation = true; - else if (PersistenceConstants.MAPKEYJOINCOLUMN.equals(matchedAnnotation)) { - mapKeyJoinCols.add(annotation); - } - } - } - if (hasMapKeyAnnotation && hasMapKeyClassAnnotation) { - // A single field cannot have the same - diagnostics.add(createDiagnostic(method, unit, - Messages.getMessage("MapKeyAnnotationsNotOnSameField"), - PersistenceConstants.DIAGNOSTIC_CODE_INVALID_ANNOTATION, null, - DiagnosticSeverity.Error)); - } - // If we have multiple MapKeyJoinColumn annotations on a single method we must - // ensure each has a name and referencedColumnName - if (mapKeyJoinCols.size() > 1) { - validateMapKeyJoinColumnAnnotations(mapKeyJoinCols, method, unit, diagnostics); - } - } - - // Go through each field to ensure they do not have both MapKey and MapKeyColumn - // Annotations - fields = type.getFields(); - for (IField field : fields) { - List mapKeyJoinCols = new ArrayList(); - boolean hasMapKeyAnnotation = false; - boolean hasMapKeyClassAnnotation = false; - allAnnotations = field.getAnnotations(); - for (IAnnotation annotation : allAnnotations) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), - PersistenceConstants.SET_OF_PERSISTENCE_ANNOTATIONS); - if (matchedAnnotation != null) { - if (PersistenceConstants.MAPKEY.equals(matchedAnnotation)) - hasMapKeyAnnotation = true; - else if (PersistenceConstants.MAPKEYCLASS.equals(matchedAnnotation)) - hasMapKeyClassAnnotation = true; - else if (PersistenceConstants.MAPKEYJOINCOLUMN.equals(matchedAnnotation)) { - mapKeyJoinCols.add(annotation); - } - } - } - if (hasMapKeyAnnotation && hasMapKeyClassAnnotation) { - // A single field cannot have the same - diagnostics.add(createDiagnostic(field, unit, - Messages.getMessage("MapKeyAnnotationsNotOnSameField"), - PersistenceConstants.DIAGNOSTIC_CODE_INVALID_ANNOTATION, null, - DiagnosticSeverity.Error)); - } - if (mapKeyJoinCols.size() > 1) { - validateMapKeyJoinColumnAnnotations(mapKeyJoinCols, field, unit, diagnostics); - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate diagnostics", e); - } - } - } - - private void validateMapKeyJoinColumnAnnotations(List annotations, IMember element, - ICompilationUnit unit, List diagnostics) { - String message = (element instanceof IMethod) ? - Messages.getMessage("MultipleMapKeyJoinColumnMethod") : - Messages.getMessage("MultipleMapKeyJoinColumnField"); - annotations.forEach(annotation -> { - boolean allNamesSpecified, allReferencedColumnNameSpecified; - try { - List memberValues = Arrays.asList(annotation.getMemberValuePairs()); - allNamesSpecified = memberValues.stream() - .anyMatch((mv) -> mv.getMemberName().equals(PersistenceConstants.NAME)); - allReferencedColumnNameSpecified = memberValues.stream() - .anyMatch((mv) -> mv.getMemberName().equals(PersistenceConstants.REFERENCEDCOLUMNNAME)); - if (!allNamesSpecified || !allReferencedColumnNameSpecified) { - diagnostics.add(createDiagnostic(element, unit, message, - PersistenceConstants.DIAGNOSTIC_CODE_MISSING_ATTRIBUTES, null, DiagnosticSeverity.Error)); - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Error while retrieving member values of @MapKeyJoinColumn Annotation", - e); - } - }); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/CompleteFilterAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/CompleteFilterAnnotationQuickFix.java deleted file mode 100644 index b2fa3a17..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/CompleteFilterAnnotationQuickFix.java +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.servlet; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.dom.IBinding; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyAnnotationProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.InsertAnnotationMissingQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * QuickFix for fixing {@link ServletConstants#DIAGNOSTIC_CODE_FILTER_MISSING_ATTRIBUTE} error - * and {@link ServletConstants#DIAGNOSTIC_CODE_FILTER_MISSING_ATTRIBUTE} error - * by providing several code actions: - * - * - * {@link ServletConstants#DIAGNOSTIC_CODE_FILTER_MISSING_ATTRIBUTE} - *
      - *
    • Add the `value` attribute to the `@WebFilter` annotation - *
    • Add the `urlPatterns` attribute to the `@WebFilter` annotation - *
    • Add the `servletNames` attribute to the `@WebFilter` annotation - *
    - * - * {@link ServletConstants#DIAGNOSTIC_CODE_FILTER_MISSING_ATTRIBUTE} - *
      - *
    • Remove the `value` attribute from the `@WebFilter` annotation - *
    • Remove the `urlPatterns` attribute from the `@WebFilter` annotation - *
    - * - * @author Kathryn Kodama - * - */ -public class CompleteFilterAnnotationQuickFix extends InsertAnnotationMissingQuickFix { - - public CompleteFilterAnnotationQuickFix() { - super("jakarta.servlet.annotation.WebFilter"); - } - - @Override - protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions) throws CoreException { - String[] annotations = getAnnotations(); - for (String annotation : annotations) { - insertAndReplaceAnnotation(diagnostic, context, parentType, codeActions, annotation); - } - } - - private static void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, - IBinding parentType, List codeActions, String annotation) throws CoreException { - - // Insert the annotation and the proper import by using JDT Core Manipulation - // API - - - // if missing an attribute, do value insertion - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE_FILTER_MISSING_ATTRIBUTE)) { - ArrayList attributes = new ArrayList<>(); - attributes.add("value"); attributes.add("urlPatterns");attributes.add("servletNames"); - // Code Action 1: add value attribute to the WebServlet annotation - // Code Action 2: add urlPatterns attribute to the WebServlet annotation - for (int i = 0; i < attributes.size(); i++) { - String attribute = attributes.get(i); - - ArrayList attributesToAdd = new ArrayList<>(); - attributesToAdd.add(attribute); - String name = getLabel(annotation, attribute, "Add"); - ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, annotation, attributesToAdd); - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(name); - if (codeAction != null) { - codeActions.add(codeAction); - } - } - } - // if duplicate attributes exist in annotations, remove attributes from annotation - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE_FILTER_DUPLICATE_ATTRIBUTES)) { - ArrayList attributes = new ArrayList<>(); - attributes.add("value"); attributes.add("urlPatterns"); - // Code Action 1: remove value attribute from the WebServlet annotation - // Code Action 2: remove urlPatterns attribute from the WebServlet annotation - for (int i = 0; i < attributes.size(); i++) { - String attribute = attributes.get(i); - - ArrayList attributesToRemove = new ArrayList<>(); - attributesToRemove.add(attribute); - String name = getLabel(annotation, attribute, "Remove"); - ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, annotation, new ArrayList(), attributesToRemove); - // Convert the proposal to LSP4J CodeAction - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(name); - if (codeAction != null) { - codeActions.add(codeAction); - } - } - } - } - - private static String getLabel(String annotation, String attribute, String labelType) { - String annotationName = annotation.substring(annotation.lastIndexOf('.') + 1, annotation.length()); - annotationName = "@" + annotationName; - if (labelType.equals("Remove")) { - return Messages.getMessage("RemoveTheAttriubuteFrom", attribute, annotationName); - } - return Messages.getMessage("AddTheAttributeTo", attribute, annotationName); - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/FilterDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/FilterDiagnosticsCollector.java deleted file mode 100644 index b816808d..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/FilterDiagnosticsCollector.java +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation, Reza Akhavan and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Reza Akhavan - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.servlet; - -import java.util.List; - -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IMemberValuePair; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -public class FilterDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public FilterDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return ServletConstants.DIAGNOSTIC_SOURCE; - } - - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit != null) { - IType[] alltypes; - IAnnotation[] allAnnotations; - - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - allAnnotations = type.getAnnotations(); - IAnnotation webFilterAnnotation = null; - - for (IAnnotation annotation : allAnnotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), - ServletConstants.WEBFILTER_FQ_NAME)) { - webFilterAnnotation = annotation; - } - } - - String[] interfaces = { ServletConstants.FILTER_FQ_NAME }; - boolean isFilterImplemented = doesImplementInterfaces(type, interfaces); - - if (webFilterAnnotation != null && !isFilterImplemented) { - diagnostics.add(createDiagnostic(type, unit, - Messages.getMessage("WebFilterMustImplement"), - ServletConstants.DIAGNOSTIC_CODE_FILTER, null, DiagnosticSeverity.Error)); - } - - /* URL pattern diagnostic check */ - if (webFilterAnnotation != null) { - IMemberValuePair[] memberValues = webFilterAnnotation.getMemberValuePairs(); - - boolean isUrlpatternSpecified = false; - boolean isServletNamesSpecified = false; - boolean isValueSpecified = false; - for (IMemberValuePair mv : memberValues) { - if (mv.getMemberName().equals(ServletConstants.URL_PATTERNS)) { - isUrlpatternSpecified = true; - continue; - } - if (mv.getMemberName().equals(ServletConstants.SERVLET_NAMES)) { - isServletNamesSpecified = true; - continue; - } - if (mv.getMemberName().equals(ServletConstants.VALUE)) { - isValueSpecified = true; - } - } - if (!isUrlpatternSpecified && !isValueSpecified && !isServletNamesSpecified) { - diagnostics.add(createDiagnostic(webFilterAnnotation, unit, - Messages.getMessage("WebFilterMustDefine"), - ServletConstants.DIAGNOSTIC_CODE_FILTER_MISSING_ATTRIBUTE, null, - DiagnosticSeverity.Error)); - } - if (isUrlpatternSpecified && isValueSpecified) { - diagnostics.add(createDiagnostic(webFilterAnnotation, unit, - Messages.getMessage("WebFilterCannotHaveBoth"), - ServletConstants.DIAGNOSTIC_CODE_FILTER_DUPLICATE_ATTRIBUTES, null, - DiagnosticSeverity.Error)); - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate diagnostics", e); - } - } - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/FilterImplementationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/FilterImplementationQuickFix.java deleted file mode 100644 index 07ee61f6..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/FilterImplementationQuickFix.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.servlet; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ImplementInterfaceProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * QuickFix for fixing HttpServlet extension error by providing the code actions - * which implements IJavaCodeActionParticipant - * - * Adapted from - * https://github.com/eclipse/lsp4mp/blob/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/health/java/ImplementHealthCheckQuickFix.java - * - * @author Credit to Angelo ZERR - * - */ -public class FilterImplementationQuickFix implements IJavaCodeActionParticipant { - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - ITypeBinding parentType = Bindings.getBindingOfParentType(node); - if (parentType != null) { - // Create code action - // interface - String title = Messages.getMessage("LetClassImplement", - BasicElementLabels.getJavaElementName(parentType.getName()), - BasicElementLabels.getJavaElementName(ServletConstants.FILTER)); - ChangeCorrectionProposal proposal = new ImplementInterfaceProposal( - title, context.getCompilationUnit(), parentType, - context.getASTRoot(), "jakarta.servlet.Filter", 0); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(title); - codeActions.add(codeAction); - } - return codeActions; - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/HttpServletQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/HttpServletQuickFix.java deleted file mode 100644 index fa3ce54c..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/HttpServletQuickFix.java +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.servlet; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ExtendClassProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * QuickFix for fixing HttpServlet extension error by providing the code actions - * which implements IJavaCodeActionParticipant - * - * Adapted from - * https://github.com/eclipse/lsp4mp/blob/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/health/java/ImplementHealthCheckQuickFix.java - * - * @author Credit to Angelo ZERR - * - */ -public class HttpServletQuickFix implements IJavaCodeActionParticipant { - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - ITypeBinding parentType = Bindings.getBindingOfParentType(node); - if (parentType != null) { - // Create code action - // interface - String title = Messages.getMessage("LetClassExtend", - BasicElementLabels.getJavaElementName(parentType.getName()), - BasicElementLabels.getJavaElementName(ServletConstants.HTTP_SERVLET)); - ChangeCorrectionProposal proposal = new ExtendClassProposal(title, - context.getCompilationUnit(), parentType, context.getASTRoot(), - "jakarta.servlet.http.HttpServlet", 0); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(title); - codeActions.add(codeAction); - } - return codeActions; - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ListenerDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ListenerDiagnosticsCollector.java deleted file mode 100644 index 6b0638d2..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ListenerDiagnosticsCollector.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation, Reza Akhavan and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Reza Akhavan - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.servlet; - -import java.util.List; - -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -public class ListenerDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public ListenerDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return ServletConstants.DIAGNOSTIC_SOURCE; - } - - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit != null) { - IType[] alltypes; - IAnnotation[] allAnnotations; - - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - allAnnotations = type.getAnnotations(); - boolean isWebListenerAnnotated = false; - for (IAnnotation annotation : allAnnotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), - ServletConstants.WEB_LISTENER_FQ_NAME)) { - isWebListenerAnnotated = true; - break; - } - } - - String[] interfaces = { ServletConstants.SERVLET_CONTEXT_LISTENER_FQ_NAME, - ServletConstants.SERVLET_CONTEXT_ATTRIBUTE_LISTENER_FQ_NAME, - ServletConstants.SERVLET_REQUEST_LISTENER_FQ_NAME, - ServletConstants.SERVLET_REQUEST_ATTRIBUTE_LISTENER_FQ_NAME, - ServletConstants.HTTP_SESSION_LISTENER_FQ_NAME, - ServletConstants.HTTP_SESSION_ATTRIBUTE_LISTENER_FQ_NAME, - ServletConstants.HTTP_SESSION_ID_LISTENER_FQ_NAME }; - boolean isImplemented = doesImplementInterfaces(type, interfaces); - - if (isWebListenerAnnotated && !isImplemented) { - diagnostics.add(createDiagnostic(type, unit, Messages.getMessage("AnnotatedWithWebListenerMustImplement"), - ServletConstants.DIAGNOSTIC_CODE_LISTENER, null, DiagnosticSeverity.Error)); - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot calculate diagnostics", e); - } - } - } - -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ListenerImplementationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ListenerImplementationQuickFix.java deleted file mode 100644 index 02e2113d..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ListenerImplementationQuickFix.java +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.servlet; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; -import org.eclipse.jdt.internal.corext.dom.Bindings; -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.IJavaCodeActionParticipant; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ImplementInterfaceProposal; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -/** - * QuickFix for fixing HttpServlet extension error by providing the code actions - * which implements IJavaCodeActionParticipant - * - * Adapted from - * https://github.com/eclipse/lsp4mp/blob/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/health/java/ImplementHealthCheckQuickFix.java - * - * @author Credit to Angelo ZERR - * - */ -public class ListenerImplementationQuickFix implements IJavaCodeActionParticipant { - @Override - public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, - IProgressMonitor monitor) throws CoreException { - List codeActions = new ArrayList<>(); - ASTNode node = context.getCoveredNode(); - ITypeBinding parentType = Bindings.getBindingOfParentType(node); - if (parentType != null) { - // Create code action - // interface - - CodeAction ca0 = setUpCodeAction(parentType, diagnostic, context, ServletConstants.SERVLET_CONTEXT_LISTENER, - "jakarta.servlet.ServletContextListener"); - CodeAction ca1 = setUpCodeAction(parentType, diagnostic, context, - ServletConstants.SERVLET_CONTEXT_ATTRIBUTE_LISTENER, - "jakarta.servlet.ServletContextAttributeListener"); - CodeAction ca2 = setUpCodeAction(parentType, diagnostic, context, ServletConstants.SERVLET_REQUEST_LISTENER, - "jakarta.servlet.ServletRequestListener"); - CodeAction ca3 = setUpCodeAction(parentType, diagnostic, context, - ServletConstants.SERVLET_REQUEST_ATTRIBUTE_LISTENER, - "jakarta.servlet.ServletRequestAttributeListener"); - CodeAction ca4 = setUpCodeAction(parentType, diagnostic, context, ServletConstants.HTTP_SESSION_LISTENER, - "jakarta.servlet.http.HttpSessionListener"); - CodeAction ca5 = setUpCodeAction(parentType, diagnostic, context, - ServletConstants.HTTP_SESSION_ATTRIBUTE_LISTENER, - "jakarta.servlet.http.HttpSessionAttributeListener"); - CodeAction ca6 = setUpCodeAction(parentType, diagnostic, context, ServletConstants.HTTP_SESSION_ID_LISTENER, - "jakarta.servlet.http.HttpSessionIdListener"); - - codeActions.add(ca0); - codeActions.add(ca1); - codeActions.add(ca2); - codeActions.add(ca3); - codeActions.add(ca4); - codeActions.add(ca5); - codeActions.add(ca6); - } - return codeActions; - } - - private CodeAction setUpCodeAction(ITypeBinding parentType, Diagnostic diagnostic, JavaCodeActionContext context, - String interfaceName, String interfaceType) throws CoreException { - String title = Messages.getMessage("LetClassImplement", - BasicElementLabels.getJavaElementName(parentType.getName()), - BasicElementLabels.getJavaElementName(interfaceName)); - ChangeCorrectionProposal proposal = new ImplementInterfaceProposal(title, - context.getCompilationUnit(), parentType, context.getASTRoot(), interfaceType, 0); - CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); - codeAction.setTitle(title); - - return codeAction; - } -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ServletDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ServletDiagnosticsCollector.java deleted file mode 100644 index 55de5df9..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ServletDiagnosticsCollector.java +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2023 IBM Corporation, Pengyu Xiong and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Pengyu Xiong - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.servlet; - -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IMemberValuePair; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; -import org.eclipse.lsp4jakarta.jdt.core.TypeHierarchyUtils; - -/** - * - * jararta.annotation Diagnostics - * - *
  • Diagnostic 1: Class annotated with @WebServlet does not extend the - * HttpServlet class.
  • - *
  • Diagnostic 2: @WebServlet missing 'urlPatterns' and 'value' attribute - * (one must be specified).
  • - *
  • Diagnostic 3: @WebServlet has both 'urlPatterns' and 'value' attributes - * specified.
  • - * - * @see https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0.html#webservlet - * - */ -public class ServletDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public ServletDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return ServletConstants.DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit != null) { - IType[] alltypes; - IAnnotation[] allAnnotations; - - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - allAnnotations = type.getAnnotations(); - - IAnnotation webServletAnnotation = null; - for (IAnnotation annotation : allAnnotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), - ServletConstants.WEB_SERVLET_FQ_NAME)) { - webServletAnnotation = annotation; - break; // get the first one, the annotation is not repeatable - } - } - - if (webServletAnnotation != null) { - // check if the class extends HttpServlet - try { - int r = TypeHierarchyUtils.doesITypeHaveSuperType(type, ServletConstants.HTTP_SERVLET); - if (r == -1) { - diagnostics.add(createDiagnostic(type, unit, - Messages.getMessage("WebServletMustExtend"), - ServletConstants.DIAGNOSTIC_CODE, null, DiagnosticSeverity.Error)); - } else if (r == 0) { // unknown super type - diagnostics.add(createDiagnostic(type, unit, - Messages.getMessage("WebServletMustExtend"), - ServletConstants.DIAGNOSTIC_CODE, null, DiagnosticSeverity.Warning)); - } - } catch (CoreException e) { - JakartaCorePlugin.logException("Cannot check type hierarchy", e); - } - - /* URL pattern diagnostic check */ - IMemberValuePair[] memberValues = webServletAnnotation.getMemberValuePairs(); - - boolean isUrlpatternSpecified = false; - boolean isValueSpecified = false; - for (IMemberValuePair mv : memberValues) { - if (mv.getMemberName().equals(ServletConstants.URL_PATTERNS)) { - isUrlpatternSpecified = true; - continue; - } - if (mv.getMemberName().equals(ServletConstants.VALUE)) { - isValueSpecified = true; - } - } - if (!isUrlpatternSpecified && !isValueSpecified) { - diagnostics.add(createDiagnostic(webServletAnnotation, unit, - Messages.getMessage("WebServletMustDefine"), - ServletConstants.DIAGNOSTIC_CODE_MISSING_ATTRIBUTE, null, - DiagnosticSeverity.Error)); - } - if (isUrlpatternSpecified && isValueSpecified) { - diagnostics.add(createDiagnostic(webServletAnnotation, unit, - Messages.getMessage("WebServletCannotHaveBoth"), - ServletConstants.DIAGNOSTIC_CODE_DUPLICATE_ATTRIBUTES, null, - DiagnosticSeverity.Error)); - } - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Cannot check type hierarchy", e); - } - } - } - -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/transactions/TransactionsConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/transactions/TransactionsConstants.java deleted file mode 100644 index daca215b..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/transactions/TransactionsConstants.java +++ /dev/null @@ -1,20 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Lana Kang -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.transactions; - -public class TransactionsConstants { - - public static final String DIAGNOSTIC_SOURCE = "jakarta-transactions"; - -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/transactions/TransactionsDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/transactions/TransactionsDiagnosticsCollector.java deleted file mode 100644 index d051873d..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/transactions/TransactionsDiagnosticsCollector.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Lana Kang -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.transactions; - -import static org.eclipse.lsp4jakarta.jdt.core.transactions.TransactionsConstants.DIAGNOSTIC_SOURCE; - -import java.util.List; - -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; - -public class TransactionsDiagnosticsCollector extends AbstractDiagnosticsCollector { - - public TransactionsDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - - } - -} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/IJDTUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/IJDTUtils.java new file mode 100644 index 00000000..d15c9d78 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/IJDTUtils.java @@ -0,0 +1,94 @@ +/******************************************************************************* +* Copyright (c) 2019, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.utils; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IOpenable; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.commons.DocumentFormat; + +/** + * JDT LS utils provides some helpful utilities. To avoid having a strong + * dependencies to JDT-LS, we use this API. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/utils/IJDTUtils.java + * + * @author Angelo ZERR + * + */ +public interface IJDTUtils { + + IFile findFile(String uriString); + + /** + * Given the uri returns a {@link ICompilationUnit}. May return null if it can + * not associate the uri with a Java file. + * + * @param uriString + * @return compilation unit + */ + ICompilationUnit resolveCompilationUnit(String uriString); + + /** + * Given the uri returns a {@link IClassFile}. May return null if it can not + * resolve the uri to a library. + * + * @param uri with 'jdt' scheme + * @return class file + */ + IClassFile resolveClassFile(String uri); + + boolean isHiddenGeneratedElement(IJavaElement element); + + /** + * Creates a range for the given offset and length for an {@link IOpenable} + * + * @param openable + * @param offset + * @param length + * @return + * @throws JavaModelException + */ + Range toRange(IOpenable openable, int offset, int length) throws JavaModelException; + + /** + * Format URIs to be consumed by clients. On Windows platforms, UNC (Universal + * Naming Convention) URIs are transformed to follow the file:// + * pattern. + * + * @param uri the String URI to transform. + * @return a String URI compatible with clients. + */ + String toClientUri(String uri); + + String toUri(ITypeRoot typeRoot); + + void waitForLifecycleJobs(IProgressMonitor monitor); + + int toOffset(IBuffer buffer, int line, int column); + + Location toLocation(IJavaElement element) throws JavaModelException; + + String getJavadoc(IMember member, DocumentFormat documentFormat) throws JavaModelException; +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/JDTJakartaUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/JDTJakartaUtils.java new file mode 100644 index 00000000..6b2ac770 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/JDTJakartaUtils.java @@ -0,0 +1,199 @@ +/******************************************************************************* +* Copyright (c) 2019, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.utils; + +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Stream; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.core.JavaProject; +import org.eclipse.lsp4jakarta.commons.ClasspathKind; + +/** + * JDT Jakarta utilities. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/utils/JDTMicroProfileUtils.java + * + * @author Angelo ZERR + */ +public class JDTJakartaUtils { + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(JDTJakartaUtils.class.getName()); + + /** Jakarta project indicator. */ + public static final String JAKARTA_RS_GET = "jakarta.ws.rs.GET"; + + /** + * Returns the project URI of the given project. + * + * @param project the java project + * @return the project URI of the given project. + */ + public static String getProjectURI(IJavaProject project) { + return getProjectURI(project.getProject()); + } + + /** + * returns the project URI of the given project. + * + * @param project the project + * @return the project URI of the given project. + */ + public static String getProjectURI(IProject project) { + return project.getLocation().toOSString(); + } + + /** + * Returns true if the given resource resource is on the 'test' + * classpath of the given java project javaProject and false + * otherwise. + * + * @param resource the resource + * @param javaProject the project. + * @return true if the given resource resource is on the 'test' + * classpath of the given java project javaProject and + * false otherwise. + */ + public static ClasspathKind getClasspathKind(IResource resource, IJavaProject javaProject) { + IPath exactPath = resource.getFullPath(); + IPath path = exactPath; + + // ensure that folders are only excluded if all of their children are excluded + int resourceType = resource.getType(); + boolean isFolderPath = resourceType == IResource.FOLDER || resourceType == IResource.PROJECT; + + IClasspathEntry[] classpath; + try { + classpath = ((JavaProject) javaProject).getResolvedClasspath(); + } catch (JavaModelException e) { + return ClasspathKind.NONE; // not a Java project + } + for (int i = 0; i < classpath.length; i++) { + IClasspathEntry entry = classpath[i]; + IPath entryPath = entry.getPath(); + if (entryPath.equals(exactPath)) { // package fragment roots must match exactly entry pathes (no exclusion + // there) + return getClasspathKind(entry); + } + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=276373 + // When a classpath entry is absolute, convert the resource's relative path to a + // file system path and compare + // e.g - /P/lib/variableLib.jar and /home/P/lib/variableLib.jar when compared + // should return true + if (entryPath.isAbsolute() + && entryPath.equals(ResourcesPlugin.getWorkspace().getRoot().getLocation().append(exactPath))) { + return getClasspathKind(entry); + } + if (entryPath.isPrefixOf(path)) { + // && !Util.isExcluded(path, ((ClasspathEntry) + // entry).fullInclusionPatternChars(), + // ((ClasspathEntry) entry).fullExclusionPatternChars(), isFolderPath)) { + return getClasspathKind(entry); + } + } + return ClasspathKind.NONE; + + } + + /** + * Returns true if the given project has a nature specified by + * natureId and false otherwise. + * + * @param project the project + * @param natureId the nature id + * @return true if the given project has a nature specified by + * natureId and false otherwise. + */ + public static boolean hasNature(IProject project, String natureId) { + try { + return project != null && project.hasNature(natureId); + } catch (CoreException e) { + return false; + } + } + + private static ClasspathKind getClasspathKind(IClasspathEntry entry) { + return entry.isTest() ? ClasspathKind.TEST : ClasspathKind.SRC; + } + + /** + * Returns true if javaProject is a Jakarta project. Returns + * false otherwise. + * + * @param javaProject the Java project to check + * @return true only if javaProject is a Jakarta project. + */ + public static boolean isJakartaProject(IJavaProject javaProject) { + + // Here we make a determination if we are in a jakarta project - we look for a + // well known + // jakarta class on the classpath - which it will find if the project has the + // jakarta EE dependency in it pom + try { + return javaProject.findType(JAKARTA_RS_GET) != null; + } catch (JavaModelException e) { + LOGGER.log(Level.INFO, "Current Java project is not a Jakarta project", e); + return false; + } + + } + + /** + * Returns an array of all the java projects that are currently loaded into the + * JDT + * workspace. + * + * @return an array of all the projects that are currently loaded into the JDT + * workspace + */ + public static IJavaProject[] getJavaProjects() { + return Stream.of(getAllProjects()) // + .filter(JDTJakartaUtils::isJavaProject) // + .map(p -> JavaCore.create(p)) // + .filter(p -> p != null) // + .toArray(IJavaProject[]::new); + } + + /** + * Returns an array of all the projects that are currently loaded into the JDT + * workspace. + * + * @return an array of all the projects that are currently loaded into the JDT + * workspace + */ + private static IProject[] getAllProjects() { + return ResourcesPlugin.getWorkspace().getRoot().getProjects(); + } + + private static boolean isJavaProject(IProject project) { + if (project == null) { + return false; + } + try { + return project.hasNature(JavaCore.NATURE_ID); + } catch (CoreException e) { + return false; + } + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/JDTTypeUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/JDTTypeUtils.java new file mode 100644 index 00000000..19081dbd --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/JDTTypeUtils.java @@ -0,0 +1,367 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.utils; + +import static org.eclipse.jdt.core.Signature.SIG_VOID; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IJarEntryResource; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IMemberValuePair; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.ISourceRange; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; + +/** + * JDT Type utilities. + * + * @author Angelo ZERR + * + */ +public class JDTTypeUtils { + + private static final List NUMBER_TYPES = Arrays.asList("short", "int", "long", "double", "float"); + + public static IType findType(IJavaProject project, String name) { + try { + return project.findType(name); + } catch (JavaModelException e) { + return null; + } + } + + /** + * Returns the resolved type name of the javaElement and null + * otherwise + * + * @param javaElement the Java element + * @return the resolved type name of the javaElement and null + * otherwise + */ + public static String getResolvedTypeName(IJavaElement javaElement) { + switch (javaElement.getElementType()) { + case IJavaElement.LOCAL_VARIABLE: + return getResolvedTypeName((ILocalVariable) javaElement); + case IJavaElement.FIELD: + return getResolvedTypeName((IField) javaElement); + default: + return null; + } + } + + /** + * Returns the resolved type name of the given localVar and null + * otherwise + * + * @param localVar the local variable + * @return the resolved type name of the given localVar and null + * otherwise + */ + public static String getResolvedTypeName(ILocalVariable localVar) { + try { + String signature = localVar.getTypeSignature().replace("/", "."); + IType primaryType = localVar.getTypeRoot().findPrimaryType(); + return JavaModelUtil.getResolvedTypeName(signature, primaryType); + } catch (JavaModelException e) { + return null; + } + } + + /** + * Returns the resolved type name of the given field and null + * otherwise + * + * @param field the field + * @return the resolved type name of the given field and null + * otherwise + */ + public static String getResolvedTypeName(IField field) { + try { + String signature = field.getTypeSignature(); + IType primaryType = field.getTypeRoot().findPrimaryType(); + return JavaModelUtil.getResolvedTypeName(signature, primaryType); + } catch (JavaModelException e) { + return null; + } + } + + /** + * Returns the resolved return type name of the given method and + * null otherwise + * + * @param method the method + * @return the resolved return type name of the given method and + * null otherwise + */ + public static String getResolvedResultTypeName(IMethod method) { + try { + String signature = method.getReturnType(); + IType primaryType = method.getTypeRoot().findPrimaryType(); + return JavaModelUtil.getResolvedTypeName(signature, primaryType); + } catch (JavaModelException e) { + return null; + } + } + + public static String getDefaultValue(IMethod method) { + try { + IMemberValuePair defaultValue = method.getDefaultValue(); + if (defaultValue == null || defaultValue.getValue() == null) { + return null; + } + switch (defaultValue.getValueKind()) { + case IMemberValuePair.K_BOOLEAN: + case IMemberValuePair.K_INT: + case IMemberValuePair.K_LONG: + case IMemberValuePair.K_SHORT: + case IMemberValuePair.K_DOUBLE: + case IMemberValuePair.K_FLOAT: + case IMemberValuePair.K_STRING: + String value = defaultValue.getValue().toString(); + return value.isEmpty() ? null : value; + case IMemberValuePair.K_QUALIFIED_NAME: + case IMemberValuePair.K_SIMPLE_NAME: + String qualifiedName = defaultValue.getValue().toString(); + int index = qualifiedName.lastIndexOf('.'); + return index != -1 ? qualifiedName.substring(index + 1, qualifiedName.length()) : qualifiedName; + case IMemberValuePair.K_UNKNOWN: + return null; + default: + return null; + } + } catch (JavaModelException e) { + return null; + } + } + + public static String getPropertyType(IType type, String typeName) { + return type != null ? type.getFullyQualifiedName('.') : typeName; + } + + /** + * Returns true if the given javaElement is from a Java binary, and + * false otherwise + * + * @param javaElement the Java element + * @return true if the given javaElement is from a Java binary, and + * false otherwise + */ + public static boolean isBinary(IJavaElement javaElement) { + if (javaElement instanceof IMember) { + return ((IMember) javaElement).isBinary(); + } else if (javaElement instanceof ILocalVariable) { + return isBinary(((ILocalVariable) javaElement).getDeclaringMember()); + } + return false; + } + + /** + * Returns the source type of the given javaElement and null + * otherwise + * + * @param javaElement the Java element + * @return the source type of the javaElement + */ + public static String getSourceType(IJavaElement javaElement) { + switch (javaElement.getElementType()) { + case IJavaElement.LOCAL_VARIABLE: + return getSourceType((ILocalVariable) javaElement); + case IJavaElement.FIELD: + return getSourceType((IField) javaElement); + case IJavaElement.METHOD: + return getSourceType((IMethod) javaElement); + case IJavaElement.TYPE: + return getSourceType((IType) javaElement); + default: + return null; + } + } + + /** + * Returns the source type of the given local variable member and + * null otherwise + * + * @param member the local variable to get the source type from + * @return the source type of the given local variable member and + * null otherwise + */ + public static String getSourceType(ILocalVariable member) { + return getSourceType(member.getDeclaringMember()); + } + + /** + * Returns the source type of the given type and null otherwise + * + * @param type the type + * @return the source type of the given type and null otherwise + */ + public static String getSourceType(IType type) { + return getPropertyType(type, null); + } + + /** + * Returns the source type of the given member and null otherwise + * + * @param member the member + * @return the source type of the given member and null otherwise + */ + public static String getSourceType(IMember member) { + return getPropertyType(member.getDeclaringType(), null); + } + + /** + * Returns the source field of the given field + * + * @param field the field + * @return the source field of the given field + */ + public static String getSourceField(IJavaElement field) { + return field.getElementName(); + } + + public static String getSourceMethod(IMethod method) throws JavaModelException { + return method.getElementName() + method.getSignature(); + } + + public static boolean isOptional(String fieldTypeName) { + return fieldTypeName.startsWith("java.util.Optional"); + } + + /** + * Returns the enclosed type declared in the given typeName and + * null otherwise. + * + * @param typeName + * @return + */ + public static String getOptionalTypeParameter(String typeName) { + if (!isOptional(typeName)) { + return null; + } + int start = typeName.indexOf('<'); + if (start == -1) { + return null; + } + // the type name follows the signature java.util.Optional + // extract the enclosed type MyType. + int end = typeName.lastIndexOf('>'); + return typeName.substring(start + 1, end); + } + + public static IType getEnclosedType(IType type, String typeName, IJavaProject javaProject) throws JavaModelException { + // type name is the string of the JDT type (which could be null if type is not + // retrieved) + String enclosedType = typeName; + if (type == null) { + // JDT type is null, in some case it's because type is optional (ex : + // java.util.Optional) + // try to extract the enclosed type from the optional type (to get 'MyType' ) + enclosedType = getOptionalTypeParameter(typeName); + if (enclosedType != null) { + type = findType(javaProject, enclosedType); + } + } + return type; + } + + public static String[] getRawTypeParameters(String fieldTypeName) { + int start = fieldTypeName.indexOf("<") + 1; + int end = fieldTypeName.lastIndexOf(">"); + String keyValue = fieldTypeName.substring(start, end); + int index = keyValue.indexOf(','); + return new String[] { + keyValue.substring(0, index), keyValue.substring(index + 1, keyValue.length()) + }; + } + + public static boolean isPrimitiveType(String valueClass) { + return valueClass.equals("java.lang.String") || valueClass.equals("java.lang.Boolean") + || valueClass.equals("java.lang.Integer") || valueClass.equals("java.lang.Long") + || valueClass.equals("java.lang.Double") || valueClass.equals("java.lang.Float"); + } + + public static boolean isMap(String mapValueClass) { + return mapValueClass.startsWith("java.util.Map"); + } + + public static boolean isList(String valueClass) { + return valueClass.startsWith("java.util.List"); + } + + public static boolean isNumber(String valueClass) { + return NUMBER_TYPES.contains(valueClass); + } + + public static boolean isPrimitiveBoolean(String valueClass) { + return valueClass.equals("boolean"); + } + + public static IJarEntryResource findPropertiesResource(IPackageFragmentRoot packageRoot, String propertiesFileName) throws JavaModelException { + Object[] resources = packageRoot.getNonJavaResources(); + if (resources != null) { + for (Object object : resources) { + if (object instanceof IJarEntryResource) { + IJarEntryResource res = (IJarEntryResource) object; + if ("META-INF".equals(res.getName())) { + IJarEntryResource[] children = res.getChildren(); + if (children != null) { + for (IJarEntryResource r : children) { + if (propertiesFileName.equals(r.getName())) { + return r; + } + } + } + return null; + } + } + } + } + return null; + } + + public static boolean isSimpleFieldType(IType type, String typeName) throws JavaModelException { + return type == null || isPrimitiveType(typeName) || isList(typeName) || isMap(typeName) || isOptional(typeName) + || (type != null && type.isEnum()); + } + + public static boolean overlaps(ISourceRange typeRange, ISourceRange methodRange) { + if (typeRange == null || methodRange == null) { + return false; + } + // method range is overlapping if it appears before or actually overlaps the + // type's range + return methodRange.getOffset() < typeRange.getOffset() || methodRange.getOffset() >= typeRange.getOffset() + && methodRange.getOffset() <= (typeRange.getOffset() + typeRange.getLength()); + } + + /** + * Return true if method returns `void`, and false otherwise + * + * @param method the method to check return value of + * @return + * @throws JavaModelException + */ + public static boolean isVoidReturnType(IMethod method) throws JavaModelException { + return SIG_VOID.equals(method.getReturnType()); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/PositionUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/PositionUtils.java new file mode 100644 index 00000000..c42e2b0a --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/PositionUtils.java @@ -0,0 +1,103 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.core.utils; + +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IOpenable; +import org.eclipse.jdt.core.ISourceRange; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4j.Range; + +/** + * Position utilities. + * + * @author Angelo ZERR + * + */ +public class PositionUtils { + + /** + * Returns the LSP range for the given field name. + * + * @param field the java field. + * @param utils the JDT utilities. + * @return the LSP range for the given field name. + * @throws JavaModelException + */ + public static Range toNameRange(IField field, IJDTUtils utils) throws JavaModelException { + IOpenable openable = field.getCompilationUnit(); + ISourceRange sourceRange = field.getNameRange(); + return utils.toRange(openable, sourceRange.getOffset(), sourceRange.getLength()); + } + + /** + * Returns the LSP range for the given type name. + * + * @param type the java type. + * @param utils the JDT utilities. + * @return the LSP range for the given type name. + * @throws JavaModelException + */ + public static Range toNameRange(IType type, IJDTUtils utils) throws JavaModelException { + IOpenable openable = type.getCompilationUnit(); + ISourceRange sourceRange = type.getNameRange(); + return utils.toRange(openable, sourceRange.getOffset(), sourceRange.getLength()); + } + + /** + * Returns the LSP range for the given method name. + * + * @param method the java type. + * @param utils the JDT utilities. + * @return the LSP range for the given method name. + * @throws JavaModelException + */ + public static Range toNameRange(IMethod method, IJDTUtils utils) throws JavaModelException { + IOpenable openable = method.getCompilationUnit(); + ISourceRange sourceRange = method.getNameRange(); + return utils.toRange(openable, sourceRange.getOffset(), sourceRange.getLength()); + } + + /** + * Returns the LSP range for the given annotation. + * + * @param annotation the java type. + * @param utils the JDT utilities. + * @return the LSP range for the given annotation. + * @throws JavaModelException + */ + public static Range toNameRange(IAnnotation annotation, IJDTUtils utils) throws JavaModelException { + IOpenable openable = annotation.getOpenable(); + ISourceRange sourceRange = annotation.getSourceRange(); + return utils.toRange(openable, sourceRange.getOffset(), sourceRange.getLength()); + } + + /** + * Returns the LSP range for the given Local variable. + * + * @param localVariable the java type. + * @param utils the JDT utilities. + * @return the LSP range for the given annotation. + * @throws JavaModelException + */ + public static Range toNameRange(ILocalVariable localVariable, IJDTUtils utils) throws JavaModelException { + IOpenable openable = localVariable.getOpenable(); + ISourceRange sourceRange = localVariable.getNameRange(); + return utils.toRange(openable, sourceRange.getOffset(), sourceRange.getLength()); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/TypeHierarchyUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/TypeHierarchyUtils.java similarity index 75% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/TypeHierarchyUtils.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/TypeHierarchyUtils.java index edd0936a..21275774 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/TypeHierarchyUtils.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/utils/TypeHierarchyUtils.java @@ -11,7 +11,7 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core; +package org.eclipse.lsp4jakarta.jdt.core.utils; import java.util.concurrent.atomic.AtomicInteger; @@ -36,12 +36,14 @@ public class TypeHierarchyUtils { /** - * + * * @param type The root type of which the super-types are checked. * @param superType The super-type to check for. - * @return An integer: 1 for {@code type} certainly extends/implements {@code superType}, - * -1 for {@code type} certainly does not extend/implement {@code superType}, - * and 0 if unknown (in the case of incomplete type hierarchy) + * @return An integer: 1 for {@code type} certainly extends/implements + * {@code superType}, + * -1 for {@code type} certainly does not extend/implement + * {@code superType}, + * and 0 if unknown (in the case of incomplete type hierarchy) * @throws CoreException */ public static int doesITypeHaveSuperType(IType type, String superType) throws CoreException { @@ -49,10 +51,10 @@ public static int doesITypeHaveSuperType(IType type, String superType) throws Co if (type.getElementName().equals(superType)) { return 1; } - + ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(DefaultWorkingCopyOwner.PRIMARY, null); int r = 0; - + // Check if the type's supertypes contain the superType IType[] parents = typeHierarchy.getAllSupertypes(type); for (IType parentType : parents) { @@ -61,10 +63,11 @@ public static int doesITypeHaveSuperType(IType type, String superType) throws Co break; } } - + // If we haven't found the supertype, check if the type indeed does not extend // superType, or if we don't know. - // We don't know if we don't have the class declaration for ANY of the super types + // We don't know if we don't have the class declaration for ANY of the super + // types if (r == 0) { boolean unknown = false; for (IType parentType : parents) { @@ -79,30 +82,30 @@ public static int doesITypeHaveSuperType(IType type, String superType) throws Co } return r; } - + private static boolean hasKnownDeclaration(IType type) throws CoreException { String typeName = type.getElementName(); final AtomicInteger references = new AtomicInteger(0); SearchEngine engine = new SearchEngine(); SearchPattern pattern = SearchPattern.createPattern(typeName, IJavaSearchConstants.CLASS, - IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH); + IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH); engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, - createSearchScope(type.getJavaProject()), new SearchRequestor() { + createSearchScope(type.getJavaProject()), new SearchRequestor() { - @Override - public void acceptSearchMatch(SearchMatch match) throws CoreException { - Object o = match.getElement(); - if (o instanceof IType) { - IType t = (IType)o; - if (t.getElementName().equals(typeName)) { - references.incrementAndGet(); - } - } - } - }, null); - if (references.get() > 0 ) { + @Override + public void acceptSearchMatch(SearchMatch match) throws CoreException { + Object o = match.getElement(); + if (o instanceof IType) { + IType t = (IType) o; + if (t.getElementName().equals(typeName)) { + references.incrementAndGet(); + } + } + } + }, null); + if (references.get() > 0) { return true; - } + } return false; } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/AddPathParamQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/AddPathParamQuickFix.java deleted file mode 100644 index e609cb89..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/AddPathParamQuickFix.java +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Lidia Ataupillco Ramos - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.websocket; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.InsertAnnotationQuickFix; - -/** - * Quick fix for adding the @PathParam annotation when one of more - * parameters on a method annotated endpoint class decorated with - * any of the annotations @OnMessage, @OnOpen, @OnClose, @OnError - * - * @author Lidia Ataupillco Ramos - */ -public class AddPathParamQuickFix extends InsertAnnotationQuickFix { - public AddPathParamQuickFix() { - super("jakarta.websocket.server.PathParam", false, "value"); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/WebSocketDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/WebSocketDiagnosticsCollector.java deleted file mode 100644 index acf7c347..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/WebSocketDiagnosticsCollector.java +++ /dev/null @@ -1,497 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022, 2023 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Giancarlo Pernudi Segura - initial API and implementation - * Lidia Ataupillco Ramos - * Aviral Saxena - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.websocket; - -import static org.eclipse.lsp4jakarta.jdt.core.TypeHierarchyUtils.doesITypeHaveSuperType; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.ILocalVariable; -import org.eclipse.jdt.core.IMemberValuePair; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.Signature; -import org.eclipse.jdt.internal.core.JavaModel; -import org.eclipse.jdt.internal.corext.util.JavaModelUtil; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.jdt.core.AbstractDiagnosticsCollector; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; -import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; -import org.eclipse.lsp4jakarta.jdt.core.Messages; - -public class WebSocketDiagnosticsCollector extends AbstractDiagnosticsCollector { - public WebSocketDiagnosticsCollector() { - super(); - } - - @Override - protected String getDiagnosticSource() { - return WebSocketConstants.DIAGNOSTIC_SOURCE; - } - - @Override - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - if (unit == null) { - return; - } - - IType[] alltypes; - HashMap checkWSEnd = null; - try { - alltypes = unit.getAllTypes(); - for (IType type : alltypes) { - checkWSEnd = isWSEndpoint(type); - // checks if the class uses annotation to create a WebSocket endpoint - if (checkWSEnd.get(WebSocketConstants.IS_ANNOTATION)) { - // WebSocket Invalid Parameters Diagnostic - invalidParamsCheck(type, unit, diagnostics); - - /* @PathParam Value Mismatch Warning */ - List endpointPathVars = findAndProcessEndpointURI(type); - /* - * WebSocket endpoint annotations must be attached to a class, and thus is - * guaranteed to be processed before any of the member method annotations - */ - if (endpointPathVars != null) { - // PathParam URI Mismatch Warning Diagnostic - uriMismatchWarningCheck(type, endpointPathVars, diagnostics, unit); - } - - // OnMessage validation for WebSocket message formats - onMessageWSMessageFormats(type, diagnostics, unit); - - // ServerEndpoint annotation diagnostics - serverEndpointErrorCheck(type, diagnostics, unit); - } - } - } catch (JavaModelException e) { - JakartaCorePlugin.logException(WebSocketConstants.DIAGNOSTIC_ERR_MSG, e); - } - } - - private void invalidParamsCheck(IType type, ICompilationUnit unit, List diagnostics) - throws JavaModelException { - IMethod[] allMethods = type.getMethods(); - for (IMethod method : allMethods) { - IAnnotation[] allAnnotations = method.getAnnotations(); - Set specialParamTypes = null, rawSpecialParamTypes = null; - - for (IAnnotation annotation : allAnnotations) { - String annotationName = annotation.getElementName(); - String diagnosticCode = null; - - if (isMatchedJavaElement(type, annotationName, WebSocketConstants.ON_OPEN)) { - specialParamTypes = WebSocketConstants.ON_OPEN_PARAM_OPT_TYPES; - rawSpecialParamTypes = WebSocketConstants.RAW_ON_OPEN_PARAM_OPT_TYPES; - diagnosticCode = WebSocketConstants.DIAGNOSTIC_CODE_ON_OPEN_INVALID_PARAMS; - } else if (isMatchedJavaElement(type, annotationName, WebSocketConstants.ON_CLOSE)) { - specialParamTypes = WebSocketConstants.ON_CLOSE_PARAM_OPT_TYPES; - rawSpecialParamTypes = WebSocketConstants.RAW_ON_CLOSE_PARAM_OPT_TYPES; - diagnosticCode = WebSocketConstants.DIAGNOSTIC_CODE_ON_CLOSE_INVALID_PARAMS; - } - if (diagnosticCode != null) { - ILocalVariable[] allParams = method.getParameters(); - for (ILocalVariable param : allParams) { - String signature = param.getTypeSignature(); - String formatSignature = signature.replace("/", "."); - String resolvedTypeName = JavaModelUtil.getResolvedTypeName(formatSignature, type); - boolean isPrimitive = JavaModelUtil.isPrimitive(formatSignature); - boolean isSpecialType; - boolean isPrimWrapped; - - if (resolvedTypeName != null) { - isSpecialType = specialParamTypes.contains(resolvedTypeName); - isPrimWrapped = isWrapper(resolvedTypeName); - } else { - String simpleParamType = Signature.getSignatureSimpleName(signature); - isSpecialType = rawSpecialParamTypes.contains(simpleParamType); - isPrimWrapped = isWrapper(simpleParamType); - } - - // check parameters valid types - if (!(isSpecialType || isPrimWrapped || isPrimitive)) { - diagnostics.add(createDiagnostic(param, unit, - createParamTypeDiagMsg(specialParamTypes, annotationName), diagnosticCode, null, - DiagnosticSeverity.Error)); - continue; - } - - if (!isSpecialType) { - // check that if parameter is not a specialType, it has a @PathParam annotation - IAnnotation[] param_annotations = param.getAnnotations(); - boolean hasPathParamAnnot = Arrays.asList(param_annotations).stream().anyMatch(annot -> { - try { - return isMatchedJavaElement(type, annot.getElementName(), - WebSocketConstants.PATH_PARAM_ANNOTATION); - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Failed to get matched annotation", e); - return false; - } - }); - if (!hasPathParamAnnot) { - diagnostics.add(createDiagnostic(param, unit, - Messages.getMessage("PathParamsAnnotationMissing"), - WebSocketConstants.DIAGNOSTIC_CODE_PATH_PARAMS_ANNOT, null, - DiagnosticSeverity.Error)); - } - } - } - } - } - } - } - - /** - * Creates a warning diagnostic if a PathParam annotation does not match any - * variable parameters of the WebSocket EndPoint URI associated with the class - * in which the method is contained - * - * @param type representing the class list of diagnostics for this class - * compilation unit with which the type is associated - */ - private void uriMismatchWarningCheck(IType type, List endpointPathVars, List diagnostics, - ICompilationUnit unit) - throws JavaModelException { - IMethod[] typeMethods = type.getMethods(); - for (IMethod method : typeMethods) { - ILocalVariable[] methodParams = method.getParameters(); - for (ILocalVariable param : methodParams) { - IAnnotation[] paramAnnotations = param.getAnnotations(); - for (IAnnotation annotation : paramAnnotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), - WebSocketConstants.PATHPARAM_ANNOTATION)) { - IMemberValuePair[] valuePairs = annotation.getMemberValuePairs(); - for (IMemberValuePair pair : valuePairs) { - if (pair.getMemberName().equals(WebSocketConstants.ANNOTATION_VALUE) - && pair.getValueKind() == IMemberValuePair.K_STRING) { - String pathValue = (String) pair.getValue(); - if (!endpointPathVars.contains(pathValue)) { - diagnostics.add(createDiagnostic(annotation, unit, - Messages.getMessage("PathParamWarning"), - WebSocketConstants.PATHPARAM_DIAGNOSTIC_CODE, null, - DiagnosticSeverity.Warning)); - } - } - } - } - } - } - } - } - - /** - * Creates an error diagnostic if there exists more than one method annotated - * with @OnMessage for a given message format. - * - * @param type - * @param diagnostics - * @param unit - * @throws JavaModel - */ - private void onMessageWSMessageFormats(IType type, List diagnostics, ICompilationUnit unit) - throws JavaModelException { - IMethod[] typeMethods = type.getMethods(); - IAnnotation onMessageTextUsed = null; - IAnnotation onMessageBinaryUsed = null; - IAnnotation onMessagePongUsed = null; - for (IMethod method : typeMethods) { - IAnnotation[] allAnnotations = method.getAnnotations(); - for (IAnnotation annotation : allAnnotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), WebSocketConstants.ON_MESSAGE)) { - ILocalVariable[] allParams = method.getParameters(); - for (ILocalVariable param : allParams) { - if (!isParamPath(type, param)) { - String signature = param.getTypeSignature(); - String formatSignature = signature.replace("/", "."); - String resolvedTypeName = JavaModelUtil.getResolvedTypeName(formatSignature, type); - String typeName = null; - if (resolvedTypeName == null) { - typeName = Signature.getSignatureSimpleName(signature); - } - if ((resolvedTypeName != null - && WebSocketConstants.LONG_MESSAGE_CLASSES.contains(resolvedTypeName)) - || WebSocketConstants.SHORT_MESSAGE_CLASSES.contains(typeName)) { - WebSocketConstants.MESSAGE_FORMAT messageFormat = resolvedTypeName != null - ? getMessageFormat(resolvedTypeName, true) - : getMessageFormat(typeName, false); - switch (messageFormat) { - case TEXT: - if (onMessageTextUsed != null) { - diagnostics.add(createDiagnostic(annotation, unit, - Messages.getMessage("OnMessageDuplicateMethod"), - WebSocketConstants.DIAGNOSTIC_CODE_ON_MESSAGE_DUPLICATE_METHOD, null, - DiagnosticSeverity.Error)); - diagnostics.add(createDiagnostic(onMessageTextUsed, unit, - Messages.getMessage("OnMessageDuplicateMethod"), - WebSocketConstants.DIAGNOSTIC_CODE_ON_MESSAGE_DUPLICATE_METHOD, null, - DiagnosticSeverity.Error)); - } - onMessageTextUsed = annotation; - break; - case BINARY: - if (onMessageBinaryUsed != null) { - diagnostics.add(createDiagnostic(annotation, unit, - Messages.getMessage("OnMessageDuplicateMethod"), - WebSocketConstants.DIAGNOSTIC_CODE_ON_MESSAGE_DUPLICATE_METHOD, null, - DiagnosticSeverity.Error)); - diagnostics.add(createDiagnostic(onMessageBinaryUsed, unit, - Messages.getMessage("OnMessageDuplicateMethod"), - WebSocketConstants.DIAGNOSTIC_CODE_ON_MESSAGE_DUPLICATE_METHOD, null, - DiagnosticSeverity.Error)); - } - onMessageBinaryUsed = annotation; - break; - case PONG: - if (onMessagePongUsed != null) { - diagnostics.add(createDiagnostic(annotation, unit, - Messages.getMessage("OnMessageDuplicateMethod"), - WebSocketConstants.DIAGNOSTIC_CODE_ON_MESSAGE_DUPLICATE_METHOD, null, - DiagnosticSeverity.Error)); - diagnostics.add(createDiagnostic(onMessagePongUsed, unit, - Messages.getMessage("OnMessageDuplicateMethod"), - WebSocketConstants.DIAGNOSTIC_CODE_ON_MESSAGE_DUPLICATE_METHOD, null, - DiagnosticSeverity.Error)); - } - onMessagePongUsed = annotation; - break; - } - } - } - } - } - } - } - } - - /** - * Create an error diagnostic if a ServerEndpoint annotation's URI contains relative - * paths, missing a leading slash, or does not follow a valid level-1 template URI. - */ - private void serverEndpointErrorCheck(IType type, List diagnostics, ICompilationUnit unit) - throws JavaModelException { - IAnnotation[] annotations = type.getAnnotations(); - for (IAnnotation annotation : annotations) { - if (isMatchedJavaElement(type, annotation.getElementName(), - WebSocketConstants.SERVER_ENDPOINT_ANNOTATION)) { - for (IMemberValuePair annotationMemberValuePair : annotation.getMemberValuePairs()) { - if (annotationMemberValuePair.getMemberName().equals(WebSocketConstants.ANNOTATION_VALUE)) { - String path = annotationMemberValuePair.getValue().toString(); - if (!JDTUtils.hasLeadingSlash(path)) { - diagnostics.add(createDiagnostic(annotation, unit, - Messages.getMessage("ServerEndpointNoSlash"), - WebSocketConstants.DIAGNOSTIC_SERVER_ENDPOINT, null, DiagnosticSeverity.Error)); - } - if (hasRelativePathURIs(path)) { - diagnostics.add(createDiagnostic(annotation, unit, - Messages.getMessage("ServerEndpointRelative"), - WebSocketConstants.DIAGNOSTIC_SERVER_ENDPOINT, null, DiagnosticSeverity.Error)); - } else if (!JDTUtils.isValidLevel1URI(path)) { - diagnostics.add(createDiagnostic(annotation, unit, - Messages.getMessage("ServerEndpointNotLevel1"), - WebSocketConstants.DIAGNOSTIC_SERVER_ENDPOINT, null, DiagnosticSeverity.Error)); - } - if (hasDuplicateURIVariables(path)) { - diagnostics.add(createDiagnostic(annotation, unit, - Messages.getMessage("ServerEndpointDuplicateVar"), - WebSocketConstants.DIAGNOSTIC_SERVER_ENDPOINT, null, DiagnosticSeverity.Error)); - } - } - } - } - } - } - - /** - * Finds a WebSocket EndPoint annotation and extracts all variable parameters in - * the EndPoint URI - * - * @param type representing the class - * @return List of variable parameters in the EndPoint URI if one exists, null - * otherwise - */ - private List findAndProcessEndpointURI(IType type) throws JavaModelException { - String endpointURI = null; - IAnnotation[] typeAnnotations = type.getAnnotations(); - String[] targetAnnotations = {WebSocketConstants.SERVER_ENDPOINT_ANNOTATION, WebSocketConstants.CLIENT_ENDPOINT_ANNOTATION}; - for (IAnnotation annotation : typeAnnotations) { - String matchedAnnotation = getMatchedJavaElementName(type, annotation.getElementName(), targetAnnotations); - if (matchedAnnotation != null) { - IMemberValuePair[] valuePairs = annotation.getMemberValuePairs(); - for (IMemberValuePair pair : valuePairs) { - if (pair.getMemberName().equals(WebSocketConstants.ANNOTATION_VALUE) - && pair.getValueKind() == IMemberValuePair.K_STRING) { - endpointURI = (String) pair.getValue(); - } - } - } - } - if (endpointURI == null) { - return null; - } - List endpointPathVars = new ArrayList(); - String[] endpointParts = endpointURI.split(WebSocketConstants.URI_SEPARATOR); - for (String part : endpointParts) { - if (part.startsWith(WebSocketConstants.CURLY_BRACE_START) - && part.endsWith(WebSocketConstants.CURLY_BRACE_END)) { - endpointPathVars.add(part.substring(1, part.length() - 1)); - } - } - return endpointPathVars; - } - - /** - * Check if valueClass is a wrapper object for a primitive value Based on - * https://github.com/eclipse/lsp4mp/blob/9789a1a996811fade43029605c014c7825e8f1da/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/utils/JDTTypeUtils.java#L294-L298 - * - * @param valueClass the resolved type of valueClass in string or the simple - * type of valueClass - * @return if valueClass is a wrapper object - */ - private boolean isWrapper(String valueClass) { - return WebSocketConstants.WRAPPER_OBJS.contains(valueClass) - || WebSocketConstants.RAW_WRAPPER_OBJS.contains(valueClass); - } - - /** - * Checks if type is a WebSocket endpoint by meeting one of the 2 conditions - * listed on - * https://jakarta.ee/specifications/websocket/2.0/websocket-spec-2.0.html#applications - * are met: class is annotated or class implements Endpoint class - * - * @param type the type representing the class - * @return the conditions for a class to be a WebSocket endpoint - * @throws JavaModelException - */ - private HashMap isWSEndpoint(IType type) throws JavaModelException { - HashMap wsEndpoint = new HashMap<>(); - - // check trivial case - if (!type.isClass()) { - wsEndpoint.put(WebSocketConstants.IS_ANNOTATION, false); - wsEndpoint.put(WebSocketConstants.IS_SUPERCLASS, false); - return wsEndpoint; - } - - // Check that class follows - // https://jakarta.ee/specifications/websocket/2.0/websocket-spec-2.0.html#applications - List endpointAnnotations = getMatchedJavaElementNames(type, - Stream.of(type.getAnnotations()).map(annotation -> annotation.getElementName()).toArray(String[]::new), - WebSocketConstants.WS_ANNOTATION_CLASS); - - boolean useSuperclass = false; - try { - useSuperclass = doesITypeHaveSuperType(type, WebSocketConstants.ENDPOINT_SUPERCLASS) >= 0; - } catch (CoreException e) { - JakartaCorePlugin.logException(WebSocketConstants.DIAGNOSTIC_ERR_MSG, e); - } - - wsEndpoint.put(WebSocketConstants.IS_ANNOTATION, (endpointAnnotations.size() > 0)); - wsEndpoint.put(WebSocketConstants.IS_SUPERCLASS, useSuperclass); - - return wsEndpoint; - } - - private boolean isParamPath(IType type, ILocalVariable param) throws JavaModelException { - IAnnotation[] allVariableAnnotations = param.getAnnotations(); - for (IAnnotation variableAnnotation : allVariableAnnotations) { - if (isMatchedJavaElement(type, variableAnnotation.getElementName(), - WebSocketConstants.PATH_PARAM_ANNOTATION)) { - return true; - } - } - return false; - } - - private WebSocketConstants.MESSAGE_FORMAT getMessageFormat(String typeName, boolean longName) { - if (longName) { - switch (typeName) { - case WebSocketConstants.STRING_CLASS_LONG: - return WebSocketConstants.MESSAGE_FORMAT.TEXT; - case WebSocketConstants.READER_CLASS_LONG: - return WebSocketConstants.MESSAGE_FORMAT.TEXT; - case WebSocketConstants.BYTEBUFFER_CLASS_LONG: - return WebSocketConstants.MESSAGE_FORMAT.BINARY; - case WebSocketConstants.INPUTSTREAM_CLASS_LONG: - return WebSocketConstants.MESSAGE_FORMAT.BINARY; - case WebSocketConstants.PONGMESSAGE_CLASS_LONG: - return WebSocketConstants.MESSAGE_FORMAT.PONG; - default: - throw new IllegalArgumentException("Invalid message format type"); - } - } - switch (typeName) { - case WebSocketConstants.STRING_CLASS_SHORT: - return WebSocketConstants.MESSAGE_FORMAT.TEXT; - case WebSocketConstants.READER_CLASS_SHORT: - return WebSocketConstants.MESSAGE_FORMAT.TEXT; - case WebSocketConstants.BYTEBUFFER_CLASS_SHORT: - return WebSocketConstants.MESSAGE_FORMAT.BINARY; - case WebSocketConstants.INPUTSTREAM_CLASS_SHORT: - return WebSocketConstants.MESSAGE_FORMAT.BINARY; - case WebSocketConstants.PONGMESSAGE_CLASS_SHORT: - return WebSocketConstants.MESSAGE_FORMAT.PONG; - default: - throw new IllegalArgumentException("Invalid message format type"); - } - } - - private String createParamTypeDiagMsg(Set methodParamOptTypes, String methodAnnotTarget) { - String paramMessage = String.join("\n- ", methodParamOptTypes); - return Messages.getMessage("WebSocketParamType", "@" + methodAnnotTarget, paramMessage); - } - - /** - * Check if a URI string contains any sequence with //, /./, or /../ - * - * @param uriString ServerEndpoint URI - * @return if a URI has a relative path - */ - private boolean hasRelativePathURIs(String uriString) { - return uriString.matches(WebSocketConstants.REGEX_RELATIVE_PATHS); - } - - /** - * Check if a URI string has a duplicate variable - * - * @param uriString ServerEndpoint URI - * @return if a URI has duplicate variables - */ - private boolean hasDuplicateURIVariables(String uriString) { - HashSet variables = new HashSet(); - for (String segment : uriString.split(WebSocketConstants.URI_SEPARATOR)) { - if (segment.matches(WebSocketConstants.REGEX_URI_VARIABLE)) { - String variable = segment.substring(1, segment.length() - 1); - if (variables.contains(variable)) { - return true; - } else { - variables.add(variable); - } - } - } - return false; - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/AbstractDiagnosticsCollector.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/DiagnosticUtils.java similarity index 70% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/AbstractDiagnosticsCollector.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/DiagnosticUtils.java index 10abcdc3..a3a10d08 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/AbstractDiagnosticsCollector.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/DiagnosticUtils.java @@ -1,397 +1,377 @@ -/******************************************************************************* - * Copyright (c) 2022 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core; - -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.core.IAnnotation; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IImportContainer; -import org.eclipse.jdt.core.IImportDeclaration; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IMember; -import org.eclipse.jdt.core.IMethod; -import org.eclipse.jdt.core.ISourceRange; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.ITypeHierarchy; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.internal.core.ImportContainerInfo; -import org.eclipse.jdt.internal.core.JavaModelManager; -import org.eclipse.jdt.internal.corext.util.JavaModelUtil; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4j.Range; - -/** - * - * Abstract class for collecting Java diagnostics. - * - */ -@SuppressWarnings("restriction") -public abstract class AbstractDiagnosticsCollector implements DiagnosticsCollector { - - /** - * Constructor - */ - public AbstractDiagnosticsCollector() { - super(); - } - - protected String getDiagnosticSource() { - return null; - } - - public void completeDiagnostic(Diagnostic diagnostic) { - this.completeDiagnostic(diagnostic, null, DiagnosticSeverity.Error); - } - - public void completeDiagnostic(Diagnostic diagnostic, String code) { - this.completeDiagnostic(diagnostic, code, DiagnosticSeverity.Error); - } - - public void completeDiagnostic(Diagnostic diagnostic, String code, DiagnosticSeverity severity) { - String source = getDiagnosticSource(); - if (source != null) - diagnostic.setSource(source); - if (code != null) - diagnostic.setCode(code); - diagnostic.setSeverity(severity); - } - - /** - * Creates and returns a new diagnostic. - * - * @param el Java element - * @param unit compilation unit of Java class - * @param msg diagnostic message - * @param code diagnostic code - * @param data diagnostic data - * @param severity diagnostic severity - * @return new Diagnostic object. - */ - protected Diagnostic createDiagnostic(IJavaElement el, ICompilationUnit unit, String msg, String code, Object data, - DiagnosticSeverity severity) throws JavaModelException { - ISourceRange nameRange = JDTUtils.getNameRange(el); - Range range = JDTUtils.toRange(unit, nameRange.getOffset(), nameRange.getLength()); - Diagnostic diagnostic = new Diagnostic(range, msg); - if (data != null) - diagnostic.setData(data); - String source = getDiagnosticSource(); - if (source != null) - diagnostic.setSource(source); - if (code != null) - diagnostic.setCode(code); - diagnostic.setSeverity(severity); - return diagnostic; - } - - /** - * Returns diagnostics for the given compilation unit. - * - * @param unit compilation unit of Java class - * @param diagnostics diagnostics for the given compilation unit to return - */ - public void collectDiagnostics(ICompilationUnit unit, List diagnostics) { - } - - /** - * Returns true if the given annotation matches the given annotation name and - * false otherwise. - * - * @param unit compilation unit of Java class. - * @param annotation given annotation object. - * @param annotationFQName the fully qualified annotation name. - * @return true if the given annotation matches the given annotation name and - * false otherwise. - */ - protected static boolean isMatchedAnnotation(ICompilationUnit unit, IAnnotation annotation, String annotationFQName) - throws JavaModelException { - String elementName = annotation.getElementName(); - if (nameEndsWith(annotationFQName, elementName) && unit != null) { - // For performance reason, we check if the import of annotation name is - // declared - if (isImportedJavaElement(unit, annotationFQName) == true) - return true; - // only check fully qualified annotations - if (annotationFQName.equals(elementName)) { - IJavaElement parent = annotation.getParent(); - IType declaringType = (parent instanceof IType) ? (IType) parent - : ((parent instanceof IMember) ? ((IMember) parent).getDeclaringType() : null); - if (declaringType != null) { - String[][] fqName = declaringType.resolveType(elementName); // the call could be expensive - if (fqName != null && fqName.length == 1) { - return annotationFQName.equals(JavaModelUtil.concatenateName(fqName[0][0], fqName[0][1])); - } - } - } - } - return false; - } - - /** - * Returns true if the java element name matches the given fully qualified java - * element name and false otherwise. - * - * @param unit compilation unit of Java class. - * @param annotation given annotation object. - * @param annotationFQName the fully qualified annotation name. - * @return true if the java element name matches the given fully qualified java - * element name and false otherwise. - */ - protected static boolean isMatchedJavaElement(IType type, String javaElementName, String javaElementFQName) - throws JavaModelException { - if (nameEndsWith(javaElementFQName, javaElementName)) { - // For performance reason, we check if the import of annotation name is - // declared - if (isImportedJavaElement(type.getCompilationUnit(), javaElementFQName) == true) - return true; - // only check fully qualified java element - if (javaElementFQName.equals(javaElementName)) { - String[][] fqName = type.resolveType(javaElementName); // the call could be expensive - if (fqName != null && fqName.length == 1) { - return javaElementFQName.equals(JavaModelUtil.concatenateName(fqName[0][0], fqName[0][1])); - } - } - } - return false; - } - - /** - * Returns true if the given Java class imports the given Java element and false - * otherwise. - * - * @param type Java class. - * @param javaElementFQName given Java element fully qualified name. - * @return true if the Java class imports the given Java element and false - * otherwise. - */ - protected static boolean isImportedJavaElement(ICompilationUnit unit, String javaElementFQName) - throws JavaModelException { - IImportContainer container = unit.getImportContainer(); - if (container == null) { - return false; - } - - // The following code uses JDT internal class and looks like - // ICompilationUnit#getImports() - // To avoid creating an array of IImportDeclaration, we do the following code: - JavaModelManager manager = JavaModelManager.getJavaModelManager(); - Object info = manager.getInfo(container); - if (info == null) { - if (manager.getInfo(unit) != null) { - // CU was opened, but no import container, then no imports - return false; - } else { - try { - unit.open(null); - } catch (JavaModelException e) { - e.printStackTrace(); - } // force opening of CU - info = manager.getInfo(container); - if (info == null) - // after opening, if no import container, then no imports - return false; - } - } - IJavaElement[] elements = ((ImportContainerInfo) info).getChildren(); - for (IJavaElement child : elements) { - IImportDeclaration importDeclaration = (IImportDeclaration) child; - if (importDeclaration.isOnDemand()) { - String fqn = importDeclaration.getElementName(); - String qualifier = fqn.substring(0, fqn.lastIndexOf('.')); - if (qualifier.equals(javaElementFQName.substring(0, javaElementFQName.lastIndexOf('.')))) { - return true; - } - } else if (importDeclaration.getElementName().equals(javaElementFQName)) { - return true; - } - } - return false; - } - - /** - * Returns true if the given Java class imports one of the given Java elements - * and false otherwise. - * - * @param type Java class. - * @param javaElementFQName given Java element fully qualified names. - * @return true if the Java class imports one of the given Java elements and - * false otherwise. - */ - protected static boolean isImportedJavaElement(ICompilationUnit unit, String[] javaElementFQNames) - throws JavaModelException { - IImportContainer container = unit.getImportContainer(); - if (container == null) { - return false; - } - - // The following code uses JDT internal class and looks like - // ICompilationUnit#getImports() - // To avoid creating an array of IImportDeclaration, we do the following code: - JavaModelManager manager = JavaModelManager.getJavaModelManager(); - Object info = manager.getInfo(container); - if (info == null) { - if (manager.getInfo(unit) != null) { - // CU was opened, but no import container, then no imports - return false; - } else { - try { - unit.open(null); - } catch (JavaModelException e) { - e.printStackTrace(); - } // force opening of CU - info = manager.getInfo(container); - if (info == null) - // after opening, if no import container, then no imports - return false; - } - } - IJavaElement[] elements = ((ImportContainerInfo) info).getChildren(); - for (IJavaElement child : elements) { - IImportDeclaration importDeclaration = (IImportDeclaration) child; - if (importDeclaration.isOnDemand()) { - String fqn = importDeclaration.getElementName(); - String qualifier = fqn.substring(0, fqn.lastIndexOf('.')); - boolean imports = Stream.of(javaElementFQNames).anyMatch(elementFQName -> { - return qualifier.equals(elementFQName.substring(0, elementFQName.lastIndexOf('.'))); - }); - if (imports == true) { - return true; - } - } else { - String importName = importDeclaration.getElementName(); - if (Stream.of(javaElementFQNames).anyMatch(elementFQName -> importName.equals(elementFQName)) == true) - return true; - } - } - return false; - } - - /** - * Returns true if the given Java class implements one of the given interfaces - * and false otherwise. - * - * @param type Java class. - * @param interfaceFQNames given interfaces with fully qualified name. - * @return true if the Java class implements one of the given interfaces and - * false otherwise. - */ - protected static boolean doesImplementInterfaces(IType type, String[] interfaceFQNames) throws JavaModelException { - String[] interfaceNames = type.getSuperInterfaceNames(); - - // should check import statements first for the performance? - - // check super hierarchy - if (interfaceNames.length > 0) { // the type implements interface(s) - ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor()); - IType[] interfaces = typeHierarchy.getAllInterfaces(); - for (IType interfase : interfaces) { - String fqName = interfase.getFullyQualifiedName(); - if (Stream.of(interfaceFQNames).anyMatch(name -> fqName.equals(name)) == true) - return true; - } - } - return false; - } - - /** - * Returns matched Java element fully qualified name. - * - * @param type Java class. - * @param javaElement Java element name - * @param javaElementFQNames given fully qualified name array. - * @return Matched fully qualified name and null otherwise. - */ - protected static String getMatchedJavaElementName(IType type, String javaElementName, String[] javaElementFQNames) - throws JavaModelException { - String[] matches = (String[]) Stream.of(javaElementFQNames) - .filter(fqName -> nameEndsWith(fqName, javaElementName)) - .toArray(String[]::new); - if (matches.length > 0) { - if (isMatchedJavaElement(type, javaElementName, matches[0]) == true) // only check the first one for now - return matches[0]; - } - return null; - } - - /** - * Returns matched Java element fully qualified names. - * - * @param type the type representing the class - * @param javaElementNames Java element names - * @param javaElementFQNames given fully qualified name array - * @return matched Java element fully qualified names - */ - protected static List getMatchedJavaElementNames(IType type, String[] javaElementNames, - String[] javaElementFQNames) { - return Stream.of(javaElementFQNames).filter(fqName -> { - boolean anyMatch = Stream.of(javaElementNames).anyMatch(name -> { - try { - return isMatchedJavaElement(type, name, fqName); - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Failed to get matched Java element FQ names", e); - return false; - } - }); - return anyMatch; - }).collect(Collectors.toList()); - } - - /** - * Returns true if the given fully qualified name ends with the given name and - * false otherwise - * - * @param fqName fully qualified name - * @param name either simple name or fully qualified name - * @return true if the given fully qualified name ends with the given name and - * false otherwise - */ - protected static boolean nameEndsWith(String fqName, String name) { - // add a prefix '.' to simple name - // e.g. 'jakarta.validation.constraints.DecimalMin' should NOT end with 'Min' - // here - return fqName.equals(name) || fqName.endsWith("." + name); - } - - /** - * Returns simple name for the given fully qualified name. - * - * @param fqName a fully qualified name or simple name - * @return simple name for given fully qualified name - */ - protected static String getSimpleName(String fqName) { - int idx = fqName.lastIndexOf('.'); - if (idx != -1 && idx != fqName.length() - 1) { - return fqName.substring(idx + 1); - } - return fqName; - } - - /** - * Returns true if the given method is a constructor and false otherwise. - * - * @param m method - * @return true if the given method is a constructor and false otherwise - */ - protected static boolean isConstructorMethod(IMethod m) { - try { - return m.isConstructor(); - } catch (JavaModelException e) { - JakartaCorePlugin.logException("Failed to check constructor method", e); - return false; - } - } -} +/******************************************************************************* + * Copyright (c) 2022 IBM Corporation and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IImportContainer; +import org.eclipse.jdt.core.IImportDeclaration; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.core.ImportContainerInfo; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; + +/** + * + * Abstract class for collecting Java diagnostics. + * + */ +@SuppressWarnings("restriction") +public class DiagnosticUtils { + + private static final String LEVEL1_URI_REGEX = "(?:\\/(?:(?:\\{(\\w|-|%20|%21|%23|%24|%25|%26|%27|%28|%29|%2A|%2B|%2C|%2F|%3A|%3B|%3D|%3F|%40|%5B|%5D)+\\})|(?:(\\w|%20|%21|%23|%24|%25|%26|%27|%28|%29|%2A|%2B|%2C|%2F|%3A|%3B|%3D|%3F|%40|%5B|%5D)+)))*\\/?"; + + /** + * Returns true if the given annotation matches the given annotation name and + * false otherwise. + * + * @param unit compilation unit of Java class. + * @param annotation given annotation object. + * @param annotationFQName the fully qualified annotation name. + * @return true if the given annotation matches the given annotation name and + * false otherwise. + */ + public static boolean isMatchedAnnotation(ICompilationUnit unit, IAnnotation annotation, String annotationFQName) throws JavaModelException { + String elementName = annotation.getElementName(); + if (nameEndsWith(annotationFQName, elementName) && unit != null) { + // For performance reason, we check if the import of annotation name is + // declared + if (isImportedJavaElement(unit, annotationFQName) == true) + return true; + // only check fully qualified annotations + if (annotationFQName.equals(elementName)) { + IJavaElement parent = annotation.getParent(); + IType declaringType = (parent instanceof IType) ? (IType) parent : ((parent instanceof IMember) ? ((IMember) parent).getDeclaringType() : null); + if (declaringType != null) { + String[][] fqName = declaringType.resolveType(elementName); // the call could be expensive + if (fqName != null && fqName.length == 1) { + return annotationFQName.equals(JavaModelUtil.concatenateName(fqName[0][0], fqName[0][1])); + } + } + } + } + return false; + } + + /** + * Returns true if the java element name matches the given fully qualified java + * element name and false otherwise. + * + * @param unit compilation unit of Java class. + * @param annotation given annotation object. + * @param annotationFQName the fully qualified annotation name. + * @return true if the java element name matches the given fully qualified java + * element name and false otherwise. + */ + public static boolean isMatchedJavaElement(IType type, String javaElementName, String javaElementFQName) throws JavaModelException { + if (nameEndsWith(javaElementFQName, javaElementName)) { + // For performance reason, we check if the import of annotation name is + // declared + if (isImportedJavaElement(type.getCompilationUnit(), javaElementFQName) == true) + return true; + // only check fully qualified java element + if (javaElementFQName.equals(javaElementName)) { + String[][] fqName = type.resolveType(javaElementName); // the call could be expensive + if (fqName != null && fqName.length == 1) { + return javaElementFQName.equals(JavaModelUtil.concatenateName(fqName[0][0], fqName[0][1])); + } + } + } + return false; + } + + /** + * Returns true if the given Java class imports the given Java element and false + * otherwise. + * + * @param type Java class. + * @param javaElementFQName given Java element fully qualified name. + * @return true if the Java class imports the given Java element and false + * otherwise. + */ + public static boolean isImportedJavaElement(ICompilationUnit unit, String javaElementFQName) throws JavaModelException { + IImportContainer container = unit.getImportContainer(); + if (container == null) { + return false; + } + + // The following code uses JDT internal class and looks like + // ICompilationUnit#getImports() + // To avoid creating an array of IImportDeclaration, we do the following code: + JavaModelManager manager = JavaModelManager.getJavaModelManager(); + Object info = manager.getInfo(container); + if (info == null) { + if (manager.getInfo(unit) != null) { + // CU was opened, but no import container, then no imports + return false; + } else { + try { + unit.open(null); + } catch (JavaModelException e) { + e.printStackTrace(); + } // force opening of CU + info = manager.getInfo(container); + if (info == null) + // after opening, if no import container, then no imports + return false; + } + } + IJavaElement[] elements = ((ImportContainerInfo) info).getChildren(); + for (IJavaElement child : elements) { + IImportDeclaration importDeclaration = (IImportDeclaration) child; + if (importDeclaration.isOnDemand()) { + String fqn = importDeclaration.getElementName(); + String qualifier = fqn.substring(0, fqn.lastIndexOf('.')); + if (qualifier.equals(javaElementFQName.substring(0, javaElementFQName.lastIndexOf('.')))) { + return true; + } + } else if (importDeclaration.getElementName().equals(javaElementFQName)) { + return true; + } + } + return false; + } + + /** + * Returns true if the given Java class imports one of the given Java elements + * and false otherwise. + * + * @param type Java class. + * @param javaElementFQName given Java element fully qualified names. + * @return true if the Java class imports one of the given Java elements and + * false otherwise. + */ + protected static boolean isImportedJavaElement(ICompilationUnit unit, String[] javaElementFQNames) throws JavaModelException { + IImportContainer container = unit.getImportContainer(); + if (container == null) { + return false; + } + + // The following code uses JDT internal class and looks like + // ICompilationUnit#getImports() + // To avoid creating an array of IImportDeclaration, we do the following code: + JavaModelManager manager = JavaModelManager.getJavaModelManager(); + Object info = manager.getInfo(container); + if (info == null) { + if (manager.getInfo(unit) != null) { + // CU was opened, but no import container, then no imports + return false; + } else { + try { + unit.open(null); + } catch (JavaModelException e) { + e.printStackTrace(); + } // force opening of CU + info = manager.getInfo(container); + if (info == null) + // after opening, if no import container, then no imports + return false; + } + } + IJavaElement[] elements = ((ImportContainerInfo) info).getChildren(); + for (IJavaElement child : elements) { + IImportDeclaration importDeclaration = (IImportDeclaration) child; + if (importDeclaration.isOnDemand()) { + String fqn = importDeclaration.getElementName(); + String qualifier = fqn.substring(0, fqn.lastIndexOf('.')); + boolean imports = Stream.of(javaElementFQNames).anyMatch(elementFQName -> { + return qualifier.equals(elementFQName.substring(0, elementFQName.lastIndexOf('.'))); + }); + if (imports == true) { + return true; + } + } else { + String importName = importDeclaration.getElementName(); + if (Stream.of(javaElementFQNames).anyMatch(elementFQName -> importName.equals(elementFQName)) == true) + return true; + } + } + return false; + } + + /** + * Returns true if the given Java class implements one of the given interfaces + * and false otherwise. + * + * @param type Java class. + * @param interfaceFQNames given interfaces with fully qualified name. + * @return true if the Java class implements one of the given interfaces and + * false otherwise. + */ + public static boolean doesImplementInterfaces(IType type, String[] interfaceFQNames) throws JavaModelException { + String[] interfaceNames = type.getSuperInterfaceNames(); + + // should check import statements first for the performance? + + // check super hierarchy + if (interfaceNames.length > 0) { // the type implements interface(s) + ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(new NullProgressMonitor()); + IType[] interfaces = typeHierarchy.getAllInterfaces(); + for (IType interfase : interfaces) { + String fqName = interfase.getFullyQualifiedName(); + if (Stream.of(interfaceFQNames).anyMatch(name -> fqName.equals(name)) == true) + return true; + } + } + return false; + } + + /** + * Returns matched Java element fully qualified name. + * + * @param type Java class. + * @param javaElement Java element name + * @param javaElementFQNames given fully qualified name array. + * @return Matched fully qualified name and null otherwise. + */ + public static String getMatchedJavaElementName(IType type, String javaElementName, String[] javaElementFQNames) throws JavaModelException { + String[] matches = (String[]) Stream.of(javaElementFQNames).filter(fqName -> nameEndsWith(fqName, javaElementName)).toArray(String[]::new); + if (matches.length > 0) { + if (isMatchedJavaElement(type, javaElementName, matches[0]) == true) // only check the first one for now + return matches[0]; + } + return null; + } + + /** + * Returns matched Java element fully qualified names. + * + * @param type the type representing the class + * @param javaElementNames Java element names + * @param javaElementFQNames given fully qualified name array + * @return matched Java element fully qualified names + */ + public static List getMatchedJavaElementNames(IType type, String[] javaElementNames, + String[] javaElementFQNames) { + return Stream.of(javaElementFQNames).filter(fqName -> { + boolean anyMatch = Stream.of(javaElementNames).anyMatch(name -> { + try { + return isMatchedJavaElement(type, name, fqName); + } catch (JavaModelException e) { + JakartaCorePlugin.logException("Failed to get matched Java element FQ names", e); + return false; + } + }); + return anyMatch; + }).collect(Collectors.toList()); + } + + /** + * Returns true if the given fully qualified name ends with the given name and + * false otherwise + * + * @param fqName fully qualified name + * @param name either simple name or fully qualified name + * @return true if the given fully qualified name ends with the given name and + * false otherwise + */ + protected static boolean nameEndsWith(String fqName, String name) { + // add a prefix '.' to simple name + // e.g. 'jakarta.validation.constraints.DecimalMin' should NOT end with 'Min' + // here + return fqName.equals(name) || fqName.endsWith("." + name); + } + + /** + * Returns simple name for the given fully qualified name. + * + * @param fqName a fully qualified name or simple name + * @return simple name for given fully qualified name + */ + public static String getSimpleName(String fqName) { + int idx = fqName.lastIndexOf('.'); + if (idx != -1 && idx != fqName.length() - 1) { + return fqName.substring(idx + 1); + } + return fqName; + } + + /** + * Returns true if the given method is a constructor and false otherwise. + * + * @param m method + * @return true if the given method is a constructor and false otherwise + */ + public static boolean isConstructorMethod(IMethod m) { + try { + return m.isConstructor(); + } catch (JavaModelException e) { + JakartaCorePlugin.logException("Failed to check constructor method", e); + return false; + } + } + + /** + * Returns a list of all accessors (getter and setter) of the given field. + * Note that for boolean fields the accessor of the form "isField" is retuned + * "getField" is not present. + * + * @param unit the compilation unit the field belongs to + * @param field the accesors of this field are returned + * @return a list of accessor methods + * @throws JavaModelException + */ + public static List getFieldAccessors(ICompilationUnit unit, IField field) throws JavaModelException { + List accessors = new ArrayList(); + String fieldName = field.getElementName(); + fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + List accessorNames = new ArrayList(); + accessorNames.add("get" + fieldName); + accessorNames.add("set" + fieldName); + accessorNames.add("is" + fieldName); + + for (IType type : unit.getAllTypes()) { + for (IMethod method : type.getMethods()) { + String methodName = method.getElementName(); + if (accessorNames.contains(methodName)) + accessors.add(method); + } + } + return accessors; + } + + /** + * Returns true if the input URI starts with a leading slash. + * + * @param uri The string URI. + * @return True if the input URI starts with a leading slash. False, otherwise. + */ + public static boolean hasLeadingSlash(String uri) { + return uri.startsWith("/"); + } + + /** + * Returns true if the input URI represents a valid level 1 (URI template) path. + * RFC 6570. + * + * @param uriString The URI. + * @return Returns true if the input URI represents a valid level 1 (URI + * template) path. + */ + public static boolean isValidLevel1URI(String uriString) { + return uriString.matches(LEVEL1_URI_REGEX); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/Messages.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/Messages.java similarity index 80% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/Messages.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/Messages.java index 11d7cb15..168fdbbc 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/Messages.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/Messages.java @@ -1,46 +1,54 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -*******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core; - -import java.text.MessageFormat; -import java.util.Locale; -import java.util.ResourceBundle; - -public final class Messages { - private static ResourceBundle resourceBundle = null; - - private static synchronized void initializeBundles() { - resourceBundle = ResourceBundle.getBundle("org.eclipse.lsp4jakarta.jdt.core.messages.messages", - Locale.getDefault()); - } - - /** - * Returns message for the given key defined in resource bundle file. - * - * @param key the given key - * @param args replacements - * @return Returns message for the given key defined in resource bundle file - */ - public static String getMessage(String key, Object... args) { - if (resourceBundle == null) { - initializeBundles(); - } - String msg = null; - try { - msg = resourceBundle.getString(key); - if (msg != null && args != null && args.length > 0) { - msg = MessageFormat.format(msg, args); - } - } catch (Exception e) { - JakartaCorePlugin.logException("Failed to get message for '" + key + "'", e); - } - return (msg == null) ? key : msg; - } -} +/******************************************************************************* +* Copyright (c) 2022, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.ResourceBundle; + +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; + +/** + * Message formatter. + */ +public final class Messages { + private static ResourceBundle resourceBundle = null; + + private static synchronized void initializeBundles() { + resourceBundle = ResourceBundle.getBundle("org.eclipse.lsp4jakarta.jdt.core.messages.messages", + Locale.getDefault()); + } + + /** + * Returns message for the given key defined in resource bundle file. + * + * @param key the given key + * @param args replacements + * @return Returns message for the given key defined in resource bundle file + */ + public static String getMessage(String key, Object... args) { + if (resourceBundle == null) { + initializeBundles(); + } + String msg = null; + try { + msg = resourceBundle.getString(key); + if (msg != null && args != null && args.length > 0) { + msg = MessageFormat.format(msg, args); + } + } catch (Exception e) { + JakartaCorePlugin.logException("Failed to get message for '" + key + "'", e); + } + return (msg == null) ? key : msg; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/AnnotationDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/AnnotationDiagnosticsParticipant.java new file mode 100644 index 00000000..368656fb --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/AnnotationDiagnosticsParticipant.java @@ -0,0 +1,272 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotatable; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IMemberValuePair; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IPackageDeclaration; +import org.eclipse.jdt.core.IType; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.jsonrpc.messages.Tuple; +import org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * + * Annotations diagnostic participant that manages the use of annotations. + * + * @see https://jakarta.ee/specifications/annotations/2.0/annotations-spec-2.0.html#annotations + * + */ +public class AnnotationDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit != null) { + ArrayList> annotatables = new ArrayList>(); + String[] validAnnotations = { Constants.GENERATED_FQ_NAME }; + String[] validTypeAnnotations = { Constants.GENERATED_FQ_NAME, + Constants.RESOURCE_FQ_NAME }; + String[] validMethodAnnotations = { Constants.GENERATED_FQ_NAME, + Constants.POST_CONSTRUCT_FQ_NAME, Constants.PRE_DESTROY_FQ_NAME, + Constants.RESOURCE_FQ_NAME }; + + IPackageDeclaration[] packages = unit.getPackageDeclarations(); + for (IPackageDeclaration p : packages) { + IAnnotation[] annotations = p.getAnnotations(); + for (IAnnotation annotation : annotations) { + if (isValidAnnotation(annotation.getElementName(), validAnnotations)) + annotatables.add(new Tuple.Two<>(annotation, p)); + } + } + + IType[] types = unit.getAllTypes(); + for (IType type : types) { + // Type + IAnnotation[] annotations = type.getAnnotations(); + for (IAnnotation annotation : annotations) { + if (isValidAnnotation(annotation.getElementName(), validTypeAnnotations)) + annotatables.add(new Tuple.Two<>(annotation, type)); + } + // Method + IMethod[] methods = type.getMethods(); + for (IMethod method : methods) { + annotations = method.getAnnotations(); + for (IAnnotation annotation : annotations) { + if (isValidAnnotation(annotation.getElementName(), validMethodAnnotations)) + annotatables.add(new Tuple.Two<>(annotation, method)); + } + // method parameters + ILocalVariable[] parameters = method.getParameters(); + for (ILocalVariable parameter : parameters) { + annotations = parameter.getAnnotations(); + for (IAnnotation annotation : annotations) { + if (isValidAnnotation(annotation.getElementName(), validAnnotations)) + annotatables.add(new Tuple.Two<>(annotation, parameter)); + } + } + } + // Field + IField[] fields = type.getFields(); + for (IField field : fields) { + annotations = field.getAnnotations(); + for (IAnnotation annotation : annotations) { + if (isValidAnnotation(annotation.getElementName(), validTypeAnnotations)) + annotatables.add(new Tuple.Two<>(annotation, field)); + } + } + } + + for (Tuple.Two annotatable : annotatables) { + IAnnotation annotation = annotatable.getFirst(); + IAnnotatable element = annotatable.getSecond(); + + if (DiagnosticUtils.isMatchedAnnotation(unit, annotation, Constants.GENERATED_FQ_NAME)) { + for (IMemberValuePair pair : annotation.getMemberValuePairs()) { + // If date element exists and is non-empty, it must follow ISO 8601 format. + if (pair.getMemberName().equals("date")) { + if (pair.getValue() instanceof String) { + String date = (String) pair.getValue(); + if (!date.equals("")) { + if (!Pattern.matches(Constants.ISO_8601_REGEX, date)) { + + Range annotationRange = PositionUtils.toNameRange(annotation, + context.getUtils()); + String diagnosticMessage = Messages.getMessage( + "AnnotationMustDefineAttributeFollowing8601", "@Generated", "date"); + diagnostics.add( + context.createDiagnostic(uri, diagnosticMessage, annotationRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidDateFormat, + DiagnosticSeverity.Error)); + } + } + } + } + } + } else if (DiagnosticUtils.isMatchedAnnotation(unit, annotation, Constants.RESOURCE_FQ_NAME)) { + if (element instanceof IType) { + IType type = (IType) element; + if (type.getElementType() == IJavaElement.TYPE && ((IType) type).isClass()) { + Range annotationRange = PositionUtils.toNameRange(annotation, context.getUtils()); + Boolean nameEmpty = true; + Boolean typeEmpty = true; + for (IMemberValuePair pair : annotation.getMemberValuePairs()) { + if (pair.getMemberName().equals("name")) { + nameEmpty = false; + } + if (pair.getMemberName().equals("type")) { + typeEmpty = false; + } + } + String diagnosticMessage; + if (nameEmpty) { + diagnosticMessage = Messages.getMessage("AnnotationMustDefineAttribute", + "@Resource", "name"); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, annotationRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.MissingResourceNameAttribute, + DiagnosticSeverity.Error)); + } + + if (typeEmpty) { + diagnosticMessage = Messages.getMessage("AnnotationMustDefineAttribute", + "@Resource", "type"); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, annotationRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.MissingResourceTypeAttribute, + DiagnosticSeverity.Error)); + } + } + } + } + if (DiagnosticUtils.isMatchedAnnotation(unit, annotation, Constants.POST_CONSTRUCT_FQ_NAME)) { + if (element instanceof IMethod) { + IMethod method = (IMethod) element; + Range methodRange = PositionUtils.toNameRange(method, context.getUtils()); + + if (method.getNumberOfParameters() != 0) { + + String diagnosticMessage = Messages.getMessage("MethodMustNotHaveParameters", + "@PostConstruct"); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, methodRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.PostConstructParams, + DiagnosticSeverity.Error)); + } + + if (!method.getReturnType().equals("V")) { + String diagnosticMessage = Messages.getMessage("MethodMustBeVoid", + "@PostConstruct"); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, methodRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.PostConstructReturnType, + DiagnosticSeverity.Error)); + } + + if (method.getExceptionTypes().length != 0) { + String diagnosticMessage = Messages.getMessage("MethodMustNotThrow", + "@PostConstruct"); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, methodRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.PostConstructException, + DiagnosticSeverity.Warning)); + } + } + } else if (DiagnosticUtils.isMatchedAnnotation(unit, annotation, + Constants.PRE_DESTROY_FQ_NAME)) { + if (element instanceof IMethod) { + IMethod method = (IMethod) element; + Range methodRange = PositionUtils.toNameRange(method, context.getUtils()); + + if (method.getNumberOfParameters() != 0) { + String diagnosticMessage = Messages.getMessage("MethodMustNotHaveParameters", + "@PreDestroy"); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, methodRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.PreDestroyParams, + DiagnosticSeverity.Error)); + } + + if (Flags.isStatic(method.getFlags())) { + String diagnosticMessage = Messages.getMessage("MethodMustNotBeStatic", + "@PreDestroy"); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, methodRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.PreDestroyStatic, + DiagnosticSeverity.Error)); + } + + if (method.getExceptionTypes().length != 0) { + String diagnosticMessage = Messages.getMessage("MethodMustNotThrow", + "@PreDestroy"); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, methodRange, + Constants.DIAGNOSTIC_SOURCE, + ErrorCode.PreDestroyException, + DiagnosticSeverity.Warning)); + } + } + } + } + } + + return diagnostics; + } + + /** + * Returns true if the input annotation is valid. False, otherwise. + * + * @param annotationName The annotation to validate. + * @param validAnnotations The list of valid annotations. + * + * @return True if the input annotation is valid. False, otherwise. + */ + private static boolean isValidAnnotation(String annotationName, String[] validAnnotations) { + for (String fqName : validAnnotations) { + if (fqName.endsWith(annotationName)) { + return true; + } + } + return false; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AnnotationConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/Constants.java similarity index 56% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AnnotationConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/Constants.java index 6a3ac849..d1b740d7 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/annotations/AnnotationConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/Constants.java @@ -1,47 +1,40 @@ -/******************************************************************************* -* Copyright (c) 2021, 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.annotations; - -public class AnnotationConstants { - - /* @Generated */ - public static final String GENERATED = "Generated"; - public static final String GENERATED_FQ_NAME = "jakarta.annotation.Generated"; - public static final String ISO_8601_REGEX = "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$"; - - /* @Resource */ - public static final String RESOURCE = "Resource"; - public static final String RESOURCE_FQ_NAME = "jakarta.annotation.Resource"; - - /* @PostConstruct */ - public static final String POST_CONSTRUCT = "PostConstruct"; - public static final String POST_CONSTRUCT_FQ_NAME = "jakarta.annotation.PostConstruct"; - - /* @PreDesotroy */ - public static final String PRE_DESTROY = "PreDestroy"; - public static final String PRE_DESTROY_FQ_NAME = "jakarta.annotation.PreDestroy"; - - /* Diagnostics fields constants */ - public static final String DIAGNOSTIC_SOURCE = "jakarta-annotations"; - public static final String DIAGNOSTIC_CODE_DATE_FORMAT = "InvalidDateFormat"; - public static final String DIAGNOSTIC_CODE_RESOURCE_RETURN_TYPE = "IncorrectResourceReturnType"; - public static final String DIAGNOSTIC_CODE_MISSING_RESOURCE_TYPE_ATTRIBUTE = "MissingResourceTypeAttribute"; - public static final String DIAGNOSTIC_CODE_MISSING_RESOURCE_NAME_ATTRIBUTE = "MissingResourceNameAttribute"; - public static final String DIAGNOSTIC_CODE_POSTCONSTRUCT_PARAMS = "PostConstructParams"; - public static final String DIAGNOSTIC_CODE_POSTCONSTRUCT_RETURN_TYPE = "PostConstructReturnType"; - public static final String DIAGNOSTIC_CODE_POSTCONSTRUCT_EXCEPTION = "PostConstructException"; - public static final String DIAGNOSTIC_CODE_PREDESTROY_PARAMS = "PreDestroyParams"; - public static final String DIAGNOSTIC_CODE_PREDESTROY_EXCEPTION = "PreDestroyException"; - public static final String DIAGNOSTIC_CODE_PREDESTROY_STATIC = "PreDestroyStatic"; -} +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +/** + * Annotation diagnostic constants. + */ +public class Constants { + + /* Diagnostics source key */ + public static final String DIAGNOSTIC_SOURCE = "jakarta-annotations"; + + /* @Generated */ + public static final String GENERATED = "Generated"; + public static final String GENERATED_FQ_NAME = "jakarta.annotation.Generated"; + public static final String ISO_8601_REGEX = "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$"; + + /* @Resource */ + public static final String RESOURCE = "Resource"; + public static final String RESOURCE_FQ_NAME = "jakarta.annotation.Resource"; + + /* @PostConstruct */ + public static final String POST_CONSTRUCT = "PostConstruct"; + public static final String POST_CONSTRUCT_FQ_NAME = "jakarta.annotation.PostConstruct"; + + /* @PreDesotroy */ + public static final String PRE_DESTROY = "PreDestroy"; + public static final String PRE_DESTROY_FQ_NAME = "jakarta.annotation.PreDestroy"; +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ErrorCode.java new file mode 100644 index 00000000..9c8406b7 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ErrorCode.java @@ -0,0 +1,38 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * Annotations error code. + */ +public enum ErrorCode implements IJavaErrorCode { + InvalidDateFormat, + MissingResourceNameAttribute, + MissingResourceTypeAttribute, + PostConstructParams, + PostConstructReturnType, + PostConstructException, + PreDestroyParams, + PreDestroyStatic, + PreDestroyException; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertNameAttributeToResourceAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertNameAttributeToResourceAnnotationQuickFix.java new file mode 100644 index 00000000..f82f6ece --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertNameAttributeToResourceAnnotationQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2021 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationAttributesQuickFix; + +/** + * Inserts the name attribute to the @Resource annotation to the active element. + */ +public class InsertNameAttributeToResourceAnnotationQuickFix extends InsertAnnotationAttributesQuickFix { + + /** + * Constructor. + */ + public InsertNameAttributeToResourceAnnotationQuickFix() { + super("jakarta.annotation.Resource", "name"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertNameAttributeToResourceAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.InsertResourceAnnotationNameAttribute; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertTypeAttributeToResourceAnnotation.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertTypeAttributeToResourceAnnotation.java new file mode 100644 index 00000000..5afb5e40 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/InsertTypeAttributeToResourceAnnotation.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationAttributesQuickFix; + +/** + * Inserts the type attribute to the @Resource annotation to the active element. + */ +public class InsertTypeAttributeToResourceAnnotation extends InsertAnnotationAttributesQuickFix { + + /** + * Constructor. + */ + public InsertTypeAttributeToResourceAnnotation() { + super("jakarta.annotation.Resource", "type"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertTypeAttributeToResourceAnnotation.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.InsertResourceAnnotationTypeAttribute; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ModifyConstructReturnTypeQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ModifyConstructReturnTypeQuickFix.java new file mode 100644 index 00000000..dcbb5678 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/ModifyConstructReturnTypeQuickFix.java @@ -0,0 +1,118 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.PrimitiveType; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ModifyReturnTypeProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Modifies the return type of the active element. + */ +public class ModifyConstructReturnTypeQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(ModifyConstructReturnTypeQuickFix.class.getName()); + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return ModifyConstructReturnTypeQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel()); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + + ICodeActionId id = JakartaCodeActionId.ChangeReturnTypeToVoid; + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), id)); + codeActions.add(codeAction); + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + + ChangeCorrectionProposal proposal = new ModifyReturnTypeProposal(getLabel(), context.getCompilationUnit(), context.getASTRoot(), parentType, 0, node.getAST().newPrimitiveType(PrimitiveType.VOID)); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to create workspace edit to change a method's retrun type", e); + } + + return toResolve; + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof MethodDeclaration) { + return ((MethodDeclaration) node.getParent()).resolveBinding(); + } + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } + + /** + * Returns the code action label. + * + * @return The code action label. + */ + private String getLabel() { + return Messages.getMessage("ChangeReturnTypeToVoid"); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemoveAllMethodParametersQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemoveAllMethodParametersQuickFix.java new file mode 100644 index 00000000..797c2f77 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemoveAllMethodParametersQuickFix.java @@ -0,0 +1,102 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.RemoveParamsProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Removes all the parameters from the active method. + */ +public class RemoveAllMethodParametersQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(RemoveAllMethodParametersQuickFix.class.getName()); + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveAllMethodParametersQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel()); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), JakartaCodeActionId.RemoveAllParameters)); + return Collections.singletonList(codeAction); + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + IMethodBinding parentMethod = parentNode.resolveBinding(); + List codeActions = new ArrayList<>(); + List parameters = (List) parentNode.parameters(); + ChangeCorrectionProposal proposal = new RemoveParamsProposal(getLabel(), context.getCompilationUnit(), context.getASTRoot(), parentMethod, 0, parameters, null); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to create workspace edit to remove all parameters", e); + } + + return toResolve; + } + + /** + * Returns the code action label. + * + * @return The code action label. + */ + public String getLabel() { + return Messages.getMessage("RemoveAllParameters"); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemovePostConstructAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemovePostConstructAnnotationQuickFix.java new file mode 100644 index 00000000..81eef450 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemovePostConstructAnnotationQuickFix.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Zijian Pei - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +/** + * Removes the @PostConstruct annotation from the declaring element. + */ +public class RemovePostConstructAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * Constructor. + */ + public RemovePostConstructAnnotationQuickFix() { + super(false, "jakarta.annotation.PostConstruct"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemovePostConstructAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.RemoveAnnotationPostConstruct; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemovePreDestroyAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemovePreDestroyAnnotationQuickFix.java new file mode 100644 index 00000000..3e9bcedd --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemovePreDestroyAnnotationQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Zijian Pei - initial implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +/** + * Removes the @PreDestroy annotation from the declaring element. + */ +public class RemovePreDestroyAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * Constructor. + */ + public RemovePreDestroyAnnotationQuickFix() { + super(false, "jakarta.annotation.PreDestroy"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemovePreDestroyAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.RemoveAnnotationPreDestroy; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemoveStaticModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemoveStaticModifierQuickFix.java new file mode 100644 index 00000000..ecc6aa3c --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/annotations/RemoveStaticModifierQuickFix.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.annotations; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveModifierConflictQuickFix; + +/** + * Removes the static modifier from the declaring element. + */ +public class RemoveStaticModifierQuickFix extends RemoveModifierConflictQuickFix { + + /** + * Constructor. + */ + public RemoveStaticModifierQuickFix() { + super("static"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveStaticModifierQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.AnnotationRemoveStaticModifier; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/BeanValidationDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/BeanValidationDiagnosticsParticipant.java new file mode 100644 index 00000000..768449b3 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/BeanValidationDiagnosticsParticipant.java @@ -0,0 +1,291 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Reza Akhavan - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.beanvalidation; + +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.ASSERT_FALSE; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.ASSERT_TRUE; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.BIG_DECIMAL; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.BIG_INTEGER; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.BOOLEAN; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.BYTE; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.CHAR_SEQUENCE; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.DECIMAL_MAX; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.DECIMAL_MIN; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.DIGITS; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.DOUBLE; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.EMAIL; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.FLOAT; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.FUTURE; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.FUTURE_OR_PRESENT; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.INTEGER; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.LONG; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.MAX; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.MIN; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.NEGATIVE; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.NEGATIVE_OR_ZERO; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.NOT_BLANK; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.PAST; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.PAST_OR_PRESENT; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.PATTERN; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.POSITIVE; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.POSTIVE_OR_ZERO; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.SET_OF_ANNOTATIONS; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.SET_OF_DATE_TYPES; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.SHORT; +import static org.eclipse.lsp4jakarta.jdt.internal.beanvalidation.Constants.STRING; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * Bean validation diagnostics participant that manages the use of validation + * element constraints. + */ +public class BeanValidationDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + String uri = context.getUri(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IType[] alltypes; + IField[] allFields; + IAnnotation[] annotations; + IMethod[] allMethods; + + alltypes = unit.getAllTypes(); + for (IType type : alltypes) { + allFields = type.getFields(); + for (IField field : allFields) { + annotations = field.getAnnotations(); + for (IAnnotation annotation : annotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, + annotation.getElementName(), + SET_OF_ANNOTATIONS.toArray(new String[0])); + if (matchedAnnotation != null) { + Range range = PositionUtils.toNameRange(field, context.getUtils()); + validAnnotation(context, uri, field, range, annotation, matchedAnnotation, diagnostics); + } + } + } + allMethods = type.getMethods(); + for (IMethod method : allMethods) { + annotations = method.getAnnotations(); + for (IAnnotation annotation : annotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, + annotation.getElementName(), + SET_OF_ANNOTATIONS.toArray(new String[0])); + if (matchedAnnotation != null) { + Range range = PositionUtils.toNameRange(method, context.getUtils()); + validAnnotation(context, uri, method, range, annotation, matchedAnnotation, diagnostics); + } + } + } + } + + return diagnostics; + } + + private void validAnnotation(JavaDiagnosticsContext context, String uri, IMember element, Range range, + IAnnotation annotation, + String matchedAnnotation, + List diagnostics) throws JavaModelException { + IType declaringType = element.getDeclaringType(); + if (declaringType != null) { + String annotationName = annotation.getElementName(); + boolean isMethod = element instanceof IMethod; + + if (Flags.isStatic(element.getFlags())) { + String message = isMethod ? Messages.getMessage("ConstraintAnnotationsMethod") : Messages.getMessage("ConstraintAnnotationsField"); + diagnostics.add(context.createDiagnostic(uri, message, range, Constants.DIAGNOSTIC_SOURCE, annotationName, + ErrorCode.InvalidConstrainAnnotationOnStaticMethodOrField, DiagnosticSeverity.Error)); + } else { + String type = (isMethod) ? ((IMethod) element).getReturnType() : ((IField) element).getTypeSignature(); + + if (matchedAnnotation.equals(ASSERT_FALSE) || matchedAnnotation.equals(ASSERT_TRUE)) { + String message = isMethod ? Messages.getMessage("AnnotationBooleanMethods", "@" + annotationName) : Messages.getMessage("AnnotationBooleanFields", + "@" + annotationName); + if (!type.equals(getSignatureFormatOfType(BOOLEAN)) && !type.equals(Signature.SIG_BOOLEAN)) { + diagnostics.add(context.createDiagnostic(uri, message, range, Constants.DIAGNOSTIC_SOURCE, + annotationName, ErrorCode.InvalidAnnotationOnNonBooleanMethodOrField, + DiagnosticSeverity.Error)); + } + } else if (matchedAnnotation.equals(DECIMAL_MAX) || matchedAnnotation.equals(DECIMAL_MIN) + || matchedAnnotation.equals(DIGITS)) { + if (!type.equals(getSignatureFormatOfType(BIG_DECIMAL)) + && !type.equals(getSignatureFormatOfType(BIG_INTEGER)) + && !type.equals(getSignatureFormatOfType(CHAR_SEQUENCE)) + && !type.equals(getSignatureFormatOfType(BYTE)) + && !type.equals(getSignatureFormatOfType(SHORT)) + && !type.equals(getSignatureFormatOfType(INTEGER)) + && !type.equals(getSignatureFormatOfType(LONG)) && !type.equals(Signature.SIG_BYTE) + && !type.equals(Signature.SIG_SHORT) && !type.equals(Signature.SIG_INT) + && !type.equals(Signature.SIG_LONG)) { + String message = isMethod ? Messages.getMessage("AnnotationBigDecimalMethods", "@" + annotationName) : Messages.getMessage("AnnotationBigDecimalFields", + "@" + annotationName); + diagnostics.add(context.createDiagnostic(uri, message, range, Constants.DIAGNOSTIC_SOURCE, + annotationName, + ErrorCode.InvalidAnnotationOnNonBigDecimalCharByteShortIntLongMethodOrField, + DiagnosticSeverity.Error)); + } + } else if (matchedAnnotation.equals(EMAIL)) { + checkStringOnly(context, uri, element, range, declaringType, diagnostics, annotationName, isMethod, + type); + } else if (matchedAnnotation.equals(NOT_BLANK)) { + checkStringOnly(context, uri, element, range, declaringType, diagnostics, annotationName, isMethod, + type); + } else if (matchedAnnotation.equals(PATTERN)) { + checkStringOnly(context, uri, element, range, declaringType, diagnostics, annotationName, isMethod, + type); + } else if (matchedAnnotation.equals(FUTURE) || matchedAnnotation.equals(FUTURE_OR_PRESENT) + || matchedAnnotation.equals(PAST) || matchedAnnotation.equals(PAST_OR_PRESENT)) { + String dataType = getDataTypeName(type); + String dataTypeFQName = DiagnosticUtils.getMatchedJavaElementName(declaringType, dataType, + SET_OF_DATE_TYPES.toArray(new String[0])); + if (dataTypeFQName == null) { + String message = isMethod ? Messages.getMessage("AnnotationDateMethods", "@" + annotationName) : Messages.getMessage("AnnotationDateFields", + "@" + annotationName); + diagnostics.add(context.createDiagnostic(uri, message, range, Constants.DIAGNOSTIC_SOURCE, + annotationName, ErrorCode.InvalidAnnotationOnNonDateTimeMethodOrField, + DiagnosticSeverity.Error)); + } + } else if (matchedAnnotation.equals(MIN) || matchedAnnotation.equals(MAX)) { + if (!type.equals(getSignatureFormatOfType(BIG_DECIMAL)) + && !type.equals(getSignatureFormatOfType(BIG_INTEGER)) + && !type.equals(getSignatureFormatOfType(BYTE)) + && !type.equals(getSignatureFormatOfType(SHORT)) + && !type.equals(getSignatureFormatOfType(INTEGER)) + && !type.equals(getSignatureFormatOfType(LONG)) && !type.equals(Signature.SIG_BYTE) + && !type.equals(Signature.SIG_SHORT) && !type.equals(Signature.SIG_INT) + && !type.equals(Signature.SIG_LONG)) { + String message = isMethod ? Messages.getMessage("AnnotationMinMaxMethods", "@" + annotationName) : Messages.getMessage("AnnotationMinMaxFields", + "@" + annotationName); + diagnostics.add(context.createDiagnostic(uri, message, range, Constants.DIAGNOSTIC_SOURCE, + annotationName, ErrorCode.InvalidAnnotationOnNonMinMaxMethodOrField, + DiagnosticSeverity.Error)); + } + } else if (matchedAnnotation.equals(NEGATIVE) || matchedAnnotation.equals(NEGATIVE_OR_ZERO) + || matchedAnnotation.equals(POSITIVE) || matchedAnnotation.equals(POSTIVE_OR_ZERO)) { + if (!type.equals(getSignatureFormatOfType(BIG_DECIMAL)) + && !type.equals(getSignatureFormatOfType(BIG_INTEGER)) + && !type.equals(getSignatureFormatOfType(BYTE)) + && !type.equals(getSignatureFormatOfType(SHORT)) + && !type.equals(getSignatureFormatOfType(INTEGER)) + && !type.equals(getSignatureFormatOfType(LONG)) + && !type.equals(getSignatureFormatOfType(FLOAT)) + && !type.equals(getSignatureFormatOfType(DOUBLE)) && !type.equals(Signature.SIG_BYTE) + && !type.equals(Signature.SIG_SHORT) && !type.equals(Signature.SIG_INT) + && !type.equals(Signature.SIG_LONG) && !type.equals(Signature.SIG_FLOAT) + && !type.equals(Signature.SIG_DOUBLE)) { + String message = isMethod ? Messages.getMessage("AnnotationPositiveMethods", "@" + annotationName) : Messages.getMessage("AnnotationPositiveFields", + "@" + annotationName); + diagnostics.add(context.createDiagnostic(uri, message, range, Constants.DIAGNOSTIC_SOURCE, + annotationName, ErrorCode.InvalidAnnotationOnNonPositiveMethodOrField, + DiagnosticSeverity.Error)); + } + } + + // These ones contains check on all collection types which requires resolving + // the String of the type somehow + // This will also require us to check if the field type was a custom collection + // subtype which means we + // have to resolve it and get the super interfaces and check to see if + // Collection, Map or Array was implemented + // for that custom type (which could as well be a user made subtype) + +// else if (annotation.getElementName().equals(NOT_EMPTY) || annotation.getElementName().equals(SIZE)) { +// +// System.out.println("--Field name: " + Signature.getTypeSignatureKind(fieldType)); +// System.out.println("--Field name: " + Signature.getParameterTypes(fieldType)); +// if ( !fieldType.equals(getSignatureFormatOfType(CHAR_SEQUENCE)) && +// !fieldType.contains("List") && +// !fieldType.contains("Set") && +// !fieldType.contains("Collection") && +// !fieldType.contains("Array") && +// !fieldType.contains("Vector") && +// !fieldType.contains("Stack") && +// !fieldType.contains("Queue") && +// !fieldType.contains("Deque") && +// !fieldType.contains("Map")) { +// +// diagnostics.add(new Diagnostic(fieldAnnotationrange, +// "This annotation can only be used on CharSequence, Collection, Array, " +// + "Map type fields.")); +// } +// } + } + } + } + + private void checkStringOnly(JavaDiagnosticsContext context, String uri, IMember element, Range range, + IType declaringType, + List diagnostics, + String annotationName, boolean isMethod, String type) throws JavaModelException { + if (!type.equals(getSignatureFormatOfType(STRING)) + && !type.equals(getSignatureFormatOfType(CHAR_SEQUENCE))) { + String message = isMethod ? Messages.getMessage("AnnotationStringMethods", "@" + annotationName) : Messages.getMessage("AnnotationStringFields", "@" + annotationName); + diagnostics.add(context.createDiagnostic(uri, message, range, Constants.DIAGNOSTIC_SOURCE, + annotationName, ErrorCode.InvalidAnnotationOnNonStringMethodOrField, + DiagnosticSeverity.Error)); + } + } + + /** + * Refer to Class signature documentation for the formating + * https://www.ibm.com/support/knowledgecenter/sl/SS5JSH_9.5.0/org.eclipse.jdt. + * doc.isv/reference/api/org/eclipse/jdt/core/Signature.html + */ + private static String getSignatureFormatOfType(String type) { + return "Q" + type + ";"; + } + + private static String getDataTypeName(String type) { + int length = type.length(); + if (length > 0 && type.charAt(0) == 'Q' && type.charAt(length - 1) == ';') { + return type.substring(1, length - 1); + } + return type; + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/Constants.java similarity index 71% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/Constants.java index c362483e..5a47061a 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/beanvalidation/BeanValidationConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/Constants.java @@ -1,87 +1,90 @@ -/******************************************************************************* -* Copyright (c) 2020, 2022 IBM Corporation, Reza Akhavan and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Reza Akhavan - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.beanvalidation; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -public class BeanValidationConstants { - - /* Annotations */ - public static final String ASSERT_TRUE = "jakarta.validation.constraints.AssertTrue"; - public static final String ASSERT_FALSE = "jakarta.validation.constraints.AssertFalse"; - public static final String DIGITS = "jakarta.validation.constraints.Digits"; - public static final String DECIMAL_MAX = "jakarta.validation.constraints.DecimalMax"; - public static final String DECIMAL_MIN = "jakarta.validation.constraints.DecimalMin"; - public static final String EMAIL = "jakarta.validation.constraints.Email"; - public static final String PAST_OR_PRESENT = "jakarta.validation.constraints.PastOrPresent"; - public static final String FUTURE_OR_PRESENT = "jakarta.validation.constraints.FutureOrPresent"; - public static final String PAST = "jakarta.validation.constraints.Past"; - public static final String FUTURE = "jakarta.validation.constraints.Future"; - public static final String MIN = "jakarta.validation.constraints.Min"; - public static final String MAX = "jakarta.validation.constraints.Max"; - public static final String NEGATIVE_OR_ZERO = "jakarta.validation.constraints.NegativeOrZero"; - public static final String POSTIVE_OR_ZERO = "jakarta.validation.constraints.PostiveOrZero"; - public static final String NEGATIVE = "jakarta.validation.constraints.Negative"; - public static final String POSITIVE = "jakarta.validation.constraints.Positive"; - public static final String NOT_BLANK = "jakarta.validation.constraints.NotBlank"; - public static final String PATTERN = "jakarta.validation.constraints.Pattern"; - public static final String SIZE = "jakarta.validation.constraints.Size"; - public static final String NOT_EMPTY = "jakarta.validation.constraints.NotEmpty"; - - /* Types */ - public static final String THAI_BUDDHIST_DATE = "java.time.chrono.ThaiBuddhistDate"; - public static final String MINGUO_DATE = "java.time.chrono.MinguoDate"; - public static final String JAPANESE_DATE = "java.time.chrono.JapaneseDate"; - public static final String HIJRAH_DATE = "java.time.chrono.HijrahDate"; - public static final String ZONED_DATE_TIME = "java.time.ZonedDateTime"; - public static final String YEAR_MONTH = "java.time.YearMonth"; - public static final String YEAR = "java.time.Year"; - public static final String OFFSET_TIME = "java.time.OffsetTime"; - public static final String OFFSET_DATE_TIME = "java.time.OffsetDateTime"; - public static final String MONTH_DAY = "java.time.MonthDay"; - public static final String LOCAL_TIME = "java.time.LocalTime"; - public static final String LOCAL_DATE_TIME = "java.time.LocalDateTime"; - public static final String LOCAL_DATE = "java.time.LocalDate"; - public static final String INSTANT = "java.time.Instant"; - public static final String CALENDAR = "java.util.Calendar"; - public static final String DATE = "java.util.Date"; - public static final String BOOLEAN = "Boolean"; - public static final String CHAR_SEQUENCE = "CharSequence"; - public static final String STRING = "String"; - public static final String DOUBLE = "Double"; - public static final String FLOAT = "Float"; - public static final String LONG = "Long"; - public static final String INTEGER = "Integer"; - public static final String SHORT = "Short"; - public static final String BYTE = "Byte"; - public static final String BIG_INTEGER = "BigInteger"; - public static final String BIG_DECIMAL = "BigDecimal"; - - public static final String DIAGNOSTIC_SOURCE = "jakarta-bean-validation"; - public static final String DIAGNOSTIC_CODE_INVALID_TYPE = "FixTypeOfElement"; - public static final String DIAGNOSTIC_CODE_STATIC = "MakeNotStatic"; - - public final static Set SET_OF_ANNOTATIONS = Collections - .unmodifiableSet(new HashSet(Arrays.asList(ASSERT_TRUE, ASSERT_FALSE, DIGITS, DECIMAL_MAX, - DECIMAL_MIN, EMAIL, PAST_OR_PRESENT, FUTURE_OR_PRESENT, PAST, FUTURE, MIN, MAX, NEGATIVE_OR_ZERO, - POSTIVE_OR_ZERO, NEGATIVE, POSITIVE, NOT_BLANK, PATTERN, SIZE, NOT_EMPTY))); - public final static Set SET_OF_DATE_TYPES = Collections - .unmodifiableSet(new HashSet(Arrays.asList(THAI_BUDDHIST_DATE, MINGUO_DATE, JAPANESE_DATE, - HIJRAH_DATE, ZONED_DATE_TIME, YEAR_MONTH, YEAR, OFFSET_TIME, OFFSET_DATE_TIME, MONTH_DAY, - LOCAL_TIME, LOCAL_DATE_TIME, LOCAL_DATE, INSTANT, CALENDAR, DATE))); - -} +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation, Reza Akhavan and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Reza Akhavan - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.beanvalidation; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Bean validation constants. + */ +public class Constants { + + /* Annotations */ + public static final String ASSERT_TRUE = "jakarta.validation.constraints.AssertTrue"; + public static final String ASSERT_FALSE = "jakarta.validation.constraints.AssertFalse"; + public static final String DIGITS = "jakarta.validation.constraints.Digits"; + public static final String DECIMAL_MAX = "jakarta.validation.constraints.DecimalMax"; + public static final String DECIMAL_MIN = "jakarta.validation.constraints.DecimalMin"; + public static final String EMAIL = "jakarta.validation.constraints.Email"; + public static final String PAST_OR_PRESENT = "jakarta.validation.constraints.PastOrPresent"; + public static final String FUTURE_OR_PRESENT = "jakarta.validation.constraints.FutureOrPresent"; + public static final String PAST = "jakarta.validation.constraints.Past"; + public static final String FUTURE = "jakarta.validation.constraints.Future"; + public static final String MIN = "jakarta.validation.constraints.Min"; + public static final String MAX = "jakarta.validation.constraints.Max"; + public static final String NEGATIVE_OR_ZERO = "jakarta.validation.constraints.NegativeOrZero"; + public static final String POSTIVE_OR_ZERO = "jakarta.validation.constraints.PostiveOrZero"; + public static final String NEGATIVE = "jakarta.validation.constraints.Negative"; + public static final String POSITIVE = "jakarta.validation.constraints.Positive"; + public static final String NOT_BLANK = "jakarta.validation.constraints.NotBlank"; + public static final String PATTERN = "jakarta.validation.constraints.Pattern"; + public static final String SIZE = "jakarta.validation.constraints.Size"; + public static final String NOT_EMPTY = "jakarta.validation.constraints.NotEmpty"; + + /* Types */ + public static final String THAI_BUDDHIST_DATE = "java.time.chrono.ThaiBuddhistDate"; + public static final String MINGUO_DATE = "java.time.chrono.MinguoDate"; + public static final String JAPANESE_DATE = "java.time.chrono.JapaneseDate"; + public static final String HIJRAH_DATE = "java.time.chrono.HijrahDate"; + public static final String ZONED_DATE_TIME = "java.time.ZonedDateTime"; + public static final String YEAR_MONTH = "java.time.YearMonth"; + public static final String YEAR = "java.time.Year"; + public static final String OFFSET_TIME = "java.time.OffsetTime"; + public static final String OFFSET_DATE_TIME = "java.time.OffsetDateTime"; + public static final String MONTH_DAY = "java.time.MonthDay"; + public static final String LOCAL_TIME = "java.time.LocalTime"; + public static final String LOCAL_DATE_TIME = "java.time.LocalDateTime"; + public static final String LOCAL_DATE = "java.time.LocalDate"; + public static final String INSTANT = "java.time.Instant"; + public static final String CALENDAR = "java.util.Calendar"; + public static final String DATE = "java.util.Date"; + public static final String BOOLEAN = "Boolean"; + public static final String CHAR_SEQUENCE = "CharSequence"; + public static final String STRING = "String"; + public static final String DOUBLE = "Double"; + public static final String FLOAT = "Float"; + public static final String LONG = "Long"; + public static final String INTEGER = "Integer"; + public static final String SHORT = "Short"; + public static final String BYTE = "Byte"; + public static final String BIG_INTEGER = "BigInteger"; + public static final String BIG_DECIMAL = "BigDecimal"; + + public static final String DIAGNOSTIC_SOURCE = "jakarta-bean-validation"; + + public final static Set SET_OF_ANNOTATIONS = Collections.unmodifiableSet(new HashSet(Arrays.asList(ASSERT_TRUE, ASSERT_FALSE, DIGITS, DECIMAL_MAX, + DECIMAL_MIN, EMAIL, PAST_OR_PRESENT, FUTURE_OR_PRESENT, PAST, + FUTURE, MIN, MAX, NEGATIVE_OR_ZERO, + POSTIVE_OR_ZERO, NEGATIVE, POSITIVE, NOT_BLANK, PATTERN, + SIZE, NOT_EMPTY))); + public final static Set SET_OF_DATE_TYPES = Collections.unmodifiableSet(new HashSet(Arrays.asList(THAI_BUDDHIST_DATE, MINGUO_DATE, JAPANESE_DATE, + HIJRAH_DATE, ZONED_DATE_TIME, YEAR_MONTH, YEAR, OFFSET_TIME, + OFFSET_DATE_TIME, MONTH_DAY, + LOCAL_TIME, LOCAL_DATE_TIME, LOCAL_DATE, INSTANT, CALENDAR, + DATE))); + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/ErrorCode.java new file mode 100644 index 00000000..5c4c7d54 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/ErrorCode.java @@ -0,0 +1,37 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.beanvalidation; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * Bean validation error code. + */ +public enum ErrorCode implements IJavaErrorCode { + InvalidConstrainAnnotationOnStaticMethodOrField, + InvalidAnnotationOnNonBooleanMethodOrField, + InvalidAnnotationOnNonBigDecimalCharByteShortIntLongMethodOrField, + InvalidAnnotationOnNonDateTimeMethodOrField, + InvalidAnnotationOnNonMinMaxMethodOrField, + InvalidAnnotationOnNonPositiveMethodOrField, + InvalidAnnotationOnNonStringMethodOrField; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/RemoveDynamicConstraintAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/RemoveDynamicConstraintAnnotationQuickFix.java new file mode 100644 index 00000000..137c5750 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/RemoveDynamicConstraintAnnotationQuickFix.java @@ -0,0 +1,137 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.beanvalidation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.RemoveAnnotationProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Removes bean validation constraints (@AssertTrue, @NotNull, @Digits, @Size, + * etc.) provided by the diagnostic data. + */ +public class RemoveDynamicConstraintAnnotationQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(RemoveDynamicConstraintAnnotationQuickFix.class.getName()); + + /** Annotation name map key */ + public static final String ANNOTATION_NAME_KEY = "annotation.name"; + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveDynamicConstraintAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + String annotationName = diagnostic.getData().toString().replace("\"", ""); + String label = getLabel(annotationName); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + List codeActions = new ArrayList<>(); + if (parentType != null) { + ExtendedCodeAction codeAction = new ExtendedCodeAction(label); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + Map extendedData = new HashMap(); + extendedData.put(ANNOTATION_NAME_KEY, annotationName); + + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), extendedData, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), JakartaCodeActionId.RemoveConstraintAnnotation)); + codeActions.add(codeAction); + } + + return codeActions; + + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + CodeActionResolveData data = (CodeActionResolveData) toResolve.getData(); + String annotationName = (String) data.getExtendedDataEntry(ANNOTATION_NAME_KEY); + String label = getLabel(annotationName); + ChangeCorrectionProposal proposal = new RemoveAnnotationProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, context.getCoveredNode().getParent(), annotationName); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action to remove a constraint annotation", + e); + } + + return toResolve; + } + + /** + * Returns the code action label. + * + * @Paran annotationName The annotation name. + * + * @return The code action label. + */ + private static String getLabel(String annotationName) { + return Messages.getMessage("RemoveConstraintAnnotation", annotationName); + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected static IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/RemoveStaticModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/RemoveStaticModifierQuickFix.java new file mode 100644 index 00000000..815bb2f0 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/beanvalidation/RemoveStaticModifierQuickFix.java @@ -0,0 +1,67 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.beanvalidation; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveModifierConflictQuickFix; + +/** + * Removes a static modifier from the declaring element. + */ +public class RemoveStaticModifierQuickFix extends RemoveModifierConflictQuickFix { + + /** + * Constructor. + */ + public RemoveStaticModifierQuickFix() { + super("static"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveStaticModifierQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.BBRemoveStaticModifier; + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + if (diagnostic.getCode().getLeft().equals(ErrorCode.InvalidConstrainAnnotationOnStaticMethodOrField.getCode())) { + codeActions = super.getCodeActions(context, diagnostic, monitor); + } + + return codeActions; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/Constants.java similarity index 70% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/Constants.java index 1fdb4f3d..eb8453c6 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/cdi/ManagedBeanConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/Constants.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2021, 2022 IBM Corporation. +* Copyright (c) 2021, 2023 IBM Corporation. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,13 +11,16 @@ * Hani Damlaj *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.cdi; +package org.eclipse.lsp4jakarta.jdt.internal.cdi; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -public class ManagedBeanConstants { +/** + * Context and Dependency Injection (CDI) diagnostic constants. + */ +public class Constants { /* Annotation Constants */ public static final String PRODUCES = "Produces"; public static final String PRODUCES_FQ_NAME = "jakarta.enterprise.inject.Produces"; @@ -31,34 +34,30 @@ public class ManagedBeanConstants { public static final String OBSERVES_ASYNC_FQ_NAME = "jakarta.enterprise.event.ObservesAsync"; public static final String DEPENDENT = "Dependent"; public static final String DEPENDENT_FQ_NAME = "jakarta.enterprise.context.Dependent"; - - + public static final String DIAGNOSTIC_SOURCE = "jakarta-cdi"; public static final String DIAGNOSTIC_CODE = "InvalidManagedBeanAnnotation"; public static final String DIAGNOSTIC_CODE_SCOPEDECL = "InvalidScopeDecl"; public static final String DIAGNOSTIC_CODE_PRODUCES_INJECT = "RemoveProducesOrInject"; - + public static final String CONSTRUCTOR_DIAGNOSTIC_CODE = "InvalidManagedBeanConstructor"; - - + public static final String DIAGNOSTIC_CODE_INVALID_INJECT_PARAM = "RemoveInjectOrConflictedAnnotations"; public static final String DIAGNOSTIC_CODE_INVALID_PRODUCES_PARAM = "RemoveProducesOrConflictedAnnotations"; public static final String DIAGNOSTIC_CODE_INVALID_DISPOSES_PARAM = "RemoveDisposesOrConflictedAnnotations"; - + public static final String DIAGNOSTIC_CODE_REDUNDANT_DISPOSES = "RemoveExtraDisposes"; - + public static final Set INVALID_INJECT_PARAMS = new HashSet(Arrays.asList(DISPOSES, OBSERVES, OBSERVES_ASYNC)); public static final String[] INVALID_INJECT_PARAMS_FQ = { DISPOSES_FQ_NAME, OBSERVES_FQ_NAME, - OBSERVES_ASYNC_FQ_NAME }; - + OBSERVES_ASYNC_FQ_NAME }; + // List can be found in the cdi doc here: // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#bean_defining_annotations - public static final Set SCOPES = new HashSet( - Arrays.asList("Dependent", "ApplicationScoped", "ConversationScoped", "RequestScoped", "SessionScoped", - "NormalScope", "Interceptor", "Decorator", "Stereotype")); - public static final Set SCOPE_FQ_NAMES = new HashSet( - Arrays.asList(DEPENDENT_FQ_NAME, "jakarta.enterprise.context.ApplicationScoped", - "jakarta.enterprise.context.ConversationScoped", "jakarta.enterprise.context.RequestScoped", - "jakarta.enterprise.context.SessionScoped", "jakarta.enterprise.context.NormalScope", - "jakarta.Interceptor", "jakarta.Decorator", "jakarta.enterprise.inject.Stereotype")); + public static final Set SCOPES = new HashSet(Arrays.asList("Dependent", "ApplicationScoped", "ConversationScoped", "RequestScoped", "SessionScoped", + "NormalScope", "Interceptor", "Decorator", "Stereotype")); + public static final Set SCOPE_FQ_NAMES = new HashSet(Arrays.asList(DEPENDENT_FQ_NAME, "jakarta.enterprise.context.ApplicationScoped", + "jakarta.enterprise.context.ConversationScoped", "jakarta.enterprise.context.RequestScoped", + "jakarta.enterprise.context.SessionScoped", "jakarta.enterprise.context.NormalScope", + "jakarta.Interceptor", "jakarta.Decorator", "jakarta.enterprise.inject.Stereotype")); } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ErrorCode.java new file mode 100644 index 00000000..0e8525d4 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ErrorCode.java @@ -0,0 +1,42 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * Contexts and Dependency Injection (CDI) error code. + */ +public enum ErrorCode implements IJavaErrorCode { + InvalidNumberOfScopedAnnotationsByManagedBean, + InvalidManagedBeanWithNonStaticPublicField, + InvalidNumberOfScopeAnnotationsByProducerField, + InvalidFieldWithProducesAndInjectAnnotations, + InvalidNumberOfScopeAnnotationsByProducerMethod, + InvalidMethodWithProducesAndInjectAnnotations, + InvalidManagedBeanWithInvalidConstructor, + InvalidGenericManagedBeanClassWithNoDependentScope, + InvalidDisposesAnnotationOnMultipleMethodParams, + InvalidDisposerMethodParamAnnotation, + InvalidProducerMethodParamAnnotation, + InvalidInjectAnnotatedMethodParamAnnotation; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertDefaultProtectedConstructorToMBeanQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertDefaultProtectedConstructorToMBeanQuickFix.java new file mode 100644 index 00000000..9b845e84 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertDefaultProtectedConstructorToMBeanQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertDefaultConstructorToClassQuickFix; + +/** + * Inserts a protected default constructor the active managed bean class. + */ +public class InsertDefaultProtectedConstructorToMBeanQuickFix extends InsertDefaultConstructorToClassQuickFix { + + /** + * Constructor. + */ + public InsertDefaultProtectedConstructorToMBeanQuickFix() { + super("protected"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertDefaultProtectedConstructorToMBeanQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIInsertProtectedCtrtToClass; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertDefaultPublicConstructorToMBeanQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertDefaultPublicConstructorToMBeanQuickFix.java new file mode 100644 index 00000000..a6f95d45 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertDefaultPublicConstructorToMBeanQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertDefaultConstructorToClassQuickFix; + +/** + * Inserts a public default constructor the active managed bean class. + */ +public class InsertDefaultPublicConstructorToMBeanQuickFix extends InsertDefaultConstructorToClassQuickFix { + + /** + * Constructor. + */ + public InsertDefaultPublicConstructorToMBeanQuickFix() { + super("public"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertDefaultPublicConstructorToMBeanQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIInsertPublicCtrtToClass; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertInjectAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertInjectAnnotationQuickFix.java new file mode 100644 index 00000000..0f65d75a --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/InsertInjectAnnotationQuickFix.java @@ -0,0 +1,66 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationMissingQuickFix; + +/** + * Inserts the @Inject annotation to the active element. + */ +public class InsertInjectAnnotationQuickFix extends InsertAnnotationMissingQuickFix { + + /** + * Constructor. + */ + public InsertInjectAnnotationQuickFix() { + super("jakarta.inject.Inject"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertInjectAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIInsertInjectAnnotation; + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("restriction") + @Override + protected IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + if (node.getParent() instanceof MethodDeclaration) { + MethodDeclaration methodDecl = (MethodDeclaration) node.getParent(); + return methodDecl.resolveBinding(); + } + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ManagedBeanDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ManagedBeanDiagnosticsParticipant.java new file mode 100644 index 00000000..321683c0 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ManagedBeanDiagnosticsParticipant.java @@ -0,0 +1,408 @@ +/******************************************************************************* + * Copyright (c) 2021, 2023 IBM Corporation. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Hani Damlaj + * Jianing Xu + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +import com.google.gson.Gson; + +/** + * CDI diagnostics participant that manages the use of a managed bean. + */ +public class ManagedBeanDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + String uri = context.getUri(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IType[] types = unit.getAllTypes(); + String[] scopeFQNames = Constants.SCOPE_FQ_NAMES.toArray(String[]::new); + for (IType type : types) { + List managedBeanAnnotations = DiagnosticUtils.getMatchedJavaElementNames(type, + Stream.of(type.getAnnotations()).map(annotation -> annotation.getElementName()).toArray(String[]::new), + scopeFQNames); + boolean isManagedBean = managedBeanAnnotations.size() > 0; + + if (managedBeanAnnotations.size() > 1) { + // convert to simple name + List diagnosticData = managedBeanAnnotations.stream().map(annotation -> DiagnosticUtils.getSimpleName(annotation)).collect(Collectors.toList()); + + Range range = PositionUtils.toNameRange(type, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ScopeTypeAnnotationsManagedBean"), range, + Constants.DIAGNOSTIC_SOURCE, (new Gson().toJsonTree(diagnosticData)), + ErrorCode.InvalidNumberOfScopedAnnotationsByManagedBean, DiagnosticSeverity.Error)); + } + + String[] injectAnnotations = { Constants.PRODUCES_FQ_NAME, Constants.INJECT_FQ_NAME }; + IField fields[] = type.getFields(); + for (IField field : fields) { + int fieldFlags = field.getFlags(); + String[] annotationNames = Stream.of(field.getAnnotations()).map(annotation -> annotation.getElementName()).toArray(String[]::new); + List fieldScopes = DiagnosticUtils.getMatchedJavaElementNames(type, annotationNames, + scopeFQNames); + + // If a managed bean has a non-static public field, it must have + // scope @Dependent. If a managed bean with a non-static public field declares + // any scope other than @Dependent, the container automatically detects the + // problem and treats it as a definition error. + // + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#managed_beans + if (isManagedBean && Flags.isPublic(fieldFlags) && !Flags.isStatic(fieldFlags) + && (fieldScopes.size() != 1 || !fieldScopes.get(0).equals(Constants.DEPENDENT_FQ_NAME))) { + Range range = PositionUtils.toNameRange(field, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ManagedBeanWithNonStaticPublicField"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidManagedBeanWithNonStaticPublicField, DiagnosticSeverity.Error)); + } + + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_bean_scope + // A bean class or producer method or field may specify at most one scope type + // annotation. If a bean class or producer method or field specifies multiple + // scope type annotations, the container automatically detects the problem and + // treats it as a definition error. + // + // Here we only look at the fields. + List fieldInjects = DiagnosticUtils.getMatchedJavaElementNames(type, annotationNames, + injectAnnotations); + boolean isProducerField = false, isInjectField = false; + for (String annotation : fieldInjects) { + if (Constants.PRODUCES_FQ_NAME.equals(annotation)) + isProducerField = true; + else if (Constants.INJECT_FQ_NAME.equals(annotation)) + isInjectField = true; + } + if (isProducerField && fieldScopes.size() > 1) { + List diagnosticData = fieldScopes.stream().map(annotation -> DiagnosticUtils.getSimpleName(annotation)).collect(Collectors.toList()); // convert + // to + // simple + // name + diagnosticData.add(Constants.PRODUCES); + Range range = PositionUtils.toNameRange(field, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ScopeTypeAnnotationsProducerField"), range, + Constants.DIAGNOSTIC_SOURCE, (new Gson().toJsonTree(diagnosticData)), + ErrorCode.InvalidNumberOfScopeAnnotationsByProducerField, DiagnosticSeverity.Error)); + } + + if (isProducerField && isInjectField) { + + // Produces and Inject Annotations Checks: + // + // go through each field and method to make sure @Produces and @Inject are not + // used together + // + // see: https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_producer_field + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_producer_method + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_injected_field + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_initializer + // + // A single field cannot have the same + Range range = PositionUtils.toNameRange(field, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ManagedBeanProducesAndInjectField"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidFieldWithProducesAndInjectAnnotations, DiagnosticSeverity.Error)); + } + + } + + IMethod[] methods = type.getMethods(); + List constructorMethods = new ArrayList(); + for (IMethod method : methods) { + + // Find all methods on the type that are constructors. + if (DiagnosticUtils.isConstructorMethod(method)) + constructorMethods.add(method); + + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html#declaring_bean_scope + // A bean class or producer method or field may specify at most one scope type + // annotation. If a bean class or producer method or field specifies multiple + // scope type annotations, the container automatically detects the problem and + // treats it as a definition error. + // + // Here we only look at the methods. + String[] annotationNames = Stream.of(method.getAnnotations()).map(annotation -> annotation.getElementName()).toArray(String[]::new); + List methodScopes = DiagnosticUtils.getMatchedJavaElementNames(type, annotationNames, + scopeFQNames); + List methodInjects = DiagnosticUtils.getMatchedJavaElementNames(type, annotationNames, + injectAnnotations); + boolean isProducerMethod = false, isInjectMethod = false; + for (String annotation : methodInjects) { + if (Constants.PRODUCES_FQ_NAME.equals(annotation)) + isProducerMethod = true; + else if (Constants.INJECT_FQ_NAME.equals(annotation)) + isInjectMethod = true; + } + + if (isProducerMethod && methodScopes.size() > 1) { + List diagnosticData = methodScopes.stream().map(annotation -> DiagnosticUtils.getSimpleName(annotation)).collect(Collectors.toList()); // convert + // to + // simple + // name + diagnosticData.add(Constants.PRODUCES); + Range range = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ScopeTypeAnnotationsProducerMethod"), range, + Constants.DIAGNOSTIC_SOURCE, (new Gson().toJsonTree(diagnosticData)), + ErrorCode.InvalidNumberOfScopeAnnotationsByProducerMethod, DiagnosticSeverity.Error)); + } + + if (isProducerMethod && isInjectMethod) { + + // Produces and Inject Annotations Checks: + // + // go through each field and method to make sure @Produces and @Inject are not + // used together + // + // see: https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_producer_field + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_producer_method + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_injected_field + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_initializer + // + // A single method cannot have the same + Range range = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ManagedBeanProducesAndInjectMethod"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidMethodWithProducesAndInjectAnnotations, DiagnosticSeverity.Error)); + } + + } + + if (isManagedBean && constructorMethods.size() > 0) { + // If the managed bean does not have a constructor that takes no parameters, + // it must have a constructor annotated @Inject. No additional special + // annotations are required. + + // If there are no constructor methods, there is an implicit empty constructor + // generated by the compiler. + List methodsNeedingDiagnostics = new ArrayList(); + for (IMethod m : constructorMethods) { + if (m.getNumberOfParameters() == 0) { + methodsNeedingDiagnostics.clear(); + break; + } + IAnnotation[] annotations = m.getAnnotations(); + boolean hasParameterizedInjectConstructor = false; + // look up '@Inject' annotation + for (IAnnotation annotation : annotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), + Constants.INJECT_FQ_NAME)) { + hasParameterizedInjectConstructor = true; + break; + } + } + if (hasParameterizedInjectConstructor) { + methodsNeedingDiagnostics.clear(); + break; + } else + methodsNeedingDiagnostics.add(m); + } + + // Deliver a diagnostic on all parameterized constructors that they must add an + // @Inject annotation + for (IMethod m : methodsNeedingDiagnostics) { + Range range = PositionUtils.toNameRange(m, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ManagedBeanConstructorWithParameters"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidManagedBeanWithInvalidConstructor, DiagnosticSeverity.Error)); + } + } + + // If a managed bean class is of generic type, it must be annotated + // with @Dependent + if (isManagedBean) { + boolean isClassGeneric = type.getTypeParameters().length != 0; + boolean isDependent = managedBeanAnnotations.stream().anyMatch(annotation -> Constants.DEPENDENT_FQ_NAME.equals(annotation)); + + if (isClassGeneric && !isDependent) { + Range range = PositionUtils.toNameRange(type, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ManagedBeanGenericType"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidGenericManagedBeanClassWithNoDependentScope, DiagnosticSeverity.Error)); + } + } + + // Inject and Disposes, Observes, ObservesAsync Annotations: + // + // go through each method to make sure @Inject + // and @Disposes, @Observes, @ObservesAsync are not used together + // + // see: https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_bean_constructor + // https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_initializer + + invalidParamsCheck(context, uri, unit, diagnostics, type, Constants.INJECT_FQ_NAME); + + if (isManagedBean) { + + // Produces and Disposes, Observes, ObservesAsync Annotations: + // + // go through each method to make sure @Produces + // and @Disposes, @Observes, @ObservesAsync are not used together + // + // see: https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0.html# + // declaring_producer_method + // + // note: + // we need to check for bean defining annotations first to make sure the + // managed bean is discovered. + invalidParamsCheck(context, uri, unit, diagnostics, type, Constants.PRODUCES_FQ_NAME); + + for (IMethod method : methods) { + int numDisposes = 0; + Set invalidAnnotations = new TreeSet<>(); + ILocalVariable[] params = method.getParameters(); + + for (ILocalVariable param : params) { + IAnnotation[] annotations = param.getAnnotations(); + for (IAnnotation annotation : annotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, + annotation.getElementName(), + Constants.INVALID_INJECT_PARAMS_FQ); + if (Constants.DISPOSES_FQ_NAME.equals(matchedAnnotation)) { + numDisposes++; + } else if (Constants.OBSERVES_FQ_NAME.equals(matchedAnnotation) + || Constants.OBSERVES_ASYNC_FQ_NAME.equals(matchedAnnotation)) { + invalidAnnotations.add("@" + annotation.getElementName()); + } + } + } + + if (numDisposes == 0) + continue; + if (numDisposes > 1) { + Range range = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ManagedBeanDisposeOneParameter"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidDisposesAnnotationOnMultipleMethodParams, DiagnosticSeverity.Error)); + } + + if (!invalidAnnotations.isEmpty()) { + Range range = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + createInvalidDisposesLabel(invalidAnnotations), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidDisposerMethodParamAnnotation, DiagnosticSeverity.Error)); + } + } + } + } + + return diagnostics; + } + + private void invalidParamsCheck(JavaDiagnosticsContext context, String uri, ICompilationUnit unit, + List diagnostics, IType type, String target) throws JavaModelException { + for (IMethod method : type.getMethods()) { + IAnnotation targetAnnotation = null; + + for (IAnnotation annotation : method.getAnnotations()) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), target)) { + targetAnnotation = annotation; + break; + } + } + + if (targetAnnotation == null) + continue; + + Set invalidAnnotations = new TreeSet<>(); + ILocalVariable[] params = method.getParameters(); + for (ILocalVariable param : params) { + List paramScopes = DiagnosticUtils.getMatchedJavaElementNames(type, + Stream.of(param.getAnnotations()).map(annotation -> annotation.getElementName()).toArray(String[]::new), + Constants.INVALID_INJECT_PARAMS_FQ); + for (String annotation : paramScopes) { + invalidAnnotations.add("@" + DiagnosticUtils.getSimpleName(annotation)); + } + } + + if (!invalidAnnotations.isEmpty()) { + Range range = PositionUtils.toNameRange(method, context.getUtils()); + if (Constants.PRODUCES_FQ_NAME.equals(target)) { + diagnostics.add(context.createDiagnostic(uri, + createInvalidProducesLabel(invalidAnnotations), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidProducerMethodParamAnnotation, DiagnosticSeverity.Error)); + } else { + diagnostics.add(context.createDiagnostic(uri, + createInvalidInjectLabel(invalidAnnotations), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidInjectAnnotatedMethodParamAnnotation, DiagnosticSeverity.Error)); + } + } + } + } + + private String createInvalidInjectLabel(Set invalidAnnotations) { + return Messages.getMessage("ManagedBeanInvalidInject", String.join(", ", invalidAnnotations)); + } + + private String createInvalidProducesLabel(Set invalidAnnotations) { + return Messages.getMessage("ManagedBeanInvalidProduces", String.join(", ", invalidAnnotations)); + } + + private String createInvalidDisposesLabel(Set invalidAnnotations) { + return Messages.getMessage("ManagedBeanInvalidDisposer", String.join(", ", invalidAnnotations)); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ManagedBeanQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ManagedBeanQuickFix.java new file mode 100644 index 00000000..c4a14054 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/ManagedBeanQuickFix.java @@ -0,0 +1,162 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Hani Damlaj +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import static org.eclipse.lsp4jakarta.jdt.internal.cdi.Constants.SCOPES; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationMissingQuickFix; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ReplaceAnnotationProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Removes scope related annotations from the declaring element. + */ +public class ManagedBeanQuickFix extends InsertAnnotationMissingQuickFix { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(ManagedBeanQuickFix.class.getName()); + + /** Annotation names to remove. */ + private static final String[] REMOVE_ANNOTATION_NAMES = new ArrayList<>(SCOPES).toArray(new String[SCOPES.size()]); + + /** + * Constructor. + */ + public ManagedBeanQuickFix() { + super("jakarta.enterprise.context.Dependent"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return ManagedBeanQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIReplaceScopeAnnotations; + } + + /** + * {@inheritDoc} + */ + @Override + protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, + List codeActions) throws CoreException { + String[] annotations = getAnnotations(); + for (String annotation : annotations) { + insertAnnotation(diagnostic, context, codeActions, annotation); + } + } + + /** + * Adds a code action to insert the given annotation. + * + * @param diagnostic The diagnostic associated with this action. + * @param context The context. + * @param codeActions The list of code actions. + * @param annotation The annotation to insert. + * @throws CoreException + */ + protected void insertAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, List codeActions, + String annotation) throws CoreException { + String name = getLabel(annotation); + ExtendedCodeAction codeAction = new ExtendedCodeAction(name); + codeAction.setRelevance(0); + codeAction.setDiagnostics(Collections.singletonList(diagnostic)); + codeAction.setKind(CodeActionKind.QuickFix); + + Map extendedData = new HashMap<>(); + extendedData.put(ANNOTATION_KEY, Arrays.asList(annotation)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), extendedData, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), getCodeActionId())); + + codeActions.add(codeAction); + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + + // Diagnostic is reported on the variable declaration, however the + // annotations that need to be replaced are on the type declaration (class + // definition) containing the variable declaration. We retrieve the type + // declaration container here. + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + ASTNode parentNode = context.getASTRoot().findDeclaringNode(parentType); + IBinding classBinding = getBinding(parentNode); + + CodeActionResolveData data = (CodeActionResolveData) toResolve.getData(); + List resolveAnnotations = (List) data.getExtendedDataEntry(ANNOTATION_KEY); + String[] resolveAnnotationsArray = resolveAnnotations.toArray(String[]::new); + + // Only a single annotation insertion is expected. + if (resolveAnnotationsArray.length == 1) { + String annotation = resolveAnnotationsArray[0]; + String name = getLabel(annotation); + ChangeCorrectionProposal proposal = new ReplaceAnnotationProposal(name, context.getCompilationUnit(), context.getASTRoot(), classBinding, 0, annotation, REMOVE_ANNOTATION_NAMES); + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to create workspace edit for code action to insert missing annotation", + e); + } + } + + return toResolve; + } + + /** + * Returns the label associated with the input annotation. + * + * @param annotation The annotation name. + * + * @return The label associated with the input annotation. + */ + private static String getLabel(String annotation) { + String annotationName = annotation.substring(annotation.lastIndexOf('.') + 1, annotation.length()); + return Messages.getMessage("ReplaceCurrentScope", "@" + annotationName); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveInjectAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveInjectAnnotationQuickFix.java new file mode 100644 index 00000000..dedb69c9 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveInjectAnnotationQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +/** + * Removes the @Inject annotation from the declaring element. + */ +public class RemoveInjectAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * Constructor. + */ + public RemoveInjectAnnotationQuickFix() { + super(false, "jakarta.inject.Inject"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveInjectAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIRemoveInjectAnnotation; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveInvalidInjectParamAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveInvalidInjectParamAnnotationQuickFix.java new file mode 100644 index 00000000..975e948d --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveInvalidInjectParamAnnotationQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; + +/** + * Removes the @Disposes, @Observes and @ObservesAsync annotations from + * the declaring element. + */ +public class RemoveInvalidInjectParamAnnotationQuickFix extends RemoveMethodParamAnnotationQuickFix { + + /** + * Constructor. + */ + public RemoveInvalidInjectParamAnnotationQuickFix() { + super(Constants.INVALID_INJECT_PARAMS.toArray((String[]::new))); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveInvalidInjectParamAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIRemoveInvalidInjectAnnotations; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveMethodParamAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveMethodParamAnnotationQuickFix.java new file mode 100644 index 00000000..b1b8cc6d --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveMethodParamAnnotationQuickFix.java @@ -0,0 +1,228 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MarkerAnnotation; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ModifyModifiersProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Removes any of @Disposes, @Observes and @ObservesAsync + * annotation from the declaring element. + */ +public abstract class RemoveMethodParamAnnotationQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(RemoveMethodParamAnnotationQuickFix.class.getName()); + + /** Map key to retrieve a list of annotations. */ + protected static final String ANNOTATIONS_KEY = "annotations"; + + /** Map key to retrieve parameter names. */ + protected static final String PARAMETER_NAME_KEY = "parameter.name"; + + /** Annotations to remove. */ + String[] annotations; + + public RemoveMethodParamAnnotationQuickFix(String... annotations) { + this.annotations = annotations; + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveMethodParamAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + List parameters = (List) parentNode.parameters(); + + for (SingleVariableDeclaration parameter : parameters) { + + List modifiers = (List) parameter.getStructuralProperty(SingleVariableDeclaration.MODIFIERS2_PROPERTY); + ArrayList annotationsToRemove = new ArrayList<>(); + + for (ASTNode modifier : modifiers) { + Name markAnnotationTypeName = ((MarkerAnnotation) modifier).getTypeName(); + if (Arrays.asList(this.annotations).stream().anyMatch(m -> m.equals(markAnnotationTypeName.toString()))) { + annotationsToRemove.add(markAnnotationTypeName.toString()); + } + } + + createCodeAction(diagnostic, context, codeActions, parameter, + (String[]) annotationsToRemove.toArray(new String[annotationsToRemove.size()])); + } + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + IMethodBinding parentMethod = parentNode.resolveBinding(); + CodeActionResolveData data = (CodeActionResolveData) toResolve.getData(); + List annotationsToRemoveList = (List) data.getExtendedDataEntry(ANNOTATIONS_KEY); + String[] annotationsToRemove = annotationsToRemoveList.toArray(String[]::new); + String parameterName = (String) data.getExtendedDataEntry(PARAMETER_NAME_KEY); + + SingleVariableDeclaration parameter = matchParameterBinding(parentNode, parameterName); + if (parameter != null) { + String label = getLabel(parameterName, annotationsToRemove); + ModifyModifiersProposal proposal = new ModifyModifiersProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentMethod, 0, parameter, new ArrayList<>(), Arrays.asList(annotationsToRemove)); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action to remove annotation", e); + } + } + return toResolve; + } + + /** + * + * The code diagnostic associated with the action to be + * created. + * + * @param context The context. + * @param codeActions The list of code action to update. + * @param parameter The method parameter for which to create a code + * action. + * @param annotationsToRemove The annotations to remove. + * + * @throws CoreException + */ + protected void createCodeAction(Diagnostic diagnostic, JavaCodeActionContext context, + List codeActions, SingleVariableDeclaration parameter, String... annotationsToRemove) throws CoreException { + String parameterName = parameter.getName().toString(); + String label = getLabel(parameterName, annotationsToRemove); + ExtendedCodeAction codeAction = new ExtendedCodeAction(label); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + Map extendedData = new HashMap(); + extendedData.put(ANNOTATIONS_KEY, Arrays.asList(annotationsToRemove)); + extendedData.put(PARAMETER_NAME_KEY, parameterName); + + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), extendedData, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), getCodeActionId())); + + codeActions.add(codeAction); + } + + /** + * Returns the object representing the method parameter associated with the + * input parameter name. + * + * @param method The method node. + * @param parameterName The parameter name. + * @return The object representing the method parameter associated with the + * input parameter name. + */ + private SingleVariableDeclaration matchParameterBinding(MethodDeclaration method, String parameterName) { + + List parameters = (List) method.parameters(); + + for (SingleVariableDeclaration parameter : parameters) { + if (parameter.getName().toString().equals(parameterName)) { + return parameter; + } + } + + return null; + } + + /** + * Returns the label associated with the input modifier. + * + * @param modifier The modifier to remove. + * @params annotationsToRemove The annotations to remove. + * + * @return The label associated with the input modifier. + */ + protected String getLabel(String parameterName, String... annotationsToRemove) { + StringBuilder sb = new StringBuilder(); + sb.append("'@").append(annotationsToRemove[0]).append("'"); + for (int i = 1; i < annotationsToRemove.length; i++) { + sb.append(", '@").append(annotationsToRemove[i]).append("'"); + } + + return Messages.getMessage("RemoveTheModifierFromParameter", sb.toString(), + parameterName); + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } else if (node.getParent() instanceof MethodDeclaration) { + return ((MethodDeclaration) node.getParent()).resolveBinding(); + } + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } + + /** + * Returns the id for this code action. + * + * @return the id for this code action + */ + protected abstract ICodeActionId getCodeActionId(); +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveProduceAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveProduceAnnotationQuickFix.java new file mode 100644 index 00000000..e3238c0b --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveProduceAnnotationQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +/** + * Removes the @Produces annotation from the declaring element. + */ +public class RemoveProduceAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * Constructor. + */ + public RemoveProduceAnnotationQuickFix() { + super(false, "jakarta.enterprise.inject.Produces"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveProduceAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIRemoveProducesAnnotation; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveProducesAndInjectAnnotationsQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveProducesAndInjectAnnotationsQuickFix.java new file mode 100644 index 00000000..0fb5be5f --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveProducesAndInjectAnnotationsQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +/** + * Removes the @Produces and the @inject annotations from the declaring element. + */ +public class RemoveProducesAndInjectAnnotationsQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * Constructor. + */ + public RemoveProducesAndInjectAnnotationsQuickFix() { + super(false, "jakarta.enterprise.inject.Produces", "jakarta.inject.Inject"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveProducesAndInjectAnnotationsQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIRemoveProducesAndInjectAnnotations; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveScopeDeclarationAnnotationsQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveScopeDeclarationAnnotationsQuickFix.java new file mode 100644 index 00000000..a4163cb5 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/RemoveScopeDeclarationAnnotationsQuickFix.java @@ -0,0 +1,81 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +import com.google.gson.JsonArray; + +/** + * Removes all scope declaration annotations with the exception of the currently + * active one. + */ +public class RemoveScopeDeclarationAnnotationsQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveScopeDeclarationAnnotationsQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.CDIRemoveScopeDeclarationAnnotationsButOne; + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + + if (parentType != null) { + JsonArray diagnosticData = (JsonArray) diagnostic.getData(); + List annotations = IntStream.range(0, diagnosticData.size()).mapToObj(idx -> diagnosticData.get(idx).getAsString()).collect(Collectors.toList()); + + annotations.remove(Constants.PRODUCES); + for (String annotation : annotations) { + List resultingAnnotations = new ArrayList<>(annotations); + resultingAnnotations.remove(annotation); + + createCodeAction(diagnostic, context, parentType, codeActions, + resultingAnnotations.toArray(new String[] {})); + } + } + + return codeActions; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/Utils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/Utils.java new file mode 100644 index 00000000..7ddb26f2 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/cdi/Utils.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2021, 2023 IBM Corporation and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.cdi; + +import static org.eclipse.lsp4jakarta.jdt.internal.cdi.Constants.SCOPES; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.jdt.core.IAnnotatable; +import org.eclipse.jdt.core.IType; + +/** + * CDI Utilities. + */ +public class Utils { + /** + * Detects if a class is a managed bean by looking for a bean defining + * annotation. + * + * @param type the type representing the potential bean class + * @return true if the class has a bean defining annotation. + */ + static boolean isManagedBean(IType type) { + return getScopeAnnotations(type, SCOPES).size() > 0; + } + + /** + * Returns the list of recognised defining annotations applied to a + * class. + * + * @param type the type representing the class + * @param scopes list of defining annotations + * @return list of recognised defining annotations applied to a class + */ + public static List getScopeAnnotations(IAnnotatable type, Set scopes) { + try { + // Construct a stream of only the annotations applied to the type that are also + // recognised annotations found in scopes. + return Arrays.stream(type.getAnnotations()).map(annotation -> annotation.getElementName()).filter(scopes::contains).distinct().collect(Collectors.toList()); + + } catch (Exception e) { + return Collections. emptyList(); + } + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ProjectLabelRegistry.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ProjectLabelRegistry.java new file mode 100644 index 00000000..7bf57fc0 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ProjectLabelRegistry.java @@ -0,0 +1,95 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.lsp4jakarta.jdt.core.IProjectLabelProvider; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; +import org.eclipse.lsp4jakarta.jdt.core.ProjectLabelDefinition; + +/** + * Registry to hold the extension point + * "org.eclipse.lsp4mp.jdt.core.projectLabelProviders". + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/ProjectLabelRegistry.java + * + */ +public class ProjectLabelRegistry { + + private static final String CLASS_ATTR = "class"; + + private static final String EXTENSION_PROJECT_LABEL_PROVIDERS = "projectLabelProviders"; + + private static final Logger LOGGER = Logger.getLogger(ProjectLabelRegistry.class.getName()); + + private static final ProjectLabelRegistry INSTANCE = new ProjectLabelRegistry(); + + private final List projectLabelDefinitions; + + private boolean projectDefinitionsLoaded; + + public static ProjectLabelRegistry getInstance() { + return INSTANCE; + } + + public ProjectLabelRegistry() { + projectDefinitionsLoaded = false; + projectLabelDefinitions = new ArrayList<>(); + } + + /** + * Returns a list of project label definitions + * + * @return a list of project label definitions + */ + public List getProjectLabelDefinitions() { + loadProjectLabelDefinitions(); + return projectLabelDefinitions; + } + + private synchronized void loadProjectLabelDefinitions() { + if (projectDefinitionsLoaded) + return; + + // Immediately set the flag, as to ensure that this method is never + // called twice + projectDefinitionsLoaded = true; + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IConfigurationElement[] cf = registry.getConfigurationElementsFor(JakartaCorePlugin.PLUGIN_ID, + EXTENSION_PROJECT_LABEL_PROVIDERS); + addProjectLabelDefinition(cf); + } + + private void addProjectLabelDefinition(IConfigurationElement[] cf) { + for (IConfigurationElement ce : cf) { + try { + IProjectLabelProvider provider = (IProjectLabelProvider) ce.createExecutableExtension(CLASS_ATTR); + synchronized (projectLabelDefinitions) { + this.projectLabelDefinitions.add(new ProjectLabelDefinition(provider)); + } + } catch (Throwable t) { + LOGGER.log(Level.SEVERE, "Error while collecting project label extension contributions", t); + } + } + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/AbstractJavaFeatureDefinition.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/AbstractJavaFeatureDefinition.java new file mode 100644 index 00000000..b3484ecf --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/AbstractJavaFeatureDefinition.java @@ -0,0 +1,41 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.java; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; + +/** + * Wrapper class around a java feature participant. + */ +public abstract class AbstractJavaFeatureDefinition { + + private static final String CLASS_ATTR = "class"; + + private final IConfigurationElement element; + + private T participant; + + public AbstractJavaFeatureDefinition(IConfigurationElement element) { + this.element = element; + } + + protected T getParticipant() throws CoreException { + if (participant == null) { + participant = (T) element.createExecutableExtension(CLASS_ATTR); + } + return participant; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ChangeUtil.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/ChangeUtil.java similarity index 79% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ChangeUtil.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/ChangeUtil.java index 4bdd2e12..5d3821fb 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/ChangeUtil.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/ChangeUtil.java @@ -11,8 +11,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core; +package org.eclipse.lsp4jakarta.jdt.internal.core.java; import java.util.ArrayList; import java.util.List; @@ -28,28 +27,25 @@ import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; - +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.text.edits.TextEdit; -/** - * Converts Change to WorkspaceEdit for further consumption. Reused from - * https://github.com/eclipse/lsp4mp/blob/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/ChangeUtil.java - * - */ public class ChangeUtil { private static final Range ZERO_RANGE = new Range(new Position(), new Position()); /** + * Converts Change to WorkspaceEdit for further consumption. + * * @param change {@link Change} to convert * @return {@link WorkspaceEdit} converted from the change * @throws CoreException */ - public static WorkspaceEdit convertToWorkspaceEdit(Change change, String uri, JDTUtils utils, - boolean resourceOperationSupported) throws CoreException { + public static WorkspaceEdit convertToWorkspaceEdit(Change change, String uri, IJDTUtils utils, + boolean resourceOperationSupported) throws CoreException { WorkspaceEdit edit = new WorkspaceEdit(); if (change instanceof CompositeChange) { convertCompositeChange((CompositeChange) change, edit, uri, utils, resourceOperationSupported); @@ -59,8 +55,8 @@ public static WorkspaceEdit convertToWorkspaceEdit(Change change, String uri, JD return edit; } - private static void convertCompositeChange(CompositeChange change, WorkspaceEdit edit, String uri, JDTUtils utils, - boolean resourceOperationSupported) throws CoreException { + private static void convertCompositeChange(CompositeChange change, WorkspaceEdit edit, String uri, IJDTUtils utils, + boolean resourceOperationSupported) throws CoreException { Change[] changes = change.getChildren(); for (Change ch : changes) { if (ch instanceof CompositeChange) { @@ -71,8 +67,8 @@ private static void convertCompositeChange(CompositeChange change, WorkspaceEdit } } - private static void convertSingleChange(Change change, WorkspaceEdit edit, String uri, JDTUtils utils, - boolean resourceOperationSupported) throws CoreException { + private static void convertSingleChange(Change change, WorkspaceEdit edit, String uri, IJDTUtils utils, + boolean resourceOperationSupported) throws CoreException { if (change instanceof CompositeChange) { return; } @@ -85,8 +81,8 @@ private static void convertSingleChange(Change change, WorkspaceEdit edit, Strin // } } - private static void convertTextChange(TextChange textChange, WorkspaceEdit rootEdit, String uri, JDTUtils utils, - boolean resourceOperationSupported) { + private static void convertTextChange(TextChange textChange, WorkspaceEdit rootEdit, String uri, IJDTUtils utils, + boolean resourceOperationSupported) { Object modifiedElement = textChange.getModifiedElement(); if (!(modifiedElement instanceof IJavaElement)) { return; @@ -96,13 +92,12 @@ private static void convertTextChange(TextChange textChange, WorkspaceEdit rootE if (textEdits == null) { return; } - ICompilationUnit compilationUnit = (ICompilationUnit) ((IJavaElement) modifiedElement) - .getAncestor(IJavaElement.COMPILATION_UNIT); + ICompilationUnit compilationUnit = (ICompilationUnit) ((IJavaElement) modifiedElement).getAncestor(IJavaElement.COMPILATION_UNIT); convertTextEdit(rootEdit, compilationUnit, textEdits, uri, utils, resourceOperationSupported); } private static void convertTextEdit(WorkspaceEdit root, ICompilationUnit unit, TextEdit edit, String uri, - JDTUtils utils, boolean resourceOperationSupported) { + IJDTUtils utils, boolean resourceOperationSupported) { if (edit == null) { return; } @@ -142,12 +137,12 @@ public static boolean hasChanges(WorkspaceEdit edit) { } boolean hasChanges = false; // @formatter:off - if ((edit.getChanges() != null && !edit.getChanges().isEmpty())) { - hasChanges = edit.getChanges().values().stream() - .filter(changes -> changes != null && !changes.isEmpty() && hasChanges(changes)).findFirst() - .isPresent(); - } - // @formatter:on + if ((edit.getChanges() != null && !edit.getChanges().isEmpty())) { + hasChanges = edit.getChanges().values().stream() + .filter(changes -> changes != null && !changes.isEmpty() && hasChanges(changes)).findFirst() + .isPresent(); + } + // @formatter:on return hasChanges; } @@ -160,8 +155,8 @@ public static boolean hasChanges(List edits) { return false; } // @formatter:off - return edits.stream().filter(edit -> (!edit.getRange().equals(ZERO_RANGE) || !"".equals(edit.getNewText()))) - .findFirst().isPresent(); - // @formatter:on + return edits.stream().filter(edit -> (!edit.getRange().equals(ZERO_RANGE) || !"".equals(edit.getNewText()))) + .findFirst().isPresent(); + // @formatter:on } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/JavaFeaturesRegistry.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/JavaFeaturesRegistry.java new file mode 100644 index 00000000..82086b47 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/JavaFeaturesRegistry.java @@ -0,0 +1,152 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.java; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.codeaction.JavaCodeActionDefinition; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.completion.JavaCompletionDefinition; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.diagnostics.JavaDiagnosticsDefinition; + +/** + * Registry to hold the extension point + * "org.eclipse.lsp4mp.jdt.core.javaFeaturesParticipants". + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/JavaFeaturesRegistry.java + * + */ +public class JavaFeaturesRegistry { + + private static final String EXTENSION_JAVA_FEATURE_PARTICIPANTS = "javaFeatureParticipants"; + + private static final String CODEACTION_ELT = "codeAction"; + private static final String COMPLETION_ELT = "completion"; + private static final String DIAGNOSTICS_ELT = "diagnostics"; + + private static final Logger LOGGER = Logger.getLogger(JavaFeaturesRegistry.class.getName()); + + private static final JavaFeaturesRegistry INSTANCE = new JavaFeaturesRegistry(); + + private final List javaCompletionDefinitions; + + private final List javaDiagnosticsDefinitions; + + private final List javaCodeActionDefinitions; + + private boolean javaFeatureDefinitionsLoaded; + + public static JavaFeaturesRegistry getInstance() { + return INSTANCE; + } + + public JavaFeaturesRegistry() { + javaFeatureDefinitionsLoaded = false; + + javaCompletionDefinitions = new ArrayList<>(); + javaDiagnosticsDefinitions = new ArrayList<>(); + javaCodeActionDefinitions = new ArrayList<>(); + } + + /** + * Returns a list of completion definition + * + * @return a list of completion definition + */ + public List getJavaCompletionDefinitions() { + loadJavaFeatureDefinitions(); + return javaCompletionDefinitions; + } + + private synchronized void loadJavaFeatureDefinitions() { + if (javaFeatureDefinitionsLoaded) + return; + + // Immediately set the flag, as to ensure that this method is never + // called twice + javaFeatureDefinitionsLoaded = true; + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IConfigurationElement[] cf = registry.getConfigurationElementsFor(JakartaCorePlugin.PLUGIN_ID, + EXTENSION_JAVA_FEATURE_PARTICIPANTS); + addJavaFeatureDefinition(cf); + } + + private void addJavaFeatureDefinition(IConfigurationElement[] cf) { + for (IConfigurationElement ce : cf) { + try { + createAndAddDefinition(ce); + } catch (Throwable t) { + LOGGER.log(Level.SEVERE, "Error while collecting java features extension contributions", t); + } + } + } + + private void createAndAddDefinition(IConfigurationElement ce) throws CoreException { + switch (ce.getName()) { + case CODEACTION_ELT: { + JavaCodeActionDefinition definition = new JavaCodeActionDefinition(ce); + synchronized (javaCodeActionDefinitions) { + javaCodeActionDefinitions.add(definition); + } + break; + } + case COMPLETION_ELT: { + JavaCompletionDefinition definition = new JavaCompletionDefinition(ce); + synchronized (javaCompletionDefinitions) { + javaCompletionDefinitions.add(definition); + } + break; + } + case DIAGNOSTICS_ELT: { + JavaDiagnosticsDefinition definition = new JavaDiagnosticsDefinition(ce); + synchronized (javaDiagnosticsDefinitions) { + javaDiagnosticsDefinitions.add(definition); + } + break; + } + default: + } + } + + /** + * Returns a list of diagnostics definition. + * + * @return a list of diagnostics definition. + */ + public List getJavaDiagnosticsDefinitions() { + loadJavaFeatureDefinitions(); + return javaDiagnosticsDefinitions; + } + + /** + * Returns a list of code action definition. + * + * @return a list of code action definition. + */ + public List getJavaCodeActionDefinitions(String codeActionKind) { + loadJavaFeatureDefinitions(); + return javaCodeActionDefinitions.stream().filter(definition -> codeActionKind.startsWith(definition.getKind())).collect(Collectors.toList()); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/TextEditConverter.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/TextEditConverter.java similarity index 95% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/TextEditConverter.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/TextEditConverter.java index d1d97e6f..ea9a0cba 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/TextEditConverter.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/TextEditConverter.java @@ -10,8 +10,7 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core; +package org.eclipse.lsp4jakarta.jdt.internal.core.java; import java.util.ArrayList; import java.util.List; @@ -27,6 +26,7 @@ import org.eclipse.jface.text.Document; import org.eclipse.lsp4j.TextDocumentEdit; import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; import org.eclipse.text.edits.CopySourceEdit; import org.eclipse.text.edits.CopyTargetEdit; import org.eclipse.text.edits.DeleteEdit; @@ -42,10 +42,9 @@ /** * Converts an {@link org.eclipse.text.edits.TextEdit} to - * {@link org.eclipse.lsp4j.TextEdit} Reused from - * https://github.com/eclipse/lsp4mp/blob/b88710cc54170844717f655b9bff8bb4c4649a8d/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/TextEditConverter.java + * {@link org.eclipse.lsp4j.TextEdit} * - * @author credit to Gorkem Ercan + * @author Gorkem Ercan * */ public class TextEditConverter extends TextEditVisitor { @@ -58,9 +57,9 @@ public class TextEditConverter extends TextEditVisitor { private final String uri; - private final JDTUtils utils; + private final IJDTUtils utils; - public TextEditConverter(ICompilationUnit unit, TextEdit edit, String uri, JDTUtils utils) { + public TextEditConverter(ICompilationUnit unit, TextEdit edit, String uri, IJDTUtils utils) { this.source = edit; this.converted = new ArrayList<>(); if (unit == null) { @@ -86,7 +85,7 @@ public TextDocumentEdit convertToTextDocumentEdit(int version) { /* * (non-Javadoc) - * + * * @see org.eclipse.text.edits.TextEditVisitor#visit(org.eclipse.text.edits. * InsertEdit) */ @@ -105,7 +104,7 @@ public boolean visit(InsertEdit edit) { /* * (non-Javadoc) - * + * * @see org.eclipse.text.edits.TextEditVisitor#visit(org.eclipse.text.edits. * CopySourceEdit) */ @@ -133,7 +132,7 @@ public boolean visit(CopySourceEdit edit) { /* * (non-Javadoc) - * + * * @see org.eclipse.text.edits.TextEditVisitor#visit(org.eclipse.text.edits. * DeleteEdit) */ @@ -152,7 +151,7 @@ public boolean visit(DeleteEdit edit) { /* * (non-Javadoc) - * + * * @see org.eclipse.text.edits.TextEditVisitor#visit(org.eclipse.text.edits. * MultiTextEdit) */ @@ -175,7 +174,7 @@ public boolean visit(MultiTextEdit edit) { /* * (non-Javadoc) - * + * * @see org.eclipse.text.edits.TextEditVisitor#visit(org.eclipse.text.edits. * ReplaceEdit) */ @@ -236,7 +235,7 @@ public boolean visit(MoveSourceEdit edit) { // original contenxt. // See issue#https://github.com/redhat-developer/vscode-java/issues/253 if (edit.getParent() != null && edit.getTargetEdit() != null - && edit.getParent().equals(edit.getTargetEdit().getParent())) { + && edit.getParent().equals(edit.getTargetEdit().getParent())) { org.eclipse.lsp4j.TextEdit te = new org.eclipse.lsp4j.TextEdit(); te.setNewText(""); te.setRange(utils.toRange(compilationUnit, edit.getOffset(), edit.getLength())); diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/codeaction/CodeActionHandler.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/codeaction/CodeActionHandler.java new file mode 100644 index 00000000..0533fec1 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/codeaction/CodeActionHandler.java @@ -0,0 +1,223 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.java.codeaction; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.manipulation.CoreASTProvider; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionContext; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.JavaFeaturesRegistry; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.corrections.DiagnosticsHelper; + +/** + * Code action handler. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/codeaction/CodeActionHandler.java + * + * @author Angelo ZERR + * + */ +public class CodeActionHandler { + + /** + * Returns all the code actions applicable for the context given by the + * parameters. + * + * The workspace edit will be resolved if code action resolve isn't supported. + * Otherwise it will be null. + * + * @param params the parameters for code actions + * @param utils the JDT utils + * @param monitor the progress monitor + * @return all the code actions applicable for the context given by the + * parameters + */ + public List codeAction(JakartaJavaCodeActionParams params, IJDTUtils utils, + IProgressMonitor monitor) { + // Get the compilation unit + String uri = params.getUri(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + if (unit == null) { + return Collections.emptyList(); + } + + // Prepare the code action invocation context + int start = DiagnosticsHelper.getStartOffset(unit, params.getRange(), utils); + int end = DiagnosticsHelper.getEndOffset(unit, params.getRange(), utils); + JavaCodeActionContext context = new JavaCodeActionContext(unit, start, end - start, utils, params); + context.setASTRoot(getASTRoot(unit, monitor)); + + // Collect the available code action kinds + List codeActionKinds = new ArrayList<>(); + if (params.getContext().getOnly() != null && !params.getContext().getOnly().isEmpty()) { + codeActionKinds.addAll(params.getContext().getOnly()); + } else { + List defaultCodeActionKinds = Arrays.asList(CodeActionKind.QuickFix); + codeActionKinds.addAll(defaultCodeActionKinds); + } + + List codeActions = new ArrayList<>(); + Map> forDiagnostics = new HashMap<>(); + + // Loop for each code action kinds to process the proper code actions + for (String codeActionKind : codeActionKinds) { + // Get list of code action definition for the given kind + List codeActionDefinitions = JavaFeaturesRegistry.getInstance().getJavaCodeActionDefinitions(codeActionKind).stream().filter(definition -> definition.isAdaptedForCodeAction(context, + monitor)).collect(Collectors.toList()); + if (codeActionDefinitions != null) { + // Loop for each code action definition + for (JavaCodeActionDefinition definition : codeActionDefinitions) { + String forDiagnostic = definition.getTargetDiagnostic(); + if (forDiagnostic != null) { + // The code action definition is for a given diagnostic code (QuickFix), store + // it + List definitionsFor = forDiagnostics.get(forDiagnostic); + if (definitionsFor == null) { + definitionsFor = new ArrayList<>(); + forDiagnostics.put(forDiagnostic, definitionsFor); + } + definitionsFor.add(definition); + } else { + // Collect the code actions + codeActions.addAll(definition.getCodeActions(context, null, monitor)); + } + } + } + } + + if (!forDiagnostics.isEmpty()) { + // It exists code action to fix diagnostics, loop for each diagnostics + params.getContext().getDiagnostics().forEach(diagnostic -> { + String code = getCode(diagnostic); + if (code != null) { + // Try to get code action definition registered with the "for" source#code + String key = diagnostic.getSource() + "#" + code; + List definitionsFor = forDiagnostics.get(key); + if (definitionsFor == null) { + // Try to get code action definition registered with the "for" code + definitionsFor = forDiagnostics.get(code); + } + if (definitionsFor != null) { + for (JavaCodeActionDefinition definition : definitionsFor) { + // Collect the code actions to fix the given diagnostic + codeActions.addAll(definition.getCodeActions(context, diagnostic, monitor)); + } + } + } + }); + } + if (!params.isResolveSupported()) { + List resolvedCodeActions = codeActions.stream().map(codeAction -> { + if (codeAction.getEdit() != null || codeAction.getCommand() != null) { + // CodeAction is already resolved + // (eg. command to update settings to ignore a property from validation) + return codeAction; + } + return this.resolveCodeAction(codeAction, utils, monitor); + }).collect(Collectors.toList()); + + ExtendedCodeAction.sort(resolvedCodeActions); + return resolvedCodeActions; + } + // sort code actions by relevant + ExtendedCodeAction.sort(codeActions); + return codeActions; + } + + /** + * Returns the given unresolved CodeAction with the workspace edit resolved. + * + * @param unresolved the unresolved CodeAction + * @param utils the JDT utils + * @param monitor the progress monitor + * @return the given unresolved CodeAction with the workspace edit resolved + */ + public CodeAction resolveCodeAction(CodeAction unresolved, IJDTUtils utils, IProgressMonitor monitor) { + CodeActionResolveData data = (CodeActionResolveData) unresolved.getData(); + String participantId = data.getParticipantId(); + String uri = data.getDocumentUri(); + + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + if (unit == null) { + return null; + } + + int start = DiagnosticsHelper.getStartOffset(unit, data.getRange(), utils); + int end = DiagnosticsHelper.getEndOffset(unit, data.getRange(), utils); + + var params = new JakartaJavaCodeActionParams(); + params.setContext(new CodeActionContext(unresolved.getDiagnostics() == null ? Collections.emptyList() : unresolved.getDiagnostics())); + params.setResourceOperationSupported(data.isResourceOperationSupported()); + params.setCommandConfigurationUpdateSupported(data.isCommandConfigurationUpdateSupported()); + params.setRange(data.getRange()); + params.setTextDocument(new VersionedTextDocumentIdentifier(uri, null)); + + JavaCodeActionResolveContext context = new JavaCodeActionResolveContext(unit, start, end - start, utils, params, unresolved); + context.setASTRoot(getASTRoot(unit, monitor)); + + IJavaCodeActionParticipant participant = JavaFeaturesRegistry.getInstance().getJavaCodeActionDefinitions(unresolved.getKind()).stream().filter(definition -> participantId.equals(definition.getParticipantId())).findFirst().orElse(null); + return participant.resolveCodeAction(context); + } + + private static CompilationUnit getASTRoot(ICompilationUnit unit, IProgressMonitor monitor) { + return CoreASTProvider.getInstance().getAST(unit, CoreASTProvider.WAIT_YES, monitor); + } + + private static String getCode(Diagnostic diagnostic) { + Object code = null; + try { + Field f = diagnostic.getClass().getDeclaredField("code"); + f.setAccessible(true); + code = f.get(diagnostic); + } catch (Exception e) { + e.printStackTrace(); + } + return getCodeString(code); + } + + private static String getCodeString(Object codeObject) { + if (codeObject instanceof String) { + return ((String) codeObject); + } + @SuppressWarnings("unchecked") + Either code = (Either) codeObject; + if (code == null || code.isRight()) { + return null; + } + return code.getLeft(); + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/codeaction/JavaCodeActionDefinition.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/codeaction/JavaCodeActionDefinition.java new file mode 100644 index 00000000..13beabf0 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/codeaction/JavaCodeActionDefinition.java @@ -0,0 +1,120 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.java.codeaction; + +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.InvalidRegistryObjectException; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.AbstractJavaFeatureDefinition; + +/** + * Wrapper class around {@link IJavaCodeActionParticipant} participants. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/codeaction/JavaCodeActionDefinition.java + */ +public class JavaCodeActionDefinition extends AbstractJavaFeatureDefinition implements IJavaCodeActionParticipant { + + private static final Logger LOGGER = Logger.getLogger(JavaCodeActionDefinition.class.getName()); + private static final String KIND_ATTR = "kind"; + private static final String TARGET_DIAGNOSTIC_ATTR = "targetDiagnostic"; + + private final String kind; + private final String targetDiagnostic; + + public JavaCodeActionDefinition(IConfigurationElement element) { + super(element); + this.kind = getKind(element); + this.targetDiagnostic = element.getAttribute(TARGET_DIAGNOSTIC_ATTR); + } + + private static String getKind(IConfigurationElement element) throws InvalidRegistryObjectException { + String kind = element.getAttribute(KIND_ATTR); + return !StringUtils.isEmpty(kind) ? kind : CodeActionKind.QuickFix; + } + + @Override + public String getParticipantId() { + try { + return getParticipant().getParticipantId(); + } catch (CoreException e) { + LOGGER.log(Level.WARNING, "Unable to get CodeAction participant", e); + return ""; + } + } + + @Override + public boolean isAdaptedForCodeAction(JavaCodeActionContext context, IProgressMonitor monitor) { + try { + return getParticipant().isAdaptedForCodeAction(context, monitor); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while calling isAdaptedForCodeAction", e); + return false; + } + } + + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) { + try { + List codeActions = getParticipant().getCodeActions(context, diagnostic, monitor); + return codeActions != null ? codeActions : Collections.emptyList(); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while calling getCodeActions", e); + return Collections.emptyList(); + } + } + + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + try { + return getParticipant().resolveCodeAction(context); + } catch (CoreException e) { + LOGGER.log(Level.WARNING, "Unable to get CodeAction participant", e); + return context.getUnresolved(); + } + } + + /** + * Returns the code action kind. + * + * @return the code action kind. + */ + public String getKind() { + return kind; + } + + /** + * Returns the target diagnostic and null otherwise. + * + * @return the target diagnostic and null otherwise. + */ + public String getTargetDiagnostic() { + return targetDiagnostic; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/completion/JavaCompletionDefinition.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/completion/JavaCompletionDefinition.java new file mode 100644 index 00000000..25e5137e --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/completion/JavaCompletionDefinition.java @@ -0,0 +1,67 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.java.completion; + +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4jakarta.jdt.core.java.completion.IJavaCompletionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.completion.JavaCompletionContext; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.AbstractJavaFeatureDefinition; + +/** + * Wrap the completion participant in try/catch + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/completion/JavaCompletionDefinition.java + * + * @author datho7561 + */ +public class JavaCompletionDefinition extends AbstractJavaFeatureDefinition implements IJavaCompletionParticipant { + + private static final Logger LOGGER = Logger.getLogger(JavaCompletionDefinition.class.getName()); + + public JavaCompletionDefinition(IConfigurationElement element) { + super(element); + } + + // -------------- Completion + + @Override + public boolean isAdaptedForCompletion(JavaCompletionContext context, IProgressMonitor monitor) { + try { + return getParticipant().isAdaptedForCompletion(context, monitor); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while calling isAdaptedForCompletion", e); + return false; + } + } + + @Override + public List collectCompletionItems(JavaCompletionContext context, + IProgressMonitor monitor) { + try { + List completionItems = getParticipant().collectCompletionItems(context, monitor); + return completionItems != null ? completionItems : Collections.emptyList(); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while calling collectCompletionItems", e); + return Collections.emptyList(); + } + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/corrections/DiagnosticsHelper.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/corrections/DiagnosticsHelper.java new file mode 100644 index 00000000..88d86e84 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/corrections/DiagnosticsHelper.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2017, 2023 Red Hat Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.java.corrections; + +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; + +/** + * Helper methods for {@link Diagnostic} + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/corrections/DiagnosticsHelper.java + * + * @author Gorkem Ercan + * + */ +public class DiagnosticsHelper { + + /** + * Gets the end offset for the diagnostic. + * + * @param unit + * @param range + * @return starting offset or negative value if can not be determined + */ + public static int getEndOffset(ICompilationUnit unit, Range range, IJDTUtils utils) { + try { + return utils.toOffset(unit.getBuffer(), range.getEnd().getLine(), range.getEnd().getCharacter()); + } catch (JavaModelException e) { + return -1; + } + } + + /** + * Gets the start offset for the diagnostic. + * + * @param unit + * @param range + * @return starting offset or negative value if can not be determined + */ + public static int getStartOffset(ICompilationUnit unit, Range range, IJDTUtils utils) { + try { + return utils.toOffset(unit.getBuffer(), range.getStart().getLine(), range.getStart().getCharacter()); + } catch (JavaModelException e) { + return -1; + } + } + + /** + * Returns the length of the diagnostic + * + * @param unit + * @param diagnostic + * @return length of the diagnostics range. + */ + public static int getLength(ICompilationUnit unit, Range range, IJDTUtils utils) { + int start = DiagnosticsHelper.getStartOffset(unit, range, utils); + int end = DiagnosticsHelper.getEndOffset(unit, range, utils); + return end - start; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/diagnostics/JavaDiagnosticsDefinition.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/diagnostics/JavaDiagnosticsDefinition.java new file mode 100644 index 00000000..5583edd2 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/java/diagnostics/JavaDiagnosticsDefinition.java @@ -0,0 +1,80 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.java.diagnostics; + +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.internal.core.java.AbstractJavaFeatureDefinition; + +/** + * Wrapper class around java participants {@link IJavaDiagnosticsParticipant}. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/java/diagnostics/JavaDiagnosticsDefinition.java + */ +public class JavaDiagnosticsDefinition extends AbstractJavaFeatureDefinition implements IJavaDiagnosticsParticipant { + private static final Logger LOGGER = Logger.getLogger(JavaDiagnosticsDefinition.class.getName()); + + public JavaDiagnosticsDefinition(IConfigurationElement element) { + super(element); + } + + // -------------- Diagnostics + + @Override + public boolean isAdaptedForDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) { + try { + return getParticipant().isAdaptedForDiagnostics(context, monitor); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while calling isAdaptedForDiagnostics", e); + return false; + } + } + + @Override + public void beginDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) { + try { + getParticipant().beginDiagnostics(context, monitor); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while calling beginDiagnostics", e); + } + } + + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) { + try { + return getParticipant().collectDiagnostics(context, monitor); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while collecting diagnostics", e); + return null; + } + } + + @Override + public void endDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) { + try { + getParticipant().endDiagnostics(context, monitor); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while calling endDiagnostics", e); + } + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/AbstractJakartaDelegateCommandHandler.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/AbstractJakartaDelegateCommandHandler.java new file mode 100644 index 00000000..815d25ab --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/AbstractJakartaDelegateCommandHandler.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.ls; + +import java.util.logging.Logger; + +import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; + +/** + * Abstract class for MicroProfile JDT LS command handler + * + * @author Angelo ZERR + * + */ +public abstract class AbstractJakartaDelegateCommandHandler implements IDelegateCommandHandler { + + private static final Logger LOGGER = Logger.getLogger(AbstractJakartaDelegateCommandHandler.class.getName()); + + private static boolean initialized; + + public AbstractJakartaDelegateCommandHandler() { + initialize(); + } + + /** + * Add MicroProfile properties changed listener if needed. + */ + private static synchronized void initialize() { + if (initialized) { + return; + } + initialized = true; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/ArgumentUtils.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/ArgumentUtils.java index 221e8c8a..93d22743 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/ArgumentUtils.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/ArgumentUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2019 Red Hat Inc. and others. +* Copyright (c) 2019, 2023 Red Hat Inc. and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -17,25 +17,26 @@ import java.util.Map; import java.util.stream.Collectors; -import com.google.gson.Gson; -import com.google.gson.JsonObject; - import org.eclipse.lsp4j.CodeActionContext; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextDocumentIdentifier; +import com.google.gson.Gson; +import com.google.gson.JsonObject; + /** * Arguments utilities. * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/ls/ArgumentUtils.java + * * @author Angelo ZERR - * - * Helper methods re-used from - * https://github.com/eclipse/lsp4mp/blob/5cf8de4874bd0ec9904c411c871f4813b024ac96/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/ls/ArgumentUtils.java + * */ public class ArgumentUtils { - + private static final String DATA_PROPERTY = "data"; private static final String SOURCE_PROPERTY = "source"; private static final String MESSAGE_PROPERTY = "message"; @@ -47,7 +48,6 @@ public class ArgumentUtils { private static final String CHARACTER_PROPERTY = "character"; private static final String LINE_PROPERTY = "line"; private static final String URI_PROPERTY = "uri"; - public static Map getFirst(List arguments) { return arguments.isEmpty() ? null : (Map) arguments.get(0); @@ -122,14 +122,37 @@ public static CodeActionContext getCodeActionContext(Map obj, St List only = null; return new CodeActionContext(diagnostics, only); } - + + /** + * Returns the child if it exists and is an object, and null otherwise + * + * @param obj the object to get the child of + * @param key the key of the child + * @return the child if it exists and is an object, and null otherwise + */ + public static Map getObject(Map obj, String key) { + Object child = obj.get(key); + if (child != null && child instanceof Map) { + return (Map) child; + } + return null; + } + + /** + * Returns the child as a JsonObject if it exists and is an object, and null + * otherwise + * + * @param obj the object to get the child of + * @param key the key of the child + * @return the child as a JsonObject if it exists and is an object, and null + * otherwise + */ public static JsonObject getObjectAsJson(Map obj, String key) { Object child = obj.get(key); if (child != null && child instanceof Map) { Gson gson = new Gson(); - return (JsonObject) gson.toJsonTree(obj); + return (JsonObject) gson.toJsonTree(child); } return null; } - -} \ No newline at end of file +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JDTUtilsLSImpl.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JDTUtilsLSImpl.java new file mode 100644 index 00000000..c0a4832d --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JDTUtilsLSImpl.java @@ -0,0 +1,141 @@ +/******************************************************************************* +* Copyright (c) 2019, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.ls; + +import java.io.Reader; +import java.util.Scanner; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IOpenable; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.ResourceUtils; +import org.eclipse.jdt.ls.core.internal.handlers.DocumentLifeCycleHandler; +import org.eclipse.jdt.ls.core.internal.handlers.JsonRpcHelpers; +import org.eclipse.jdt.ls.core.internal.javadoc.JavadocContentAccess; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.commons.DocumentFormat; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; + +/** + * {@link IJDTUtils} implementation with JDT S {@link JDTUtils}. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/ls/JDTUtilsLSImpl.java + * + * @author Angelo ZERR + * + */ +public class JDTUtilsLSImpl implements IJDTUtils { + + private static final int COMPILATION_UNIT_UPDATE_TIMEOUT = 3000; + + private static final IJDTUtils INSTANCE = new JDTUtilsLSImpl(); + + public static IJDTUtils getInstance() { + return INSTANCE; + } + + private JDTUtilsLSImpl() {} + + @Override + public IFile findFile(String uriString) { + return JDTUtils.findFile(uriString); + } + + @Override + public ICompilationUnit resolveCompilationUnit(String uriString) { + ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uriString); + try { + // Give underlying resource time to catch up + // (timeout at COMPILATION_UNIT_UPDATE_TIMEOUT milliseconds). + long endTime = System.currentTimeMillis() + COMPILATION_UNIT_UPDATE_TIMEOUT; + while (!unit.isConsistent() && System.currentTimeMillis() < endTime) { + } + } catch (JavaModelException e) { + } + return unit; + } + + @Override + public IClassFile resolveClassFile(String uriString) { + return JDTUtils.resolveClassFile(uriString); + } + + @Override + public boolean isHiddenGeneratedElement(IJavaElement element) { + return JDTUtils.isHiddenGeneratedElement(element); + } + + @Override + public Range toRange(IOpenable openable, int offset, int length) throws JavaModelException { + return JDTUtils.toRange(openable, offset, length); + } + + @Override + public String toClientUri(String uri) { + return ResourceUtils.toClientUri(uri); + } + + @Override + public String toUri(ITypeRoot typeRoot) { + return JDTUtils.toUri(typeRoot); + } + + @Override + public void waitForLifecycleJobs(IProgressMonitor monitor) { + try { + Job.getJobManager().join(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor); + } catch (OperationCanceledException ignorable) { + // No need to pollute logs when query is cancelled + } catch (Exception e) { + JavaLanguageServerPlugin.logException(e.getMessage(), e); + } + } + + @Override + public int toOffset(IBuffer buffer, int line, int column) { + return JsonRpcHelpers.toOffset(buffer, line, column); + } + + @Override + public Location toLocation(IJavaElement element) throws JavaModelException { + return JDTUtils.toLocation(element); + } + + @Override + public String getJavadoc(IMember member, DocumentFormat documentFormat) throws JavaModelException { + boolean markdown = DocumentFormat.Markdown.equals(documentFormat); + Reader reader = markdown ? JavadocContentAccess.getMarkdownContentReader(member) : JavadocContentAccess.getPlainTextContentReader(member); + return reader != null ? toString(reader) : null; + } + + private static String toString(Reader reader) { + try (Scanner s = new java.util.Scanner(reader)) { + s.useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JakartaDelegateCommandHandlerForJava.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JakartaDelegateCommandHandlerForJava.java index abe75474..6db4e996 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JakartaDelegateCommandHandlerForJava.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JakartaDelegateCommandHandlerForJava.java @@ -1,219 +1,287 @@ -/******************************************************************************* +/******************************************************************************* * Copyright (c) 2022, 2023 IBM Corporation and others. - * + * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: + * + * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.lsp4jakarta.jdt.internal.core.ls; import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.*; -import java.util.ArrayList; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getBoolean; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getCodeActionContext; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getFirst; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getObject; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getPosition; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getRange; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getString; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getStringList; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getTextDocumentIdentifier; + import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; +import org.eclipse.jdt.ls.core.internal.JSONUtility; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionContext; +import org.eclipse.lsp4j.CompletionList; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.jsonrpc.CompletableFutures; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextKind; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionResult; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsSettings; import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; -import org.eclipse.lsp4jakarta.jdt.core.JDTServicesManager; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.jdt.core.PropertiesManagerForJava; /** * Delegate Command Handler for LSP4Jakarta JDT LS extension commands */ -public class JakartaDelegateCommandHandlerForJava implements IDelegateCommandHandler { +public class JakartaDelegateCommandHandlerForJava extends AbstractJakartaDelegateCommandHandler { - private static final String JAVA_CODEACTION_COMMAND_ID = "jakarta/java/codeaction"; - private static final String JAVA_CLASSPATH_COMMAND_ID = "jakarta/java/classpath"; - private static final String JAVA_CURSORCONTEXT_COMMAND_ID = "jakarta/java/cursorcontext"; + private static final String JAVA_CODEACTION_COMMAND_ID = "jakarta/java/codeAction"; + private static final String JAVA_CODEACTION_RESOLVE_COMMAND_ID = "jakarta/java/codeActionResolve"; + private static final String JAVA_COMPLETION_COMMAND_ID = "jakarta/java/completion"; private static final String JAVA_DIAGNOSTICS_COMMAND_ID = "jakarta/java/diagnostics"; - public JakartaDelegateCommandHandlerForJava() { - } + public JakartaDelegateCommandHandlerForJava() {} + /** + * Return the result for the given commandId + * + * @param commandId String name of command message + * @param arguments request data from the Jakarta LS + * @param monitor + * @return the resulting response object for the given commandId based on the data in argument map + * @throws Exception + */ @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception { - JavaLanguageServerPlugin - .logInfo(String.format("Executing command '%s' in LSP4Jakarta JDT LS extension", commandId)); + JavaLanguageServerPlugin.logInfo(String.format("Executing command '%s' in LSP4Jakarta JDT LS extension", commandId)); switch (commandId) { case JAVA_CODEACTION_COMMAND_ID: - return getCodeActionForJava(arguments, commandId, monitor).get(); - case JAVA_CLASSPATH_COMMAND_ID: - return getContextBasedFilter(arguments, commandId, monitor).get(); - case JAVA_CURSORCONTEXT_COMMAND_ID: - return getJavaCursorContext(arguments, commandId, monitor).get(); + return getCodeActionForJava(arguments, commandId, monitor); + case JAVA_CODEACTION_RESOLVE_COMMAND_ID: + return resolveCodeActionForJava(arguments, commandId, monitor); + case JAVA_COMPLETION_COMMAND_ID: + return getCompletionForJava(arguments, commandId, monitor); case JAVA_DIAGNOSTICS_COMMAND_ID: - return getDiagnosticsForJava(arguments, commandId, monitor).get(); + return getDiagnosticsForJava(arguments, commandId, monitor); default: throw new UnsupportedOperationException(String.format("Unsupported command '%s'!", commandId)); } } - /** - * Returns the completion items for the given arguments - * - * @param arguments JakartaClasspathParams @see - * org.eclipse.lsp4jakarta.commons.JakartaClasspathParams - * @param monitor - * @return list of completion items as CompletableFuture - */ - private CompletableFuture getContextBasedFilter(List arguments, String commandId, - IProgressMonitor monitor) { - Map obj = ArgumentUtils.getFirst(arguments); - if (obj == null) { - throw new UnsupportedOperationException( - String.format("Command '%s' must be called with one JakartaClasspathParams argument", commandId)); - } - String uri = ArgumentUtils.getString(obj, "uri"); - List snippetCtx = ArgumentUtils.getStringList(obj, "snippetCtx"); - return CompletableFutures.computeAsync((cancelChecker) -> { - return JDTServicesManager.getInstance().getExistingContextsFromClassPath(uri, snippetCtx); - }); - } - /** * Return the completion result for the given arguments * - * @param arguments file uri and position indicator - * @param commandId + * @param arguments Map of completion request data from Jakarta LS + * @param commandId String name of command message * @param monitor * @return the completion result for the given arguments * @throws JavaModelException * @throws CoreException */ - private CompletableFuture getJavaCursorContext(List arguments, String commandId, - IProgressMonitor monitor) throws JavaModelException, CoreException { - final JakartaJavaCompletionParams params = createJakartaJavaCompletionParams(arguments, commandId); - final JDTUtils utils = new JDTUtils(); - return CompletableFutures.computeAsync((cancelChecker) -> { - JavaCursorContextResult cursorContext; - try { - cursorContext = JDTServicesManager.getInstance().javaCursorContext(params, utils, monitor); - } catch (JavaModelException e) { - JavaLanguageServerPlugin - .logException(String.format("Command '%s' unable to form completion context", commandId), e); - cursorContext = new JavaCursorContextResult(JavaCursorContextKind.BEFORE_CLASS, ""); - } - return cursorContext; - }); + private static JakartaJavaCompletionResult getCompletionForJava(List arguments, String commandId, + IProgressMonitor monitor) throws JavaModelException, CoreException { + JakartaJavaCompletionParams params = createJakartaJavaCompletionParams(arguments, commandId); + CompletionList completionList = PropertiesManagerForJava.getInstance().completion(params, + JDTUtilsLSImpl.getInstance(), monitor); + JavaCursorContextResult cursorContext = PropertiesManagerForJava.getInstance().javaCursorContext(params, + JDTUtilsLSImpl.getInstance(), monitor); + return new JakartaJavaCompletionResult(completionList, cursorContext); } /** * Create the completion parameters from the given argument map * - * @param arguments - * @param commandId - * @return the completion parameters from the given argument map + * @param arguments Map of completion data from Jakarta LS + * @param commandId String name of command message + * @return the completion results parameter object based on the given argument map */ private static JakartaJavaCompletionParams createJakartaJavaCompletionParams(List arguments, - String commandId) { + String commandId) { Map obj = getFirst(arguments); if (obj == null) { throw new UnsupportedOperationException(String.format( - "Command '%s' must be called with one JakartaJavaCompletionParams argument!", commandId)); + "Command '%s' must be called with one JakartaJavaCompletionParams argument!", commandId)); } String javaFileUri = getString(obj, "uri"); if (javaFileUri == null) { throw new UnsupportedOperationException(String.format( - "Command '%s' must be called with required JakartaJavaCompletionParams.uri (java URI)!", - commandId)); + "Command '%s' must be called with required JakartaJavaCompletionParams.uri (java URI)!", + commandId)); } Position position = getPosition(obj, "position"); if (position == null) { throw new UnsupportedOperationException(String.format( - "Command '%s' must be called with required JakartaJavaCompletionParams.position (completion trigger location)!", - commandId)); + "Command '%s' must be called with required JakartaJavaCompletionParams.position (completion trigger location)!", + commandId)); } JakartaJavaCompletionParams params = new JakartaJavaCompletionParams(javaFileUri, position); return params; } /** - * Returns the publish diagnostics list for a given java file URIs. + * Returns the code action for the given Java file. * - * @param arguments JakartaDiagnosticsParams @see - * org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams + * @param arguments Map of CodeAction data from Jakarta LS + * @param commandId String name of command message * @param monitor - * @return list of diagnostics as - * CompletableFuture> + * @return the code action for the given Java file. + * @throws CoreException + * @throws JavaModelException */ - private CompletableFuture> getDiagnosticsForJava(List arguments, - String commandId, IProgressMonitor monitor) { - Map obj = ArgumentUtils.getFirst(arguments); - if (obj == null) { - throw new UnsupportedOperationException( - String.format("Command '%s' must be called with one JakartaDiagnosticsParams argument", commandId)); - } - List uri = ArgumentUtils.getStringList(obj, "uris"); - return CompletableFutures.computeAsync((cancelChecker) -> { - List publishDiagnostics = new ArrayList(); - publishDiagnostics = JDTServicesManager.getInstance().getJavaDiagnostics(uri, monitor); - return publishDiagnostics; - }); + private static List getCodeActionForJava(List arguments, String commandId, + IProgressMonitor monitor) throws JavaModelException, CoreException { + // Create java code action parameter + JakartaJavaCodeActionParams params = createJakartaJavaCodeActionParams(arguments, commandId); + // Return code action from the code action parameter + return PropertiesManagerForJava.getInstance().codeAction(params, JDTUtilsLSImpl.getInstance(), monitor); } /** - * Returns the code actions list for the given arguments - * - * @param arguments JakartaJavaCodeActionParams @see - * org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams - * @param commandId - * @param monitor - * @return list of code actions as CompletableFuture> + * Create java code action parameter from the given arguments map. + * + * @param arguments Map of code action data from Jakarta LS + * @param commandId String name of command message + * + * @return java code action parameter */ - private CompletableFuture> getCodeActionForJava(List arguments, String commandId, - IProgressMonitor monitor) { - Map obj = ArgumentUtils.getFirst(arguments); + private static JakartaJavaCodeActionParams createJakartaJavaCodeActionParams(List arguments, + String commandId) { + Map obj = getFirst(arguments); if (obj == null) { - throw new UnsupportedOperationException(String - .format("Command '%s' must be called with one JakartaJavaCodeActionParams argument", commandId)); + throw new UnsupportedOperationException(String.format( + "Command '%s' must be called with one JakartaJavaCodeActionParams argument!", commandId)); } - // reconstruct JakartaJavaCodeActionParams - TextDocumentIdentifier textDocumentIdentifier = ArgumentUtils.getTextDocumentIdentifier(obj, "textDocument"); - if (textDocumentIdentifier == null) { + TextDocumentIdentifier texdDocumentIdentifier = getTextDocumentIdentifier(obj, "textDocument"); + if (texdDocumentIdentifier == null) { throw new UnsupportedOperationException(String.format( - "Command '%s' must be called with required JakartaJavaCodeActionParams.texdDocumentIdentifier", - commandId)); + "Command '%s' must be called with required JakartaJavaCodeActionParams.texdDocumentIdentifier", + commandId)); } - Range range = ArgumentUtils.getRange(obj, "range"); - CodeActionContext context = ArgumentUtils.getCodeActionContext(obj, "context"); - boolean resourceOperationSupported = ArgumentUtils.getBoolean(obj, "resourceOperationSupported"); + Range range = getRange(obj, "range"); + CodeActionContext context = getCodeActionContext(obj, "context"); + boolean resourceOperationSupported = getBoolean(obj, "resourceOperationSupported"); + boolean commandConfigurationUpdateSupported = getBoolean(obj, "commandConfigurationUpdateSupported"); + boolean resolveSupported = getBoolean(obj, "resolveSupported"); JakartaJavaCodeActionParams params = new JakartaJavaCodeActionParams(); - params.setTextDocument(textDocumentIdentifier); + params.setTextDocument(texdDocumentIdentifier); params.setRange(range); params.setContext(context); params.setResourceOperationSupported(resourceOperationSupported); - JDTUtils utils = new JDTUtils(); - return CompletableFutures.computeAsync((cancelChecker) -> { - List codeActions = new ArrayList(); - try { - codeActions = JDTServicesManager.getInstance().getCodeAction(params, utils, monitor); - } catch (JavaModelException e) { - JavaLanguageServerPlugin - .logException(String.format("Command '%s' unable to gather code actions", commandId), e); - } - return codeActions; - }); + params.setCommandConfigurationUpdateSupported(commandConfigurationUpdateSupported); + params.setResolveSupported(resolveSupported); + return params; + } + + /** + * Return the resolveCodeAction result for the given arguments + * + * @param arguments Map of code action data from Jakarta LS + * @param commandId String name of command message + * @param monitor + * @return the resolved CodeAction result for the given map of arguments + * @throws JavaModelException + * @throws CoreException + */ + private static CodeAction resolveCodeActionForJava(List arguments, String commandId, + IProgressMonitor monitor) throws JavaModelException, CoreException { + // Create java code action parameter + CodeAction unresolved = createJakartaJavaCodeActionResolveParams(arguments, commandId); + // Return code action from the code action parameter + return PropertiesManagerForJava.getInstance().resolveCodeAction(unresolved, JDTUtilsLSImpl.getInstance(), + monitor); } + /** + * Create java resolve code action parameter from the given arguments map. + * + * @param arguments Map of code action data from Jakarta LS + * @param commandId String name of command message + * + * @return java resolved code action parameter + */ + private static CodeAction createJakartaJavaCodeActionResolveParams(List arguments, String commandId) { + Map obj = getFirst(arguments); + if (obj == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be called with one CodeAction argument!", commandId)); + } + CodeAction codeAction = JSONUtility.toModel(obj, CodeAction.class); + if (codeAction == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be called with one CodeAction argument!", commandId)); + } + CodeActionResolveData resolveData = JSONUtility.toModel(codeAction.getData(), CodeActionResolveData.class); + if (resolveData == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be called with a CodeAction that has CodeActionResolveData!", commandId)); + } + codeAction.setData(resolveData); + return codeAction; + } + + /** + * Returns the publish diagnostics list for a given java file URIs in arguments map + * + * @param arguments map of code action data from Jakarta LS + * @param monitor + * @return list of diagnostics as + * List> + */ + private static List getDiagnosticsForJava(List arguments, String commandId, + IProgressMonitor monitor) throws JavaModelException { + // Create java diagnostics parameter + JakartaJavaDiagnosticsParams params = createJakartaJavaDiagnosticsParams(arguments, commandId); + // Return diagnostics from parameter + return PropertiesManagerForJava.getInstance().diagnostics(params, JDTUtilsLSImpl.getInstance(), monitor); + } + + /** + * Returns the java diagnostics parameters from the given arguments map. + * + * @param arguments JakartaJavaDiagnosticsParams @see + * org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams + * @param commandId String name of command message + * + * @return the java diagnostics parameters + */ + private static JakartaJavaDiagnosticsParams createJakartaJavaDiagnosticsParams(List arguments, + String commandId) { + Map obj = getFirst(arguments); + if (obj == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be called with one JakartaJavaDiagnosticsParams argument!", commandId)); + } + List javaFileUri = getStringList(obj, "uris"); + if (javaFileUri == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be called with required JakartaJavaDiagnosticsParams.uri (java URIs)!", + commandId)); + } + JakartaJavaDiagnosticsSettings settings = null; + Map settingsObj = getObject(obj, "settings"); + if (settingsObj != null) { + List patterns = getStringList(settingsObj, "patterns"); + settings = new JakartaJavaDiagnosticsSettings(patterns); + } + return new JakartaJavaDiagnosticsParams(javaFileUri, settings); + } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JakartaJavaProjectDelegateCommandHandler.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JakartaJavaProjectDelegateCommandHandler.java new file mode 100644 index 00000000..33817f92 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/ls/JakartaJavaProjectDelegateCommandHandler.java @@ -0,0 +1,66 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.ls; + +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getFirst; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getString; +import static org.eclipse.lsp4jakarta.jdt.internal.core.ls.ArgumentUtils.getStringList; + +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.lsp4jakarta.commons.JakartaJavaProjectLabelsParams; +import org.eclipse.lsp4jakarta.jdt.core.ProjectLabelManager; + +/** + * Delegate command handler for Java project information + * + */ +public class JakartaJavaProjectDelegateCommandHandler extends AbstractJakartaDelegateCommandHandler { + + private static final String PROJECT_LABELS_COMMAND_ID = "jakarta/java/projectLabels"; + + public JakartaJavaProjectDelegateCommandHandler() {} + + @Override + public Object executeCommand(String commandId, List arguments, IProgressMonitor progress) throws Exception { + switch (commandId) { + case PROJECT_LABELS_COMMAND_ID: + return getProjectLabelInfo(arguments, commandId, progress); + default: + throw new UnsupportedOperationException(String.format("Unsupported command '%s'!", commandId)); + } + } + + private static Object getProjectLabelInfo(List arguments, String commandId, IProgressMonitor monitor) { + Map obj = getFirst(arguments); + if (obj == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be called with one JakartaJavaProjectLabelsParams argument!", commandId)); + } + // Get project name from the java file URI + String javaFileUri = getString(obj, "uri"); + if (javaFileUri == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be called with required JakartaJavaProjectLabelsParams.uri (java file URI)!", + commandId)); + } + List types = getStringList(obj, "types"); + JakartaJavaProjectLabelsParams params = new JakartaJavaProjectLabelsParams(); + params.setUri(javaFileUri); + params.setTypes(types); + return ProjectLabelManager.getInstance().getProjectLabelInfo(params, JDTUtilsLSImpl.getInstance(), monitor); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/providers/JakartaProjectLabelProvider.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/providers/JakartaProjectLabelProvider.java new file mode 100644 index 00000000..7679a463 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/core/providers/JakartaProjectLabelProvider.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.core.providers; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4jakarta.jdt.core.IProjectLabelProvider; +import org.eclipse.lsp4jakarta.jdt.core.utils.JDTJakartaUtils;; + +/** + * Provides a Jakarta-specific label to a project if the project is a Jakarta + * project. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/internal/core/providers/MicroProfileProjectLabelProvider.java + * + * @author Angelo ZERR + * + */ +public class JakartaProjectLabelProvider implements IProjectLabelProvider { + + public static String JAKARTA_LABEL = "jakarta"; + + @Override + public List getProjectLabels(IJavaProject project) throws JavaModelException { + if (JDTJakartaUtils.isJakartaProject(project)) { + return Collections.singletonList(JAKARTA_LABEL); + } + + return Collections.emptyList(); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/DependencyInjectionConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/Constants.java similarity index 62% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/DependencyInjectionConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/Constants.java index ef20dfe2..dda8bf87 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/di/DependencyInjectionConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/Constants.java @@ -10,26 +10,20 @@ * Contributors: * Himanshu Chotwani *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.di; - -public class DependencyInjectionConstants { +package org.eclipse.lsp4jakarta.jdt.internal.di; +/** + * Dependency injection diagnostic constants. + */ +public class Constants { /* Annotation Constants */ - public static final String PRODUCES = "Produces"; public static final String INJECT = "Inject"; public static final String INJECT_FQ_NAME = "jakarta.inject.Inject"; public static final String QUALIFIER = "Qualifier"; public static final String NAMED = "Named"; - /* Diagnostics fields constants */ public static final String DIAGNOSTIC_SOURCE = "jakarta-di"; - public static final String DIAGNOSTIC_CODE_INJECT_FINAL = "RemoveInjectOrFinal"; - public static final String DIAGNOSTIC_CODE_INJECT_CONSTRUCTOR = "RemoveInject"; - - public static final String DIAGNOSTIC_CODE_INJECT_ABSTRACT = "RemoveInjectOrAbstract"; - public static final String DIAGNOSTIC_CODE_INJECT_STATIC = "RemoveInjectOrStatic"; - public static final String DIAGNOSTIC_CODE_INJECT_GENERIC = "RemoveInjectForGeneric"; } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/DependencyInjectionDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/DependencyInjectionDiagnosticsParticipant.java new file mode 100644 index 00000000..217856b6 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/DependencyInjectionDiagnosticsParticipant.java @@ -0,0 +1,150 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0 +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Himanshu Chotwani - initial API and implementation +* Ananya Rao - Diagnostic Collection for multiple constructors annotated with inject +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.di; + +import static org.eclipse.lsp4jakarta.jdt.internal.di.Constants.INJECT_FQ_NAME; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * Dependency injection diagnostics participant that manages the use of + * the @Inject annotation. + */ +public class DependencyInjectionDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IType[] alltypes; + + alltypes = unit.getAllTypes(); + for (IType type : alltypes) { + IField[] allFields = type.getFields(); + for (IField field : allFields) { + if (Flags.isFinal(field.getFlags()) + && containsAnnotation(type, field.getAnnotations(), INJECT_FQ_NAME)) { + String msg = Messages.getMessage("InjectNoFinalField"); + Range range = PositionUtils.toNameRange(field, + context.getUtils()); + diagnostics.add( + context.createDiagnostic(uri, msg, range, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidInjectAnnotationOnFinalField, + DiagnosticSeverity.Error)); + } + } + + List injectedConstructors = new ArrayList(); + IMethod[] allMethods = type.getMethods(); + for (IMethod method : allMethods) { + int methodFlag = method.getFlags(); + boolean isFinal = Flags.isFinal(methodFlag); + boolean isAbstract = Flags.isAbstract(methodFlag); + boolean isStatic = Flags.isStatic(methodFlag); + boolean isGeneric = method.getTypeParameters().length != 0; + Range range = PositionUtils.toNameRange(method, + context.getUtils()); + if (containsAnnotation(type, method.getAnnotations(), INJECT_FQ_NAME)) { + if (DiagnosticUtils.isConstructorMethod(method)) + injectedConstructors.add(method); + if (isFinal) { + String msg = Messages.getMessage("InjectNoFinalMethod"); + + diagnostics.add(context.createDiagnostic(uri, msg, range, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidInjectAnnotationOnFinalMethod, + DiagnosticSeverity.Error)); + } + if (isAbstract) { + String msg = Messages.getMessage("InjectNoAbstractMethod"); + diagnostics.add(context.createDiagnostic(uri, msg, range, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidInjectAnnotationOnAbstractMethod, + DiagnosticSeverity.Error)); + } + if (isStatic) { + String msg = Messages.getMessage("InjectNoStaticMethod"); + diagnostics.add(context.createDiagnostic(uri, msg, range, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidInjectAnnotationOnStaticMethod, + DiagnosticSeverity.Error)); + } + + if (isGeneric) { + String msg = Messages.getMessage("InjectNoGenericMethod"); + diagnostics.add(context.createDiagnostic(uri, msg, range, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidInjectAnnotationOnGenericMethod, + DiagnosticSeverity.Error)); + } + } + } + + // if more than one 'inject' constructor, add diagnostic to all constructors + if (injectedConstructors.size() > 1) { + String msg = Messages.getMessage("InjectMoreThanOneConstructor"); + for (IMethod method : injectedConstructors) { + Range range = PositionUtils.toNameRange(method, + context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, msg, range, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidInjectAnnotationOnMultipleConstructors, + DiagnosticSeverity.Error)); + } + } + } + + return diagnostics; + } + + private boolean containsAnnotation(IType type, IAnnotation[] annotations, String annotationFQName) { + return Stream.of(annotations).anyMatch(annotation -> { + try { + return DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), annotationFQName); + } catch (JavaModelException e) { + JakartaCorePlugin.logException("Cannot validate annotations", e); + return false; + } + }); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/ErrorCode.java new file mode 100644 index 00000000..9633210e --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/ErrorCode.java @@ -0,0 +1,36 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.di; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * Bean validation error code. + */ +public enum ErrorCode implements IJavaErrorCode { + InvalidInjectAnnotationOnFinalField, + InvalidInjectAnnotationOnFinalMethod, + InvalidInjectAnnotationOnAbstractMethod, + InvalidInjectAnnotationOnStaticMethod, + InvalidInjectAnnotationOnGenericMethod, + InvalidInjectAnnotationOnMultipleConstructors; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveAbstractModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveAbstractModifierQuickFix.java new file mode 100644 index 00000000..d0cb5900 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveAbstractModifierQuickFix.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.di; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveModifierConflictQuickFix; + +/** + * Removes the abstract modifier from the declaring element. + */ +public class RemoveAbstractModifierQuickFix extends RemoveModifierConflictQuickFix { + + /** + * Constructor. + */ + public RemoveAbstractModifierQuickFix() { + super(false, "abstract"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveAbstractModifierQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.DIRemoveAbstractModifier; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveFinalModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveFinalModifierQuickFix.java new file mode 100644 index 00000000..db35c6fa --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveFinalModifierQuickFix.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.di; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveModifierConflictQuickFix; + +/** + * Removes the final modifier from the declaring element. + */ +public class RemoveFinalModifierQuickFix extends RemoveModifierConflictQuickFix { + + /** + * Constructor. + */ + public RemoveFinalModifierQuickFix() { + super(false, "final"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveFinalModifierQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.DIRemoveFinalModifier; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveInjectAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveInjectAnnotationQuickFix.java new file mode 100644 index 00000000..326a6a27 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveInjectAnnotationQuickFix.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.di; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +/** + * Removes the @Inject annotation from the declaring element. + */ +public class RemoveInjectAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * Constructor. + */ + public RemoveInjectAnnotationQuickFix() { + super(false, "jakarta.inject.Inject"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveInjectAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.DIRemoveInjectAnnotation; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveStaticModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveStaticModifierQuickFix.java new file mode 100644 index 00000000..5f4e083f --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/di/RemoveStaticModifierQuickFix.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.di; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveModifierConflictQuickFix; + +/** + * Removes the static modifier from the declaring element. + */ +public class RemoveStaticModifierQuickFix extends RemoveModifierConflictQuickFix { + + /** + * Constructor. + */ + public RemoveStaticModifierQuickFix() { + super(false, "static"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveStaticModifierQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.DIRemoveStaticModifier; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ClassConstructorDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ClassConstructorDiagnosticsParticipant.java new file mode 100644 index 00000000..0ca2149c --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ClassConstructorDiagnosticsParticipant.java @@ -0,0 +1,137 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Matthew Shocrylas - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.jaxrs; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * JAX-RS diagnostic participant that manages the use of constructors. + */ +public class ClassConstructorDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + ITypeRoot typeRoot = context.getTypeRoot(); + String uri = context.getUri(); + IJavaElement[] elements = typeRoot.getChildren(); + List diagnostics = new ArrayList<>(); + + for (IJavaElement element : elements) { + if (monitor.isCanceled()) { + return null; + } + if (element.getElementType() == IJavaElement.TYPE) { + IType type = (IType) element; + if (!type.isClass()) { + continue; + } + boolean isRootResource = false; + boolean isProviderResource = false; + IAnnotation[] annotationList = type.getAnnotations(); + + for (IAnnotation annotation : annotationList) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, + annotation.getElementName(), + Constants.SET_OF_JAXRS_ANNOTATIONS1); + if (matchedAnnotation != null) { + if (Constants.PATH_ANNOTATION.equals(matchedAnnotation)) { + isRootResource = true; + } else if (Constants.PROVIDER_ANNOTATION.equals(matchedAnnotation)) { + isProviderResource = true; + } + } + } + + if (isRootResource || isProviderResource) { // annotated class + List nonPublicConstructors = new ArrayList(); + boolean hasPublicConstructor = false; + int maxParams = 0; + Map constructorParamsMap = new HashMap(); + IMethod[] methods = type.getMethods(); + for (IMethod method : methods) { + if (DiagnosticUtils.isConstructorMethod(method)) { + if (Flags.isPublic(method.getFlags())) { + hasPublicConstructor = true; + nonPublicConstructors.clear(); // ignore all non-public constructors + if (isRootResource) { + int numParams = method.getNumberOfParameters(); + if (numParams > maxParams) { + maxParams = numParams; + } + constructorParamsMap.put(method, numParams); + } + } else if (!hasPublicConstructor) { + nonPublicConstructors.add(method); + } + } + } + // no public constructor defined + if (nonPublicConstructors.size() > 0) { + String diagnosticMessage = isRootResource ? Messages.getMessage("RootResourceClasses") : Messages.getMessage("ProviderClasses"); + for (IMethod constructor : nonPublicConstructors) { + Range methodRange = PositionUtils.toNameRange(constructor, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, diagnosticMessage, methodRange, + Constants.DIAGNOSTIC_SOURCE, ErrorCode.NoPublicConstructors, + DiagnosticSeverity.Error)); + } + } + // check public constructors' parameters + ArrayList equalMaxParamMethods = new ArrayList(); + for (Map.Entry entry : constructorParamsMap.entrySet()) { + if (entry.getValue() == maxParams) { + equalMaxParamMethods.add(entry.getKey()); + } else if (entry.getValue() < maxParams) { + IMethod method = entry.getKey(); + Range methodRange = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, Messages.getMessage("ConstructorIsUnused"), + methodRange, Constants.DIAGNOSTIC_SOURCE, ErrorCode.UnusedConstructor, + DiagnosticSeverity.Warning)); + } + } + if (equalMaxParamMethods.size() > 1) { // more than one + for (IMethod method : equalMaxParamMethods) { + Range methodRange = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("MultipleConstructorsNumberOfParameters"), methodRange, + Constants.DIAGNOSTIC_SOURCE, ErrorCode.AmbiguousConstructors, + DiagnosticSeverity.Warning)); + } + } + } + } + } + + return diagnostics; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/Constants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/Constants.java new file mode 100644 index 00000000..a2a57343 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/Constants.java @@ -0,0 +1,58 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Matthew Shocrylas - initial API and implementation. +* Bera Sogut +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.jaxrs; + +import java.util.ArrayList; +import java.util.List; + +/** + * JAX-RS diagnostics constants. + */ +public class Constants { + + public static final String DIAGNOSTIC_SOURCE = "jakarta-jaxrs"; + public static final String RESOURCE_METHOD = "ResourceMethod"; + + /* Annotation Constants */ + public static final ArrayList METHOD_DESIGNATORS = new ArrayList(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS")); + + /* Annotations which make a resource method parameter a non entity parameter. */ + public static final ArrayList NON_ENTITY_PARAM_ANNOTATIONS = new ArrayList(List.of("FormParam", "MatrixParam", "QueryParam", "PathParam", "CookieParam", + "HeaderParam", "Context")); + + public static final String PATH_ANNOTATION = "jakarta.ws.rs.Path"; + public static final String PROVIDER_ANNOTATION = "jakarta.ws.rs.ext.Provider"; + + public final static String[] SET_OF_METHOD_DESIGNATORS_ANNOTATIONS = { + "jakarta.ws.rs.GET", + "jakarta.ws.rs.POST", + "jakarta.ws.rs.PUT", + "jakarta.ws.rs.DELETE", + "jakarta.ws.rs.PATCH", + "jakarta.ws.rs.HEAD", + "jakarta.ws.rs.OPTIONS" }; + public final static String[] SET_OF_NON_ENTITY_PARAM_ANNOTATIONS = { + "jakarta.ws.rs.FormParam", + "jakarta.ws.rs.MatrixParam", + "jakarta.ws.rs.QueryParam", + "jakarta.ws.rs.PathParam", + "jakarta.ws.rs.CookieParam", + "jakarta.ws.rs.HeaderParam", + "jakarta.ws.rs.core.Context" }; + public final static String[] SET_OF_JAXRS_ANNOTATIONS1 = { + PATH_ANNOTATION, + PROVIDER_ANNOTATION }; + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ErrorCode.java new file mode 100644 index 00000000..66fb087c --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ErrorCode.java @@ -0,0 +1,35 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jaxrs; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * JAX-RS error code. + */ +public enum ErrorCode implements IJavaErrorCode { + NonPublicResourceMethod, + ResourceMethodMultipleEntityParams, + UnusedConstructor, + AmbiguousConstructors, + NoPublicConstructors; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/InsertDefaultPublicConstructorQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/InsertDefaultPublicConstructorQuickFix.java new file mode 100644 index 00000000..3ad3f13b --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/InsertDefaultPublicConstructorQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jaxrs; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertDefaultConstructorToClassQuickFix; + +/** + * Inserts a default public constructor to the active class. + */ +public class InsertDefaultPublicConstructorQuickFix extends InsertDefaultConstructorToClassQuickFix { + + /** + * Constructor. + */ + public InsertDefaultPublicConstructorQuickFix() { + super("public"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertDefaultPublicConstructorQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.jaxrsInsertPublicCtrtToClass; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/RemoveMethodEntityParamsWithExclusionQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/RemoveMethodEntityParamsWithExclusionQuickFix.java new file mode 100644 index 00000000..a91505b0 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/RemoveMethodEntityParamsWithExclusionQuickFix.java @@ -0,0 +1,224 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jaxrs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.IExtendedModifier; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.jsonrpc.messages.Tuple; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.RemoveParamsProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Removes a resource method's entity parameters with the exception of the + * currently selected element representing the parameter to keep. + */ +public class RemoveMethodEntityParamsWithExclusionQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(RemoveMethodEntityParamsWithExclusionQuickFix.class.getName()); + + /** Map key to retrieve an entity parameter name. */ + public static final String ENTITY_PARAM_NAME_TO_KEEP_ID_KEY = "entity.param.to.keep.identifier"; + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveMethodEntityParamsWithExclusionQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + IMethodBinding parentMethod = parentNode.resolveBinding(); + + if (parentMethod != null) { + Tuple.Two, SingleVariableDeclaration> entityParams = getEntityParams( + context, null); + + for (SingleVariableDeclaration entityParam : entityParams.getFirst()) { + ExtendedCodeAction codeAction = createCodeAction(context, diagnostic, entityParam); + codeActions.add(codeAction); + } + } + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + IMethodBinding parentMethod = parentNode.resolveBinding(); + CodeActionResolveData data = (CodeActionResolveData) toResolve.getData(); + String entityParamToKeepId = (String) data.getExtendedDataEntry(ENTITY_PARAM_NAME_TO_KEEP_ID_KEY); + Tuple.Two, SingleVariableDeclaration> dataTuple = getEntityParams(context, + entityParamToKeepId); + List entityParams = dataTuple.getFirst(); + SingleVariableDeclaration entityParamToKeep = dataTuple.getSecond(); + String label = getLabel(entityParamToKeepId); + ChangeCorrectionProposal proposal = new RemoveParamsProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentMethod, 0, entityParams, entityParamToKeep); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action edit to remove entity parameters", e); + } + + return toResolve; + } + + /** + * Creates a code action. + * + * @param context The code action context. + * @param diagnostic The diagnostic associated to this code action. + * @param enityParamToKeep The entity parameter to keep. + * + * @return a code action. + */ + private ExtendedCodeAction createCodeAction(JavaCodeActionContext context, Diagnostic diagnostic, + SingleVariableDeclaration enityParamToKeep) { + String paramId = getIdentifier(enityParamToKeep); + String label = getLabel(paramId); + ICodeActionId id = JakartaCodeActionId.RemoveAllEntityParametersExcept; + ExtendedCodeAction codeAction = new ExtendedCodeAction(label); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + Map extendedData = new HashMap(); + extendedData.put(ENTITY_PARAM_NAME_TO_KEEP_ID_KEY, paramId); + + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), extendedData, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), id)); + + return codeAction; + + } + + /** + * Returns a Tuple object containing a list of entity parameters and the entity + * parameter associated with the + * input search parameter identifier. + * + * @param context The code action context. + * @param searchParmId The inpust search parameter identifier. It can be null; + * + * @return The Tuple object containing a list of entity parameters and the + * entity parameter associated with the + * input search parameter identifier. + */ + private Tuple.Two, SingleVariableDeclaration> getEntityParams( + JavaCodeActionContext context, String searchParmId) { + + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + List entityParams = new ArrayList(); + + List params = (List) parentNode.parameters(); + SingleVariableDeclaration foundId = null; + for (SingleVariableDeclaration param : params) { + if (isEntityParam(param)) { + if (foundId == null && searchParmId != null && getIdentifier(param).equals(searchParmId)) { + foundId = param; + } + entityParams.add(param); + } + } + + return Tuple.two(entityParams, foundId); + } + + /** + * Returns a boolean variable that indicates whether the given parameter is an + * entity parameter or not. + * + * @param param the parameter to check whether it is an entity parameter or not + * @return true if the given parameter is an entity parameter, false otherwise + */ + private boolean isEntityParam(SingleVariableDeclaration param) { + ArrayList nonEntityParamAnnotations = Constants.NON_ENTITY_PARAM_ANNOTATIONS; + + boolean isEntityParam = true; + List modifiers = param.modifiers(); + for (Object o : modifiers) { + IExtendedModifier modifier = (IExtendedModifier) o; + if (modifier.isAnnotation()) { + Name typeName = ((Annotation) modifier).getTypeName(); + if (nonEntityParamAnnotations.contains(typeName.toString())) { + isEntityParam = false; + break; + } + } + } + + return isEntityParam; + } + + /** + * Returns the code action label. + * + * @param identifier The identifier to be associated to the label. + * + * @return The code action label. + */ + private String getLabel(String identifier) { + return Messages.getMessage("RemoveAllEntityParametersExcept", identifier); + } + + /** + * Returns the identifier associated with the input parameter object. + * + * @param entityParam The entity parameter. + * + * @return The identifier associated with the input parameter object. + */ + private String getIdentifier(SingleVariableDeclaration entityParam) { + return entityParam.getName().getIdentifier(); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ResourceMethodDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ResourceMethodDiagnosticsParticipant.java new file mode 100644 index 00000000..c420939f --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/ResourceMethodDiagnosticsParticipant.java @@ -0,0 +1,124 @@ +/******************************************************************************* +* Copyright (c) 2021, 2023 IBM Corporation and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation, Matthew Shocrylas - initial API and implementation + * Bera Sogut + *******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.jaxrs; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * JAX-RS diagnostic participant that manages the use of resource methods. + */ +public class ResourceMethodDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + ITypeRoot typeRoot = context.getTypeRoot(); + String uri = context.getUri(); + IJavaElement[] elements = typeRoot.getChildren(); + List diagnostics = new ArrayList<>(); + + String[] methodDesignators = ArrayUtils.addAll(Constants.SET_OF_METHOD_DESIGNATORS_ANNOTATIONS, + Constants.PATH_ANNOTATION); + + for (IJavaElement element : elements) { + if (monitor.isCanceled()) { + return null; + } + + if (element.getElementType() == IJavaElement.TYPE) { + IType type = (IType) element; + if (!type.isClass()) { + continue; + } + + IMethod[] methods = type.getMethods(); + boolean isInterface = type.isInterface(); + + for (IMethod method : methods) { + IAnnotation[] methodAnnotations = method.getAnnotations(); + boolean isResourceMethod = false; + boolean isValid = true; + boolean isPublic = Flags.isPublic(method.getFlags()); + boolean usesDfltAccessModifier = Flags.isPackageDefault(method.getFlags()); + Range methodRange = PositionUtils.toNameRange(method, context.getUtils()); + + for (IAnnotation annotation : methodAnnotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, + annotation.getElementName(), methodDesignators); + if (matchedAnnotation != null) { + if (isValid && !isPublic && !(usesDfltAccessModifier && isInterface)) + isValid = false; + if (!Constants.PATH_ANNOTATION.equals(matchedAnnotation)) { + isResourceMethod = true; + break; + } + } + } + if (!isValid) { + diagnostics.add(context.createDiagnostic(uri, Messages.getMessage("OnlyPublicMethods"), + methodRange, Constants.DIAGNOSTIC_SOURCE, ErrorCode.NonPublicResourceMethod, + DiagnosticSeverity.Error)); + } + if (isResourceMethod) { + int numEntityParams = 0; + ILocalVariable[] parameters = method.getParameters(); + for (ILocalVariable param : parameters) { + boolean isEntityParam = true; + IAnnotation[] annotations = param.getAnnotations(); + for (IAnnotation annotation : annotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, + annotation.getElementName(), Constants.SET_OF_NON_ENTITY_PARAM_ANNOTATIONS); + if (matchedAnnotation != null) { + isEntityParam = false; + break; + } + } + if (isEntityParam) + numEntityParams++; + } + if (numEntityParams > 1) { + diagnostics.add( + context.createDiagnostic(uri, Messages.getMessage("ResourceMethodsEntityParameter"), + methodRange, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.ResourceMethodMultipleEntityParams, DiagnosticSeverity.Error)); + } + } + } + + } + } + + return diagnostics; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/UpdateContructorAccessToPublicQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/UpdateContructorAccessToPublicQuickFix.java new file mode 100644 index 00000000..04179b4e --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/UpdateContructorAccessToPublicQuickFix.java @@ -0,0 +1,125 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jaxrs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ModifyModifiersProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Updates a constructor's access type modifier to public. + */ +public class UpdateContructorAccessToPublicQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(UpdateContructorAccessToPublicQuickFix.class.getName()); + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return UpdateContructorAccessToPublicQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + IMethodBinding parentMethod = parentNode.resolveBinding(); + List codeActions = new ArrayList<>(); + if (parentMethod != null) { + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel()); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), JakartaCodeActionId.MakeConstructorPublic)); + codeActions.add(codeAction); + } + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + IMethodBinding parentMethod = parentNode.resolveBinding(); + String label = getLabel(); + + ChangeCorrectionProposal proposal = new ModifyModifiersProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentMethod, 0, null, Arrays.asList("public")); + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action edit to make a constructor public.", + e); + } + + return toResolve; + } + + /** + * Returns the code action label. + * + * @return The code action label. + */ + private static String getLabel() { + return Messages.getMessage("MakeConstructorPublic"); + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected static IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/UpdateMethodAccessToPublicQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/UpdateMethodAccessToPublicQuickFix.java new file mode 100644 index 00000000..cd1cd6c9 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jaxrs/UpdateMethodAccessToPublicQuickFix.java @@ -0,0 +1,126 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jaxrs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ModifyModifiersProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Updates a method's access type modifier to public. + */ +public class UpdateMethodAccessToPublicQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(UpdateContructorAccessToPublicQuickFix.class.getName()); + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return UpdateContructorAccessToPublicQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + IMethodBinding parentMethod = parentNode.resolveBinding(); + List codeActions = new ArrayList<>(); + if (parentMethod != null) { + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel()); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), JakartaCodeActionId.MakeMethodPublic)); + codeActions.add(codeAction); + } + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + MethodDeclaration parentNode = (MethodDeclaration) node.getParent(); + IMethodBinding parentMethod = parentNode.resolveBinding(); + String label = getLabel(); + + ChangeCorrectionProposal proposal = new ModifyModifiersProposal(label, context.getCompilationUnit(), context.getASTRoot(), parentMethod, 0, null, Arrays.asList("public")); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action edit to make a method public.", + e); + } + + return toResolve; + } + + /** + * Returns the code action label. + * + * @return The code action label. + */ + private static String getLabel() { + return Messages.getMessage("MakeMethodPublic"); + } + + /** + * Returns the named entity associated to the given node. + * + * @param node The AST Node + * + * @return The named entity associated to the given node. + */ + @SuppressWarnings("restriction") + protected static IBinding getBinding(ASTNode node) { + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/Constants.java similarity index 75% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/Constants.java index be0bfbf7..5dbd5c53 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonb/JsonbConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/Constants.java @@ -10,21 +10,18 @@ * Contributors: * *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.jsonb; +package org.eclipse.lsp4jakarta.jdt.internal.jsonb; import java.util.List; -public class JsonbConstants { +/** + * JSON Binding (JSON-B) constants. + */ +public class Constants { /* Source */ public static final String DIAGNOSTIC_SOURCE = "jakarta-jsonb"; - /* Code */ - public static final String DIAGNOSTIC_CODE_ANNOTATION = "MultipleJsonbCreatorAnnotations"; - public static final String DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_FIELD = "NonmutualJsonbTransientAnnotation"; - public static final String DIAGNOSTIC_CODE_ANNOTATION_TRANSIENT_ACCESSOR = "NonmutualJsonbTransientAnnotationOnAccessor"; - - /* Annotation Constants */ public static final String JSONB_PACKAGE = "jakarta.json.bind.annotation."; public static final String JSONB_PREFIX = "Jsonb"; @@ -46,8 +43,9 @@ public class JsonbConstants { public static final String JSONB_TYPE_SERIALIZER = JSONB_PACKAGE + JSONB_PREFIX + "TypeSerializer"; public static final String JSONB_VISIBILITY = JSONB_PACKAGE + JSONB_PREFIX + "Visibility"; - public static final List JSONB_ANNOTATIONS = List.of(JSONB_CREATOR, JSONB_TRANSIENT_FQ_NAME, JSONB_ANNOTATION, - JSONB_DATE_FORMAT, JSONB_NILLABLE, JSONB_NUMBER_FORMAT, JSONB_PROPERTY, JSONB_PROPERTY_ORDER, - JSONB_TYPE_ADAPTER, JSONB_TYPE_DESERIALIZER, JSONB_TYPE_SERIALIZER, JSONB_VISIBILITY); + public static final List JSONB_ANNOTATIONS = List.of(JSONB_CREATOR, JSONB_TRANSIENT_FQ_NAME, + JSONB_ANNOTATION, + JSONB_DATE_FORMAT, JSONB_NILLABLE, JSONB_NUMBER_FORMAT, JSONB_PROPERTY, JSONB_PROPERTY_ORDER, + JSONB_TYPE_ADAPTER, JSONB_TYPE_DESERIALIZER, JSONB_TYPE_SERIALIZER, JSONB_VISIBILITY); } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/ErrorCode.java new file mode 100644 index 00000000..27429b76 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/ErrorCode.java @@ -0,0 +1,32 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jsonb; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * JSON Binding (JSON-B) error code. + */ +public enum ErrorCode implements IJavaErrorCode { + InvalidNumerOfJsonbCreatorAnnotationsInClass, + InvalidJSonBindindAnnotationWithJsonbTransientOnField, + InvalidJSonBindindAnnotationWithJsonbTransientOnAccessor; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/JsonbDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/JsonbDiagnosticsParticipant.java new file mode 100644 index 00000000..ca180ecb --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/JsonbDiagnosticsParticipant.java @@ -0,0 +1,204 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Matheus Cruz, Yijia Jing - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.jsonb; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IAnnotatable; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; + +/** + * JSON-B diagnostic participant that manages the use of @JsonbTransient, + * and @JsonbCreator annotations. + */ +public class JsonbDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IType[] types = unit.getAllTypes(); + IMethod[] methods; + IAnnotation[] allAnnotations; + + for (IType type : types) { + methods = type.getMethods(); + List jonbMethods = new ArrayList(); + // methods + for (IMethod method : type.getMethods()) { + if (DiagnosticUtils.isConstructorMethod(method) || Flags.isStatic(method.getFlags())) { + allAnnotations = method.getAnnotations(); + for (IAnnotation annotation : allAnnotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), + Constants.JSONB_CREATOR)) + jonbMethods.add(method); + } + } + } + if (jonbMethods.size() > Constants.MAX_METHOD_WITH_JSONBCREATOR) { + for (IMethod method : methods) { + String msg = Messages.getMessage("ErrorMessageJsonbCreator"); + Range range = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, msg, range, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidNumerOfJsonbCreatorAnnotationsInClass, DiagnosticSeverity.Error)); + } + } + // fields + for (IField field : type.getFields()) { + collectJsonbTransientFieldDiagnostics(context, uri, unit, type, diagnostics, field); + collectJsonbTransientAccessorDiagnostics(context, uri, unit, type, diagnostics, field); + } + } + return diagnostics; + } + + private void collectJsonbTransientFieldDiagnostics(JavaDiagnosticsContext context, String uri, + ICompilationUnit unit, IType type, List diagnostics, IField field) throws JavaModelException { + List jsonbAnnotationsForField = getJsonbAnnotationNames(type, field); + if (jsonbAnnotationsForField.contains(Constants.JSONB_TRANSIENT_FQ_NAME)) { + boolean hasAccessorConflict = false; + // Diagnostics on the accessors of the field are created when they are + // annotated with Jsonb annotations other than JsonbTransient. + List accessors = DiagnosticUtils.getFieldAccessors(unit, field); + for (IMethod accessor : accessors) { + List jsonbAnnotationsForAccessor = getJsonbAnnotationNames(type, accessor); + if (hasJsonbAnnotationOtherThanTransient(jsonbAnnotationsForAccessor)) { + Range range = PositionUtils.toNameRange(accessor, context.getUtils()); + createJsonbTransientDiagnostic(context, uri, range, unit, diagnostics, accessor, + jsonbAnnotationsForAccessor, + ErrorCode.InvalidJSonBindindAnnotationWithJsonbTransientOnField); + hasAccessorConflict = true; + } + } + // Diagnostic is created on the field if @JsonbTransient is not mutually + // exclusive or + // accessor has annotations other than JsonbTransient + if (hasAccessorConflict || hasJsonbAnnotationOtherThanTransient(jsonbAnnotationsForField)) { + Range range = PositionUtils.toNameRange(field, context.getUtils()); + createJsonbTransientDiagnostic(context, uri, range, unit, diagnostics, field, jsonbAnnotationsForField, + ErrorCode.InvalidJSonBindindAnnotationWithJsonbTransientOnField); + } + } + } + + private void collectJsonbTransientAccessorDiagnostics(JavaDiagnosticsContext context, String uri, + ICompilationUnit unit, IType type, + List diagnostics, IField field) throws JavaModelException { + boolean createDiagnosticForField = false; + List jsonbAnnotationsForField = getJsonbAnnotationNames(type, field); + List accessors = DiagnosticUtils.getFieldAccessors(unit, field); + for (IMethod accessor : accessors) { + List jsonbAnnotationsForAccessor = getJsonbAnnotationNames(type, accessor); + boolean hasFieldConflict = false; + if (jsonbAnnotationsForAccessor.contains(Constants.JSONB_TRANSIENT_FQ_NAME)) { + // Diagnostic is created if the field of this accessor has a annotation other + // then JsonbTransient + if (hasJsonbAnnotationOtherThanTransient(jsonbAnnotationsForField)) { + createDiagnosticForField = true; + hasFieldConflict = true; + } + + // Diagnostic is created on the accessor if field has annotation other than + // JsonbTransient + // or if @JsonbTransient is not mutually exclusive + if (hasFieldConflict || hasJsonbAnnotationOtherThanTransient(jsonbAnnotationsForAccessor)) { + Range range = PositionUtils.toNameRange(accessor, context.getUtils()); + createJsonbTransientDiagnostic(context, uri, range, unit, diagnostics, accessor, + jsonbAnnotationsForAccessor, + ErrorCode.InvalidJSonBindindAnnotationWithJsonbTransientOnAccessor); + } + + } + } + if (createDiagnosticForField) { + Range range = PositionUtils.toNameRange(field, context.getUtils()); + createJsonbTransientDiagnostic(context, uri, range, unit, diagnostics, field, + jsonbAnnotationsForField, + ErrorCode.InvalidJSonBindindAnnotationWithJsonbTransientOnAccessor); + } + } + + private boolean createJsonbTransientDiagnostic(JavaDiagnosticsContext context, String uri, Range range, + ICompilationUnit unit, + List diagnostics, + IMember member, + List jsonbAnnotations, ErrorCode errorCode) throws JavaModelException { + String diagnosticErrorMessage = null; + if (errorCode.equals(ErrorCode.InvalidJSonBindindAnnotationWithJsonbTransientOnField)) { + diagnosticErrorMessage = Messages.getMessage("ErrorMessageJsonbTransientOnField"); + } else if (errorCode.equals(ErrorCode.InvalidJSonBindindAnnotationWithJsonbTransientOnAccessor)) { + diagnosticErrorMessage = Messages.getMessage("ErrorMessageJsonbTransientOnAccessor"); + } + // convert to simple name for current tests + List diagnosticData = jsonbAnnotations.stream().map(annotation -> DiagnosticUtils.getSimpleName(annotation)).collect(Collectors.toList()); + diagnostics.add(context.createDiagnostic(uri, diagnosticErrorMessage, range, Constants.DIAGNOSTIC_SOURCE, + (JsonArray) (new Gson().toJsonTree(diagnosticData)), + errorCode, DiagnosticSeverity.Error)); + + return true; + } + + private List getJsonbAnnotationNames(IType type, IAnnotatable annotable) throws JavaModelException { + List jsonbAnnotationNames = new ArrayList(); + IAnnotation annotations[] = annotable.getAnnotations(); + for (IAnnotation annotation : annotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, annotation.getElementName(), + Constants.JSONB_ANNOTATIONS.toArray(String[]::new)); + if (matchedAnnotation != null) { + jsonbAnnotationNames.add(matchedAnnotation); + } + } + return jsonbAnnotationNames; + } + + private boolean hasJsonbAnnotationOtherThanTransient(List jsonbAnnotations) throws JavaModelException { + for (String annotationName : jsonbAnnotations) + if (Constants.JSONB_ANNOTATIONS.contains(annotationName) + && !annotationName.equals(Constants.JSONB_TRANSIENT_FQ_NAME)) + return true; + return false; + } + +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveAllButJsonbTransientAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveAllButJsonbTransientAnnotationQuickFix.java new file mode 100644 index 00000000..bb047784 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveAllButJsonbTransientAnnotationQuickFix.java @@ -0,0 +1,75 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jsonb; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +import com.google.gson.JsonArray; + +/** + * Removes all annotations with the exception of @JsonbTransient from declaring + * element. + */ +public class RemoveAllButJsonbTransientAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveAllButJsonbTransientAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.JSONBRemoveAllButJsonbTransientAnnotation; + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + if (parentType != null) { + JsonArray diagnosticData = (JsonArray) diagnostic.getData(); + List annotations = IntStream.range(0, diagnosticData.size()).mapToObj(idx -> diagnosticData.get(idx).getAsString()).collect(Collectors.toList()); + + annotations.remove(Constants.JSONB_TRANSIENT); + if (annotations.size() > 0) { + createCodeAction(diagnostic, context, parentType, codeActions, annotations.toArray(new String[0])); + } + } + + return codeActions; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveJsonbCreatorAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveJsonbCreatorAnnotationQuickFix.java new file mode 100644 index 00000000..edee338d --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveJsonbCreatorAnnotationQuickFix.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jsonb; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +/** + * Removes the @JsonbCreator annotation from declaring element. + */ +public class RemoveJsonbCreatorAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * Constructor. + */ + public RemoveJsonbCreatorAnnotationQuickFix() { + super("jakarta.json.bind.annotation.JsonbCreator"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveJsonbCreatorAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.JSONBRemoveJsonbCreatorAnnotation; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveJsonbTransientAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveJsonbTransientAnnotationQuickFix.java new file mode 100644 index 00000000..ff9f1d3a --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonb/RemoveJsonbTransientAnnotationQuickFix.java @@ -0,0 +1,73 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jsonb; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +import com.google.gson.JsonArray; + +/** + * Removes the JsonbTransient annotation from declaring element. + */ +public class RemoveJsonbTransientAnnotationQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveJsonbTransientAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.JSONBRemoveJsonbTransientAnnotation; + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + ASTNode node = context.getCoveredNode(); + IBinding parentType = getBinding(node); + if (parentType != null) { + JsonArray diagnosticData = (JsonArray) diagnostic.getData(); + List annotations = IntStream.range(0, diagnosticData.size()).mapToObj(idx -> diagnosticData.get(idx).getAsString()).collect(Collectors.toList()); + if (annotations.contains(Constants.JSONB_TRANSIENT)) { + createCodeAction(diagnostic, context, parentType, codeActions, + "jakarta.json.bind.annotation.JsonbTransient"); + } + } + + return codeActions; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonp/JsonpConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/Constants.java similarity index 85% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonp/JsonpConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/Constants.java index f4f89a98..fcb89fe5 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/jsonp/JsonpConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/Constants.java @@ -10,14 +10,16 @@ * Contributors: * Yijia Jing *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jsonp; -package org.eclipse.lsp4jakarta.jdt.core.jsonp; - -public class JsonpConstants { +/** + * JSON Processing (JSON-P) diagnostic constants. + */ +public class Constants { /* Source */ public static final String DIAGNOSTIC_SOURCE = "jakarta-jsonp"; - + /* Constants */ public static final String CREATE_POINTER = "createPointer"; public static final String JSON_FQ_NAME = "jakarta.json.Json"; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/ErrorCode.java new file mode 100644 index 00000000..6db707ad --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/ErrorCode.java @@ -0,0 +1,30 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jsonp; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * JSON Processing (JSON-P) error code. + */ +public enum ErrorCode implements IJavaErrorCode { + InvalidJsonCreatePointerTarget; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/JsonpDiagnosticParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/JsonpDiagnosticParticipant.java new file mode 100644 index 00000000..9bf18dbe --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/jsonp/JsonpDiagnosticParticipant.java @@ -0,0 +1,116 @@ +/******************************************************************************* +* Copyright (c) 2022, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Yijia Jing +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.jsonp; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.StringLiteral; +import org.eclipse.jdt.ls.core.internal.JDTUtils; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.ASTUtils; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * Json Processing (JSON-P) diagnostic participant. + */ +public class JsonpDiagnosticParticipant implements IJavaDiagnosticsParticipant { + + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + List allMethodInvocations = ASTUtils.getMethodInvocations(unit); + List createPointerInvocations = allMethodInvocations.stream().filter(mi -> { + try { + return isMatchedJsonCreatePointer(unit, mi); + } catch (JavaModelException e) { + return false; + } + }).collect(Collectors.toList()); + for (MethodInvocation m : createPointerInvocations) { + Expression arg = (Expression) m.arguments().get(0); + if (isInvalidArgument(arg)) { + // If the argument supplied to a createPointer invocation is a String literal + // and is neither an empty String + // or a sequence of '/' prefixed tokens, a diagnostic highlighting the invalid + // argument is created. + try { + String msg = Messages.getMessage("CreatePointerErrorMessage"); + Range range = JDTUtils.toRange(unit, arg.getStartPosition(), arg.getLength()); + diagnostics.add(context.createDiagnostic(uri, msg, range, Constants.DIAGNOSTIC_SOURCE, + ErrorCode.InvalidJsonCreatePointerTarget, DiagnosticSeverity.Error)); + } catch (JavaModelException e) { + JakartaCorePlugin.logException("Cannot calculate diagnostics", e); + } + } + } + + return diagnostics; + } + + private boolean isInvalidArgument(Expression arg) { + if (arg instanceof StringLiteral) { + String argValue = ((StringLiteral) arg).getLiteralValue(); + if (!(argValue.isEmpty() || argValue.matches("^(\\/[^\\/]+)+$"))) { + return true; + } + } + + return false; + } + + private boolean isMatchedJsonCreatePointer(ICompilationUnit unit, MethodInvocation mi) throws JavaModelException { + if (mi.arguments().size() == 1 && Constants.CREATE_POINTER.equals(mi.getName().getIdentifier()) + && mi.getExpression() != null) { + Expression ex = mi.getExpression(); + String qualifier = ex.toString(); + if (Constants.JSON_FQ_NAME.endsWith(qualifier)) { + // For performance reason, we check if the import of Java element name is + // declared + if (DiagnosticUtils.isImportedJavaElement(unit, Constants.JSON_FQ_NAME) == true) + return true; + // only check fully qualified java element + if (Constants.JSON_FQ_NAME.equals(qualifier)) { + ITypeBinding itb = ex.resolveTypeBinding(); + return itb != null && qualifier.equals(itb.getQualifiedName()); + } + } + } + + return false; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/Constants.java similarity index 58% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/Constants.java index 369d4d0f..dc75c619 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/persistence/PersistenceConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/Constants.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2020, 2022 IBM Corporation, Ankush Sharma and others. +* Copyright (c) 2020, 2023 IBM Corporation and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,9 +11,12 @@ * IBM Corporation, Ankush Sharma - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.persistence; +package org.eclipse.lsp4jakarta.jdt.internal.persistence; -public class PersistenceConstants { +/** + * Persistence diagnostic constants. + */ +public class Constants { /* Annotation Constants */ public static final String ENTITY = "jakarta.persistence.Entity"; public static final String MAPKEY = "jakarta.persistence.MapKey"; @@ -27,15 +30,5 @@ public class PersistenceConstants { /* Source */ public static final String DIAGNOSTIC_SOURCE = "jakarta-persistence"; - /* Entity Codes */ - public static final String DIAGNOSTIC_CODE_MISSING_EMPTY_CONSTRUCTOR = "MissingEmptyConstructor"; - public static final String DIAGNOSTIC_CODE_FINAL_METHODS = "RemoveFinalMethods"; - public static final String DIAGNOSTIC_CODE_FINAL_VARIABLES = "RemoveFinalVariables"; - public static final String DIAGNOSTIC_CODE_FINAL_CLASS = "InvalidClass"; - - /* MapKey Codes */ - public static final String DIAGNOSTIC_CODE_INVALID_ANNOTATION = "RemoveMapKeyorMapKeyClass"; - public static final String DIAGNOSTIC_CODE_MISSING_ATTRIBUTES = "SupplyAttributesToAnnotations"; - - public final static String[] SET_OF_PERSISTENCE_ANNOTATIONS = {MAPKEY, MAPKEYCLASS, MAPKEYJOINCOLUMN}; + public final static String[] SET_OF_PERSISTENCE_ANNOTATIONS = { MAPKEY, MAPKEYCLASS, MAPKEYJOINCOLUMN }; } \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/ErrorCode.java new file mode 100644 index 00000000..c7716214 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/ErrorCode.java @@ -0,0 +1,37 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.persistence; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * Persistence error code. + */ +public enum ErrorCode implements IJavaErrorCode { + InvalidFinalMethodInEntityAnnotatedClass, + InvalidPersistentFieldInEntityAnnotatedClass, + InvalidConstructorInEntityAnnotatedClass, + InvalidFinalModifierOnEntityAnnotatedClass, + InvalidMapKeyAnnotationsOnSameMethod, + InvalidMapKeyAnnotationsOnSameField, + InvalidMethodWithMultipleMPJCAnnotations, + InvalidFieldWithMultipleMPJCAnnotations; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InserMapKeyJoinColumAnnotationAttributesQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InserMapKeyJoinColumAnnotationAttributesQuickFix.java new file mode 100644 index 00000000..1cae78c6 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InserMapKeyJoinColumAnnotationAttributesQuickFix.java @@ -0,0 +1,56 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.persistence; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationAttributesQuickFix; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Inserts the @MapKeyJoinColumn along with its name and referencedColumnName + * attributes if missing. + */ +public class InserMapKeyJoinColumAnnotationAttributesQuickFix extends InsertAnnotationAttributesQuickFix { + + /** + * Constructor. + */ + public InserMapKeyJoinColumAnnotationAttributesQuickFix() { + super("jakarta.persistence.MapKeyJoinColumn", "name", "referencedColumnName"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InserMapKeyJoinColumAnnotationAttributesQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.PersistenceInsertAttributesToMKJCAnnotation; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getLabel(String annotation, String[] attributes) { + return Messages.getMessage("InsertTheMissingAttributes"); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InsertDefaultProtectedConstructorQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InsertDefaultProtectedConstructorQuickFix.java new file mode 100644 index 00000000..b77a9cb9 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InsertDefaultProtectedConstructorQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.persistence; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertDefaultConstructorToClassQuickFix; + +/** + * Inserts a protected default constructor the active class. + */ +public class InsertDefaultProtectedConstructorQuickFix extends InsertDefaultConstructorToClassQuickFix { + + /** + * Constructor. + */ + public InsertDefaultProtectedConstructorQuickFix() { + super("protected"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertDefaultProtectedConstructorQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.PersistenceInsertProtectedCtrtToClass; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InsertDefaultPublicConstructorQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InsertDefaultPublicConstructorQuickFix.java new file mode 100644 index 00000000..4cf4381f --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/InsertDefaultPublicConstructorQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.persistence; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertDefaultConstructorToClassQuickFix; + +/** + * Inserts a public default constructor the active class. + */ +public class InsertDefaultPublicConstructorQuickFix extends InsertDefaultConstructorToClassQuickFix { + + /** + * Constructor. + */ + public InsertDefaultPublicConstructorQuickFix() { + super("public"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertDefaultPublicConstructorQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.PersistenceInsertPublicCtrtToClass; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/PersistenceEntityDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/PersistenceEntityDiagnosticsParticipant.java new file mode 100644 index 00000000..c7fb7cc5 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/PersistenceEntityDiagnosticsParticipant.java @@ -0,0 +1,194 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation, Ankush Sharma and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Ankush Sharma - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.persistence; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.Flags; + +/** + * @author ankushsharma + * @brief Diagnostics implementation for Jakarta Persistence 3.0 + */ + +// Imports +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * Persistence diagnostic participant that manages the use of @Entity + * annotations. + */ +public class PersistenceEntityDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IType[] alltypes; + IAnnotation[] allAnnotations; + + alltypes = unit.getAllTypes(); + for (IType type : alltypes) { + allAnnotations = type.getAnnotations(); + + IAnnotation EntityAnnotation = null; + for (IAnnotation annotation : allAnnotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), + Constants.ENTITY)) { + EntityAnnotation = annotation; + } + } + + if (EntityAnnotation != null) { + // Define boolean requirements for the diagnostics + boolean hasPublicOrProtectedNoArgConstructor = false; + boolean hasArgConstructor = false; + boolean isEntityClassFinal = false; + + // Get the Methods of the annotated Class + for (IMethod method : type.getMethods()) { + if (DiagnosticUtils.isConstructorMethod(method)) { + // We have found a method that is a constructor + if (method.getNumberOfParameters() > 0) { + hasArgConstructor = true; + continue; + } + // Don't need to perform subtractions to check flags because eclipse notifies on + // illegal constructor modifiers + if (method.getFlags() != Flags.AccPublic && method.getFlags() != Flags.AccProtected) + continue; + hasPublicOrProtectedNoArgConstructor = true; + } + // All Methods of this class should not be final + if (isFinal(method.getFlags())) { + Range range = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("EntityNoFinalMethods"), range, + Constants.DIAGNOSTIC_SOURCE, method.getElementType(), + ErrorCode.InvalidFinalMethodInEntityAnnotatedClass, DiagnosticSeverity.Error)); + } + } + + // Go through the instance variables and make sure no instance vars are final + for (IField field : type.getFields()) { + // If a field is static, we do not care about it, we care about all other field + if (isStatic(field.getFlags())) { + continue; + } + // If we find a non-static variable that is final, this is a problem + if (isFinal(field.getFlags())) { + Range range = PositionUtils.toNameRange(field, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("EntityNoFinalVariables"), range, + Constants.DIAGNOSTIC_SOURCE, field.getElementType(), + ErrorCode.InvalidPersistentFieldInEntityAnnotatedClass, DiagnosticSeverity.Error)); + } + } + + // Ensure that the Entity class is not given a final modifier + if (isFinal(type.getFlags())) + isEntityClassFinal = true; + + // Create Diagnostics if needed + if (!hasPublicOrProtectedNoArgConstructor && hasArgConstructor) { + Range range = PositionUtils.toNameRange(type, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("EntityNoArgConstructor"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidConstructorInEntityAnnotatedClass, DiagnosticSeverity.Error)); + + } + + if (isEntityClassFinal) { + Range range = PositionUtils.toNameRange(type, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("EntityNoFinalClass"), range, + Constants.DIAGNOSTIC_SOURCE, type.getElementType(), + ErrorCode.InvalidFinalModifierOnEntityAnnotatedClass, DiagnosticSeverity.Error)); + } + } + } + + return diagnostics; + } + + /** + * check if the modifier provided is static + * + * @param flag + * @return + * @note modifier flags are an addition of all flags combined + */ + private boolean isStatic(int flag) { + // If a field is static, we do not care about it, we care about all other field + Integer isPublicStatic = flag - Flags.AccPublic; + Integer isPrivateStatic = flag - Flags.AccPrivate; + Integer isFinalStatic = flag - Flags.AccFinal; + Integer isProtectedStatic = flag - Flags.AccProtected; + Integer isStatic = flag; + if (isPublicStatic.equals(Flags.AccStatic) || isPrivateStatic.equals(Flags.AccStatic) + || isStatic.equals(Flags.AccStatic) || isFinalStatic.equals(Flags.AccStatic) + || isProtectedStatic.equals(Flags.AccStatic)) { + return true; + } + return false; + } + + /** + * check if the modifier provided is final + * + * @param flag + * @return + * @note modifier flags are an addition of all flags combined + */ + private boolean isFinal(int flag) { + Integer isPublicFinal = flag - Flags.AccPublic; + Integer isPrivateFinal = flag - Flags.AccPrivate; + Integer isProtectedFinal = flag - Flags.AccProtected; + Integer isFinal = flag; + if (isPublicFinal.equals(Flags.AccFinal) || isPrivateFinal.equals(Flags.AccFinal) + || isProtectedFinal.equals(Flags.AccFinal) || isFinal.equals(Flags.AccFinal)) { + return true; + } + return false; + } + +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/PersistenceMapKeyDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/PersistenceMapKeyDiagnosticsParticipant.java new file mode 100644 index 00000000..4a6fc85a --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/PersistenceMapKeyDiagnosticsParticipant.java @@ -0,0 +1,179 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Ankush Sharma - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.persistence; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IMemberValuePair; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * Persistence diagnostic participant that manages the use + * of @MapKeyClass, @MapKey, and @MapKeyJoinColumn annotations. + */ +public class PersistenceMapKeyDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IType[] alltypes; + IAnnotation[] allAnnotations; + + alltypes = unit.getAllTypes(); + IMethod[] methods; + IField[] fields; + + for (IType type : alltypes) { + methods = type.getMethods(); + for (IMethod method : methods) { + List mapKeyJoinCols = new ArrayList(); + boolean hasMapKeyAnnotation = false; + boolean hasMapKeyClassAnnotation = false; + allAnnotations = method.getAnnotations(); + for (IAnnotation annotation : allAnnotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, + annotation.getElementName(), + Constants.SET_OF_PERSISTENCE_ANNOTATIONS); + if (matchedAnnotation != null) { + if (Constants.MAPKEY.equals(matchedAnnotation)) + hasMapKeyAnnotation = true; + else if (Constants.MAPKEYCLASS.equals(matchedAnnotation)) + hasMapKeyClassAnnotation = true; + else if (Constants.MAPKEYJOINCOLUMN.equals(matchedAnnotation)) { + mapKeyJoinCols.add(annotation); + } + } + } + if (hasMapKeyAnnotation && hasMapKeyClassAnnotation) { + // A single field cannot have the same + Range range = PositionUtils.toNameRange(method, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("MapKeyAnnotationsNotOnSameMethod"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidMapKeyAnnotationsOnSameMethod, DiagnosticSeverity.Error)); + } + // If we have multiple MapKeyJoinColumn annotations on a single method we must + // ensure each has a name and referencedColumnName + if (mapKeyJoinCols.size() > 1) { + validateMapKeyJoinColumnAnnotations(context, uri, mapKeyJoinCols, method, unit, diagnostics); + } + } + + // Go through each field to ensure they do not have both MapKey and MapKeyColumn + // Annotations + fields = type.getFields(); + for (IField field : fields) { + List mapKeyJoinCols = new ArrayList(); + boolean hasMapKeyAnnotation = false; + boolean hasMapKeyClassAnnotation = false; + allAnnotations = field.getAnnotations(); + for (IAnnotation annotation : allAnnotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, + annotation.getElementName(), + Constants.SET_OF_PERSISTENCE_ANNOTATIONS); + if (matchedAnnotation != null) { + if (Constants.MAPKEY.equals(matchedAnnotation)) + hasMapKeyAnnotation = true; + else if (Constants.MAPKEYCLASS.equals(matchedAnnotation)) + hasMapKeyClassAnnotation = true; + else if (Constants.MAPKEYJOINCOLUMN.equals(matchedAnnotation)) { + mapKeyJoinCols.add(annotation); + } + } + } + if (hasMapKeyAnnotation && hasMapKeyClassAnnotation) { + // A single field cannot have the same + Range range = PositionUtils.toNameRange(field, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("MapKeyAnnotationsNotOnSameField"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidMapKeyAnnotationsOnSameField, DiagnosticSeverity.Error)); + } + if (mapKeyJoinCols.size() > 1) { + validateMapKeyJoinColumnAnnotations(context, uri, mapKeyJoinCols, field, unit, diagnostics); + } + } + } + + return diagnostics; + } + + private void validateMapKeyJoinColumnAnnotations(JavaDiagnosticsContext context, String uri, + List annotations, + IMember element, + ICompilationUnit unit, List diagnostics) throws CoreException { + + annotations.forEach(annotation -> { + boolean allNamesSpecified, allReferencedColumnNameSpecified; + try { + Range range = null; + String message = null; + ErrorCode errorCode = null; + if (element instanceof IMethod) { + range = PositionUtils.toNameRange((IMethod) element, context.getUtils()); + errorCode = ErrorCode.InvalidMethodWithMultipleMPJCAnnotations; + message = Messages.getMessage("MultipleMapKeyJoinColumnMethod"); + } else { + range = PositionUtils.toNameRange((IField) element, context.getUtils()); + errorCode = ErrorCode.InvalidFieldWithMultipleMPJCAnnotations; + message = Messages.getMessage("MultipleMapKeyJoinColumnField"); + } + + List memberValues = Arrays.asList(annotation.getMemberValuePairs()); + allNamesSpecified = memberValues.stream().anyMatch((mv) -> mv.getMemberName().equals(Constants.NAME)); + allReferencedColumnNameSpecified = memberValues.stream().anyMatch((mv) -> mv.getMemberName().equals(Constants.REFERENCEDCOLUMNNAME)); + if (!allNamesSpecified || !allReferencedColumnNameSpecified) { + diagnostics.add(context.createDiagnostic(uri, + message, range, + Constants.DIAGNOSTIC_SOURCE, null, + errorCode, DiagnosticSeverity.Error)); + } + } catch (JavaModelException e) { + JakartaCorePlugin.logException("Error while retrieving member values of @MapKeyJoinColumn Annotation", + e); + } + }); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/RemoveFinalModifierQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/RemoveFinalModifierQuickFix.java new file mode 100644 index 00000000..460d2086 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/RemoveFinalModifierQuickFix.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.persistence; + +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveModifierConflictQuickFix; + +/** + * Removes the final modifier from the declaring element. + */ +public class RemoveFinalModifierQuickFix extends RemoveModifierConflictQuickFix { + + /** + * Constructor. + */ + public RemoveFinalModifierQuickFix() { + super("final"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveFinalModifierQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.PersistenceRemoveFinalModifier; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/RemoveMapKeyAnnotationsQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/RemoveMapKeyAnnotationsQuickFix.java new file mode 100644 index 00000000..26216e8b --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/persistence/RemoveMapKeyAnnotationsQuickFix.java @@ -0,0 +1,45 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.persistence; + +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.RemoveAnnotationConflictQuickFix; + +/** + * Removes @MapKey and @MapKeyClass annotations from the declaring element. + */ +public class RemoveMapKeyAnnotationsQuickFix extends RemoveAnnotationConflictQuickFix { + + /** + * Constructor. + */ + public RemoveMapKeyAnnotationsQuickFix() { + super(false, "jakarta.persistence.annotation.MapKeyClass", "jakarta.persistence.annotation.MapKey"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return RemoveMapKeyAnnotationsQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected JakartaCodeActionId getCodeActionId() { + return JakartaCodeActionId.PersistenceRemoveMapKeyAnnotation; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/CompleteServletAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/CompleteFilterAnnotationQuickFix.java similarity index 62% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/CompleteServletAnnotationQuickFix.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/CompleteFilterAnnotationQuickFix.java index 361784ce..38a4fc27 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/CompleteServletAnnotationQuickFix.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/CompleteFilterAnnotationQuickFix.java @@ -12,69 +12,76 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.core.servlet; +package org.eclipse.lsp4jakarta.jdt.internal.servlet; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4jakarta.jdt.codeAction.CodeActionHandler; -import org.eclipse.lsp4jakarta.jdt.codeAction.JavaCodeActionContext; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ChangeCorrectionProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.ModifyAnnotationProposal; -import org.eclipse.lsp4jakarta.jdt.codeAction.proposal.quickfix.InsertAnnotationMissingQuickFix; -import org.eclipse.lsp4jakarta.jdt.core.Messages; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationMissingQuickFix; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ModifyAnnotationProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; /** - * QuickFix for fixing - * {@link ServletConstants#DIAGNOSTIC_CODE_MISSING_ATTRIBUTE} error and - * {@link ServletConstants#DIAGNOSTIC_CODE_DUPLICATE_ATTRIBUTES} error by - * providing several code actions: - * - * {@link ServletConstants#DIAGNOSTIC_CODE_MISSING_ATTRIBUTE} - *
      - *
    • Add the `value` attribute to the `@WebServlet` annotation - *
    • Add the `urlPatterns` attribute to the `@WebServlet` annotation - *
    - * - * {@link ServletConstants#DIAGNOSTIC_CODE_DUPLICATE_ATTRIBUTES} - *
      - *
    • Remove the `value` attribute to the `@WebServlet` annotation - *
    • Remove the `urlPatterns` attribute to the `@WebServlet` annotation - *
    + * Adds/Removes missing/duplicate attributes (value/urlPatters/etc) to/from + * a @WebFilter annotation. * * @author Kathryn Kodama - * */ -public class CompleteServletAnnotationQuickFix extends InsertAnnotationMissingQuickFix { +public class CompleteFilterAnnotationQuickFix extends InsertAnnotationMissingQuickFix { + + public CompleteFilterAnnotationQuickFix() { + super("jakarta.servlet.annotation.WebFilter"); + } - public CompleteServletAnnotationQuickFix() { - super("jakarta.servlet.annotation.WebServlet"); + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return CompleteFilterAnnotationQuickFix.class.getName(); } + /** + * {@inheritDoc} + */ @Override - protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, IBinding parentType, - List codeActions) throws CoreException { + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.ServletCompleteWebFilterAnnotation; + } + + @Override + protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, + List codeActions) throws CoreException { String[] annotations = getAnnotations(); for (String annotation : annotations) { - insertAndReplaceAnnotation(diagnostic, context, parentType, codeActions, annotation); + insertAndReplaceAnnotation(diagnostic, context, codeActions, annotation); } } - private static void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, - IBinding parentType, List codeActions, String annotation) throws CoreException { + private void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, + List codeActions, String annotation) throws CoreException { + + ASTNode node = context.getCoveringNode(); + IBinding parentType = getBinding(node); // Insert the annotation and the proper import by using JDT Core Manipulation // API // if missing an attribute, do value insertion - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE_MISSING_ATTRIBUTE)) { + if (diagnostic.getCode().getLeft().equals(ErrorCode.WebFilterAnnotationMissingAttributes.getCode())) { ArrayList attributes = new ArrayList<>(); attributes.add("value"); attributes.add("urlPatterns"); + attributes.add("servletNames"); // Code Action 1: add value attribute to the WebServlet annotation // Code Action 2: add urlPatterns attribute to the WebServlet annotation for (int i = 0; i < attributes.size(); i++) { @@ -83,8 +90,7 @@ private static void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeAc ArrayList attributesToAdd = new ArrayList<>(); attributesToAdd.add(attribute); String name = getLabel(annotation, attribute, "Add"); - ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, annotation, attributesToAdd); + ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, annotation, attributesToAdd); // Convert the proposal to LSP4J CodeAction CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); codeAction.setTitle(name); @@ -95,7 +101,7 @@ private static void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeAc } // if duplicate attributes exist in annotations, remove attributes from // annotation - if (diagnostic.getCode().getLeft().equals(ServletConstants.DIAGNOSTIC_CODE_DUPLICATE_ATTRIBUTES)) { + if (diagnostic.getCode().getLeft().equals(ErrorCode.WebFilterAnnotationAttributeConflict.getCode())) { ArrayList attributes = new ArrayList<>(); attributes.add("value"); attributes.add("urlPatterns"); @@ -107,8 +113,7 @@ private static void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeAc ArrayList attributesToRemove = new ArrayList<>(); attributesToRemove.add(attribute); String name = getLabel(annotation, attribute, "Remove"); - ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), - context.getASTRoot(), parentType, 0, annotation, new ArrayList(), attributesToRemove); + ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, annotation, new ArrayList(), attributesToRemove); // Convert the proposal to LSP4J CodeAction CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); codeAction.setTitle(name); diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/CompleteServletAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/CompleteServletAnnotationQuickFix.java new file mode 100644 index 00000000..e49a2495 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/CompleteServletAnnotationQuickFix.java @@ -0,0 +1,136 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationMissingQuickFix; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ModifyAnnotationProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Adds/Removes missing/duplicate attributes (value/urlPatters) to/from + * a @WebServlet annotation. + * + * @author Kathryn Kodama + */ +public class CompleteServletAnnotationQuickFix extends InsertAnnotationMissingQuickFix { + + /** + * Constructor. + */ + public CompleteServletAnnotationQuickFix() { + super("jakarta.servlet.annotation.WebServlet"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return CompleteServletAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.ServletCompleteServletAnnotation; + } + + /** + * {@inheritDoc} + */ + @Override + protected void insertAnnotations(Diagnostic diagnostic, JavaCodeActionContext context, + List codeActions) throws CoreException { + String[] annotations = getAnnotations(); + for (String annotation : annotations) { + insertAndReplaceAnnotation(diagnostic, context, codeActions, annotation); + } + } + + private void insertAndReplaceAnnotation(Diagnostic diagnostic, JavaCodeActionContext context, + List codeActions, String annotation) throws CoreException { + ASTNode node = context.getCoveringNode(); + IBinding parentType = getBinding(node); + // Insert the annotation and the proper import by using JDT Core Manipulation + // API + + // if missing an attribute, do value insertion + if (diagnostic.getCode().getLeft().equals(ErrorCode.WebServletAnnotationMissingAttributes.getCode())) { + ArrayList attributes = new ArrayList<>(); + attributes.add("value"); + attributes.add("urlPatterns"); + // Code Action 1: add value attribute to the WebServlet annotation + // Code Action 2: add urlPatterns attribute to the WebServlet annotation + for (int i = 0; i < attributes.size(); i++) { + String attribute = attributes.get(i); + + ArrayList attributesToAdd = new ArrayList<>(); + attributesToAdd.add(attribute); + String name = getLabel(annotation, attribute, "Add"); + ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, annotation, attributesToAdd); + // Convert the proposal to LSP4J CodeAction + CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); + codeAction.setTitle(name); + if (codeAction != null) { + codeActions.add(codeAction); + } + } + } + // if duplicate attributes exist in annotations, remove attributes from + // annotation + if (diagnostic.getCode().getLeft().equals(ErrorCode.WebServletAnnotationAttributeConflict.getCode())) { + ArrayList attributes = new ArrayList<>(); + attributes.add("value"); + attributes.add("urlPatterns"); + // Code Action 1: remove value attribute from the WebServlet annotation + // Code Action 2: remove urlPatterns attribute from the WebServlet annotation + for (int i = 0; i < attributes.size(); i++) { + String attribute = attributes.get(i); + + ArrayList attributesToRemove = new ArrayList<>(); + attributesToRemove.add(attribute); + String name = getLabel(annotation, attribute, "Remove"); + ChangeCorrectionProposal proposal = new ModifyAnnotationProposal(name, context.getCompilationUnit(), context.getASTRoot(), parentType, 0, annotation, new ArrayList(), attributesToRemove); + // Convert the proposal to LSP4J CodeAction + CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic); + codeAction.setTitle(name); + if (codeAction != null) { + codeActions.add(codeAction); + } + } + } + } + + private static String getLabel(String annotation, String attribute, String labelType) { + String annotationName = annotation.substring(annotation.lastIndexOf('.') + 1, annotation.length()); + annotationName = "@" + annotationName; + if (labelType.equals("Remove")) { + return Messages.getMessage("RemoveTheAttriubuteFrom", attribute, annotationName); + } + return Messages.getMessage("AddTheAttributeTo", attribute, annotationName); + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ServletConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/Constants.java similarity index 77% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ServletConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/Constants.java index fb6ab2c6..72f17343 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/servlet/ServletConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/Constants.java @@ -1,61 +1,56 @@ -/******************************************************************************* -* Copyright (c) 2020, 2022 IBM Corporation, Pengyu Xiong and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation, Pengyu Xiong - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.servlet; - -public class ServletConstants { - - /* @WEBListener */ - public static final String WEB_LISTENER = "WebListener"; - public static final String WEB_LISTENER_FQ_NAME = "jakarta.servlet.annotation.WebListener"; - public static final String SERVLET_CONTEXT_LISTENER = "ServletContextListener"; - public static final String SERVLET_CONTEXT_LISTENER_FQ_NAME = "jakarta.servlet.ServletContextListener"; - public static final String SERVLET_CONTEXT_ATTRIBUTE_LISTENER = "ServletContextAttributeListener"; - public static final String SERVLET_CONTEXT_ATTRIBUTE_LISTENER_FQ_NAME = "jakarta.servlet.ServletContextAttributeListener"; - public static final String SERVLET_REQUEST_LISTENER = "ServletRequestListener"; - public static final String SERVLET_REQUEST_LISTENER_FQ_NAME = "jakarta.servlet.ServletRequestListener"; - public static final String SERVLET_REQUEST_ATTRIBUTE_LISTENER = "ServletRequestAttributeListener"; - public static final String SERVLET_REQUEST_ATTRIBUTE_LISTENER_FQ_NAME = "jakarta.servlet.ServletRequestAttributeListener"; - public static final String HTTP_SESSION_LISTENER = "HttpSessionListener"; - public static final String HTTP_SESSION_LISTENER_FQ_NAME = "jakarta.servlet.http.HttpSessionListener"; - public static final String HTTP_SESSION_ATTRIBUTE_LISTENER = "HttpSessionAttributeListener"; - public static final String HTTP_SESSION_ATTRIBUTE_LISTENER_FQ_NAME = "jakarta.servlet.http.HttpSessionAttributeListener"; - public static final String HTTP_SESSION_ID_LISTENER = "HttpSessionIdListener"; - public static final String HTTP_SESSION_ID_LISTENER_FQ_NAME = "jakarta.servlet.http.HttpSessionIdListener"; - - /* @WEBServlet */ - public static final String WEB_SERVLET = "WebServlet"; - public static final String WEB_SERVLET_FQ_NAME = "jakarta.servlet.annotation.WebServlet"; - public static final String HTTP_SERVLET = "HttpServlet"; - - /* @WEBFilter */ - public static final String WEBFILTER = "WebFilter"; - public static final String WEBFILTER_FQ_NAME = "jakarta.servlet.annotation.WebFilter"; - public static final String FILTER = "Filter"; - public static final String FILTER_FQ_NAME = "jakarta.servlet.Filter"; - - /* Annotation Member Value names */ - public static final String URL_PATTERNS = "urlPatterns"; - public static final String VALUE = "value"; - public static final String SERVLET_NAMES = "servletNames"; - - /* Diagnostics fields constants */ - public static final String DIAGNOSTIC_SOURCE = "jakarta-servlet"; - public static final String DIAGNOSTIC_CODE = "ExtendHttpServlet"; - public static final String DIAGNOSTIC_CODE_MISSING_ATTRIBUTE = "CompleteHttpServletAttributes"; - public static final String DIAGNOSTIC_CODE_DUPLICATE_ATTRIBUTES = "InvalidHttpServletAttribute"; - public static final String DIAGNOSTIC_CODE_FILTER = "ImplementFilter"; - public static final String DIAGNOSTIC_CODE_FILTER_MISSING_ATTRIBUTE = "CompleteWebFilterAttributes"; - public static final String DIAGNOSTIC_CODE_FILTER_DUPLICATE_ATTRIBUTES = "InvalidWebFilterAttribute"; - public static final String DIAGNOSTIC_CODE_LISTENER = "ImplementListener"; -} +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Pengyu Xiong - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +/** + * Servlet constants. + */ +public class Constants { + + /* @WEBListener */ + public static final String WEB_LISTENER = "WebListener"; + public static final String WEB_LISTENER_FQ_NAME = "jakarta.servlet.annotation.WebListener"; + public static final String SERVLET_CONTEXT_LISTENER = "ServletContextListener"; + public static final String SERVLET_CONTEXT_LISTENER_FQ_NAME = "jakarta.servlet.ServletContextListener"; + public static final String SERVLET_CONTEXT_ATTRIBUTE_LISTENER = "ServletContextAttributeListener"; + public static final String SERVLET_CONTEXT_ATTRIBUTE_LISTENER_FQ_NAME = "jakarta.servlet.ServletContextAttributeListener"; + public static final String SERVLET_REQUEST_LISTENER = "ServletRequestListener"; + public static final String SERVLET_REQUEST_LISTENER_FQ_NAME = "jakarta.servlet.ServletRequestListener"; + public static final String SERVLET_REQUEST_ATTRIBUTE_LISTENER = "ServletRequestAttributeListener"; + public static final String SERVLET_REQUEST_ATTRIBUTE_LISTENER_FQ_NAME = "jakarta.servlet.ServletRequestAttributeListener"; + public static final String HTTP_SESSION_LISTENER = "HttpSessionListener"; + public static final String HTTP_SESSION_LISTENER_FQ_NAME = "jakarta.servlet.http.HttpSessionListener"; + public static final String HTTP_SESSION_ATTRIBUTE_LISTENER = "HttpSessionAttributeListener"; + public static final String HTTP_SESSION_ATTRIBUTE_LISTENER_FQ_NAME = "jakarta.servlet.http.HttpSessionAttributeListener"; + public static final String HTTP_SESSION_ID_LISTENER = "HttpSessionIdListener"; + public static final String HTTP_SESSION_ID_LISTENER_FQ_NAME = "jakarta.servlet.http.HttpSessionIdListener"; + + /* @WEBServlet */ + public static final String WEB_SERVLET = "WebServlet"; + public static final String WEB_SERVLET_FQ_NAME = "jakarta.servlet.annotation.WebServlet"; + public static final String HTTP_SERVLET = "HttpServlet"; + + /* @WEBFilter */ + public static final String WEBFILTER = "WebFilter"; + public static final String WEBFILTER_FQ_NAME = "jakarta.servlet.annotation.WebFilter"; + public static final String FILTER = "Filter"; + public static final String FILTER_FQ_NAME = "jakarta.servlet.Filter"; + + /* Annotation Member Value names */ + public static final String URL_PATTERNS = "urlPatterns"; + public static final String VALUE = "value"; + public static final String SERVLET_NAMES = "servletNames"; + + /* Diagnostics fields constants */ + public static final String DIAGNOSTIC_SOURCE = "jakarta-servlet"; +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ErrorCode.java new file mode 100644 index 00000000..76e2d962 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ErrorCode.java @@ -0,0 +1,37 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * Servlet error code. + */ +public enum ErrorCode implements IJavaErrorCode { + ClassWebFilterAnnotatedNoFilterInterfaceImpl, + WebFilterAnnotationMissingAttributes, + WebFilterAnnotationAttributeConflict, + WebFilterAnnotatedClassReqIfaceNoImpl, + WebServletAnnotatedClassDoesNotExtendHttpServlet, + WebServletAnnotatedClassUnknownSuperTypeDoesNotExtendHttpServlet, + WebServletAnnotationMissingAttributes, + WebServletAnnotationAttributeConflict; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/FilterDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/FilterDiagnosticsParticipant.java new file mode 100644 index 00000000..6daf6520 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/FilterDiagnosticsParticipant.java @@ -0,0 +1,119 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation, Reza Akhavan and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Reza Akhavan - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IMemberValuePair; +import org.eclipse.jdt.core.IType; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * @WebFilter annotation diagnostic participant. + */ +public class FilterDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IAnnotation[] allAnnotations; + + IType[] alltypes = unit.getAllTypes(); + for (IType type : alltypes) { + allAnnotations = type.getAnnotations(); + IAnnotation webFilterAnnotation = null; + + for (IAnnotation annotation : allAnnotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), + Constants.WEBFILTER_FQ_NAME)) { + webFilterAnnotation = annotation; + } + } + + String[] interfaces = { Constants.FILTER_FQ_NAME }; + boolean isFilterImplemented = DiagnosticUtils.doesImplementInterfaces(type, interfaces); + + if (webFilterAnnotation != null && !isFilterImplemented) { + Range range = PositionUtils.toNameRange(type, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("WebFilterMustImplement"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.ClassWebFilterAnnotatedNoFilterInterfaceImpl, DiagnosticSeverity.Error)); + } + + /* URL pattern diagnostic check */ + if (webFilterAnnotation != null) { + IMemberValuePair[] memberValues = webFilterAnnotation.getMemberValuePairs(); + + boolean isUrlpatternSpecified = false; + boolean isServletNamesSpecified = false; + boolean isValueSpecified = false; + for (IMemberValuePair mv : memberValues) { + if (mv.getMemberName().equals(Constants.URL_PATTERNS)) { + isUrlpatternSpecified = true; + continue; + } + if (mv.getMemberName().equals(Constants.SERVLET_NAMES)) { + isServletNamesSpecified = true; + continue; + } + if (mv.getMemberName().equals(Constants.VALUE)) { + isValueSpecified = true; + } + } + if (!isUrlpatternSpecified && !isValueSpecified && !isServletNamesSpecified) { + Range range = PositionUtils.toNameRange(webFilterAnnotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("WebFilterMustDefine"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.WebFilterAnnotationMissingAttributes, DiagnosticSeverity.Error)); + } + if (isUrlpatternSpecified && isValueSpecified) { + Range range = PositionUtils.toNameRange(webFilterAnnotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("WebFilterCannotHaveBoth"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.WebFilterAnnotationAttributeConflict, DiagnosticSeverity.Error)); + } + } + } + + return diagnostics; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertExtendsClauseToExtendHttpServletQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertExtendsClauseToExtendHttpServletQuickFix.java new file mode 100644 index 00000000..4bb3184d --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertExtendsClauseToExtendHttpServletQuickFix.java @@ -0,0 +1,111 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.internal.corext.dom.Bindings; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ExtendClassProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Inserts the extends clause for the active class to extend the HTTPServlet + * class. + */ +public class InsertExtendsClauseToExtendHttpServletQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(InsertExtendsClauseToExtendHttpServletQuickFix.class.getName()); + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertExtendsClauseToExtendHttpServletQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + ASTNode node = context.getCoveredNode(); + ITypeBinding parentType = Bindings.getBindingOfParentType(node); + List codeActions = new ArrayList<>(); + if (parentType != null) { + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel(Constants.HTTP_SERVLET, parentType.getName())); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), JakartaCodeActionId.ServletExtendClass)); + codeActions.add(codeAction); + } + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + ITypeBinding parentType = Bindings.getBindingOfParentType(node); + String label = getLabel(Constants.HTTP_SERVLET, parentType.getName()); + ChangeCorrectionProposal proposal = new ExtendClassProposal(label, context.getCompilationUnit(), parentType, context.getASTRoot(), "jakarta.servlet.http.HttpServlet", 0); + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action edit to insert the extends clause to a classs.", + e); + } + + return toResolve; + } + + /** + * Returns the code action label. + * + * @param interfaceName The interface name. + * @param classTypeName The class type element name. + * + * @return The code action label. + */ + @SuppressWarnings("restriction") + private String getLabel(String interfaceName, String classTypeName) { + return Messages.getMessage("LetClassExtend", + org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels.getJavaElementName(classTypeName), + org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels.getJavaElementName(interfaceName)); + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertImplementsClauseToImplFilterQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertImplementsClauseToImplFilterQuickFix.java new file mode 100644 index 00000000..1586488e --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertImplementsClauseToImplFilterQuickFix.java @@ -0,0 +1,112 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.internal.corext.dom.Bindings; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ImplementInterfaceProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Inserts the implements clause for the active class to implement the Filter + * interface. + */ +public class InsertImplementsClauseToImplFilterQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(InsertImplementsClauseToImplFilterQuickFix.class.getName()); + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertImplementsClauseToImplFilterQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + ASTNode node = context.getCoveredNode(); + ITypeBinding parentType = Bindings.getBindingOfParentType(node); + List codeActions = new ArrayList<>(); + if (parentType != null) { + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel(Constants.FILTER, parentType.getName())); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), null, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), JakartaCodeActionId.ServletFilterImplementation)); + codeActions.add(codeAction); + } + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + ITypeBinding parentType = Bindings.getBindingOfParentType(node); + String label = getLabel(Constants.FILTER, parentType.getName()); + + ChangeCorrectionProposal proposal = new ImplementInterfaceProposal(label, context.getCompilationUnit(), parentType, context.getASTRoot(), "jakarta.servlet.Filter", 0); + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action edit to implement Filter.", + e); + } + + return toResolve; + } + + /** + * Returns the code action label. + * + * @param interfaceName The interface name. + * @param interfaceType The type interface type. + * + * @return The code action label. + */ + @SuppressWarnings("restriction") + private String getLabel(String interfaceName, String interfaceType) { + return Messages.getMessage("LetClassImplement", + org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels.getJavaElementName(interfaceType), + org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels.getJavaElementName(interfaceName)); + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertImplementsClauseToImplListenerQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertImplementsClauseToImplListenerQuickFix.java new file mode 100644 index 00000000..ad448c6d --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/InsertImplementsClauseToImplListenerQuickFix.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2023 IBM Corporation and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.internal.corext.dom.Bindings; +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4jakarta.commons.codeaction.CodeActionResolveData; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.ExtendedCodeAction; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.IJavaCodeActionParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionContext; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.JavaCodeActionResolveContext; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ChangeCorrectionProposal; +import org.eclipse.lsp4jakarta.jdt.core.java.corrections.proposal.ImplementInterfaceProposal; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; + +/** + * Inserts the implements clause to the active class to implement the HTTP + * session and servlet listener interfaces. + */ +public class InsertImplementsClauseToImplListenerQuickFix implements IJavaCodeActionParticipant { + + /** Logger object to record events for this class. */ + private static final Logger LOGGER = Logger.getLogger(InsertImplementsClauseToImplListenerQuickFix.class.getName()); + + /** Map key to retrieve an interface type */ + private static final String INTERFACE_TYPE_KEY = "interface.type"; + + /** Map key to retrieve an interface name */ + private static final String INTERFACE_NAME_KEY = "interface.name"; + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertImplementsClauseToImplListenerQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic, + IProgressMonitor monitor) throws CoreException { + List codeActions = new ArrayList<>(); + ASTNode node = context.getCoveredNode(); + ITypeBinding parentType = Bindings.getBindingOfParentType(node); + + if (parentType != null) { + createCodeAction(context, diagnostic, parentType, Constants.SERVLET_CONTEXT_LISTENER, + "jakarta.servlet.ServletContextListener", codeActions); + createCodeAction(context, diagnostic, parentType, Constants.SERVLET_CONTEXT_ATTRIBUTE_LISTENER, + "jakarta.servlet.ServletContextAttributeListener", codeActions); + createCodeAction(context, diagnostic, parentType, Constants.SERVLET_REQUEST_LISTENER, + "jakarta.servlet.ServletRequestListener", codeActions); + createCodeAction(context, diagnostic, parentType, Constants.SERVLET_REQUEST_ATTRIBUTE_LISTENER, + "jakarta.servlet.ServletRequestAttributeListener", codeActions); + createCodeAction(context, diagnostic, parentType, Constants.HTTP_SESSION_LISTENER, + "jakarta.servlet.http.HttpSessionListener", codeActions); + createCodeAction(context, diagnostic, parentType, Constants.HTTP_SESSION_ATTRIBUTE_LISTENER, + "jakarta.servlet.http.HttpSessionAttributeListener", codeActions); + createCodeAction(context, diagnostic, parentType, Constants.HTTP_SESSION_ID_LISTENER, + "jakarta.servlet.http.HttpSessionIdListener", codeActions); + } + + return codeActions; + } + + /** + * {@inheritDoc} + */ + @Override + public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) { + CodeAction toResolve = context.getUnresolved(); + ASTNode node = context.getCoveredNode(); + ITypeBinding parentType = Bindings.getBindingOfParentType(node); + + CodeActionResolveData data = (CodeActionResolveData) toResolve.getData(); + String interfaceName = (String) data.getExtendedDataEntry(INTERFACE_NAME_KEY); + String interfaceType = (String) data.getExtendedDataEntry(INTERFACE_TYPE_KEY); + + String label = getLabel(parentType.getName(), interfaceName); + + ChangeCorrectionProposal proposal = new ImplementInterfaceProposal(label, context.getCompilationUnit(), parentType, context.getASTRoot(), interfaceType, 0); + + try { + toResolve.setEdit(context.convertToWorkspaceEdit(proposal)); + } catch (CoreException e) { + LOGGER.log(Level.SEVERE, "Unable to resolve code action edit to add the implements clause to a class.", + e); + } + + return toResolve; + } + + /** + * Creates a code action. + * + * @param context The code action context. + * @param diagnostic The diagnostic associated with this code action. + * @param parentType The parent binding associated with the current node. + * @param interfaceName The interface name. + * @param interfaceType The interface type. + * @param codeActions The list of code actions to populate. + */ + protected void createCodeAction(JavaCodeActionContext context, Diagnostic diagnostic, ITypeBinding parentType, + String interfaceName, String interfaceType, List codeActions) { + + ExtendedCodeAction codeAction = new ExtendedCodeAction(getLabel(parentType.getName(), interfaceName)); + codeAction.setRelevance(0); + codeAction.setKind(CodeActionKind.QuickFix); + codeAction.setDiagnostics(Arrays.asList(diagnostic)); + Map extendedData = new HashMap(); + extendedData.put(INTERFACE_NAME_KEY, interfaceName); + extendedData.put(INTERFACE_TYPE_KEY, interfaceType); + codeAction.setData(new CodeActionResolveData(context.getUri(), getParticipantId(), context.getParams().getRange(), extendedData, context.getParams().isResourceOperationSupported(), context.getParams().isCommandConfigurationUpdateSupported(), JakartaCodeActionId.ServletListenerImplementation)); + codeActions.add(codeAction); + } + + /** + * Returns the code action label. + * + * @param classTypeName The class type element name. + * @param interfaceName The interface name. + * + * @return The code action label. + */ + @SuppressWarnings("restriction") + private String getLabel(String classTypeName, String interfaceName) { + return Messages.getMessage("LetClassImplement", + org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels.getJavaElementName(classTypeName), + org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels.getJavaElementName(interfaceName)); + } +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ListenerDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ListenerDiagnosticsParticipant.java new file mode 100644 index 00000000..c5c85438 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ListenerDiagnosticsParticipant.java @@ -0,0 +1,89 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation, Reza Akhavan and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Reza Akhavan - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IType; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * Servlet and HTTP session listener diagnostic participant. + */ +public class ListenerDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IType[] alltypes; + IAnnotation[] allAnnotations; + + alltypes = unit.getAllTypes(); + for (IType type : alltypes) { + allAnnotations = type.getAnnotations(); + boolean isWebListenerAnnotated = false; + for (IAnnotation annotation : allAnnotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), + Constants.WEB_LISTENER_FQ_NAME)) { + isWebListenerAnnotated = true; + break; + } + } + + String[] interfaces = { Constants.SERVLET_CONTEXT_LISTENER_FQ_NAME, + Constants.SERVLET_CONTEXT_ATTRIBUTE_LISTENER_FQ_NAME, + Constants.SERVLET_REQUEST_LISTENER_FQ_NAME, + Constants.SERVLET_REQUEST_ATTRIBUTE_LISTENER_FQ_NAME, + Constants.HTTP_SESSION_LISTENER_FQ_NAME, + Constants.HTTP_SESSION_ATTRIBUTE_LISTENER_FQ_NAME, + Constants.HTTP_SESSION_ID_LISTENER_FQ_NAME }; + boolean isImplemented = DiagnosticUtils.doesImplementInterfaces(type, interfaces); + + if (isWebListenerAnnotated && !isImplemented) { + Range range = PositionUtils.toNameRange(type, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("AnnotatedWithWebListenerMustImplement"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.WebFilterAnnotatedClassReqIfaceNoImpl, DiagnosticSeverity.Error)); + } + } + + return diagnostics; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ServletDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ServletDiagnosticsParticipant.java new file mode 100644 index 00000000..c04454bd --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/servlet/ServletDiagnosticsParticipant.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation, Pengyu Xiong - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.servlet; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IMemberValuePair; +import org.eclipse.jdt.core.IType; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.TypeHierarchyUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * Servlet diagnostic participant. + * + * @see https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0.html#webservlet + */ +public class ServletDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + IType[] alltypes; + IAnnotation[] allAnnotations; + + alltypes = unit.getAllTypes(); + for (IType type : alltypes) { + allAnnotations = type.getAnnotations(); + + IAnnotation webServletAnnotation = null; + for (IAnnotation annotation : allAnnotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), + Constants.WEB_SERVLET_FQ_NAME)) { + webServletAnnotation = annotation; + break; // get the first one, the annotation is not repeatable + } + } + + if (webServletAnnotation != null) { + // check if the class extends HttpServlet + try { + int r = TypeHierarchyUtils.doesITypeHaveSuperType(type, Constants.HTTP_SERVLET); + if (r == -1) { + Range range = PositionUtils.toNameRange(type, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("WebServletMustExtend"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.WebServletAnnotatedClassDoesNotExtendHttpServlet, DiagnosticSeverity.Error)); + } else if (r == 0) { // unknown super type + Range range = PositionUtils.toNameRange(type, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("WebServletMustExtend"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.WebServletAnnotatedClassUnknownSuperTypeDoesNotExtendHttpServlet, + DiagnosticSeverity.Warning)); + } + } catch (CoreException e) { + JakartaCorePlugin.logException("Cannot check type hierarchy", e); + } + + /* URL pattern diagnostic check */ + IMemberValuePair[] memberValues = webServletAnnotation.getMemberValuePairs(); + + boolean isUrlpatternSpecified = false; + boolean isValueSpecified = false; + for (IMemberValuePair mv : memberValues) { + if (mv.getMemberName().equals(Constants.URL_PATTERNS)) { + isUrlpatternSpecified = true; + continue; + } + if (mv.getMemberName().equals(Constants.VALUE)) { + isValueSpecified = true; + } + } + if (!isUrlpatternSpecified && !isValueSpecified) { + Range range = PositionUtils.toNameRange(webServletAnnotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("WebServletMustDefine"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.WebServletAnnotationMissingAttributes, DiagnosticSeverity.Error)); + } + if (isUrlpatternSpecified && isValueSpecified) { + Range range = PositionUtils.toNameRange(webServletAnnotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("WebServletCannotHaveBoth"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.WebServletAnnotationAttributeConflict, DiagnosticSeverity.Error)); + } + } + } + + return diagnostics; + } + +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/WebSocketConstants.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/Constants.java similarity index 66% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/WebSocketConstants.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/Constants.java index 35b6ff14..c5552a7e 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/core/websocket/WebSocketConstants.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/Constants.java @@ -1,19 +1,19 @@ -/******************************************************************************* - * Copyright (c) 2022, 2023 IBM Corporation and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Giancarlo Pernudi Segura - initial API and implementation - * Lidia Ataupillco Ramos - * Aviral Saxena - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.core.websocket; +/******************************************************************************* +* Copyright (c) 2022, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Giancarlo Pernudi Segura - initial API and implementation +* Lidia Ataupillco Ramos +* Aviral Saxena +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.internal.websocket; import java.util.Arrays; import java.util.HashSet; @@ -22,7 +22,10 @@ import org.eclipse.lsp4j.DiagnosticSeverity; -public class WebSocketConstants { +/** + * WebSocket diagnostic and code action constants. + */ +public class Constants { public static final String DIAGNOSTIC_SOURCE = "jakarta-websocket"; public static final DiagnosticSeverity ERROR = DiagnosticSeverity.Error; @@ -40,16 +43,6 @@ public class WebSocketConstants { public static final String CURLY_BRACE_START = "{"; public static final String CURLY_BRACE_END = "}"; - public static final String DIAGNOSTIC_CODE_PATH_PARAMS_ANNOT = "AddPathParamsAnnotation"; - - /* Diagnostic codes */ - public static final String DIAGNOSTIC_CODE_ON_OPEN_INVALID_PARAMS = "OnOpenChangeInvalidParam"; - public static final String DIAGNOSTIC_CODE_ON_CLOSE_INVALID_PARAMS = "OnCloseChangeInvalidParam"; - - public static final String DIAGNOSTIC_CODE_ON_MESSAGE_DUPLICATE_METHOD = "OnMessageDuplicateMethod"; - - public static final String DIAGNOSTIC_SERVER_ENDPOINT = "ChangeInvalidServerEndpoint"; - /* * https://jakarta.ee/specifications/websocket/2.0/websocket-spec-2.0.html# * applications @@ -74,10 +67,10 @@ public class WebSocketConstants { public static final String INPUTSTREAM_CLASS_SHORT = "InputStream"; public static final String PONGMESSAGE_CLASS_LONG = "jakarta.websocket.PongMessage"; public static final String PONGMESSAGE_CLASS_SHORT = "PongMessage"; - public static final Set LONG_MESSAGE_CLASSES = new HashSet<>( - Arrays.asList(STRING_CLASS_LONG, READER_CLASS_LONG, BYTEBUFFER_CLASS_LONG, INPUTSTREAM_CLASS_LONG, PONGMESSAGE_CLASS_LONG)); - public static final Set SHORT_MESSAGE_CLASSES = new HashSet<>( - Arrays.asList(STRING_CLASS_SHORT, READER_CLASS_SHORT, BYTEBUFFER_CLASS_SHORT, INPUTSTREAM_CLASS_SHORT, PONGMESSAGE_CLASS_SHORT)); + public static final Set LONG_MESSAGE_CLASSES = new HashSet<>(Arrays.asList(STRING_CLASS_LONG, READER_CLASS_LONG, BYTEBUFFER_CLASS_LONG, INPUTSTREAM_CLASS_LONG, + PONGMESSAGE_CLASS_LONG)); + public static final Set SHORT_MESSAGE_CLASSES = new HashSet<>(Arrays.asList(STRING_CLASS_SHORT, READER_CLASS_SHORT, BYTEBUFFER_CLASS_SHORT, INPUTSTREAM_CLASS_SHORT, + PONGMESSAGE_CLASS_SHORT)); public static final String SESSION_CLASS = "jakarta.websocket.Session"; /* Annotations */ @@ -91,20 +84,14 @@ public class WebSocketConstants { public static final String PATH_PARAM_ANNOTATION = "jakarta.websocket.server.PathParam"; // For OnOpen annotation - public static final Set ON_OPEN_PARAM_OPT_TYPES = new HashSet<>( - Arrays.asList("jakarta.websocket.EndpointConfig", SESSION_CLASS)); - public static final Set RAW_ON_OPEN_PARAM_OPT_TYPES = new HashSet<>( - Arrays.asList("EndpointConfig", "Session")); - - public static final Set ON_CLOSE_PARAM_OPT_TYPES = new HashSet<>( - Arrays.asList("jakarta.websocket.CloseReason", SESSION_CLASS)); - public static final Set RAW_ON_CLOSE_PARAM_OPT_TYPES = new HashSet<>( - Arrays.asList("CloseReason", "Session")); - - public static final Set RAW_WRAPPER_OBJS = new HashSet<>( - Arrays.asList("String", "Boolean", "Integer", "Long", "Double", "Float")); - public static final Set WRAPPER_OBJS = RAW_WRAPPER_OBJS.stream().map(raw -> "java.lang.".concat(raw)) - .collect(Collectors.toSet()); + public static final Set ON_OPEN_PARAM_OPT_TYPES = new HashSet<>(Arrays.asList("jakarta.websocket.EndpointConfig", SESSION_CLASS)); + public static final Set RAW_ON_OPEN_PARAM_OPT_TYPES = new HashSet<>(Arrays.asList("EndpointConfig", "Session")); + + public static final Set ON_CLOSE_PARAM_OPT_TYPES = new HashSet<>(Arrays.asList("jakarta.websocket.CloseReason", SESSION_CLASS)); + public static final Set RAW_ON_CLOSE_PARAM_OPT_TYPES = new HashSet<>(Arrays.asList("CloseReason", "Session")); + + public static final Set RAW_WRAPPER_OBJS = new HashSet<>(Arrays.asList("String", "Boolean", "Integer", "Long", "Double", "Float")); + public static final Set WRAPPER_OBJS = RAW_WRAPPER_OBJS.stream().map(raw -> "java.lang.".concat(raw)).collect(Collectors.toSet()); // Enums public enum MESSAGE_FORMAT { diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/ErrorCode.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/ErrorCode.java new file mode 100644 index 00000000..38fb1e7c --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/ErrorCode.java @@ -0,0 +1,38 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.websocket; + +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaErrorCode; + +/** + * WebSocket error code. + */ +public enum ErrorCode implements IJavaErrorCode { + InvalidOnOpenParams, + InvalidOnCloseParams, + PathParamsMissingFromParam, + PathParamDoesNotMatchEndpointURI, + OnMessageDuplicateMethod, + InvalidEndpointPathWithNoStartingSlash, + InvalidEndpointPathWithRelativePaths, + InvalidEndpointPathNotTempleateOrPartialURI, + InvalidEndpointPathDuplicateVariable; + + /** + * {@inheritDoc} + */ + @Override + public String getCode() { + return name(); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/InsertPathParamAnnotationQuickFix.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/InsertPathParamAnnotationQuickFix.java new file mode 100644 index 00000000..48154862 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/InsertPathParamAnnotationQuickFix.java @@ -0,0 +1,78 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.websocket; + +import java.text.MessageFormat; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.lsp4jakarta.commons.codeaction.ICodeActionId; +import org.eclipse.lsp4jakarta.commons.codeaction.JakartaCodeActionId; +import org.eclipse.lsp4jakarta.jdt.core.java.codeaction.InsertAnnotationAttributesQuickFix; + +/** + * Inserts the @PathParam annotation with a default value attribute to a method + * parameter. + */ +public class InsertPathParamAnnotationQuickFix extends InsertAnnotationAttributesQuickFix { + private static final String CODE_ACTION_LABEL = "Insert @{0}"; + + public InsertPathParamAnnotationQuickFix() { + super("jakarta.websocket.server.PathParam", "value"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getParticipantId() { + return InsertPathParamAnnotationQuickFix.class.getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected ICodeActionId getCodeActionId() { + return JakartaCodeActionId.WBInsertPathParamAnnotationWithValueAttrib; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getLabel(String annotation, String[] attributes) { + String[] parts = annotation.split("\\."); + String AnnotationName = (parts.length > 1) ? parts[parts.length - 1] : annotation; + return MessageFormat.format(CODE_ACTION_LABEL, AnnotationName); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("restriction") + @Override + protected IBinding getBinding(ASTNode node) { + // handle annotation insertions for a single variable declaration + if (node.getParent() instanceof SingleVariableDeclaration) { + return ((SingleVariableDeclaration) node.getParent()).resolveBinding(); + } + + if (node.getParent() instanceof VariableDeclarationFragment) { + return ((VariableDeclarationFragment) node.getParent()).resolveBinding(); + } + return org.eclipse.jdt.internal.corext.dom.Bindings.getBindingOfParentType(node); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/WebSocketDiagnosticsParticipant.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/WebSocketDiagnosticsParticipant.java new file mode 100644 index 00000000..2f1499c5 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/java/org/eclipse/lsp4jakarta/jdt/internal/websocket/WebSocketDiagnosticsParticipant.java @@ -0,0 +1,536 @@ +/******************************************************************************* +* Copyright (c) 2022, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Giancarlo Pernudi Segura - initial API and implementation +* Lidia Ataupillco Ramos +* Aviral Saxena +*******************************************************************************/ +package org.eclipse.lsp4jakarta.jdt.internal.websocket; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IMemberValuePair; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.internal.core.JavaModel; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.jdt.core.JakartaCorePlugin; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.IJavaDiagnosticsParticipant; +import org.eclipse.lsp4jakarta.jdt.core.java.diagnostics.JavaDiagnosticsContext; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.PositionUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.TypeHierarchyUtils; +import org.eclipse.lsp4jakarta.jdt.internal.DiagnosticUtils; +import org.eclipse.lsp4jakarta.jdt.internal.Messages; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; + +/** + * WebSocket Diagnostic participant. + */ +public class WebSocketDiagnosticsParticipant implements IJavaDiagnosticsParticipant { + + /** + * {@inheritDoc} + */ + @Override + public List collectDiagnostics(JavaDiagnosticsContext context, IProgressMonitor monitor) throws CoreException { + String uri = context.getUri(); + IJDTUtils utils = JDTUtilsLSImpl.getInstance(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + List diagnostics = new ArrayList<>(); + + if (unit == null) { + return diagnostics; + } + + IType[] alltypes; + HashMap checkWSEnd = null; + + alltypes = unit.getAllTypes(); + for (IType type : alltypes) { + checkWSEnd = isWSEndpoint(type); + // checks if the class uses annotation to create a WebSocket endpoint + if (checkWSEnd.get(Constants.IS_ANNOTATION)) { + // WebSocket Invalid Parameters Diagnostic + invalidParamsCheck(context, uri, type, unit, diagnostics); + + /* @PathParam Value Mismatch Warning */ + List endpointPathVars = findAndProcessEndpointURI(type); + /* + * WebSocket endpoint annotations must be attached to a class, and thus is + * guaranteed to be processed before any of the member method annotations + */ + if (endpointPathVars != null) { + // PathParam URI Mismatch Warning Diagnostic + uriMismatchWarningCheck(context, uri, type, endpointPathVars, diagnostics, unit); + } + + // OnMessage validation for WebSocket message formats + onMessageWSMessageFormats(context, uri, type, diagnostics, unit); + + // ServerEndpoint annotation diagnostics + serverEndpointErrorCheck(context, uri, type, diagnostics, unit); + } + } + + return diagnostics; + } + + private void invalidParamsCheck(JavaDiagnosticsContext context, String uri, IType type, ICompilationUnit unit, + List diagnostics) throws JavaModelException { + IMethod[] allMethods = type.getMethods(); + for (IMethod method : allMethods) { + IAnnotation[] allAnnotations = method.getAnnotations(); + Set specialParamTypes = null, rawSpecialParamTypes = null; + + for (IAnnotation annotation : allAnnotations) { + String annotationName = annotation.getElementName(); + ErrorCode diagnosticErrorCode = null; + + if (DiagnosticUtils.isMatchedJavaElement(type, annotationName, Constants.ON_OPEN)) { + specialParamTypes = Constants.ON_OPEN_PARAM_OPT_TYPES; + rawSpecialParamTypes = Constants.RAW_ON_OPEN_PARAM_OPT_TYPES; + diagnosticErrorCode = ErrorCode.InvalidOnOpenParams; + } else if (DiagnosticUtils.isMatchedJavaElement(type, annotationName, Constants.ON_CLOSE)) { + specialParamTypes = Constants.ON_CLOSE_PARAM_OPT_TYPES; + rawSpecialParamTypes = Constants.RAW_ON_CLOSE_PARAM_OPT_TYPES; + diagnosticErrorCode = ErrorCode.InvalidOnCloseParams; + } + if (diagnosticErrorCode != null) { + ILocalVariable[] allParams = method.getParameters(); + for (ILocalVariable param : allParams) { + String signature = param.getTypeSignature(); + String formatSignature = signature.replace("/", "."); + String resolvedTypeName = JavaModelUtil.getResolvedTypeName(formatSignature, type); + boolean isPrimitive = JavaModelUtil.isPrimitive(formatSignature); + boolean isSpecialType; + boolean isPrimWrapped; + + if (resolvedTypeName != null) { + isSpecialType = specialParamTypes.contains(resolvedTypeName); + isPrimWrapped = isWrapper(resolvedTypeName); + } else { + String simpleParamType = Signature.getSignatureSimpleName(signature); + isSpecialType = rawSpecialParamTypes.contains(simpleParamType); + isPrimWrapped = isWrapper(simpleParamType); + } + + // check parameters valid types + if (!(isSpecialType || isPrimWrapped || isPrimitive)) { + Range range = PositionUtils.toNameRange(param, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + createParamTypeDiagMsg(specialParamTypes, annotationName), range, + Constants.DIAGNOSTIC_SOURCE, null, + diagnosticErrorCode, DiagnosticSeverity.Error)); + continue; + } + + if (!isSpecialType) { + // check that if parameter is not a specialType, it has a @PathParam annotation + IAnnotation[] param_annotations = param.getAnnotations(); + boolean hasPathParamAnnot = Arrays.asList(param_annotations).stream().anyMatch(annot -> { + try { + return DiagnosticUtils.isMatchedJavaElement(type, annot.getElementName(), + Constants.PATH_PARAM_ANNOTATION); + } catch (JavaModelException e) { + JakartaCorePlugin.logException("Failed to get matched annotation", e); + return false; + } + }); + if (!hasPathParamAnnot) { + Range range = PositionUtils.toNameRange(param, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("PathParamsAnnotationMissing"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.PathParamsMissingFromParam, DiagnosticSeverity.Error)); + } + } + } + } + } + } + } + + /** + * Creates a warning diagnostic if a PathParam annotation does not match any + * variable parameters of the WebSocket EndPoint URI associated with the class + * in which the method is contained + * + * @param type representing the class list of diagnostics for this class + * compilation unit with which the type is associated + */ + private void uriMismatchWarningCheck(JavaDiagnosticsContext context, String uri, IType type, + List endpointPathVars, List diagnostics, + ICompilationUnit unit) throws JavaModelException { + IMethod[] typeMethods = type.getMethods(); + for (IMethod method : typeMethods) { + ILocalVariable[] methodParams = method.getParameters(); + for (ILocalVariable param : methodParams) { + IAnnotation[] paramAnnotations = param.getAnnotations(); + for (IAnnotation annotation : paramAnnotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), + Constants.PATHPARAM_ANNOTATION)) { + IMemberValuePair[] valuePairs = annotation.getMemberValuePairs(); + for (IMemberValuePair pair : valuePairs) { + if (pair.getMemberName().equals(Constants.ANNOTATION_VALUE) + && pair.getValueKind() == IMemberValuePair.K_STRING) { + String pathValue = (String) pair.getValue(); + if (!endpointPathVars.contains(pathValue)) { + Range range = PositionUtils.toNameRange(annotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("PathParamWarning"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.PathParamDoesNotMatchEndpointURI, DiagnosticSeverity.Warning)); + } + } + } + } + } + } + } + } + + /** + * Creates an error diagnostic if there exists more than one method annotated + * with @OnMessage for a given message format. + * + * @param type + * @param diagnostics + * @param unit + * @throws JavaModel + */ + private void onMessageWSMessageFormats(JavaDiagnosticsContext context, String uri, IType type, + List diagnostics, ICompilationUnit unit) throws JavaModelException { + IMethod[] typeMethods = type.getMethods(); + IAnnotation onMessageTextUsed = null; + IAnnotation onMessageBinaryUsed = null; + IAnnotation onMessagePongUsed = null; + for (IMethod method : typeMethods) { + IAnnotation[] allAnnotations = method.getAnnotations(); + for (IAnnotation annotation : allAnnotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), Constants.ON_MESSAGE)) { + ILocalVariable[] allParams = method.getParameters(); + for (ILocalVariable param : allParams) { + if (!isParamPath(type, param)) { + String signature = param.getTypeSignature(); + String formatSignature = signature.replace("/", "."); + String resolvedTypeName = JavaModelUtil.getResolvedTypeName(formatSignature, type); + String typeName = null; + if (resolvedTypeName == null) { + typeName = Signature.getSignatureSimpleName(signature); + } + if ((resolvedTypeName != null + && Constants.LONG_MESSAGE_CLASSES.contains(resolvedTypeName)) + || Constants.SHORT_MESSAGE_CLASSES.contains(typeName)) { + Constants.MESSAGE_FORMAT messageFormat = resolvedTypeName != null ? getMessageFormat(resolvedTypeName, true) : getMessageFormat(typeName, false); + switch (messageFormat) { + case TEXT: + if (onMessageTextUsed != null) { + Range range = PositionUtils.toNameRange(annotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("OnMessageDuplicateMethod"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.OnMessageDuplicateMethod, + DiagnosticSeverity.Error)); + + range = PositionUtils.toNameRange(onMessageTextUsed, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("OnMessageDuplicateMethod"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.OnMessageDuplicateMethod, + DiagnosticSeverity.Error)); + } + onMessageTextUsed = annotation; + break; + case BINARY: + if (onMessageBinaryUsed != null) { + Range range = PositionUtils.toNameRange(annotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("OnMessageDuplicateMethod"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.OnMessageDuplicateMethod, + DiagnosticSeverity.Error)); + + range = PositionUtils.toNameRange(onMessageBinaryUsed, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("OnMessageDuplicateMethod"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.OnMessageDuplicateMethod, + DiagnosticSeverity.Error)); + + } + onMessageBinaryUsed = annotation; + break; + case PONG: + if (onMessagePongUsed != null) { + Range range = PositionUtils.toNameRange(annotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("OnMessageDuplicateMethod"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.OnMessageDuplicateMethod, + DiagnosticSeverity.Error)); + + range = PositionUtils.toNameRange(onMessagePongUsed, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("OnMessageDuplicateMethod"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.OnMessageDuplicateMethod, + DiagnosticSeverity.Error)); + } + onMessagePongUsed = annotation; + break; + } + } + } + } + } + } + } + } + + /** + * Create an error diagnostic if a ServerEndpoint annotation's URI contains + * relative + * paths, missing a leading slash, or does not follow a valid level-1 template + * URI. + */ + private void serverEndpointErrorCheck(JavaDiagnosticsContext context, String uri, IType type, + List diagnostics, ICompilationUnit unit) throws JavaModelException { + IAnnotation[] annotations = type.getAnnotations(); + for (IAnnotation annotation : annotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, annotation.getElementName(), + Constants.SERVER_ENDPOINT_ANNOTATION)) { + for (IMemberValuePair annotationMemberValuePair : annotation.getMemberValuePairs()) { + if (annotationMemberValuePair.getMemberName().equals(Constants.ANNOTATION_VALUE)) { + String path = annotationMemberValuePair.getValue().toString(); + if (!DiagnosticUtils.hasLeadingSlash(path)) { + Range range = PositionUtils.toNameRange(annotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ServerEndpointNoSlash"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidEndpointPathWithNoStartingSlash, + DiagnosticSeverity.Error)); + } + if (hasRelativePathURIs(path)) { + Range range = PositionUtils.toNameRange(annotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ServerEndpointRelative"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidEndpointPathWithRelativePaths, + DiagnosticSeverity.Error)); + } else if (!DiagnosticUtils.isValidLevel1URI(path)) { + Range range = PositionUtils.toNameRange(annotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ServerEndpointNotLevel1"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidEndpointPathNotTempleateOrPartialURI, + DiagnosticSeverity.Error)); + } + if (hasDuplicateURIVariables(path)) { + Range range = PositionUtils.toNameRange(annotation, context.getUtils()); + diagnostics.add(context.createDiagnostic(uri, + Messages.getMessage("ServerEndpointDuplicateVar"), range, + Constants.DIAGNOSTIC_SOURCE, null, + ErrorCode.InvalidEndpointPathDuplicateVariable, + DiagnosticSeverity.Error)); + } + } + } + } + } + } + + /** + * Finds a WebSocket EndPoint annotation and extracts all variable parameters in + * the EndPoint URI + * + * @param type representing the class + * @return List of variable parameters in the EndPoint URI if one exists, null + * otherwise + */ + private List findAndProcessEndpointURI(IType type) throws JavaModelException { + String endpointURI = null; + IAnnotation[] typeAnnotations = type.getAnnotations(); + String[] targetAnnotations = { Constants.SERVER_ENDPOINT_ANNOTATION, Constants.CLIENT_ENDPOINT_ANNOTATION }; + for (IAnnotation annotation : typeAnnotations) { + String matchedAnnotation = DiagnosticUtils.getMatchedJavaElementName(type, annotation.getElementName(), + targetAnnotations); + if (matchedAnnotation != null) { + IMemberValuePair[] valuePairs = annotation.getMemberValuePairs(); + for (IMemberValuePair pair : valuePairs) { + if (pair.getMemberName().equals(Constants.ANNOTATION_VALUE) + && pair.getValueKind() == IMemberValuePair.K_STRING) { + endpointURI = (String) pair.getValue(); + } + } + } + } + if (endpointURI == null) { + return null; + } + List endpointPathVars = new ArrayList(); + String[] endpointParts = endpointURI.split(Constants.URI_SEPARATOR); + for (String part : endpointParts) { + if (part.startsWith(Constants.CURLY_BRACE_START) + && part.endsWith(Constants.CURLY_BRACE_END)) { + endpointPathVars.add(part.substring(1, part.length() - 1)); + } + } + return endpointPathVars; + } + + /** + * Check if valueClass is a wrapper object for a primitive value Based on + * https://github.com/eclipse/lsp4mp/blob/9789a1a996811fade43029605c014c7825e8f1da/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/jdt/core/utils/JDTTypeUtils.java#L294-L298 + * + * @param valueClass the resolved type of valueClass in string or the simple + * type of valueClass + * @return if valueClass is a wrapper object + */ + private boolean isWrapper(String valueClass) { + return Constants.WRAPPER_OBJS.contains(valueClass) + || Constants.RAW_WRAPPER_OBJS.contains(valueClass); + } + + /** + * Checks if type is a WebSocket endpoint by meeting one of the 2 conditions + * listed on + * https://jakarta.ee/specifications/websocket/2.0/websocket-spec-2.0.html#applications + * are met: class is annotated or class implements Endpoint class + * + * @param type the type representing the class + * @return the conditions for a class to be a WebSocket endpoint + * @throws JavaModelException + */ + private HashMap isWSEndpoint(IType type) throws JavaModelException { + HashMap wsEndpoint = new HashMap<>(); + + // check trivial case + if (!type.isClass()) { + wsEndpoint.put(Constants.IS_ANNOTATION, false); + wsEndpoint.put(Constants.IS_SUPERCLASS, false); + return wsEndpoint; + } + + // Check that class follows + // https://jakarta.ee/specifications/websocket/2.0/websocket-spec-2.0.html#applications + List endpointAnnotations = DiagnosticUtils.getMatchedJavaElementNames(type, + Stream.of(type.getAnnotations()).map(annotation -> annotation.getElementName()).toArray(String[]::new), + Constants.WS_ANNOTATION_CLASS); + + boolean useSuperclass = false; + try { + useSuperclass = TypeHierarchyUtils.doesITypeHaveSuperType(type, Constants.ENDPOINT_SUPERCLASS) >= 0; + } catch (CoreException e) { + JakartaCorePlugin.logException(Constants.DIAGNOSTIC_ERR_MSG, e); + } + + wsEndpoint.put(Constants.IS_ANNOTATION, (endpointAnnotations.size() > 0)); + wsEndpoint.put(Constants.IS_SUPERCLASS, useSuperclass); + + return wsEndpoint; + } + + private boolean isParamPath(IType type, ILocalVariable param) throws JavaModelException { + IAnnotation[] allVariableAnnotations = param.getAnnotations(); + for (IAnnotation variableAnnotation : allVariableAnnotations) { + if (DiagnosticUtils.isMatchedJavaElement(type, variableAnnotation.getElementName(), + Constants.PATH_PARAM_ANNOTATION)) { + return true; + } + } + return false; + } + + private Constants.MESSAGE_FORMAT getMessageFormat(String typeName, boolean longName) { + if (longName) { + switch (typeName) { + case Constants.STRING_CLASS_LONG: + return Constants.MESSAGE_FORMAT.TEXT; + case Constants.READER_CLASS_LONG: + return Constants.MESSAGE_FORMAT.TEXT; + case Constants.BYTEBUFFER_CLASS_LONG: + return Constants.MESSAGE_FORMAT.BINARY; + case Constants.INPUTSTREAM_CLASS_LONG: + return Constants.MESSAGE_FORMAT.BINARY; + case Constants.PONGMESSAGE_CLASS_LONG: + return Constants.MESSAGE_FORMAT.PONG; + default: + throw new IllegalArgumentException("Invalid message format type"); + } + } + switch (typeName) { + case Constants.STRING_CLASS_SHORT: + return Constants.MESSAGE_FORMAT.TEXT; + case Constants.READER_CLASS_SHORT: + return Constants.MESSAGE_FORMAT.TEXT; + case Constants.BYTEBUFFER_CLASS_SHORT: + return Constants.MESSAGE_FORMAT.BINARY; + case Constants.INPUTSTREAM_CLASS_SHORT: + return Constants.MESSAGE_FORMAT.BINARY; + case Constants.PONGMESSAGE_CLASS_SHORT: + return Constants.MESSAGE_FORMAT.PONG; + default: + throw new IllegalArgumentException("Invalid message format type"); + } + } + + private String createParamTypeDiagMsg(Set methodParamOptTypes, String methodAnnotTarget) { + String paramMessage = String.join("\n- ", methodParamOptTypes); + return Messages.getMessage("WebSocketParamType", "@" + methodAnnotTarget, paramMessage); + } + + /** + * Check if a URI string contains any sequence with //, /./, or /../ + * + * @param uriString ServerEndpoint URI + * @return if a URI has a relative path + */ + private boolean hasRelativePathURIs(String uriString) { + return uriString.matches(Constants.REGEX_RELATIVE_PATHS); + } + + /** + * Check if a URI string has a duplicate variable + * + * @param uriString ServerEndpoint URI + * @return if a URI has duplicate variables + */ + private boolean hasDuplicateURIVariables(String uriString) { + HashSet variables = new HashSet(); + for (String segment : uriString.split(Constants.URI_SEPARATOR)) { + if (segment.matches(Constants.REGEX_URI_VARIABLE)) { + String variable = segment.substring(1, segment.length() - 1); + if (variables.contains(variable)) { + return true; + } else { + variables.add(variable); + } + } + } + return false; + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/resources/org/eclipse/lsp4jakarta/jdt/core/messages/messages.properties b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/resources/org/eclipse/lsp4jakarta/jdt/core/messages/messages.properties index e8a9d72e..21790ab6 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/resources/org/eclipse/lsp4jakarta/jdt/core/messages/messages.properties +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.core/src/main/resources/org/eclipse/lsp4jakarta/jdt/core/messages/messages.properties @@ -11,7 +11,7 @@ # NLS_MESSAGEFORMAT_VAR # NLS_ENCODING=UNICODE -# AnnotationDiagnosticsCollector +# AnnotationDiagnosticsParticipant AnnotationMustDefineAttribute = The {0} annotation must define the attribute ''{1}''. AnnotationMustDefineAttributeFollowing8601 = The {0} annotation must define the attribute ''{1}'' following the ISO 8601 standard. MethodMustNotHaveParameters = A method with the {0} annotation must not have any parameters. @@ -25,36 +25,19 @@ LetClassExtend = Let ''{0}'' extend ''{1}'' # FilterImplementationQuickFix LetClassImplement = Let ''{0}'' implement ''{1}'' -# InsertAnnotationAttributesQuickFix -AddAtoB = Add {0} to {1} - -# InsertAnnotationMissingQuickFix -InsertItem = Insert {0} - -# RemoveAnnotationConflictQuickFix -RemoveItem = Remove {0} - -# RemoveParamAnnotationQuickFix +# RemoveMethodParamAnnotationQuickFix RemoveTheModifierFromParameter = Remove the {0} modifier from parameter ''{1}'' -# RemoveModifierConflictQuickFix -RemoveTheModifierFromThisVariable = Remove the ''{0}'' modifier from this variable -RemoveTheModifierFromThisField = Remove the ''{0}'' modifier from this field -RemoveTheModifierFromThisMethod = Remove the ''{0}'' modifier from this method -RemoveTheModifierFromThisClass = Remove the ''{0}'' modifier from this class -RemoveTheModifierFromThis = Remove the ''{0}'' modifier from this {1} - -# RemoveMethodParametersQuickFix +# RemoveAllMethodParametersQuickFix RemoveAllParameters = Remove all parameters # PostConstructReturnTypeQuickFix ChangeReturnTypeToVoid = Change return type to void -# BeanValidationQuickFix +# RemoveDynamicConstraintAnnotationQuickFix RemoveConstraintAnnotation = Remove constraint annotation {0} from element -RemoveStaticModifier = Remove static modifier from element -# BeanValidationDiagnosticsCollector +# BeanValidationDiagnosticsParticipant ConstraintAnnotationsMethod = Constraint annotations are not allowed on static methods. ConstraintAnnotationsField = Constraint annotations are not allowed on static fields. AnnotationBooleanMethods = The {0} annotation can only be used on boolean and Boolean type methods. @@ -72,7 +55,7 @@ AnnotationMinMaxFields = The {0} annotation can only be used on \n- BigDecimal \ AnnotationPositiveMethods = The {0} annotation can only be used on \n- BigDecimal \n- BigInteger\n- byte, short, int, long, float, double (and their respective wrappers) \n type methods. AnnotationPositiveFields = The {0} annotation can only be used on \n- BigDecimal \n- BigInteger\n- byte, short, int, long, float, double (and their respective wrappers) \n type fields. -# ManagedBeanDiagnosticsCollector +# ManagedBeanDiagnosticsParticipant ScopeTypeAnnotationsManagedBean = Scope type annotations must be specified by a managed bean class at most once. ScopeTypeAnnotationsProducerField = Scope type annotations must be specified by a producer field at most once. ScopeTypeAnnotationsProducerMethod = Scope type annotations must be specified by a producer method at most once. @@ -83,16 +66,13 @@ ManagedBeanInvalidDisposer = A disposer method cannot have parameter(s) annotate ManagedBeanInvalidProduces = A producer method cannot have parameter(s) annotated with {0}. ManagedBeanInvalidInject = A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with {0}. ManagedBeanGenericType = Managed bean class of generic type must have scope @Dependent. -ManagedBeanProducesAndInject = The @Produces and @Inject annotations must not be used on the same field or property. - -# ManagedBeanNoArgConstructorQuickFix -AddProtectedConstructor = Add a no-arg protected constructor to this class -AddPublicConstructor = Add a no-arg public constructor to this class +ManagedBeanProducesAndInjectField = The @Produces and @Inject annotations must not be used on the same field or property. +ManagedBeanProducesAndInjectMethod = The @Produces and @Inject annotations must not be used on the same method. # ManagedBeanQuickFix ReplaceCurrentScope = Replace current scope with {0} -# DependencyInjectionDiagnosticsCollector +# DependencyInjectionDiagnosticsParticipant InjectNoFinalField = The @Inject annotation must not define a final field. InjectNoFinalMethod = The @Inject annotation must not define a final method. InjectNoAbstractMethod = The @Inject annotation must not define an abstract method. @@ -100,71 +80,67 @@ InjectNoStaticMethod = The @Inject annotation must not define a static method. InjectNoGenericMethod = The @Inject annotation must not define a generic method. InjectMoreThanOneConstructor = The @Inject annotation must not define more than one constructor. -# Jax_RSClassDiagnosticsCollector +# ClassConstructorDiagnosticsParticipant RootResourceClasses = Root resource classes are instantiated by the JAX-RS runtime and MUST have a public constructor. ProviderClasses = Provider classes are instantiated by the JAX-RS runtime and MUST have a public constructor. ConstructorIsUnused = This constructor is unused, as root resource classes will only use the constructor with the most parameters. MultipleConstructorsNumberOfParameters = Multiple constructors have the same number of parameters, it might be ambiguous which constructor is used. -# NonPublicResourceMethodQuickFix +# UpdateMethodAccessToPublicQuickFix MakeMethodPublic = Make method public -# NoResourcePublicConstructorQuickFix +# UpdateContructorAccessToPublicQuickFix MakeConstructorPublic = Make constructor public -NoargPublicConstructor = Add a no-arg public constructor to this class -# ResourceMethodDiagnosticsCollector +# ResourceMethodDiagnosticsParticipant OnlyPublicMethods = Only public methods can be exposed as resource methods. ResourceMethodsEntityParameter = Resource methods cannot have more than one entity parameter. -# ResourceMethodMultipleEntityParamsQuickFix +# RemoveMethodEntityParamsWithExclusionQuickFix RemoveAllEntityParametersExcept = Remove all entity parameters except {0} -# JsonbDiagnosticsCollector +# JsonbDiagnosticsParticipant ErrorMessageJsonbCreator = Only one constructor or static factory method can be annotated with @JsonbCreator in a given class. ErrorMessageJsonbTransientOnField = When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations. ErrorMessageJsonbTransientOnAccessor = When an accessor is annotated with @JsonbTransient, its field or the accessor must not be annotated with other JSON Binding annotations. -# JsonpDiagnosticCollector +# JsonpDiagnosticParticipant CreatePointerErrorMessage = Json.createPointer target must be a sequence of '/' prefixed tokens or an empty String. -# PersistenceAnnotationQuickFix -AddTheMissingAttributes = Add the missing attributes to the @MapKeyJoinColumn annotation - -# PersistenceEntityDiagnosticsCollector +# PersistenceEntityDiagnosticsParticipant EntityNoFinalMethods = A class using the @Entity annotation cannot contain any methods that are declared final. EntityNoFinalVariables = A class using the @Entity annotation cannot contain any persistent instance variables that are declared final. EntityNoArgConstructor = A class using the @Entity annotation must contain a public or protected constructor with no arguments. EntityNoFinalClass = A class using the @Entity annotation must not be final. -# PersistenceEntityQuickFix -AddNoArgProtectedConstructor = Add a no-arg protected constructor to this class -AddNoArgPublicConstructor = Add a no-arg public constructor to this class - -# PersistenceMapKeyDiagnosticsCollector +# PersistenceMapKeyDiagnosticsParticipant MapKeyAnnotationsNotOnSameField = @MapKeyClass and @MapKey annotations cannot be used on the same field or property. +MapKeyAnnotationsNotOnSameMethod = @MapKeyClass and @MapKey annotations cannot be used on the same method. MultipleMapKeyJoinColumnMethod = A method with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations. MultipleMapKeyJoinColumnField = A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations. -# CompleteFilterAnnotationQuickFix +# InserMapKeyJoinColumAnnotationAttributesQuickFix +InsertTheMissingAttributes = Insert the missing attributes to the @MapKeyJoinColumn annotation + +# CompleteFilterAnnotationQuickFix, CompleteServletAnnotationQuickFix AddTheAttributeTo = Add the `{0}` attribute to {1} RemoveTheAttriubuteFrom = Remove the `{0}` attribute from {1} -# FilterDiagnosticsCollector +# FilterDiagnosticsParticipant WebFilterMustImplement = Annotated classes with @WebFilter must implement the Filter interface. WebFilterMustDefine = The annotation @WebFilter must define the attribute 'urlPatterns', 'servletNames' or 'value'. WebFilterCannotHaveBoth = The annotation @WebFilter can not have both 'value' and 'urlPatterns' attributes specified at once. -# ListenerDiagnosticsCollector +# ListenerDiagnosticsParticipant AnnotatedWithWebListenerMustImplement = Annotated classes with @WebListener must implement one or more of the following interfaces: ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener, HttpSessionListener, HttpSessionAttributeListener, or HttpSessionIdListener. -# ServletDiagnosticsCollector +# ServletDiagnosticsParticipant WebServletMustExtend = Annotated classes with @WebServlet must extend the HttpServlet class. WebServletShouldExtend = Annotated classes with @WebServlet should extend the HttpServlet class. WebServletMustDefine = The @WebServlet annotation must define the attribute 'urlPatterns' or 'value'. WebServletCannotHaveBoth = The @WebServlet annotation cannot have both 'value' and 'urlPatterns' attributes specified at once. -# WebSocketDiagnosticsCollector +# WebSocketDiagnosticsParticipant WebSocketParamType = Invalid parameter type. When using {0}, parameter must be of type: \n- {1}\n- annotated with @PathParams and of type String or any Java primitive type or boxed version thereof. PathParamsAnnotationMissing = Parameters of type String, any Java primitive type, or boxed version thereof must be annotated with @PathParams. PathParamWarning = PathParam value does not match specified Endpoint URI. @@ -172,4 +148,4 @@ OnMessageDuplicateMethod = Classes annotated with @ServerEndpoint or @ClientEndp ServerEndpointNoSlash = Server endpoint paths must start with a leading '/'. ServerEndpointRelative = Server endpoint paths must not contain the sequences '/../', '/./' or '//'. ServerEndpointNotLevel1 = Server endpoint paths must be a URI-template (level-1) or a partial URI. -ServerEndpointDuplicateVar = Server endpoint paths must not use the same variable more than once in a path. +ServerEndpointDuplicateVar = Server endpoint paths must not use the same variable more than once in a path. \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.site/pom.xml b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.site/pom.xml index bcbe9b06..be77c916 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.site/pom.xml +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.site/pom.xml @@ -1,13 +1,16 @@ - - 4.0.0 - - org.eclipse.lsp4jakarta - jdt-parent - 0.1.2-SNAPSHOT - - org.eclipse.lsp4jakarta.jdt.site - eclipse-repository - - Eclipse LSP4Jakarta JDT Plugin P2 Site - Eclipse LSP4Jakarta JDT Plugin P2 Site + + + 4.0.0 + + org.eclipse.lsp4jakarta + jdt-parent + 0.2.0-SNAPSHOT + + org.eclipse.lsp4jakarta.jdt.site + eclipse-repository + Eclipse LSP4Jakarta JDT Plugin P2 Site + Eclipse LSP4Jakarta JDT Plugin P2 Site \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/META-INF/MANIFEST.MF b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/META-INF/MANIFEST.MF index 198c3711..d1b54d91 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/META-INF/MANIFEST.MF +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: Eclipse LSP4Jakarta JDT Test Plugin (Incubation) Bundle-Vendor: Eclipse LSP4Jakarta Bundle-SymbolicName: org.eclipse.lsp4jakarta.jdt.test -Bundle-Version: 0.1.2.qualifier +Bundle-Version: 0.2.0.qualifier Automatic-Module-Name: org.eclipse.lsp4jakarta.jdt.test Bundle-RequiredExecutionEnvironment: JavaSE-17 Require-Bundle: diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/pom.xml b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/pom.xml index 61d12f6c..d3c8e50c 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/pom.xml +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/pom.xml @@ -1,64 +1,63 @@ - - 4.0.0 - - org.eclipse.lsp4jakarta - jdt-parent - 0.1.2-SNAPSHOT - - org.eclipse.lsp4jakarta.jdt.test - eclipse-test-plugin - - Eclipse LSP4Jakarta JDT Test Plugin - Eclipse LSP4Jakarta JDT Test Plugin - - - Eclipse LSP4Jakarta - https://github.com/eclipse/lsp4jakarta - - - - - EPL-2.0 - https://www.eclipse.org/legal/epl-2.0/ - Eclipse Public License 2.0 - - - - - ${project.build.directory}/projects - vscode/snapshots/builds/jakarta-jdt/${project.version} - false - - - - - - - org.eclipse.tycho - target-platform-configuration - ${tycho.version} - - - - - - maven-assembly-plugin - - - package - - single - - - true - ${project.build.directory}/projects - - src/main/assembly/projects.xml - - - - - - - + + + 4.0.0 + + org.eclipse.lsp4jakarta + jdt-parent + 0.2.0-SNAPSHOT + + org.eclipse.lsp4jakarta.jdt.test + eclipse-test-plugin + Eclipse LSP4Jakarta JDT Test Plugin + Eclipse LSP4Jakarta JDT Test Plugin + + Eclipse LSP4Jakarta + https://github.com/eclipse/lsp4jakarta + + + + EPL-2.0 + https://www.eclipse.org/legal/epl-2.0/ + Eclipse Public License 2.0 + + + + ${project.build.directory}/projects + vscode/snapshots/builds/jakarta-jdt/${project.version} + false + + + + + + org.eclipse.tycho + target-platform-configuration + ${tycho.version} + + + + + + maven-assembly-plugin + + + package + + single + + + true + ${project.build.directory}/projects + + src/main/assembly/projects.xml + + + + + + + \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/pom.xml b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/pom.xml index 4c0f1861..944c0829 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/pom.xml +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/pom.xml @@ -1,24 +1,21 @@ - - - 4.0.0 - - io.openliberty - demo-servlet-no-diagnostics - 0.0.1-SNAPSHOT - - demo-servlet-no-diagnostics - - http://www.example.com - - - UTF-8 - 1.7 - 1.7 - - - + + 4.0.0 + io.openliberty + demo-servlet-no-diagnostics + 0.0.1-SNAPSHOT + demo-servlet-no-diagnostics + + http://www.example.com + + UTF-8 + 17 + 17 + + jakarta.platform @@ -76,18 +73,15 @@ test - ${project.artifactId} - io.openliberty.tools liberty-maven-plugin - 3.8.2 + 3.9 - org.apache.maven.plugins maven-war-plugin @@ -106,4 +100,4 @@ - + \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/MultipleEntityParamsResourceMethod.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/MultipleEntityParamsResourceMethod.java similarity index 94% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/MultipleEntityParamsResourceMethod.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/MultipleEntityParamsResourceMethod.java index 1a563458..c390b11d 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/MultipleEntityParamsResourceMethod.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/MultipleEntityParamsResourceMethod.java @@ -11,7 +11,7 @@ * Bera Sogut *******************************************************************************/ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.fake.rs.DELETE; import jakarta.fake.rs.FormParam; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorClass.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorClass.java similarity index 81% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorClass.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorClass.java index e541bff6..bf179d42 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorClass.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorClass.java @@ -1,4 +1,4 @@ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.fake.rs.Path; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorProviderClass.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorProviderClass.java similarity index 95% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorProviderClass.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorProviderClass.java index 19a8f154..1a9ff3da 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorProviderClass.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorProviderClass.java @@ -1,4 +1,4 @@ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import java.io.IOException; import java.io.InputStream; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NotPublicResourceMethod.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NotPublicResourceMethod.java similarity index 93% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NotPublicResourceMethod.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NotPublicResourceMethod.java index 31be4fc7..b9dc2564 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/NotPublicResourceMethod.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/NotPublicResourceMethod.java @@ -11,7 +11,7 @@ * IBM Corporation, Matthew Shocrylas - initial API and implementation *******************************************************************************/ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.fake.rs.HEAD; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsDiffLen.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsDiffLen.java similarity index 83% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsDiffLen.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsDiffLen.java index d8e139ab..97a9ebec 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsDiffLen.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsDiffLen.java @@ -1,4 +1,4 @@ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.fake.rs.Path; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsEqualLen.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsEqualLen.java similarity index 83% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsEqualLen.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsEqualLen.java index 9e341572..e4683642 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsEqualLen.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/demo-servlet-no-diagnostics/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsEqualLen.java @@ -1,4 +1,4 @@ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.fake.rs.Path; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/pom.xml b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/pom.xml index 6ac27b8c..a8266d15 100755 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/pom.xml +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/pom.xml @@ -1,20 +1,20 @@ - + 4.0.0 - io.openliberty jakarta-servlet 1.0-SNAPSHOT war - - 1.8 - 1.8 + 17 + 17 UTF-8 UTF-8 ${project.artifactId} - @@ -73,18 +73,15 @@ test - ${project.artifactId} - io.openliberty.tools liberty-maven-plugin - 3.8.2 + 3.9 - org.apache.maven.plugins maven-war-plugin @@ -103,4 +100,4 @@ - + \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/MultipleEntityParamsResourceMethod.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/MultipleEntityParamsResourceMethod.java similarity index 94% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/MultipleEntityParamsResourceMethod.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/MultipleEntityParamsResourceMethod.java index 1ab0fc4c..093d3059 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/MultipleEntityParamsResourceMethod.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/MultipleEntityParamsResourceMethod.java @@ -11,7 +11,7 @@ * Bera Sogut *******************************************************************************/ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.FormParam; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorClass.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorClass.java similarity index 81% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorClass.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorClass.java index 128787c3..1e289fb9 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorClass.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorClass.java @@ -1,4 +1,4 @@ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.ws.rs.Path; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorProviderClass.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorProviderClass.java similarity index 95% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorProviderClass.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorProviderClass.java index f5d859c7..ca9ca189 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorProviderClass.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorProviderClass.java @@ -1,4 +1,4 @@ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import java.io.IOException; import java.io.InputStream; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NotPublicResourceMethod.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NotPublicResourceMethod.java similarity index 93% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NotPublicResourceMethod.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NotPublicResourceMethod.java index 20783fb2..ff949bd0 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/NotPublicResourceMethod.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/NotPublicResourceMethod.java @@ -11,7 +11,7 @@ * IBM Corporation, Matthew Shocrylas - initial API and implementation *******************************************************************************/ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.ws.rs.HEAD; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsDiffLen.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsDiffLen.java similarity index 83% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsDiffLen.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsDiffLen.java index b8bc06d5..7818bf0e 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsDiffLen.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsDiffLen.java @@ -1,4 +1,4 @@ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.ws.rs.Path; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsEqualLen.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsEqualLen.java similarity index 83% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsEqualLen.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsEqualLen.java index 2b91f0b6..d2119975 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsEqualLen.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsEqualLen.java @@ -1,4 +1,4 @@ -package io.openliberty.sample.jakarta.jax_rs; +package io.openliberty.sample.jakarta.jaxrs; import jakarta.ws.rs.Path; diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/liberty/config/server.xml b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/liberty/config/server.xml index a90353bd..0d77d05f 100755 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/liberty/config/server.xml +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/projects/jakarta-sample/src/main/liberty/config/server.xml @@ -7,4 +7,4 @@ - + \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/GeneratedAnnotationTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/GeneratedAnnotationTest.java index 8a28aad5..d998c2bc 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/GeneratedAnnotationTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/GeneratedAnnotationTest.java @@ -22,37 +22,34 @@ import org.eclipse.jdt.core.IJavaProject; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class GeneratedAnnotationTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void GeneratedAnnotation() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/annotations/GeneratedAnnotation.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/annotations/GeneratedAnnotation.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // expected annotations Diagnostic d1 = d(7, 4, 63, - "The @Generated annotation must define the attribute 'date' following the ISO 8601 standard.", - DiagnosticSeverity.Error, "jakarta-annotations", "InvalidDateFormat"); - - Diagnostic d2 = d(13, 4, 70, - "The @Generated annotation must define the attribute 'date' following the ISO 8601 standard.", - DiagnosticSeverity.Error, "jakarta-annotations", "InvalidDateFormat"); - + "The @Generated annotation must define the attribute 'date' following the ISO 8601 standard.", + DiagnosticSeverity.Error, "jakarta-annotations", "InvalidDateFormat"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2); + Diagnostic d2 = d(13, 4, 70, + "The @Generated annotation must define the attribute 'date' following the ISO 8601 standard.", + DiagnosticSeverity.Error, "jakarta-annotations", "InvalidDateFormat"); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); } - -} +} \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/PostConstructAnnotationTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/PostConstructAnnotationTest.java index 2827c250..c0ea4194 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/PostConstructAnnotationTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/PostConstructAnnotationTest.java @@ -28,50 +28,51 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class PostConstructAnnotationTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void GeneratedAnnotation() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/annotations/PostConstructAnnotation.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/annotations/PostConstructAnnotation.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // expected Diagnostics Diagnostic d1 = d(15, 19, 31, "A method with the @PostConstruct annotation must be void.", - DiagnosticSeverity.Error, "jakarta-annotations", "PostConstructReturnType"); + DiagnosticSeverity.Error, "jakarta-annotations", "PostConstructReturnType"); Diagnostic d2 = d(20, 16, 28, "A method with the @PostConstruct annotation must not have any parameters.", - DiagnosticSeverity.Error, "jakarta-annotations", "PostConstructParams"); + DiagnosticSeverity.Error, "jakarta-annotations", "PostConstructParams"); Diagnostic d3 = d(25, 16, 28, "A method with the @PostConstruct annotation must not throw checked exceptions.", - DiagnosticSeverity.Warning, "jakarta-annotations", "PostConstructException"); + DiagnosticSeverity.Warning, "jakarta-annotations", "PostConstructException"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2, d3); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3); JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d2); TextEdit te1 = te(19, 4, 20, 4, ""); TextEdit te2 = te(20, 29, 20, 40, ""); CodeAction ca1 = ca(uri, "Remove @PostConstruct", d2, te1); CodeAction ca2 = ca(uri, "Remove all parameters", d2, te2); - assertJavaCodeAction(codeActionParams1, JDT_UTILS, ca1, ca2); - + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1, ca2); + JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d1); TextEdit te3 = te(15, 11, 15, 18, "void"); CodeAction ca3 = ca(uri, "Change return type to void", d1, te3); - assertJavaCodeAction(codeActionParams2, JDT_UTILS, ca3); + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca3); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/PreDestroyAnnotationTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/PreDestroyAnnotationTest.java index 0c17a53a..ecc4f7ae 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/PreDestroyAnnotationTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/PreDestroyAnnotationTest.java @@ -12,7 +12,12 @@ *******************************************************************************/ package org.eclipse.lsp4jakarta.jdt.annotations; -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.*; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaCodeAction; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.ca; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.createCodeActionParams; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.te; import java.util.Arrays; @@ -23,54 +28,52 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class PreDestroyAnnotationTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void GeneratedAnnotation() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/annotations/PreDestroyAnnotation.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/annotations/PreDestroyAnnotation.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // expected annotations - + Diagnostic d1 = d(20, 16, 28, "A method with the @PreDestroy annotation must not have any parameters.", - DiagnosticSeverity.Error, "jakarta-annotations", "PreDestroyParams"); - + DiagnosticSeverity.Error, "jakarta-annotations", "PreDestroyParams"); + Diagnostic d2 = d(26, 20, 31, "A method with the @PreDestroy annotation must not be static.", - DiagnosticSeverity.Error, "jakarta-annotations", "PreDestroyStatic"); - d2.setData(9); - + DiagnosticSeverity.Error, "jakarta-annotations", "PreDestroyStatic"); + Diagnostic d3 = d(31, 13, 25, "A method with the @PreDestroy annotation must not throw checked exceptions.", - DiagnosticSeverity.Warning, "jakarta-annotations", "PreDestroyException"); + DiagnosticSeverity.Warning, "jakarta-annotations", "PreDestroyException"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d2, d1, d3); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d2, d1, d3); - - JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d1); - TextEdit te = te(19, 1, 20, 1,""); - TextEdit te1 = te(20, 29, 20, 40,""); + TextEdit te = te(19, 1, 20, 1, ""); + TextEdit te1 = te(20, 29, 20, 40, ""); CodeAction ca = ca(uri, "Remove @PreDestroy", d1, te); - CodeAction ca1= ca(uri, "Remove all parameters", d1, te1); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca, ca1); - + CodeAction ca1 = ca(uri, "Remove all parameters", d1, te1); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca, ca1); + JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d2); - TextEdit te2 = te(25, 1, 26, 1,""); - TextEdit te3 = te(26, 7, 26, 14,""); + TextEdit te2 = te(25, 1, 26, 1, ""); + TextEdit te3 = te(26, 7, 26, 14, ""); CodeAction ca2 = ca(uri, "Remove @PreDestroy", d2, te2); - CodeAction ca3= ca(uri, "Remove the 'static' modifier from this method", d2, te3); - assertJavaCodeAction(codeActionParams1, JDT_UTILS, ca2, ca3); + CodeAction ca3 = ca(uri, "Remove the 'static' modifier", d2, te3); + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca2, ca3); } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourceAnnotationTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourceAnnotationTest.java index 6572c656..40f8fec8 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourceAnnotationTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/annotations/ResourceAnnotationTest.java @@ -28,46 +28,44 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class ResourceAnnotationTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void GeneratedAnnotation() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/annotations/ResourceAnnotation.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/annotations/ResourceAnnotation.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // expected annotations Diagnostic d1 = d(22, 0, 22, "The @Resource annotation must define the attribute 'type'.", - DiagnosticSeverity.Error, "jakarta-annotations", "MissingResourceTypeAttribute"); + DiagnosticSeverity.Error, "jakarta-annotations", "MissingResourceTypeAttribute"); Diagnostic d2 = d(39, 0, 30, "The @Resource annotation must define the attribute 'name'.", - DiagnosticSeverity.Error, "jakarta-annotations", "MissingResourceNameAttribute"); + DiagnosticSeverity.Error, "jakarta-annotations", "MissingResourceNameAttribute"); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2); - - JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d1); TextEdit te = te(22, 0, 22, 22, "@Resource(name = \"aa\", type = \"\")"); - CodeAction ca= ca(uri, "Add type to jakarta.annotation.Resource", d1, te); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca); - + CodeAction ca = ca(uri, "Insert 'type' attribute to @Resource", d1, te); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca); + JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d2); TextEdit te1 = te(39, 0, 39, 30, "@Resource(type = \"\", name = \"\")"); - CodeAction ca1= ca(uri, "Add name to jakarta.annotation.Resource", d2, te1); - assertJavaCodeAction(codeActionParams1, JDT_UTILS, ca1); + CodeAction ca1 = ca(uri, "Insert 'name' attribute to @Resource", d2, te1); + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1); } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/beanvalidation/BeanValidationTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/beanvalidation/BeanValidationTest.java index e5bfd6c9..d04b2669 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/beanvalidation/BeanValidationTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/beanvalidation/BeanValidationTest.java @@ -28,213 +28,233 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; public class BeanValidationTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void validFieldConstraints() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/beanvalidation/ValidConstraints.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/beanvalidation/ValidConstraints.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - // should be no errors - assertJavaDiagnostics(diagnosticsParams, utils); + // should be no errors + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS); } @Test public void fieldConstraintValidation() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/beanvalidation/FieldConstraintValidation.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/beanvalidation/FieldConstraintValidation.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // Test diagnostics Diagnostic d1 = d(10, 16, 23, - "The @AssertTrue annotation can only be used on boolean and Boolean type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "AssertTrue"); + "The @AssertTrue annotation can only be used on boolean and Boolean type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonBooleanMethodOrField", + "AssertTrue"); Diagnostic d2 = d(13, 19, 24, - "The @AssertFalse annotation can only be used on boolean and Boolean type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "AssertFalse"); + "The @AssertFalse annotation can only be used on boolean and Boolean type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonBooleanMethodOrField", + "AssertFalse"); Diagnostic d3 = d(17, 19, 29, - "The @DecimalMax annotation can only be used on: \n" - + "- BigDecimal \n" - + "- BigInteger \n" - + "- CharSequence\n" - + "- byte, short, int, long (and their respective wrappers) \n" - + " type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "DecimalMax"); + "The @DecimalMax annotation can only be used on: \n" + + "- BigDecimal \n" + + "- BigInteger \n" + + "- CharSequence\n" + + "- byte, short, int, long (and their respective wrappers) \n" + + " type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", + "InvalidAnnotationOnNonBigDecimalCharByteShortIntLongMethodOrField", "DecimalMax"); Diagnostic d4 = d(17, 19, 29, - "The @DecimalMin annotation can only be used on: \n" - + "- BigDecimal \n" - + "- BigInteger \n" - + "- CharSequence\n" - + "- byte, short, int, long (and their respective wrappers) \n" - + " type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "DecimalMin"); + "The @DecimalMin annotation can only be used on: \n" + + "- BigDecimal \n" + + "- BigInteger \n" + + "- CharSequence\n" + + "- byte, short, int, long (and their respective wrappers) \n" + + " type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", + "InvalidAnnotationOnNonBigDecimalCharByteShortIntLongMethodOrField", "DecimalMin"); Diagnostic d5 = d(20, 20, 26, - "The @Digits annotation can only be used on: \n" - + "- BigDecimal \n" - + "- BigInteger \n" - + "- CharSequence\n" - + "- byte, short, int, long (and their respective wrappers) \n" - + " type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Digits"); + "The @Digits annotation can only be used on: \n" + + "- BigDecimal \n" + + "- BigInteger \n" + + "- CharSequence\n" + + "- byte, short, int, long (and their respective wrappers) \n" + + " type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", + "InvalidAnnotationOnNonBigDecimalCharByteShortIntLongMethodOrField", "Digits"); Diagnostic d6 = d(23, 20, 32, - "The @Email annotation can only be used on String and CharSequence type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Email"); + "The @Email annotation can only be used on String and CharSequence type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonStringMethodOrField", + "Email"); Diagnostic d7 = d(26, 20, 34, - "The @FutureOrPresent annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "FutureOrPresent"); + "The @FutureOrPresent annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonDateTimeMethodOrField", + "FutureOrPresent"); Diagnostic d8 = d(29, 19, 30, - "The @Future annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Future"); + "The @Future annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonDateTimeMethodOrField", + "Future"); Diagnostic d9 = d(33, 20, 23, - "The @Min annotation can only be used on \n" - + "- BigDecimal \n" - + "- BigInteger\n" - + "- byte, short, int, long (and their respective wrappers) \n" - + " type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Min"); + "The @Min annotation can only be used on \n" + + "- BigDecimal \n" + + "- BigInteger\n" + + "- byte, short, int, long (and their respective wrappers) \n" + + " type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", + "InvalidAnnotationOnNonMinMaxMethodOrField", "Min"); Diagnostic d10 = d(33, 20, 23, - "The @Max annotation can only be used on \n" - + "- BigDecimal \n" - + "- BigInteger\n" - + "- byte, short, int, long (and their respective wrappers) \n" - + " type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Max"); + "The @Max annotation can only be used on \n" + + "- BigDecimal \n" + + "- BigInteger\n" + + "- byte, short, int, long (and their respective wrappers) \n" + + " type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", + "InvalidAnnotationOnNonMinMaxMethodOrField", "Max"); Diagnostic d11 = d(36, 20, 27, - "The @Negative annotation can only be used on \n" - + "- BigDecimal \n" - + "- BigInteger\n" - + "- byte, short, int, long, float, double (and their respective wrappers) \n" - + " type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Negative"); + "The @Negative annotation can only be used on \n" + + "- BigDecimal \n" + + "- BigInteger\n" + + "- byte, short, int, long, float, double (and their respective wrappers) \n" + + " type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", + "InvalidAnnotationOnNonPositiveMethodOrField", "Negative"); Diagnostic d12 = d(39, 19, 25, - "The @NegativeOrZero annotation can only be used on \n" - + "- BigDecimal \n" - + "- BigInteger\n" - + "- byte, short, int, long, float, double (and their respective wrappers) \n" - + " type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "NegativeOrZero"); + "The @NegativeOrZero annotation can only be used on \n" + + "- BigDecimal \n" + + "- BigInteger\n" + + "- byte, short, int, long, float, double (and their respective wrappers) \n" + + " type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", + "InvalidAnnotationOnNonPositiveMethodOrField", "NegativeOrZero"); Diagnostic d13 = d(42, 20, 32, - "The @NotBlank annotation can only be used on String and CharSequence type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "NotBlank"); + "The @NotBlank annotation can only be used on String and CharSequence type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonStringMethodOrField", + "NotBlank"); Diagnostic d14 = d(45, 21, 31, - "The @Pattern annotation can only be used on String and CharSequence type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Pattern"); + "The @Pattern annotation can only be used on String and CharSequence type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonStringMethodOrField", + "Pattern"); Diagnostic d15 = d(48, 19, 33, - "The @Past annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Past"); + "The @Past annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonDateTimeMethodOrField", + "Past"); Diagnostic d16 = d(51, 19, 33, - "The @PastOrPresent annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "PastOrPresent"); + "The @PastOrPresent annotation can only be used on: Date, Calendar, Instant, LocalDate, LocalDateTime, LocalTime, MonthDay, OffsetDateTime, OffsetTime, Year, YearMonth, ZonedDateTime, HijrahDate, JapaneseDate, JapaneseDate, MinguoDate and ThaiBuddhistDate type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonDateTimeMethodOrField", + "PastOrPresent"); Diagnostic d17 = d(54, 21, 25, - "The @Positive annotation can only be used on \n" - + "- BigDecimal \n" - + "- BigInteger\n" - + "- byte, short, int, long, float, double (and their respective wrappers) \n" - + " type fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "Positive"); + "The @Positive annotation can only be used on \n" + + "- BigDecimal \n" + + "- BigInteger\n" + + "- byte, short, int, long, float, double (and their respective wrappers) \n" + + " type fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonPositiveMethodOrField", + "Positive"); // not yet implemented // Diagnostic d18 = d(11, 17, 24, // "The @PositiveOrZero annotation can only be used on boolean and Boolean type fields.", // DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "PositiveOrZero"); Diagnostic d19 = d(60, 27, 36, - "Constraint annotations are not allowed on static fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "MakeNotStatic", "AssertTrue"); + "Constraint annotations are not allowed on static fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidConstrainAnnotationOnStaticMethodOrField", + "AssertTrue"); Diagnostic d20 = d(63, 27, 36, - "Constraint annotations are not allowed on static fields.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "MakeNotStatic", "Past"); - - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2, d3, d4, d5, d6, d7, d8, - d9, d10, d11, d12, d13, d14, d15, d16, d17, d19, d20); + "Constraint annotations are not allowed on static fields.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidConstrainAnnotationOnStaticMethodOrField", + "Past"); - // Test quickfix codeActions - type (1-17), static, static+type (should only display static) + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3, d4, d5, d6, d7, d8, + d9, d10, d11, d12, d13, d14, d15, d16, d17, d19, d20); + + // Test quickfix codeActions - type (1-17), static, static+type (should only + // display static) JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d1); TextEdit te = te(9, 4, 10, 4, ""); - CodeAction ca = ca(uri, "Remove constraint annotation AssertTrue from element", d1, te); + CodeAction ca = ca(uri, "Remove constraint annotation AssertTrue from element", d1, te); - assertJavaCodeAction(codeActionParams, utils, ca); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca); JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d19); TextEdit te1 = te(59, 4, 60, 4, ""); TextEdit te2 = te(60, 11, 60, 18, ""); CodeAction ca1 = ca(uri, "Remove constraint annotation AssertTrue from element", d19, te1); - CodeAction ca2 = ca(uri, "Remove static modifier from element", d19, te2); + CodeAction ca2 = ca(uri, "Remove the 'static' modifier", d19, te2); - assertJavaCodeAction(codeActionParams2, utils, ca1, ca2); - + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca1, ca2); JakartaJavaCodeActionParams codeActionParams3 = createCodeActionParams(uri, d20); TextEdit te3 = te(62, 4, 63, 4, ""); TextEdit te4 = te(63, 11, 63, 18, ""); CodeAction ca3 = ca(uri, "Remove constraint annotation Past from element", d20, te3); - CodeAction ca4 = ca(uri, "Remove static modifier from element", d20, te4); + CodeAction ca4 = ca(uri, "Remove the 'static' modifier", d20, te4); - assertJavaCodeAction(codeActionParams3, utils, ca3, ca4); + assertJavaCodeAction(codeActionParams3, IJDT_UTILS, ca3, ca4); } - + @Test public void methodConstraintValidation() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/beanvalidation/MethodConstraintValidation.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/beanvalidation/MethodConstraintValidation.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // Test diagnostics Diagnostic d1 = d(20, 26, 38, - "Constraint annotations are not allowed on static methods.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "MakeNotStatic", "AssertTrue"); + "Constraint annotations are not allowed on static methods.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidConstrainAnnotationOnStaticMethodOrField", + "AssertTrue"); Diagnostic d2 = d(25, 18, 28, - "The @AssertTrue annotation can only be used on boolean and Boolean type methods.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "FixTypeOfElement", "AssertTrue"); + "The @AssertTrue annotation can only be used on boolean and Boolean type methods.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidAnnotationOnNonBooleanMethodOrField", + "AssertTrue"); Diagnostic d3 = d(30, 23, 33, - "Constraint annotations are not allowed on static methods.", - DiagnosticSeverity.Error, "jakarta-bean-validation", "MakeNotStatic", "AssertFalse"); - - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2, d3); + "Constraint annotations are not allowed on static methods.", + DiagnosticSeverity.Error, "jakarta-bean-validation", "InvalidConstrainAnnotationOnStaticMethodOrField", + "AssertFalse"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3); // Test quickfix codeActions JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d1); TextEdit te = te(19, 4, 20, 4, ""); TextEdit te2 = te(20, 10, 20, 17, ""); CodeAction ca = ca(uri, "Remove constraint annotation AssertTrue from element", d1, te); - CodeAction ca2 = ca(uri, "Remove static modifier from element", d1, te2); + CodeAction ca2 = ca(uri, "Remove the 'static' modifier", d1, te2); - assertJavaCodeAction(codeActionParams, utils, ca, ca2); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca, ca2); codeActionParams = createCodeActionParams(uri, d2); te = te(24, 4, 25, 4, ""); - ca = ca(uri, "Remove constraint annotation AssertTrue from element", d2, te); + ca = ca(uri, "Remove constraint annotation AssertTrue from element", d2, te); + + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca); - assertJavaCodeAction(codeActionParams, utils, ca); - codeActionParams = createCodeActionParams(uri, d3); te = te(19, 4, 20, 4, ""); te2 = te(20, 10, 20, 17, ""); ca = ca(uri, "Remove constraint annotation AssertFalse from element", d3, te); - ca2 = ca(uri, "Remove static modifier from element", d3, te2); + ca2 = ca(uri, "Remove the 'static' modifier", d3, te2); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/cdi/ManagedBeanConstructorTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/cdi/ManagedBeanConstructorTest.java index 4ebd2b88..e0963adc 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/cdi/ManagedBeanConstructorTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/cdi/ManagedBeanConstructorTest.java @@ -13,7 +13,12 @@ package org.eclipse.lsp4jakarta.jdt.cdi; -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.*; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaCodeAction; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.ca; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.createCodeActionParams; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.te; import java.util.Arrays; @@ -24,45 +29,45 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class ManagedBeanConstructorTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void managedBeanAnnotations() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ManagedBeanConstructor.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ManagedBeanConstructor.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // test expected diagnostic Diagnostic d = d(21, 8, 30, - "The @Inject annotation must define a managed bean constructor that takes parameters, or the managed bean must resolve to having a no-arg constructor instead.", - DiagnosticSeverity.Error, "jakarta-cdi", "InvalidManagedBeanConstructor"); + "The @Inject annotation must define a managed bean constructor that takes parameters, or the managed bean must resolve to having a no-arg constructor instead.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidManagedBeanWithInvalidConstructor"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); // test expected quick-fix JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d); TextEdit te1 = te(15, 44, 21, 1, - "\nimport jakarta.inject.Inject;\n\n@Dependent\npublic class ManagedBeanConstructor {\n private int a;\n \n @Inject\n "); + "\nimport jakarta.inject.Inject;\n\n@Dependent\npublic class ManagedBeanConstructor {\n private int a;\n \n @Inject\n "); TextEdit te2 = te(19, 1, 19, 1, - "protected ManagedBeanConstructor() {\n\t}\n\n\t"); + "protected ManagedBeanConstructor() {\n\t}\n\n\t"); TextEdit te3 = te(19, 1, 19, 1, - "public ManagedBeanConstructor() {\n\t}\n\n\t"); - CodeAction ca1 = ca(uri, "Insert @Inject", d, te1); - CodeAction ca2 = ca(uri, "Add a no-arg protected constructor to this class", d, te2); - CodeAction ca3 = ca(uri, "Add a no-arg public constructor to this class", d, te3); - assertJavaCodeAction(codeActionParams1, JDT_UTILS, ca1, ca2, ca3); + "public ManagedBeanConstructor() {\n\t}\n\n\t"); + CodeAction ca1 = ca(uri, "Add a default 'protected' constructor to this class", d, te2); + CodeAction ca2 = ca(uri, "Add a default 'public' constructor to this class", d, te3); + CodeAction ca3 = ca(uri, "Insert @Inject", d, te1); + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1, ca2, ca3); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/cdi/ManagedBeanTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/cdi/ManagedBeanTest.java index 1c3575b9..cb70204d 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/cdi/ManagedBeanTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/cdi/ManagedBeanTest.java @@ -29,77 +29,76 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; import com.google.gson.Gson; public class ManagedBeanTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void managedBeanAnnotations() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ManagedBean.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ManagedBean.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // test expected diagnostic Diagnostic d1 = d(6, 12, 13, - "The @Dependent annotation must be the only scope defined by a managed bean with a non-static public field.", - DiagnosticSeverity.Error, "jakarta-cdi", "InvalidManagedBeanAnnotation"); - + "The @Dependent annotation must be the only scope defined by a managed bean with a non-static public field.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidManagedBeanWithNonStaticPublicField"); + Diagnostic d2 = d(5, 13, 24, - "Managed bean class of generic type must have scope @Dependent.", - DiagnosticSeverity.Error, "jakarta-cdi", "InvalidManagedBeanAnnotation"); + "Managed bean class of generic type must have scope @Dependent.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidGenericManagedBeanClassWithNoDependentScope"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); // Assert for the diagnostic d1 JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); TextEdit te1 = te(4, 0, 5, 0, "@Dependent\n"); CodeAction ca1 = ca(uri, "Replace current scope with @Dependent", d1, te1); - assertJavaCodeAction(codeActionParams1, JDT_UTILS, ca1); - + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1); + // Assert for the diagnostic d2 JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); TextEdit te2 = te(4, 0, 5, 0, "@Dependent\n"); CodeAction ca2 = ca(uri, "Replace current scope with @Dependent", d2, te2); - assertJavaCodeAction(codeActionParams2, JDT_UTILS, ca2); + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca2); } - + @Test public void scopeDeclaration() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ScopeDeclaration.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ScopeDeclaration.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // test expected diagnostic Diagnostic d1 = d(12, 16, 17, - "Scope type annotations must be specified by a producer field at most once.", - DiagnosticSeverity.Error, "jakarta-cdi", "InvalidScopeDecl"); + "Scope type annotations must be specified by a producer field at most once.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidNumberOfScopeAnnotationsByProducerField"); d1.setData(new Gson().toJsonTree(Arrays.asList("Dependent", "ApplicationScoped", "Produces"))); Diagnostic d2 = d(15, 25, 41, "Scope type annotations must be specified by a producer method at most once.", - DiagnosticSeverity.Error, "jakarta-cdi", "InvalidScopeDecl"); + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidNumberOfScopeAnnotationsByProducerMethod"); d2.setData(new Gson().toJsonTree(Arrays.asList("ApplicationScoped", "RequestScoped", "Produces"))); - + Diagnostic d3 = d(10, 13, 29, "Scope type annotations must be specified by a managed bean class at most once.", - DiagnosticSeverity.Error, "jakarta-cdi", "InvalidScopeDecl"); + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidNumberOfScopedAnnotationsByManagedBean"); d3.setData(new Gson().toJsonTree(Arrays.asList("ApplicationScoped", "RequestScoped"))); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2, d3); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3); // Assert for the diagnostic d1 JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); @@ -107,111 +106,109 @@ public void scopeDeclaration() throws Exception { TextEdit te2 = te(11, 14, 11, 33, ""); CodeAction ca1 = ca(uri, "Remove @ApplicationScoped", d1, te2); CodeAction ca2 = ca(uri, "Remove @Dependent", d1, te1); - - assertJavaCodeAction(codeActionParams1, JDT_UTILS, ca1, ca2); - + + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1, ca2); + // Assert for the diagnostic d2 JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); TextEdit te3 = te(14, 33, 15, 4, ""); TextEdit te4 = te(14, 14, 14, 33, ""); - CodeAction ca3 = ca(uri, "Remove @RequestScoped", d2, te3); - CodeAction ca4 = ca(uri, "Remove @ApplicationScoped", d2, te4); - - assertJavaCodeAction(codeActionParams2, JDT_UTILS, ca3, ca4); - + CodeAction ca3 = ca(uri, "Remove @ApplicationScoped", d2, te4); + CodeAction ca4 = ca(uri, "Remove @RequestScoped", d2, te3); + + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca3, ca4); + // Assert for the diagnostic d3 JakartaJavaCodeActionParams codeActionParams3 = createCodeActionParams(uri, d3); TextEdit te5 = te(9, 19, 10, 0, ""); TextEdit te6 = te(9, 0, 9, 19, ""); - CodeAction ca5 = ca(uri, "Remove @RequestScoped", d3, te5); - CodeAction ca6 = ca(uri, "Remove @ApplicationScoped", d3, te6); - - assertJavaCodeAction(codeActionParams3, JDT_UTILS, ca5, ca6); + CodeAction ca5 = ca(uri, "Remove @ApplicationScoped", d3, te6); + CodeAction ca6 = ca(uri, "Remove @RequestScoped", d3, te5); + + assertJavaCodeAction(codeActionParams3, IJDT_UTILS, ca5, ca6); } @Test public void producesAndInject() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ProducesAndInjectTogether.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ProducesAndInjectTogether.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - Diagnostic d1 = d(16, 18, 23, "The @Produces and @Inject annotations must not be used on the same field or property.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrInject"); + Diagnostic d1 = d(16, 18, 23, + "The @Produces and @Inject annotations must not be used on the same method.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidMethodWithProducesAndInjectAnnotations"); - Diagnostic d2 = d(11, 19, 27, "The @Produces and @Inject annotations must not be used on the same field or property.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrInject"); + Diagnostic d2 = d(11, 19, 27, + "The @Produces and @Inject annotations must not be used on the same field or property.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidFieldWithProducesAndInjectAnnotations"); - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); TextEdit te1 = te(14, 4, 15, 4, ""); TextEdit te2 = te(15, 4, 16, 4, ""); - CodeAction ca1 = ca(uri, "Remove @Produces", d1, te1); - CodeAction ca2 = ca(uri, "Remove @Inject", d1, te2); + CodeAction ca1 = ca(uri, "Remove @Inject", d1, te2); + CodeAction ca2 = ca(uri, "Remove @Produces", d1, te1); - assertJavaCodeAction(codeActionParams1, utils, ca1, ca2); + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1, ca2); JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); TextEdit te3 = te(9, 4, 10, 4, ""); TextEdit te4 = te(10, 4, 11, 4, ""); - CodeAction ca3 = ca(uri, "Remove @Produces", d2, te3); - CodeAction ca4 = ca(uri, "Remove @Inject", d2, te4); + CodeAction ca3 = ca(uri, "Remove @Inject", d2, te4); + CodeAction ca4 = ca(uri, "Remove @Produces", d2, te3); - assertJavaCodeAction(codeActionParams2, utils, ca3, ca4); + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca3, ca4); } @Test public void injectAndDisposesObservesObservesAsync() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject().getFile(new Path( - "src/main/java/io/openliberty/sample/jakarta/cdi/InjectAndDisposesObservesObservesAsync.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/InjectAndDisposesObservesObservesAsync.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d1 = d(10, 18, 31, - "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveInjectOrConflictedAnnotations"); + "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidInjectAnnotatedMethodParamAnnotation"); Diagnostic d2 = d(16, 18, 31, - "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Observes.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveInjectOrConflictedAnnotations"); + "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Observes.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidInjectAnnotatedMethodParamAnnotation"); Diagnostic d3 = d(22, 18, 36, - "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveInjectOrConflictedAnnotations"); + "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidInjectAnnotatedMethodParamAnnotation"); Diagnostic d4 = d(28, 18, 39, - "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes, @Observes.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveInjectOrConflictedAnnotations"); + "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes, @Observes.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidInjectAnnotatedMethodParamAnnotation"); Diagnostic d5 = d(34, 18, 44, - "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Observes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveInjectOrConflictedAnnotations"); + "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Observes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidInjectAnnotatedMethodParamAnnotation"); Diagnostic d6 = d(40, 18, 44, - "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveInjectOrConflictedAnnotations"); + "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidInjectAnnotatedMethodParamAnnotation"); Diagnostic d7 = d(46, 18, 52, - "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes, @Observes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveInjectOrConflictedAnnotations"); - + "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes, @Observes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidInjectAnnotatedMethodParamAnnotation"); + Diagnostic d8 = d(51, 18, 53, - "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes, @Observes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveInjectOrConflictedAnnotations"); + "A bean constructor or a method annotated with @Inject cannot have parameter(s) annotated with @Disposes, @Observes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidInjectAnnotatedMethodParamAnnotation"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3, d4, d5, d6, d7, d8); - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2, d3, d4, d5, d6, d7, d8); - JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); TextEdit te1 = te(9, 4, 10, 4, ""); @@ -219,8 +216,8 @@ public void injectAndDisposesObservesObservesAsync() throws Exception { CodeAction ca1 = ca(uri, "Remove @Inject", d1, te1); CodeAction ca2 = ca(uri, "Remove the '@Disposes' modifier from parameter 'name'", d1, te2); - assertJavaCodeAction(codeActionParams1, utils, ca1, ca2); - + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1, ca2); + JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); TextEdit te3 = te(15, 4, 16, 4, ""); @@ -228,8 +225,8 @@ public void injectAndDisposesObservesObservesAsync() throws Exception { CodeAction ca3 = ca(uri, "Remove @Inject", d2, te3); CodeAction ca4 = ca(uri, "Remove the '@Observes' modifier from parameter 'name'", d2, te4); - assertJavaCodeAction(codeActionParams2, utils, ca3, ca4); - + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca3, ca4); + JakartaJavaCodeActionParams codeActionParams3 = createCodeActionParams(uri, d3); TextEdit te5 = te(21, 4, 22, 4, ""); @@ -237,8 +234,8 @@ public void injectAndDisposesObservesObservesAsync() throws Exception { CodeAction ca5 = ca(uri, "Remove @Inject", d3, te5); CodeAction ca6 = ca(uri, "Remove the '@ObservesAsync' modifier from parameter 'name'", d3, te6); - assertJavaCodeAction(codeActionParams3, utils, ca5, ca6); - + assertJavaCodeAction(codeActionParams3, IJDT_UTILS, ca5, ca6); + JakartaJavaCodeActionParams codeActionParams4 = createCodeActionParams(uri, d4); TextEdit te7 = te(27, 4, 28, 4, ""); @@ -248,8 +245,8 @@ public void injectAndDisposesObservesObservesAsync() throws Exception { CodeAction ca8 = ca(uri, "Remove the '@Disposes' modifier from parameter 'name1'", d4, te8); CodeAction ca9 = ca(uri, "Remove the '@Observes' modifier from parameter 'name2'", d4, te9); - assertJavaCodeAction(codeActionParams4, utils, ca7, ca8, ca9); - + assertJavaCodeAction(codeActionParams4, IJDT_UTILS, ca7, ca8, ca9); + JakartaJavaCodeActionParams codeActionParams5 = createCodeActionParams(uri, d5); TextEdit te10 = te(33, 4, 34, 4, ""); @@ -259,8 +256,8 @@ public void injectAndDisposesObservesObservesAsync() throws Exception { CodeAction ca11 = ca(uri, "Remove the '@Observes' modifier from parameter 'name1'", d5, te11); CodeAction ca12 = ca(uri, "Remove the '@ObservesAsync' modifier from parameter 'name2'", d5, te12); - assertJavaCodeAction(codeActionParams5, utils, ca10, ca11, ca12); - + assertJavaCodeAction(codeActionParams5, IJDT_UTILS, ca10, ca11, ca12); + JakartaJavaCodeActionParams codeActionParams6 = createCodeActionParams(uri, d6); TextEdit te13 = te(39, 4, 40, 4, ""); @@ -270,8 +267,8 @@ public void injectAndDisposesObservesObservesAsync() throws Exception { CodeAction ca14 = ca(uri, "Remove the '@Disposes' modifier from parameter 'name1'", d6, te14); CodeAction ca15 = ca(uri, "Remove the '@ObservesAsync' modifier from parameter 'name2'", d6, te15); - assertJavaCodeAction(codeActionParams6, utils, ca13, ca14, ca15); - + assertJavaCodeAction(codeActionParams6, IJDT_UTILS, ca13, ca14, ca15); + JakartaJavaCodeActionParams codeActionParams7 = createCodeActionParams(uri, d7); TextEdit te16 = te(45, 4, 46, 4, ""); @@ -283,81 +280,79 @@ public void injectAndDisposesObservesObservesAsync() throws Exception { CodeAction ca18 = ca(uri, "Remove the '@Observes' modifier from parameter 'name2'", d7, te18); CodeAction ca19 = ca(uri, "Remove the '@ObservesAsync' modifier from parameter 'name3'", d7, te19); - assertJavaCodeAction(codeActionParams7, utils, ca16, ca17, ca18, ca19); - + assertJavaCodeAction(codeActionParams7, IJDT_UTILS, ca16, ca17, ca18, ca19); + JakartaJavaCodeActionParams codeActionParams8 = createCodeActionParams(uri, d8); TextEdit te20 = te(50, 4, 51, 4, ""); TextEdit te21 = te(51, 54, 51, 89, ""); CodeAction ca20 = ca(uri, "Remove @Inject", d8, te20); - CodeAction ca21 = ca(uri, "Remove the '@Disposes', '@Observes', '@ObservesAsync' modifier from parameter 'name'", d8, te21); + CodeAction ca21 = ca(uri, + "Remove the '@Disposes', '@Observes', '@ObservesAsync' modifier from parameter 'name'", d8, te21); - assertJavaCodeAction(codeActionParams8, utils, ca20, ca21); + assertJavaCodeAction(codeActionParams8, IJDT_UTILS, ca20, ca21); } @Test public void producesAndDisposesObservesObservesAsync() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject().getFile(new Path( - "src/main/java/io/openliberty/sample/jakarta/cdi/ProducesAndDisposesObservesObservesAsync.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/ProducesAndDisposesObservesObservesAsync.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d1 = d(12, 18, 31, - "A producer method cannot have parameter(s) annotated with @Disposes.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrConflictedAnnotations"); + "A producer method cannot have parameter(s) annotated with @Disposes.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidProducerMethodParamAnnotation"); Diagnostic d2 = d(18, 18, 31, - "A producer method cannot have parameter(s) annotated with @Observes.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrConflictedAnnotations"); + "A producer method cannot have parameter(s) annotated with @Observes.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidProducerMethodParamAnnotation"); Diagnostic d3 = d(24, 18, 36, - "A producer method cannot have parameter(s) annotated with @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrConflictedAnnotations"); + "A producer method cannot have parameter(s) annotated with @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidProducerMethodParamAnnotation"); Diagnostic d4 = d(30, 18, 39, - "A producer method cannot have parameter(s) annotated with @Disposes, @Observes.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrConflictedAnnotations"); + "A producer method cannot have parameter(s) annotated with @Disposes, @Observes.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidProducerMethodParamAnnotation"); Diagnostic d5 = d(36, 18, 44, - "A producer method cannot have parameter(s) annotated with @Observes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrConflictedAnnotations"); + "A producer method cannot have parameter(s) annotated with @Observes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidProducerMethodParamAnnotation"); Diagnostic d6 = d(42, 18, 44, - "A producer method cannot have parameter(s) annotated with @Disposes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrConflictedAnnotations"); + "A producer method cannot have parameter(s) annotated with @Disposes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidProducerMethodParamAnnotation"); Diagnostic d7 = d(48, 18, 52, - "A producer method cannot have parameter(s) annotated with @Disposes, @Observes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrConflictedAnnotations"); - + "A producer method cannot have parameter(s) annotated with @Disposes, @Observes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidProducerMethodParamAnnotation"); + Diagnostic d8 = d(54, 18, 53, - "A producer method cannot have parameter(s) annotated with @Disposes, @Observes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveProducesOrConflictedAnnotations"); - + "A producer method cannot have parameter(s) annotated with @Disposes, @Observes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidProducerMethodParamAnnotation"); + Diagnostic d9 = d(30, 18, 39, - "A disposer method cannot have parameter(s) annotated with @Observes.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveDisposesOrConflictedAnnotations"); - + "A disposer method cannot have parameter(s) annotated with @Observes.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidDisposerMethodParamAnnotation"); + Diagnostic d10 = d(42, 18, 44, - "A disposer method cannot have parameter(s) annotated with @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveDisposesOrConflictedAnnotations"); - + "A disposer method cannot have parameter(s) annotated with @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidDisposerMethodParamAnnotation"); + Diagnostic d11 = d(48, 18, 52, - "A disposer method cannot have parameter(s) annotated with @Observes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveDisposesOrConflictedAnnotations"); - + "A disposer method cannot have parameter(s) annotated with @Observes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidDisposerMethodParamAnnotation"); + Diagnostic d12 = d(54, 18, 53, - "A disposer method cannot have parameter(s) annotated with @Observes, @ObservesAsync.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveDisposesOrConflictedAnnotations"); - - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12); + "A disposer method cannot have parameter(s) annotated with @Observes, @ObservesAsync.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidDisposerMethodParamAnnotation"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12); - JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); TextEdit te1 = te(11, 4, 12, 4, ""); @@ -365,8 +360,8 @@ public void producesAndDisposesObservesObservesAsync() throws Exception { CodeAction ca1 = ca(uri, "Remove @Produces", d1, te1); CodeAction ca2 = ca(uri, "Remove the '@Disposes' modifier from parameter 'name'", d1, te2); - assertJavaCodeAction(codeActionParams1, utils, ca1, ca2); - + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1, ca2); + JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); TextEdit te3 = te(17, 4, 18, 4, ""); @@ -374,8 +369,8 @@ public void producesAndDisposesObservesObservesAsync() throws Exception { CodeAction ca3 = ca(uri, "Remove @Produces", d2, te3); CodeAction ca4 = ca(uri, "Remove the '@Observes' modifier from parameter 'name'", d2, te4); - assertJavaCodeAction(codeActionParams2, utils, ca3, ca4); - + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca3, ca4); + JakartaJavaCodeActionParams codeActionParams3 = createCodeActionParams(uri, d3); TextEdit te5 = te(23, 4, 24, 4, ""); @@ -383,8 +378,8 @@ public void producesAndDisposesObservesObservesAsync() throws Exception { CodeAction ca5 = ca(uri, "Remove @Produces", d3, te5); CodeAction ca6 = ca(uri, "Remove the '@ObservesAsync' modifier from parameter 'name'", d3, te6); - assertJavaCodeAction(codeActionParams3, utils, ca5, ca6); - + assertJavaCodeAction(codeActionParams3, IJDT_UTILS, ca5, ca6); + JakartaJavaCodeActionParams codeActionParams4 = createCodeActionParams(uri, d4); TextEdit te7 = te(29, 4, 30, 4, ""); @@ -394,8 +389,8 @@ public void producesAndDisposesObservesObservesAsync() throws Exception { CodeAction ca8 = ca(uri, "Remove the '@Disposes' modifier from parameter 'name1'", d4, te8); CodeAction ca9 = ca(uri, "Remove the '@Observes' modifier from parameter 'name2'", d4, te9); - assertJavaCodeAction(codeActionParams4, utils, ca7, ca8, ca9); - + assertJavaCodeAction(codeActionParams4, IJDT_UTILS, ca7, ca8, ca9); + JakartaJavaCodeActionParams codeActionParams5 = createCodeActionParams(uri, d5); TextEdit te10 = te(35, 4, 36, 4, ""); @@ -405,8 +400,8 @@ public void producesAndDisposesObservesObservesAsync() throws Exception { CodeAction ca11 = ca(uri, "Remove the '@Observes' modifier from parameter 'name1'", d5, te11); CodeAction ca12 = ca(uri, "Remove the '@ObservesAsync' modifier from parameter 'name2'", d5, te12); - assertJavaCodeAction(codeActionParams5, utils, ca10, ca11, ca12); - + assertJavaCodeAction(codeActionParams5, IJDT_UTILS, ca10, ca11, ca12); + JakartaJavaCodeActionParams codeActionParams6 = createCodeActionParams(uri, d6); TextEdit te13 = te(41, 4, 42, 4, ""); @@ -416,8 +411,8 @@ public void producesAndDisposesObservesObservesAsync() throws Exception { CodeAction ca14 = ca(uri, "Remove the '@Disposes' modifier from parameter 'name1'", d6, te14); CodeAction ca15 = ca(uri, "Remove the '@ObservesAsync' modifier from parameter 'name2'", d6, te15); - assertJavaCodeAction(codeActionParams6, utils, ca13, ca14, ca15); - + assertJavaCodeAction(codeActionParams6, IJDT_UTILS, ca13, ca14, ca15); + JakartaJavaCodeActionParams codeActionParams7 = createCodeActionParams(uri, d7); TextEdit te16 = te(47, 4, 48, 4, ""); @@ -429,34 +424,33 @@ public void producesAndDisposesObservesObservesAsync() throws Exception { CodeAction ca18 = ca(uri, "Remove the '@Observes' modifier from parameter 'name2'", d7, te18); CodeAction ca19 = ca(uri, "Remove the '@ObservesAsync' modifier from parameter 'name3'", d7, te19); - assertJavaCodeAction(codeActionParams7, utils, ca16, ca17, ca18, ca19); - + assertJavaCodeAction(codeActionParams7, IJDT_UTILS, ca16, ca17, ca18, ca19); + JakartaJavaCodeActionParams codeActionParams8 = createCodeActionParams(uri, d8); TextEdit te20 = te(53, 4, 54, 4, ""); TextEdit te21 = te(54, 54, 54, 89, ""); CodeAction ca20 = ca(uri, "Remove @Produces", d8, te20); - CodeAction ca21 = ca(uri, "Remove the '@Disposes', '@Observes', '@ObservesAsync' modifier from parameter 'name'", d8, te21); + CodeAction ca21 = ca(uri, + "Remove the '@Disposes', '@Observes', '@ObservesAsync' modifier from parameter 'name'", d8, te21); + + assertJavaCodeAction(codeActionParams8, IJDT_UTILS, ca20, ca21); - assertJavaCodeAction(codeActionParams8, utils, ca20, ca21); - } - + @Test public void multipleDisposes() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/MultipleDisposes.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/cdi/MultipleDisposes.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - + Diagnostic d = d(9, 18, 23, - "The @Disposes annotation must not be defined on more than one parameter of a method.", - DiagnosticSeverity.Error, "jakarta-cdi", "RemoveExtraDisposes"); - - assertJavaDiagnostics(diagnosticsParams, utils, d); + "The @Disposes annotation must not be defined on more than one parameter of a method.", + DiagnosticSeverity.Error, "jakarta-cdi", "InvalidDisposesAnnotationOnMultipleMethodParams"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/BaseJakartaTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/BaseJakartaTest.java index fc1c1142..2f940fbd 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/BaseJakartaTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/BaseJakartaTest.java @@ -38,8 +38,7 @@ */ public class BaseJakartaTest { - protected static IJavaProject loadJavaProject(String projectName, String parentDirName) - throws CoreException, Exception { + protected static IJavaProject loadJavaProject(String projectName, String parentDirName) throws CoreException, Exception { // Move project to working directory File projectFolder = copyProjectToWorkingDirectory(projectName, parentDirName); @@ -65,15 +64,13 @@ public void run(IProgressMonitor monitor) throws CoreException { waitForBackgroundJobs(monitor); } - IJavaProject javaProject = JavaModelManager.getJavaModelManager().getJavaModel() - .getJavaProject(description.getName()); + IJavaProject javaProject = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(description.getName()); return javaProject; } private static File copyProjectToWorkingDirectory(String projectName, String parentDirName) throws IOException { File from = new File("projects/" + parentDirName + "/" + projectName); - File to = new File(getWorkingProjectDirectory(), - java.nio.file.Paths.get(parentDirName, projectName).toString()); + File to = new File(getWorkingProjectDirectory(), java.nio.file.Paths.get(parentDirName, projectName).toString()); if (to.exists()) { FileUtils.forceDelete(to); diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JakartaForJavaAssert.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JakartaForJavaAssert.java index 42dca4fa..92bb046a 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JakartaForJavaAssert.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JakartaForJavaAssert.java @@ -36,8 +36,9 @@ import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; import org.junit.Assert; /** @@ -59,10 +60,9 @@ public static JakartaJavaCodeActionParams createCodeActionParams(String uri, Dia return codeActionParams; } - public static void assertJavaCodeAction(JakartaJavaCodeActionParams params, JDTUtils utils, CodeAction... expected) - throws JavaModelException { - List actual = JDTServicesManager.getInstance().getCodeAction(params, utils, - new NullProgressMonitor()); + public static void assertJavaCodeAction(JakartaJavaCodeActionParams params, IJDTUtils utils, CodeAction... expected) throws JavaModelException { + List actual = PropertiesManagerForJava.getInstance().codeAction(params, utils, + new NullProgressMonitor()); assertCodeActions(actual != null && actual.size() > 0 ? actual : Collections.emptyList(), expected); } @@ -81,7 +81,7 @@ public static void assertCodeActions(List actual, CodeActi }); Assert.assertEquals(expected.length, actual.size()); - + // remove extra '\r' for (CodeAction ca : actual) { ca.setTitle(replaceNewLineCharacters(ca.getTitle())); @@ -92,13 +92,13 @@ public static void assertCodeActions(List actual, CodeActi te.setNewText(replaceNewLineCharacters(te.getNewText())); } } - } - + } + for (int i = 0; i < expected.length; i++) { Assert.assertEquals("Assert title [" + i + "]", expected[i].getTitle(), - ((CodeAction) actual.get(i)).getTitle()); + ((CodeAction) actual.get(i)).getTitle()); Assert.assertEquals("Assert edit [" + i + "]", expected[i].getEdit(), - ((CodeAction) actual.get(i)).getEdit()); + ((CodeAction) actual.get(i)).getEdit()); } } @@ -126,23 +126,21 @@ public static TextEdit te(int startLine, int startCharacter, int endLine, int en // Assert for diagnostics public static Diagnostic d(int line, int startCharacter, int endCharacter, String message, - DiagnosticSeverity severity, final String source, String code, Object data) { - Diagnostic d = new Diagnostic(r(line, startCharacter, line, endCharacter), message, severity, source, - code != null ? code : null); + DiagnosticSeverity severity, final String source, String code, Object data) { + Diagnostic d = new Diagnostic(r(line, startCharacter, line, endCharacter), message, severity, source, code != null ? code : null); d.setData(data); return d; } - + public static Diagnostic d(int line, int startCharacter, int endCharacter, String message, - DiagnosticSeverity severity, final String source, String code) { + DiagnosticSeverity severity, final String source, String code) { return d(line, startCharacter, line, endCharacter, message, severity, source, code); } public static Diagnostic d(int startLine, int startCharacter, int endLine, int endCharacter, String message, - DiagnosticSeverity severity, final String source, String code) { + DiagnosticSeverity severity, final String source, String code) { // Diagnostic on 1 line - return new Diagnostic(r(startLine, startCharacter, endLine, endCharacter), message, severity, source, - code != null ? code : null); + return new Diagnostic(r(startLine, startCharacter, endLine, endCharacter), message, severity, source, code != null ? code : null); } public static Range r(int line, int startCharacter, int endCharacter) { @@ -157,13 +155,14 @@ public static Position p(int line, int character) { return new Position(line, character); } - public static void assertJavaDiagnostics(JakartaDiagnosticsParams params, JDTUtils utils, Diagnostic... expected) - throws JavaModelException { - List actual = JDTServicesManager.getInstance().getJavaDiagnostics(params); + public static void assertJavaDiagnostics(JakartaJavaDiagnosticsParams params, IJDTUtils utils, + Diagnostic... expected) throws JavaModelException { + List actual = PropertiesManagerForJava.getInstance().diagnostics(params, + utils, new NullProgressMonitor()); assertDiagnostics( - actual != null && actual.size() > 0 ? actual.get(0).getDiagnostics() : Collections.emptyList(), - expected); + actual != null && actual.size() > 0 ? actual.get(0).getDiagnostics() : Collections.emptyList(), + expected); } public static void assertDiagnostics(List actual, Diagnostic... expected) { @@ -175,12 +174,10 @@ public static void assertDiagnostics(List actual, List e * ordering of diagnostics should not matter when testing for equality, so we * sort diagnostics by their range. */ - Comparator posOrder = (a, b) -> a.getLine() == b.getLine() ? b.getCharacter() - a.getCharacter() - : b.getLine() - a.getLine(); + Comparator posOrder = (a, b) -> a.getLine() == b.getLine() ? b.getCharacter() - a.getCharacter() : b.getLine() - a.getLine(); - Comparator rangePosOrder = (a, b) -> posOrder.compare(a.getStart(), b.getStart()) == 0 - ? posOrder.compare(a.getEnd(), b.getEnd()) - : posOrder.compare(a.getStart(), b.getStart()); + Comparator rangePosOrder = (a, b) -> posOrder.compare(a.getStart(), b.getStart()) == 0 ? posOrder.compare(a.getEnd(), b.getEnd()) : posOrder.compare(a.getStart(), + b.getStart()); Comparator diagnosticRangeOrder = (a, b) -> rangePosOrder.compare(a.getRange(), b.getRange()); @@ -197,7 +194,7 @@ public static void assertDiagnostics(List actual, List e List received = actual; final boolean filterMessage; if (expected != null && !expected.isEmpty() - && (expected.get(0).getMessage() == null || expected.get(0).getMessage().isEmpty())) { + && (expected.get(0).getMessage() == null || expected.get(0).getMessage().isEmpty())) { filterMessage = true; } else { filterMessage = false; @@ -225,7 +222,7 @@ public static String fixURI(URI uri) { /** * Returns new string without '\r' for new lines. (Windows generates an extra * '\r' for each new line, it makes test messages fail to compare.) - * + * * @param source test message string * @return new string without '\r' for new lines */ diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JobHelpers.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JobHelpers.java index 03308fbd..515218b1 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JobHelpers.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/core/JobHelpers.java @@ -39,7 +39,6 @@ public final class JobHelpers { private static final Logger LOGGER = Logger.getLogger(JobHelpers.class.getName()); - private JobHelpers() { //no instantiation } @@ -50,7 +49,7 @@ private JobHelpers() { public static void waitForJobsToComplete() { try { waitForJobsToComplete(new NullProgressMonitor()); - } catch(Exception ex) { + } catch (Exception ex) { throw new IllegalStateException(ex); } } @@ -76,24 +75,23 @@ public static void waitForJobsToComplete(IProgressMonitor monitor) throws Interr jobManager.suspend(); try { Job[] jobs = jobManager.find(null); - for(int i = 0; i < jobs.length; i++ ) { - if(jobs[i] instanceof WorkspaceJob || jobs[i].getClass().getName().endsWith("JREUpdateJob")) { + for (int i = 0; i < jobs.length; i++) { + if (jobs[i] instanceof WorkspaceJob || jobs[i].getClass().getName().endsWith("JREUpdateJob")) { jobs[i].join(); } } workspace.run(new IWorkspaceRunnable() { @Override - public void run(IProgressMonitor monitor) { - } + public void run(IProgressMonitor monitor) {} }, workspace.getRoot(), 0, monitor); // Now we flush all background processing queues boolean processed = flushProcessingQueues(jobManager, monitor); - for(int i = 0; i < 10 && processed; i++ ) { + for (int i = 0; i < 10 && processed; i++) { processed = flushProcessingQueues(jobManager, monitor); try { Thread.sleep(10); - } catch(InterruptedException e) { + } catch (InterruptedException e) { } } if (processed) { @@ -106,16 +104,15 @@ public void run(IProgressMonitor monitor) { waitForBuildJobs(); } - private static boolean flushProcessingQueues(IJobManager jobManager, IProgressMonitor monitor) - throws InterruptedException, CoreException { + private static boolean flushProcessingQueues(IJobManager jobManager, IProgressMonitor monitor) throws InterruptedException, CoreException { boolean processed = false; - for(IBackgroundProcessingQueue queue : getProcessingQueues(jobManager)) { + for (IBackgroundProcessingQueue queue : getProcessingQueues(jobManager)) { queue.join(); - if(!queue.isEmpty()) { + if (!queue.isEmpty()) { Deque context = MavenExecutionContext.suspend(); try { IStatus status = queue.run(monitor); - if(!status.isOK()) { + if (!status.isOK()) { throw new CoreException(status); } processed = true; @@ -123,7 +120,7 @@ private static boolean flushProcessingQueues(IJobManager jobManager, IProgressMo MavenExecutionContext.resume(context); } } - if(queue.isEmpty()) { + if (queue.isEmpty()) { queue.cancel(); } } @@ -132,8 +129,8 @@ private static boolean flushProcessingQueues(IJobManager jobManager, IProgressMo private static List getProcessingQueues(IJobManager jobManager) { ArrayList queues = new ArrayList<>(); - for(Job job : jobManager.find(null)) { - if(job instanceof IBackgroundProcessingQueue) { + for (Job job : jobManager.find(null)) { + if (job instanceof IBackgroundProcessingQueue) { queues.add((IBackgroundProcessingQueue) job); } } @@ -158,9 +155,9 @@ public static void waitForDownloadSourcesJobs(int maxTimeMillis) { public static void waitForJobs(IJobMatcher matcher, int maxWaitMillis) { final long limit = System.currentTimeMillis() + maxWaitMillis; - while(true) { + while (true) { Job job = getJob(matcher); - if(job == null) { + if (job == null) { return; } boolean timeout = System.currentTimeMillis() > limit; @@ -171,7 +168,7 @@ public static void waitForJobs(IJobMatcher matcher, int maxWaitMillis) { job.wakeUp(); try { Thread.sleep(POLLING_DELAY); - } catch(InterruptedException e) { + } catch (InterruptedException e) { // ignore and keep waiting } } @@ -179,8 +176,8 @@ public static void waitForJobs(IJobMatcher matcher, int maxWaitMillis) { private static Job getJob(IJobMatcher matcher) { Job[] jobs = Job.getJobManager().find(null); - for(Job job : jobs) { - if(matcher.matches(job)) { + for (Job job : jobs) { + if (matcher.matches(job)) { return job; } } @@ -200,7 +197,7 @@ static class BuildJobMatcher implements IJobMatcher { @Override public boolean matches(Job job) { return (job instanceof WorkspaceJob) || job.getClass().getName().matches("(.*\\.AutoBuild.*)") - || job.getClass().getName().endsWith("JREUpdateJob"); + || job.getClass().getName().endsWith("JREUpdateJob"); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/di/DependencyInjectionTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/di/DependencyInjectionTest.java index e5e3e339..320def19 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/di/DependencyInjectionTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/di/DependencyInjectionTest.java @@ -25,106 +25,84 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IType; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class DependencyInjectionTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void DependencyInjectionDiagnostics() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/di/GreetingServlet.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/di/GreetingServlet.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - /* create expected diagnostics - * - */ + // Create expected diagnostics. Diagnostic d1 = d(17, 27, 35, "The @Inject annotation must not define a final field.", - DiagnosticSeverity.Error, "jakarta-di", "RemoveInjectOrFinal"); - d1.setData(IType.FIELD); + DiagnosticSeverity.Error, "jakarta-di", "InvalidInjectAnnotationOnFinalField"); + // d1.setData(IType.FIELD); Diagnostic d2 = d(33, 25, 39, "The @Inject annotation must not define an abstract method.", - DiagnosticSeverity.Error, "jakarta-di", "RemoveInjectOrAbstract"); - d2.setData(IType.METHOD); - + DiagnosticSeverity.Error, "jakarta-di", "InvalidInjectAnnotationOnAbstractMethod"); + // d2.setData(IType.METHOD); + Diagnostic d3 = d(26, 22, 33, "The @Inject annotation must not define a final method.", - DiagnosticSeverity.Error, "jakarta-di", "RemoveInjectOrFinal"); - d3.setData(IType.METHOD); - + DiagnosticSeverity.Error, "jakarta-di", "InvalidInjectAnnotationOnFinalMethod"); + // d3.setData(IType.METHOD); + Diagnostic d4 = d(43, 23, 36, "The @Inject annotation must not define a generic method.", - DiagnosticSeverity.Error, "jakarta-di", "RemoveInjectForGeneric"); - d4.setData(IType.METHOD); - + DiagnosticSeverity.Error, "jakarta-di", "InvalidInjectAnnotationOnGenericMethod"); + // d4.setData(IType.METHOD); + Diagnostic d5 = d(37, 23, 35, "The @Inject annotation must not define a static method.", - DiagnosticSeverity.Error, "jakarta-di", "RemoveInjectOrStatic"); - d5.setData(IType.METHOD); - - - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2, d3, d4, d5); - - - /* create expected quickFixes - * - */ - - // for d1 + DiagnosticSeverity.Error, "jakarta-di", "InvalidInjectAnnotationOnStaticMethod"); + // d5.setData(IType.METHOD); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3, d4, d5); + + // Create expected quick fixes. JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d1); - TextEdit te = te(16, 4, 17, 4, - ""); + TextEdit te = te(16, 4, 17, 4, ""); CodeAction ca = ca(uri, "Remove @Inject", d1, te); - TextEdit te1 = te(17, 11, 17, 17, - ""); - CodeAction ca1 = ca(uri, "Remove the 'final' modifier from this field", d1, te1); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca, ca1); - - // for d2 + TextEdit te1 = te(17, 11, 17, 17, ""); + CodeAction ca1 = ca(uri, "Remove the 'final' modifier", d1, te1); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca, ca1); + codeActionParams = createCodeActionParams(uri, d2); - te = te(32, 4, 33, 4, - ""); + te = te(32, 4, 33, 4, ""); ca = ca(uri, "Remove @Inject", d2, te); - te1 = te(33, 10, 33, 19, - ""); - ca1 = ca(uri, "Remove the 'abstract' modifier from this method", d2, te1); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca, ca1); - - // for d3 + te1 = te(33, 10, 33, 19, ""); + ca1 = ca(uri, "Remove the 'abstract' modifier", d2, te1); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca, ca1); + codeActionParams = createCodeActionParams(uri, d3); - te = te(25, 4, 26, 4, - ""); + te = te(25, 4, 26, 4, ""); ca = ca(uri, "Remove @Inject", d3, te); - te1 = te(26, 10, 26, 16, - ""); - ca1 = ca(uri, "Remove the 'final' modifier from this method", d3, te1); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca, ca1); - - // for d4 + te1 = te(26, 10, 26, 16, ""); + ca1 = ca(uri, "Remove the 'final' modifier", d3, te1); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca, ca1); + codeActionParams = createCodeActionParams(uri, d4); - te = te(42, 4, 43, 4, - ""); + te = te(42, 4, 43, 4, ""); ca = ca(uri, "Remove @Inject", d4, te); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca); - - // for d5 + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca); + codeActionParams = createCodeActionParams(uri, d5); - te = te(36, 4, 37, 4, - ""); + te = te(36, 4, 37, 4, ""); ca = ca(uri, "Remove @Inject", d5, te); - te1 = te(37, 10, 37, 17, - ""); - ca1 = ca(uri, "Remove the 'static' modifier from this method", d5, te1); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca, ca1); + te1 = te(37, 10, 37, 17, ""); + ca1 = ca(uri, "Remove the 'static' modifier", d5, te1); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca, ca1); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/di/MultipleConstructorInjectTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/di/MultipleConstructorInjectTest.java index f4d64979..7946a9e4 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/di/MultipleConstructorInjectTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/di/MultipleConstructorInjectTest.java @@ -13,7 +13,12 @@ package org.eclipse.lsp4jakarta.jdt.di; -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.*; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaCodeAction; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.ca; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.createCodeActionParams; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.te; import java.util.Arrays; @@ -24,54 +29,50 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class MultipleConstructorInjectTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void multipleInject() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/di/MultipleConstructorWithInject.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/di/MultipleConstructorWithInject.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - // test expected diagnostic Diagnostic d1 = d(22, 11, 40, - "The @Inject annotation must not define more than one constructor.", - DiagnosticSeverity.Error, "jakarta-di", "RemoveInject"); - + "The @Inject annotation must not define more than one constructor.", + DiagnosticSeverity.Error, "jakarta-di", "InvalidInjectAnnotationOnMultipleConstructors"); + Diagnostic d2 = d(26, 11, 40, - "The @Inject annotation must not define more than one constructor.", - DiagnosticSeverity.Error, "jakarta-di", "RemoveInject"); - + "The @Inject annotation must not define more than one constructor.", + DiagnosticSeverity.Error, "jakarta-di", "InvalidInjectAnnotationOnMultipleConstructors"); + Diagnostic d3 = d(31, 14, 43, - "The @Inject annotation must not define more than one constructor.", - DiagnosticSeverity.Error, "jakarta-di", "RemoveInject"); - - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1,d2,d3); - + "The @Inject annotation must not define more than one constructor.", + DiagnosticSeverity.Error, "jakarta-di", "InvalidInjectAnnotationOnMultipleConstructors"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3); + // test expected quick-fix JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); - TextEdit te = te(21, 4, 22, 4,""); + TextEdit te = te(21, 4, 22, 4, ""); CodeAction ca = ca(uri, "Remove @Inject", d1, te); - assertJavaCodeAction(codeActionParams1, JDT_UTILS, ca); - + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca); + JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d3); - TextEdit te2 = te(30, 4, 31, 4,""); + TextEdit te2 = te(30, 4, 31, 4, ""); CodeAction ca2 = ca(uri, "Remove @Inject", d3, te2); - assertJavaCodeAction(codeActionParams2, JDT_UTILS, ca2); - - + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca2); } - } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jax_rs/ResourceClassConstructorTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jax_rs/ResourceClassConstructorTest.java deleted file mode 100644 index 3f02c672..00000000 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jax_rs/ResourceClassConstructorTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2021, 2023 IBM Corporation, Matthew Shocrylas and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation, Matthew Shocrylas - initial API and implementation - *******************************************************************************/ - -package org.eclipse.lsp4jakarta.jdt.jax_rs; - -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; - -import java.util.Arrays; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.Path; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; -import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; -import org.junit.Test; - -public class ResourceClassConstructorTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); - - @Test - public void MultipleConstructorsWithEqualParams() throws Exception { - JDTUtils utils = JDT_UTILS; - IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject().getFile(new Path( - "src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsEqualLen.java")); - String uri = javaFile.getLocation().toFile().toURI().toString(); - - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); - diagnosticsParams.setUris(Arrays.asList(uri)); - - // test expected diagnostics - Diagnostic d1 = d(7, 8, 45, - "Multiple constructors have the same number of parameters, it might be ambiguous which constructor is used.", - DiagnosticSeverity.Warning, "jakarta-jax_rs", "AmbiguousConstructors"); - - Diagnostic d2 = d(11, 8, 45, - "Multiple constructors have the same number of parameters, it might be ambiguous which constructor is used.", - DiagnosticSeverity.Warning, "jakarta-jax_rs", "AmbiguousConstructors"); - - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2); - - } - - @Test - public void MultipleConstructorsWithDifferentLength() throws Exception { - JDTUtils utils = JDT_UTILS; - IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject().getFile(new Path( - "src/main/java/io/openliberty/sample/jakarta/jax_rs/RootResourceClassConstructorsDiffLen.java")); - String uri = javaFile.getLocation().toFile().toURI().toString(); - - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); - diagnosticsParams.setUris(Arrays.asList(uri)); - - // test expected diagnostics - Diagnostic d = d(7, 8, 44, - "This constructor is unused, as root resource classes will only use the constructor with the most parameters.", - DiagnosticSeverity.Warning, "jakarta-jax_rs", "UnusedConstructor"); - - assertJavaDiagnostics(diagnosticsParams, utils, d); - } - - @Test - public void NoPublicConstructor() throws Exception { - JDTUtils utils = JDT_UTILS; - IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorClass.java")); - String uri = javaFile.getLocation().toFile().toURI().toString(); - - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); - diagnosticsParams.setUris(Arrays.asList(uri)); - - // test expected diagnostics - Diagnostic d1 = d(7, 12, 36, - "Root resource classes are instantiated by the JAX-RS runtime and MUST have a public constructor.", - DiagnosticSeverity.Error, "jakarta-jax_rs", "NoPublicConstructors"); - - Diagnostic d2 = d(11, 14, 38, - "Root resource classes are instantiated by the JAX-RS runtime and MUST have a public constructor.", - DiagnosticSeverity.Error, "jakarta-jax_rs", "NoPublicConstructors"); - - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2); - } - - - @Test - public void NoPublicConstructorProviderClass() throws Exception { - JDTUtils utils = JDT_UTILS; - IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jax_rs/NoPublicConstructorProviderClass.java")); - String uri = javaFile.getLocation().toFile().toURI().toString(); - - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); - diagnosticsParams.setUris(Arrays.asList(uri)); - - Diagnostic d1 = d(19, 12, 44, - "Provider classes are instantiated by the JAX-RS runtime and MUST have a public constructor.", - DiagnosticSeverity.Error, "jakarta-jax_rs", "NoPublicConstructors"); - - Diagnostic d2 = d(23, 14, 46, - "Provider classes are instantiated by the JAX-RS runtime and MUST have a public constructor.", - DiagnosticSeverity.Error, "jakarta-jax_rs", "NoPublicConstructors"); - - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2); - } -} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jaxrs/ResourceClassConstructorTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jaxrs/ResourceClassConstructorTest.java new file mode 100644 index 00000000..67aa6b37 --- /dev/null +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jaxrs/ResourceClassConstructorTest.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2021, 2023 IBM Corporation, Matthew Shocrylas and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation, Matthew Shocrylas - initial API and implementation + *******************************************************************************/ + +package org.eclipse.lsp4jakarta.jdt.jaxrs; + +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; + +import java.util.Arrays; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; +import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; +import org.junit.Test; + +public class ResourceClassConstructorTest extends BaseJakartaTest { + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); + + @Test + public void MultipleConstructorsWithEqualParams() throws Exception { + IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsEqualLen.java")); + String uri = javaFile.getLocation().toFile().toURI().toString(); + + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); + diagnosticsParams.setUris(Arrays.asList(uri)); + + // test expected diagnostics + Diagnostic d1 = d(7, 8, 45, + "Multiple constructors have the same number of parameters, it might be ambiguous which constructor is used.", + DiagnosticSeverity.Warning, "jakarta-jaxrs", "AmbiguousConstructors"); + + Diagnostic d2 = d(11, 8, 45, + "Multiple constructors have the same number of parameters, it might be ambiguous which constructor is used.", + DiagnosticSeverity.Warning, "jakarta-jaxrs", "AmbiguousConstructors"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); + + } + + @Test + public void MultipleConstructorsWithDifferentLength() throws Exception { + IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jaxrs/RootResourceClassConstructorsDiffLen.java")); + String uri = javaFile.getLocation().toFile().toURI().toString(); + + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); + diagnosticsParams.setUris(Arrays.asList(uri)); + + // test expected diagnostics + Diagnostic d = d(7, 8, 44, + "This constructor is unused, as root resource classes will only use the constructor with the most parameters.", + DiagnosticSeverity.Warning, "jakarta-jaxrs", "UnusedConstructor"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); + } + + @Test + public void NoPublicConstructor() throws Exception { + IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorClass.java")); + String uri = javaFile.getLocation().toFile().toURI().toString(); + + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); + diagnosticsParams.setUris(Arrays.asList(uri)); + + // test expected diagnostics + Diagnostic d1 = d(7, 12, 36, + "Root resource classes are instantiated by the JAX-RS runtime and MUST have a public constructor.", + DiagnosticSeverity.Error, "jakarta-jaxrs", "NoPublicConstructors"); + + Diagnostic d2 = d(11, 14, 38, + "Root resource classes are instantiated by the JAX-RS runtime and MUST have a public constructor.", + DiagnosticSeverity.Error, "jakarta-jaxrs", "NoPublicConstructors"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); + } + + @Test + public void NoPublicConstructorProviderClass() throws Exception { + IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jaxrs/NoPublicConstructorProviderClass.java")); + String uri = javaFile.getLocation().toFile().toURI().toString(); + + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); + diagnosticsParams.setUris(Arrays.asList(uri)); + + Diagnostic d1 = d(19, 12, 44, + "Provider classes are instantiated by the JAX-RS runtime and MUST have a public constructor.", + DiagnosticSeverity.Error, "jakarta-jaxrs", "NoPublicConstructors"); + + Diagnostic d2 = d(23, 14, 46, + "Provider classes are instantiated by the JAX-RS runtime and MUST have a public constructor.", + DiagnosticSeverity.Error, "jakarta-jaxrs", "NoPublicConstructors"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); + } +} diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jax_rs/ResourceMethodTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jaxrs/ResourceMethodTest.java similarity index 60% rename from jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jax_rs/ResourceMethodTest.java rename to jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jaxrs/ResourceMethodTest.java index b207379a..9895421d 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jax_rs/ResourceMethodTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jaxrs/ResourceMethodTest.java @@ -11,9 +11,14 @@ * IBM Corporation, Matthew Shocrylas - initial API and implementation, Bera Sogut *******************************************************************************/ -package org.eclipse.lsp4jakarta.jdt.jax_rs; +package org.eclipse.lsp4jakarta.jdt.jaxrs; -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.*; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaCodeAction; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.ca; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.createCodeActionParams; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.te; import java.util.Arrays; @@ -24,57 +29,51 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class ResourceMethodTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); - + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); + @Test public void NonPublicMethod() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jax_rs/NotPublicResourceMethod.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jaxrs/NotPublicResourceMethod.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - - + Diagnostic d = d(20, 17, 30, "Only public methods can be exposed as resource methods.", - DiagnosticSeverity.Error, "jakarta-jax_rs", "NonPublicResourceMethod"); - - assertJavaDiagnostics(diagnosticsParams, utils, d); - + DiagnosticSeverity.Error, "jakarta-jaxrs", "NonPublicResourceMethod"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); + // Test for quick-fix code action JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d); TextEdit te = te(20, 4, 20, 11, "public"); // range may need to change CodeAction ca = ca(uri, "Make method public", d, te); - assertJavaCodeAction(codeActionParams, utils, ca); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca); } @Test public void multipleEntityParamsMethod() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jax_rs/MultipleEntityParamsResourceMethod.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jaxrs/MultipleEntityParamsResourceMethod.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - Diagnostic d = d(21, 13, 46, "Resource methods cannot have more than one entity parameter.", - DiagnosticSeverity.Error, "jakarta-jax_rs", "ResourceMethodMultipleEntityParams"); - - assertJavaDiagnostics(diagnosticsParams, utils, d); + DiagnosticSeverity.Error, "jakarta-jaxrs", "ResourceMethodMultipleEntityParams"); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); // Test for quick-fix code action JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d); @@ -85,7 +84,7 @@ public void multipleEntityParamsMethod() throws Exception { TextEdit te2 = te(21, 47, 21, 68, ""); CodeAction ca2 = ca(uri, "Remove all entity parameters except entityParam2", d, te2); - assertJavaCodeAction(codeActionParams, utils, ca1, ca2); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca1, ca2); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jsonb/JsonbDiagnosticsCollectorTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jsonb/JsonbDiagnosticsCollectorTest.java index a377a402..62f4778d 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jsonb/JsonbDiagnosticsCollectorTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jsonb/JsonbDiagnosticsCollectorTest.java @@ -28,148 +28,144 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; import com.google.gson.Gson; - public class JsonbDiagnosticsCollectorTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void deleteExtraJsonbCreatorAnnotation() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/jsonb/ExtraJsonbCreatorAnnotations.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/jsonb/ExtraJsonbCreatorAnnotations.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d1 = d(18, 11, 39, - "Only one constructor or static factory method can be annotated with @JsonbCreator in a given class.", - DiagnosticSeverity.Error, "jakarta-jsonb", "MultipleJsonbCreatorAnnotations"); - + "Only one constructor or static factory method can be annotated with @JsonbCreator in a given class.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidNumerOfJsonbCreatorAnnotationsInClass"); + Diagnostic d2 = d(21, 48, 61, - "Only one constructor or static factory method can be annotated with @JsonbCreator in a given class.", - DiagnosticSeverity.Error, "jakarta-jsonb", "MultipleJsonbCreatorAnnotations"); + "Only one constructor or static factory method can be annotated with @JsonbCreator in a given class.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidNumerOfJsonbCreatorAnnotationsInClass"); - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); // test code actions JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); TextEdit te1 = te(17, 4, 18, 4, ""); CodeAction ca1 = ca(uri, "Remove @JsonbCreator", d1, te1); - - assertJavaCodeAction(codeActionParams1, utils, ca1); + + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1); JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); TextEdit te2 = te(20, 4, 21, 4, ""); CodeAction ca2 = ca(uri, "Remove @JsonbCreator", d2, te2); - assertJavaCodeAction(codeActionParams2, utils, ca2); + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca2); } - + @Test public void JsonbTransientNotMutuallyExclusive() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jsonb/JsonbTransientDiagnostic.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/jsonb/JsonbTransientDiagnostic.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - + // Diagnostic for the field "id" Diagnostic d1 = d(21, 16, 18, - "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", - DiagnosticSeverity.Error, "jakarta-jsonb", "NonmutualJsonbTransientAnnotation"); + "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidJSonBindindAnnotationWithJsonbTransientOnField"); d1.setData(new Gson().toJsonTree(Arrays.asList("JsonbTransient"))); // Diagnostic for the field "name" Diagnostic d2 = d(25, 19, 23, - "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", - DiagnosticSeverity.Error, "jakarta-jsonb", "NonmutualJsonbTransientAnnotation"); - d2.setData(new Gson().toJsonTree(Arrays.asList("JsonbProperty", "JsonbTransient"))); + "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidJSonBindindAnnotationWithJsonbTransientOnField"); + d2.setData(new Gson().toJsonTree(Arrays.asList("JsonbProperty", "JsonbTransient"))); // Diagnostic for the field "favoriteLanguage" Diagnostic d3 = d(30, 19, 35, - "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", - DiagnosticSeverity.Error, "jakarta-jsonb", "NonmutualJsonbTransientAnnotation"); - d3.setData(new Gson().toJsonTree(Arrays.asList("JsonbProperty", "JsonbAnnotation", "JsonbTransient"))); - + "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidJSonBindindAnnotationWithJsonbTransientOnField"); + d3.setData(new Gson().toJsonTree(Arrays.asList("JsonbProperty", "JsonbAnnotation", "JsonbTransient"))); + // Diagnostic for the field "favoriteEditor" Diagnostic d4 = d(39, 19, 33, - "When an accessor is annotated with @JsonbTransient, its field or the accessor must not be annotated with other JSON Binding annotations.", - DiagnosticSeverity.Error, "jakarta-jsonb", "NonmutualJsonbTransientAnnotationOnAccessor"); + "When an accessor is annotated with @JsonbTransient, its field or the accessor must not be annotated with other JSON Binding annotations.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidJSonBindindAnnotationWithJsonbTransientOnAccessor"); d4.setData(new Gson().toJsonTree(Arrays.asList("JsonbProperty"))); - + // Diagnostic for the getter "getId" Diagnostic d5 = d(42, 16, 21, - "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", - DiagnosticSeverity.Error, "jakarta-jsonb", "NonmutualJsonbTransientAnnotation"); + "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidJSonBindindAnnotationWithJsonbTransientOnField"); d5.setData(new Gson().toJsonTree(Arrays.asList("JsonbProperty"))); - + // Diagnostic for the setter "setId" Diagnostic d6 = d(49, 17, 22, - "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", - DiagnosticSeverity.Error, "jakarta-jsonb", "NonmutualJsonbTransientAnnotation"); + "When a class field is annotated with @JsonbTransient, this field, getter or setter must not be annotated with other JSON Binding annotations.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidJSonBindindAnnotationWithJsonbTransientOnField"); d6.setData(new Gson().toJsonTree(Arrays.asList("JsonbAnnotation"))); - + // Diagnostic for the getter "getFavoriteEditor" Diagnostic d7 = d(67, 19, 36, - "When an accessor is annotated with @JsonbTransient, its field or the accessor must not be annotated with other JSON Binding annotations.", - DiagnosticSeverity.Error, "jakarta-jsonb", "NonmutualJsonbTransientAnnotationOnAccessor"); + "When an accessor is annotated with @JsonbTransient, its field or the accessor must not be annotated with other JSON Binding annotations.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidJSonBindindAnnotationWithJsonbTransientOnAccessor"); d7.setData(new Gson().toJsonTree(Arrays.asList("JsonbTransient"))); - + // Diagnostic for the setter "setFavoriteEditor" Diagnostic d8 = d(74, 17, 34, - "When an accessor is annotated with @JsonbTransient, its field or the accessor must not be annotated with other JSON Binding annotations.", - DiagnosticSeverity.Error, "jakarta-jsonb", "NonmutualJsonbTransientAnnotationOnAccessor"); + "When an accessor is annotated with @JsonbTransient, its field or the accessor must not be annotated with other JSON Binding annotations.", + DiagnosticSeverity.Error, "jakarta-jsonb", "InvalidJSonBindindAnnotationWithJsonbTransientOnAccessor"); d8.setData(new Gson().toJsonTree(Arrays.asList("JsonbAnnotation", "JsonbTransient"))); - - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2, d3, d4, d5, d6, d7, d8); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3, d4, d5, d6, d7, d8); // Test code actions // Quick fix for the field "id" JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); TextEdit te1 = te(20, 4, 21, 4, ""); CodeAction ca1 = ca(uri, "Remove @JsonbTransient", d1, te1); - assertJavaCodeAction(codeActionParams1, utils, ca1); - + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1); + // Quick fix for the field "name" JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); TextEdit te3 = te(24, 4, 25, 4, ""); TextEdit te4 = te(23, 4, 24, 4, ""); CodeAction ca3 = ca(uri, "Remove @JsonbTransient", d2, te3); CodeAction ca4 = ca(uri, "Remove @JsonbProperty", d2, te4); - assertJavaCodeAction(codeActionParams2, utils, ca3, ca4); - + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca4, ca3); + // Quick fix for the field "favoriteLanguage" JakartaJavaCodeActionParams codeActionParams3 = createCodeActionParams(uri, d3); TextEdit te5 = te(29, 4, 30, 4, ""); TextEdit te6 = te(27, 4, 29, 4, ""); CodeAction ca5 = ca(uri, "Remove @JsonbTransient", d3, te5); CodeAction ca6 = ca(uri, "Remove @JsonbProperty, @JsonbAnnotation", d3, te6); - assertJavaCodeAction(codeActionParams3, utils, ca5, ca6); - + assertJavaCodeAction(codeActionParams3, IJDT_UTILS, ca6, ca5); + // Quick fix for the accessor "getId" JakartaJavaCodeActionParams codeActionParams4 = createCodeActionParams(uri, d5); TextEdit te7 = te(41, 4, 42, 4, ""); CodeAction ca7 = ca(uri, "Remove @JsonbProperty", d5, te7); - assertJavaCodeAction(codeActionParams4, utils, ca7); + assertJavaCodeAction(codeActionParams4, IJDT_UTILS, ca7); // Quick fix for the accessor "setId" JakartaJavaCodeActionParams codeActionParams5 = createCodeActionParams(uri, d6); TextEdit te8 = te(48, 4, 49, 4, ""); CodeAction ca8 = ca(uri, "Remove @JsonbAnnotation", d6, te8); - assertJavaCodeAction(codeActionParams5, utils, ca8); + assertJavaCodeAction(codeActionParams5, IJDT_UTILS, ca8); } } \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jsonp/JakartaJsonpTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jsonp/JakartaJsonpTest.java index 81c83d53..61237550 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jsonp/JakartaJsonpTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/jsonp/JakartaJsonpTest.java @@ -12,7 +12,8 @@ *******************************************************************************/ package org.eclipse.lsp4jakarta.jdt.jsonp; -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.*; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; import java.util.Arrays; @@ -21,36 +22,37 @@ import org.eclipse.jdt.core.IJavaProject; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class JakartaJsonpTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void invalidPointerTarget() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/jsonp/CreatePointerInvalidTarget.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/jsonp/CreatePointerInvalidTarget.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); - - Diagnostic d1 = d(20, 60, 64, - "Json.createPointer target must be a sequence of '/' prefixed tokens or an empty String.", - DiagnosticSeverity.Error, "jakarta-jsonp", "InvalidCreatePointerArg"); - - Diagnostic d2 = d(21, 62, 70, - "Json.createPointer target must be a sequence of '/' prefixed tokens or an empty String.", - DiagnosticSeverity.Error, "jakarta-jsonp", "InvalidCreatePointerArg"); - - Diagnostic d3 = d(22, 60, 80, - "Json.createPointer target must be a sequence of '/' prefixed tokens or an empty String.", - DiagnosticSeverity.Error, "jakarta-jsonp", "InvalidCreatePointerArg"); - - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2, d3); + + Diagnostic d1 = d(20, 60, 64, + "Json.createPointer target must be a sequence of '/' prefixed tokens or an empty String.", + DiagnosticSeverity.Error, "jakarta-jsonp", "InvalidJsonCreatePointerTarget"); + + Diagnostic d2 = d(21, 62, 70, + "Json.createPointer target must be a sequence of '/' prefixed tokens or an empty String.", + DiagnosticSeverity.Error, "jakarta-jsonp", "InvalidJsonCreatePointerTarget"); + + Diagnostic d3 = d(22, 60, 80, + "Json.createPointer target must be a sequence of '/' prefixed tokens or an empty String.", + DiagnosticSeverity.Error, "jakarta-jsonp", "InvalidJsonCreatePointerTarget"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/nodiagnostics/NoDiagnosticsTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/nodiagnostics/NoDiagnosticsTest.java index e12afb44..fb45cff6 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/nodiagnostics/NoDiagnosticsTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/nodiagnostics/NoDiagnosticsTest.java @@ -25,9 +25,10 @@ import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -37,46 +38,43 @@ @RunWith(Parameterized.class) public class NoDiagnosticsTest extends BaseJakartaTest { - @Parameter - public String filePath; + @Parameter + public String filePath; - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); - @Test - public void checkForNoDiagnostics() throws Exception { + @Test + public void checkForNoDiagnostics() throws Exception { + IJavaProject javaProject = loadJavaProject("demo-servlet-no-diagnostics", ""); + IFile javaFile = javaProject.getProject().getFile(new Path(filePath)); + String uri = javaFile.getLocation().toFile().toURI().toString(); - JDTUtils utils = JDT_UTILS; + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); + diagnosticsParams.setUris(Arrays.asList(uri)); - IJavaProject javaProject = loadJavaProject("demo-servlet-no-diagnostics", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path(filePath)); - String uri = javaFile.getLocation().toFile().toURI().toString(); + // should be no diagnostics in the file. + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); - diagnosticsParams.setUris(Arrays.asList(uri)); + } - // should be no diagnostics in the file. - assertJavaDiagnostics(diagnosticsParams, utils); + // list of java files in demo-servlet-no-diagnostics. + @Parameters + public static List projectFileProvider() throws Exception { - } + String packagePath = "/src/main/java/io/openliberty/sample/jakarta/"; + String basePath = System.getProperty("user.dir") + + "/projects/demo-servlet-no-diagnostics/"; + File dir = new File(basePath + packagePath); + String[] extensions = new String[] { "java" }; + List results = new ArrayList(); - // list of java files in demo-servlet-no-diagnostics. - @Parameters - public static List projectFileProvider() throws Exception { + Collection files = FileUtils.listFiles(dir, extensions, true); + for (File file : files) { + // Get relative path from source folder and add it in the results array. + results.add(file.getAbsolutePath().substring(basePath.length())); + } - String packagePath = "/src/main/java/io/openliberty/sample/jakarta/"; - String basePath = System.getProperty("user.dir") - + "/projects/demo-servlet-no-diagnostics/"; - File dir = new File(basePath + packagePath); - String[] extensions = new String[] { "java" }; - List results = new ArrayList(); - - Collection files = FileUtils.listFiles(dir, extensions, true); - for (File file : files) { - // Get relative path from source folder and add it in the results array. - results.add(file.getAbsolutePath().substring(basePath.length())); - } - return results; - } + return results; + } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/persistence/JakartaPersistenceTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/persistence/JakartaPersistenceTest.java index 23fb3952..0eb17fcf 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/persistence/JakartaPersistenceTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/persistence/JakartaPersistenceTest.java @@ -12,7 +12,12 @@ *******************************************************************************/ package org.eclipse.lsp4jakarta.jdt.persistence; -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.*; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaCodeAction; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.ca; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.createCodeActionParams; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.te; import java.util.Arrays; @@ -24,36 +29,36 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class JakartaPersistenceTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void deleteMapKeyOrMapKeyClass() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/persistence/MapKeyAndMapKeyClassTogether.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/persistence/MapKeyAndMapKeyClassTogether.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d1 = d(16, 32, 42, - "@MapKeyClass and @MapKey annotations cannot be used on the same field or property.", - DiagnosticSeverity.Error, "jakarta-persistence", "RemoveMapKeyorMapKeyClass"); + "@MapKeyClass and @MapKey annotations cannot be used on the same method.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidMapKeyAnnotationsOnSameMethod"); Diagnostic d2 = d(11, 25, 32, - "@MapKeyClass and @MapKey annotations cannot be used on the same field or property.", - DiagnosticSeverity.Error, "jakarta-persistence", "RemoveMapKeyorMapKeyClass"); + "@MapKeyClass and @MapKey annotations cannot be used on the same field or property.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidMapKeyAnnotationsOnSameField"); - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); @@ -62,7 +67,7 @@ public void deleteMapKeyOrMapKeyClass() throws Exception { CodeAction ca1 = ca(uri, "Remove @MapKeyClass", d1, te1); CodeAction ca2 = ca(uri, "Remove @MapKey", d1, te2); - assertJavaCodeAction(codeActionParams1, utils, ca1, ca2); + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca2, ca1); JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); @@ -71,161 +76,160 @@ public void deleteMapKeyOrMapKeyClass() throws Exception { CodeAction ca3 = ca(uri, "Remove @MapKeyClass", d2, te3); CodeAction ca4 = ca(uri, "Remove @MapKey", d2, te4); - assertJavaCodeAction(codeActionParams2, utils, ca3, ca4); + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca4, ca3); } - @Test public void completeMapKeyJoinColumnAnnotation() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/persistence/MultipleMapKeyAnnotations.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/persistence/MultipleMapKeyAnnotations.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // test diagnostics are present Diagnostic d1 = d(12, 25, 30, - "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", - DiagnosticSeverity.Error, "jakarta-persistence", "SupplyAttributesToAnnotations"); + "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidFieldWithMultipleMPJCAnnotations"); Diagnostic d2 = d(12, 25, 30, - "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", - DiagnosticSeverity.Error, "jakarta-persistence", "SupplyAttributesToAnnotations"); + "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidFieldWithMultipleMPJCAnnotations"); Diagnostic d3 = d(16, 25, 30, - "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", - DiagnosticSeverity.Error, "jakarta-persistence", "SupplyAttributesToAnnotations"); + "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidFieldWithMultipleMPJCAnnotations"); Diagnostic d4 = d(16, 25, 30, - "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", - DiagnosticSeverity.Error, "jakarta-persistence", "SupplyAttributesToAnnotations"); + "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidFieldWithMultipleMPJCAnnotations"); Diagnostic d5 = d(20, 25, 30, - "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", - DiagnosticSeverity.Error, "jakarta-persistence", "SupplyAttributesToAnnotations"); - - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2, d3, d4, d5); - + "A field with multiple @MapKeyJoinColumn annotations must specify both the name and referencedColumnName attributes in the corresponding @MapKeyJoinColumn annotations.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidFieldWithMultipleMPJCAnnotations"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3, d4, d5); + // test quick fixes JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); - TextEdit te1 = te(10, 4, 11, 23, "@MapKeyJoinColumn(name = \"\", referencedColumnName = \"\")\n\t@MapKeyJoinColumn(name = \"\", referencedColumnName = \"\")"); - CodeAction ca1 = ca(uri, "Add the missing attributes to the @MapKeyJoinColumn annotation", d1, te1); + TextEdit te1 = te(10, 4, 11, 23, + "@MapKeyJoinColumn(name = \"\", referencedColumnName = \"\")\n\t@MapKeyJoinColumn(name = \"\", referencedColumnName = \"\")"); + CodeAction ca1 = ca(uri, "Insert the missing attributes to the @MapKeyJoinColumn annotation", d1, te1); + + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1); - assertJavaCodeAction(codeActionParams1, utils, ca1); - JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d3); - TextEdit te2 = te(14, 4, 15, 52, "@MapKeyJoinColumn(referencedColumnName = \"rcn2\", name = \"\")\n\t@MapKeyJoinColumn(name = \"n1\", referencedColumnName = \"\")"); - CodeAction ca2 = ca(uri, "Add the missing attributes to the @MapKeyJoinColumn annotation", d3, te2); + TextEdit te2 = te(14, 4, 15, 52, + "@MapKeyJoinColumn(referencedColumnName = \"rcn2\", name = \"\")\n\t@MapKeyJoinColumn(name = \"n1\", referencedColumnName = \"\")"); + CodeAction ca2 = ca(uri, "Insert the missing attributes to the @MapKeyJoinColumn annotation", d3, te2); + + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca2); - assertJavaCodeAction(codeActionParams2, utils, ca2); - JakartaJavaCodeActionParams codeActionParams3 = createCodeActionParams(uri, d5); - TextEdit te3 = te(18, 4, 19, 23, "@MapKeyJoinColumn(name = \"\", referencedColumnName = \"\")\n\t@MapKeyJoinColumn(name = \"n1\", referencedColumnName = \"rcn1\")"); - CodeAction ca3 = ca(uri, "Add the missing attributes to the @MapKeyJoinColumn annotation", d5, te3); + TextEdit te3 = te(18, 4, 19, 23, + "@MapKeyJoinColumn(name = \"\", referencedColumnName = \"\")\n\t@MapKeyJoinColumn(name = \"n1\", referencedColumnName = \"rcn1\")"); + CodeAction ca3 = ca(uri, "Insert the missing attributes to the @MapKeyJoinColumn annotation", + d5, te3); - assertJavaCodeAction(codeActionParams3, utils, ca3); + assertJavaCodeAction(codeActionParams3, IJDT_UTILS, ca3); } @Test public void addEmptyConstructor() throws Exception { - JDTUtils utils = JDT_UTILS; - IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/persistence/EntityMissingConstructor.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/persistence/EntityMissingConstructor.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // test diagnostics are present Diagnostic d = d(5, 13, 37, - "A class using the @Entity annotation must contain a public or protected constructor with no arguments.", - DiagnosticSeverity.Error, "jakarta-persistence", "MissingEmptyConstructor"); + "A class using the @Entity annotation must contain a public or protected constructor with no arguments.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidConstructorInEntityAnnotatedClass"); - assertJavaDiagnostics(diagnosticsParams, utils, d); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); // test quick fixes JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d); - TextEdit te1 = te(7, 4, 7, 4, "protected EntityMissingConstructor() {\n\t}\n\n\t"); - CodeAction ca1 = ca(uri, "Add a no-arg protected constructor to this class", d, te1); - TextEdit te2 = te(7, 4, 7, 4, "public EntityMissingConstructor() {\n\t}\n\n\t"); - CodeAction ca2 = ca(uri, "Add a no-arg public constructor to this class", d, te2); + TextEdit te1 = te(7, 4, 7, 4, "protected EntityMissingConstructor() {\n\t}\n\n\t"); + CodeAction ca1 = ca(uri, "Add a default 'protected' constructor to this class", d, te1); + TextEdit te2 = te(7, 4, 7, 4, "public EntityMissingConstructor() {\n\t}\n\n\t"); + CodeAction ca2 = ca(uri, "Add a default 'public' constructor to this class", d, te2); - assertJavaCodeAction(codeActionParams1, utils, ca1, ca2); + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1, ca2); } @Test public void removeFinalModifiers() throws Exception { - JDTUtils utils = JDT_UTILS; IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/persistence/FinalModifiers.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/persistence/FinalModifiers.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // test diagnostics are present Diagnostic d1 = d(10, 21, 28, - "A class using the @Entity annotation cannot contain any methods that are declared final.", - DiagnosticSeverity.Error, "jakarta-persistence", "RemoveFinalMethods"); + "A class using the @Entity annotation cannot contain any methods that are declared final.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidFinalMethodInEntityAnnotatedClass"); d1.setData(IJavaElement.METHOD); Diagnostic d2 = d(7, 14, 15, - "A class using the @Entity annotation cannot contain any persistent instance variables that are declared final.", - DiagnosticSeverity.Error, "jakarta-persistence", "RemoveFinalVariables"); + "A class using the @Entity annotation cannot contain any persistent instance variables that are declared final.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidPersistentFieldInEntityAnnotatedClass"); d2.setData(IJavaElement.FIELD); Diagnostic d3 = d(8, 17, 18, - "A class using the @Entity annotation cannot contain any persistent instance variables that are declared final.", - DiagnosticSeverity.Error, "jakarta-persistence", "RemoveFinalVariables"); + "A class using the @Entity annotation cannot contain any persistent instance variables that are declared final.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidPersistentFieldInEntityAnnotatedClass"); d3.setData(IJavaElement.FIELD); Diagnostic d4 = d(8, 30, 31, - "A class using the @Entity annotation cannot contain any persistent instance variables that are declared final.", - DiagnosticSeverity.Error, "jakarta-persistence", "RemoveFinalVariables"); + "A class using the @Entity annotation cannot contain any persistent instance variables that are declared final.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidPersistentFieldInEntityAnnotatedClass"); d4.setData(IJavaElement.FIELD); Diagnostic d5 = d(5, 19, 33, - "A class using the @Entity annotation must not be final.", - DiagnosticSeverity.Error, "jakarta-persistence", "InvalidClass"); + "A class using the @Entity annotation must not be final.", + DiagnosticSeverity.Error, "jakarta-persistence", "InvalidFinalModifierOnEntityAnnotatedClass"); d5.setData(IJavaElement.TYPE); - assertJavaDiagnostics(diagnosticsParams, utils, d1, d2, d3, d4, d5); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3, d4, d5); // test quick fixes JakartaJavaCodeActionParams codeActionParams1 = createCodeActionParams(uri, d1); TextEdit te1 = te(10, 10, 10, 16, ""); - CodeAction ca1 = ca(uri, "Remove the 'final' modifier from this method", d1, te1); + CodeAction ca1 = ca(uri, "Remove the 'final' modifier", d1, te1); - assertJavaCodeAction(codeActionParams1, utils, ca1); + assertJavaCodeAction(codeActionParams1, IJDT_UTILS, ca1); JakartaJavaCodeActionParams codeActionParams2 = createCodeActionParams(uri, d2); TextEdit te2 = te(7, 4, 7, 10, ""); - CodeAction ca2 = ca(uri, "Remove the 'final' modifier from this field", d2, te2); + CodeAction ca2 = ca(uri, "Remove the 'final' modifier", d2, te2); - assertJavaCodeAction(codeActionParams2, utils, ca2); + assertJavaCodeAction(codeActionParams2, IJDT_UTILS, ca2); JakartaJavaCodeActionParams codeActionParams3 = createCodeActionParams(uri, d3); TextEdit te3 = te(8, 4, 8, 10, ""); - CodeAction ca3 = ca(uri, "Remove the 'final' modifier from this field", d3, te3); + CodeAction ca3 = ca(uri, "Remove the 'final' modifier", d3, te3); - assertJavaCodeAction(codeActionParams3, utils, ca3); + assertJavaCodeAction(codeActionParams3, IJDT_UTILS, ca3); JakartaJavaCodeActionParams codeActionParams4 = createCodeActionParams(uri, d4); TextEdit te4 = te(8, 4, 8, 10, ""); - CodeAction ca4 = ca(uri, "Remove the 'final' modifier from this field", d4, te4); + CodeAction ca4 = ca(uri, "Remove the 'final' modifier", d4, te4); - assertJavaCodeAction(codeActionParams4, utils, ca4); + assertJavaCodeAction(codeActionParams4, IJDT_UTILS, ca4); JakartaJavaCodeActionParams codeActionParams5 = createCodeActionParams(uri, d5); TextEdit te5 = te(5, 6, 5, 12, ""); - CodeAction ca5 = ca(uri, "Remove the 'final' modifier from this class", d5, te5); + CodeAction ca5 = ca(uri, "Remove the 'final' modifier", d5, te5); - assertJavaCodeAction(codeActionParams5, utils, ca5); + assertJavaCodeAction(codeActionParams5, IJDT_UTILS, ca5); } } \ No newline at end of file diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/servlet/JakartaServletTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/servlet/JakartaServletTest.java index 996aee67..57d67c7b 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/servlet/JakartaServletTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/servlet/JakartaServletTest.java @@ -12,7 +12,12 @@ *******************************************************************************/ package org.eclipse.lsp4jakarta.jdt.servlet; -import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.*; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaCodeAction; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.assertJavaDiagnostics; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.ca; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.createCodeActionParams; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.d; +import static org.eclipse.lsp4jakarta.jdt.core.JakartaForJavaAssert.te; import java.util.Arrays; @@ -23,57 +28,57 @@ import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Ignore; import org.junit.Test; public class JakartaServletTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test @Ignore // getAllSuperTypes() returns nothing for tests. See #232 public void ExtendWebServlet() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/servlet/DontExtendHttpServlet.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/servlet/DontExtendHttpServlet.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // expected Diagnostic d = d(5, 13, 34, "Annotated classes with @WebServlet must extend the HttpServlet class.", - DiagnosticSeverity.Warning, "jakarta-servlet", "ExtendHttpServlet"); + DiagnosticSeverity.Warning, "jakarta-servlet", + "WebServletAnnotatedClassUnknownSuperTypeDoesNotExtendHttpServlet"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); // test associated quick-fix code action JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d); TextEdit te = te(5, 34, 5, 34, " extends HttpServlet"); CodeAction ca = ca(uri, "Let 'DontExtendHttpServlet' extend 'HttpServlet'", d, te); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca); } @Test @Ignore // getAllSuperTypes() returns nothing for tests. See #232 public void CompleteWebServletAnnotation() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/servlet/InvalidWebServlet.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/servlet/InvalidWebServlet.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d = d(9, 0, 13, - "The annotation @WebServlet must define the attribute urlPatterns or the attribute value.", - DiagnosticSeverity.Error, "jakarta-servlet", "CompleteHttpServletAttributes"); + "The @WebServlet annotation must define the attribute 'urlPatterns' or 'value'.", + DiagnosticSeverity.Error, "jakarta-servlet", "WebServletAnnotationMissingAttributes"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); JakartaJavaCodeActionParams codeActionParams = createCodeActionParams(uri, d); TextEdit te1 = te(9, 0, 10, 0, "@WebServlet(value = \"\")\n"); @@ -81,7 +86,7 @@ public void CompleteWebServletAnnotation() throws Exception { TextEdit te2 = te(9, 0, 10, 0, "@WebServlet(urlPatterns = \"\")\n"); CodeAction ca2 = ca(uri, "Add the `urlPatterns` attribute to @WebServlet", d, te2); - assertJavaCodeAction(codeActionParams, JDT_UTILS, ca1, ca2); + assertJavaCodeAction(codeActionParams, IJDT_UTILS, ca1, ca2); } } diff --git a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/websocket/JakartaWebSocketTest.java b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/websocket/JakartaWebSocketTest.java index 7d797680..7100c995 100644 --- a/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/websocket/JakartaWebSocketTest.java +++ b/jakarta.jdt/org.eclipse.lsp4jakarta.jdt.test/src/main/java/org/eclipse/lsp4jakarta/jdt/websocket/JakartaWebSocketTest.java @@ -21,179 +21,170 @@ import java.util.Arrays; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IJavaProject; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.TextEdit; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.Path; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; -import org.eclipse.lsp4jakarta.jdt.core.JDTUtils; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; +import org.eclipse.lsp4jakarta.jdt.core.BaseJakartaTest; +import org.eclipse.lsp4jakarta.jdt.core.utils.IJDTUtils; +import org.eclipse.lsp4jakarta.jdt.internal.core.ls.JDTUtilsLSImpl; import org.junit.Test; public class JakartaWebSocketTest extends BaseJakartaTest { - protected static JDTUtils JDT_UTILS = new JDTUtils(); + protected static IJDTUtils IJDT_UTILS = JDTUtilsLSImpl.getInstance(); @Test public void addPathParamsAnnotation() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/AnnotationTest.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/AnnotationTest.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // OnOpen PathParams Annotation check Diagnostic d1 = d(18, 47, 64, - "Parameters of type String, any Java primitive type, or boxed version thereof must be annotated with @PathParams.", - DiagnosticSeverity.Error, "jakarta-websocket", "AddPathParamsAnnotation" - ); - + "Parameters of type String, any Java primitive type, or boxed version thereof must be annotated with @PathParams.", + DiagnosticSeverity.Error, "jakarta-websocket", "PathParamsMissingFromParam"); + // OnClose PathParams Annotation check Diagnostic d2 = d(24, 49, 67, - "Parameters of type String, any Java primitive type, or boxed version thereof must be annotated with @PathParams.", - DiagnosticSeverity.Error, "jakarta-websocket", "AddPathParamsAnnotation" - ); - + "Parameters of type String, any Java primitive type, or boxed version thereof must be annotated with @PathParams.", + DiagnosticSeverity.Error, "jakarta-websocket", "PathParamsMissingFromParam"); + Diagnostic d3 = d(24, 76, 94, - "Parameters of type String, any Java primitive type, or boxed version thereof must be annotated with @PathParams.", - DiagnosticSeverity.Error, "jakarta-websocket", "AddPathParamsAnnotation" - ); + "Parameters of type String, any Java primitive type, or boxed version thereof must be annotated with @PathParams.", + DiagnosticSeverity.Error, "jakarta-websocket", "PathParamsMissingFromParam"); + + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2, d3); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2, d3); - // Expected code actions JakartaJavaCodeActionParams codeActionsParams = createCodeActionParams(uri, d1); - String newText = "\nimport jakarta.websocket.server.PathParam;\nimport jakarta.websocket.server.ServerEndpoint;\nimport jakarta.websocket.Session;\n\n" - + "/**\n * Expected Diagnostics are related to validating that the parameters have the \n * valid annotation @PathParam (code: AddPathParamsAnnotation)\n * See issue #247 (onOpen) and #248 (onClose)\n */\n" - + "@ServerEndpoint(value = \"/infos\")\npublic class AnnotationTest {\n // @PathParam missing annotation for \"String missingAnnotation\"\n @OnOpen\n public void OnOpen(Session session, @PathParam(value = \"\") "; + String newText = "\nimport jakarta.websocket.server.PathParam;\nimport jakarta.websocket.server.ServerEndpoint;\nimport jakarta.websocket.Session;\n\n" + + "/**\n * Expected Diagnostics are related to validating that the parameters have the \n * valid annotation @PathParam (code: AddPathParamsAnnotation)\n * See issue #247 (onOpen) and #248 (onClose)\n */\n" + + "@ServerEndpoint(value = \"/infos\")\npublic class AnnotationTest {\n // @PathParam missing annotation for \"String missingAnnotation\"\n @OnOpen\n public void OnOpen(Session session, @PathParam(value = \"\") "; TextEdit te = te(5, 32, 18, 40, newText); - CodeAction ca = ca(uri, "Insert @jakarta.websocket.server.PathParam", d1, te); - assertJavaCodeAction(codeActionsParams, JDT_UTILS, ca); + CodeAction ca = ca(uri, "Insert @PathParam", d1, te); + assertJavaCodeAction(codeActionsParams, IJDT_UTILS, ca); } @Test public void changeInvalidParamType() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/InvalidParamType.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/InvalidParamType.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); // OnOpen Invalid Param Types Diagnostic d1 = d(19, 47, 59, - "Invalid parameter type. When using @OnOpen, parameter must be of type: \n- jakarta.websocket.EndpointConfig\n- jakarta.websocket.Session\n- annotated with @PathParams and of type String or any Java primitive type or boxed version thereof.", - DiagnosticSeverity.Error, "jakarta-websocket", "OnOpenChangeInvalidParam"); + "Invalid parameter type. When using @OnOpen, parameter must be of type: \n- jakarta.websocket.EndpointConfig\n- jakarta.websocket.Session\n- annotated with @PathParams and of type String or any Java primitive type or boxed version thereof.", + DiagnosticSeverity.Error, "jakarta-websocket", "InvalidOnOpenParams"); // OnClose Invalid Param Type Diagnostic d2 = d(24, 73, 85, - "Invalid parameter type. When using @OnClose, parameter must be of type: \n- jakarta.websocket.CloseReason\n- jakarta.websocket.Session\n- annotated with @PathParams and of type String or any Java primitive type or boxed version thereof.", - DiagnosticSeverity.Error, "jakarta-websocket", "OnCloseChangeInvalidParam"); + "Invalid parameter type. When using @OnClose, parameter must be of type: \n- jakarta.websocket.CloseReason\n- jakarta.websocket.Session\n- annotated with @PathParams and of type String or any Java primitive type or boxed version thereof.", + DiagnosticSeverity.Error, "jakarta-websocket", "InvalidOnCloseParams"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); } @Test public void testPathParamInvalidURI() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/websockets/PathParamURIWarningTest.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/websockets/PathParamURIWarningTest.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d = d(22, 59, 77, "PathParam value does not match specified Endpoint URI.", - DiagnosticSeverity.Warning, "jakarta-websocket", "ChangePathParamValue"); + DiagnosticSeverity.Warning, "jakarta-websocket", "PathParamDoesNotMatchEndpointURI"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); } @Test public void testServerEndpointRelativeURI() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); IFile javaFile = javaProject.getProject().getFile( - new Path("src/main/java/io/openliberty/sample/jakarta/websocket/ServerEndpointRelativePathTest.java")); + new Path("src/main/java/io/openliberty/sample/jakarta/websocket/ServerEndpointRelativePathTest.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d = d(6, 0, 27, "Server endpoint paths must not contain the sequences '/../', '/./' or '//'.", - DiagnosticSeverity.Error, "jakarta-websocket", "ChangeInvalidServerEndpoint"); + DiagnosticSeverity.Error, "jakarta-websocket", "InvalidEndpointPathWithRelativePaths"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); } @Test public void testServerEndpointNoSlashURI() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/ServerEndpointNoSlash.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/ServerEndpointNoSlash.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d1 = d(7, 0, 23, "Server endpoint paths must start with a leading '/'.", DiagnosticSeverity.Error, - "jakarta-websocket", "ChangeInvalidServerEndpoint"); + "jakarta-websocket", "InvalidEndpointPathWithNoStartingSlash"); Diagnostic d2 = d(7, 0, 23, "Server endpoint paths must be a URI-template (level-1) or a partial URI.", - DiagnosticSeverity.Error, "jakarta-websocket", "ChangeInvalidServerEndpoint"); + DiagnosticSeverity.Error, "jakarta-websocket", "InvalidEndpointPathNotTempleateOrPartialURI"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); } @Test public void testServerEndpointInvalidTemplateURI() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject().getFile(new Path( - "src/main/java/io/openliberty/sample/jakarta/websocket/ServerEndpointInvalidTemplateURI.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/ServerEndpointInvalidTemplateURI.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d = d(6, 0, 46, "Server endpoint paths must be a URI-template (level-1) or a partial URI.", - DiagnosticSeverity.Error, "jakarta-websocket", "ChangeInvalidServerEndpoint"); + DiagnosticSeverity.Error, "jakarta-websocket", "InvalidEndpointPathNotTempleateOrPartialURI"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); } @Test public void testServerEndpointDuplicateVariableURI() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject().getFile(new Path( - "src/main/java/io/openliberty/sample/jakarta/websocket/ServerEndpointDuplicateVariableURI.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/ServerEndpointDuplicateVariableURI.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d = d(6, 0, 40, "Server endpoint paths must not use the same variable more than once in a path.", - DiagnosticSeverity.Error, "jakarta-websocket", "ChangeInvalidServerEndpoint"); + DiagnosticSeverity.Error, "jakarta-websocket", "InvalidEndpointPathDuplicateVariable"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d); } public void testDuplicateOnMessage() throws Exception { IJavaProject javaProject = loadJavaProject("jakarta-sample", ""); - IFile javaFile = javaProject.getProject() - .getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/DuplicateOnMessage.java")); + IFile javaFile = javaProject.getProject().getFile(new Path("src/main/java/io/openliberty/sample/jakarta/websocket/DuplicateOnMessage.java")); String uri = javaFile.getLocation().toFile().toURI().toString(); - JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams(); + JakartaJavaDiagnosticsParams diagnosticsParams = new JakartaJavaDiagnosticsParams(); diagnosticsParams.setUris(Arrays.asList(uri)); Diagnostic d1 = d(11, 4, 14, - "Classes annotated with @ServerEndpoint or @ClientEndpoint may only have one @OnMessage annotated method for each of the native WebSocket message formats: text, binary and pong.", - DiagnosticSeverity.Error, "jakarta-websocket", "OnMessageDuplicateMethod"); + "Classes annotated with @ServerEndpoint or @ClientEndpoint may only have one @OnMessage annotated method for each of the native WebSocket message formats: text, binary and pong.", + DiagnosticSeverity.Error, "jakarta-websocket", "OnMessageDuplicateMethod"); Diagnostic d2 = d(16, 4, 14, - "Classes annotated with @ServerEndpoint or @ClientEndpoint may only have one @OnMessage annotated method for each of the native WebSocket message formats: text, binary and pong.", - DiagnosticSeverity.Error, "jakarta-websocket", "OnMessageDuplicateMethod"); + "Classes annotated with @ServerEndpoint or @ClientEndpoint may only have one @OnMessage annotated method for each of the native WebSocket message formats: text, binary and pong.", + DiagnosticSeverity.Error, "jakarta-websocket", "OnMessageDuplicateMethod"); - assertJavaDiagnostics(diagnosticsParams, JDT_UTILS, d1, d2); + assertJavaDiagnostics(diagnosticsParams, IJDT_UTILS, d1, d2); } } diff --git a/jakarta.jdt/pom.xml b/jakarta.jdt/pom.xml index f757391f..3d6455eb 100644 --- a/jakarta.jdt/pom.xml +++ b/jakarta.jdt/pom.xml @@ -1,189 +1,206 @@ - - 4.0.0 - org.eclipse.lsp4jakarta - jdt-parent - 0.1.2-SNAPSHOT - pom - - Eclipse LSP4Jakarta JDT Plugin Parent - Eclipse LSP4Jakarta JDT Plugin Parent - - - Eclipse LSP4Jakarta - https://github.com/eclipse/lsp4jakarta - - - - scm:git:git@github.com:eclipse/lsp4jakarta.git - scm:git:git@github.com:eclipse/lsp4jakarta.git - git@github.com:eclipse/lsp4jakarta.git - HEAD - - - - - EPL-2.0 - https://www.eclipse.org/legal/epl-2.0/ - Eclipse Public License 2.0 - - - - - UTF-8 - UTF-8 - 3.0.3 - ${tycho.version} - scm:git:https://github.com/eclipse/lsp4mp - true - 1.21.0.20230316144047 - repo.eclipse.org - https://repo.eclipse.org/content/repositories/lsp4jakarta-releases/ - repo.eclipse.org - https://repo.eclipse.org/content/repositories/lsp4jakarta-snapshots/ - - - - 2022-09 - p2 - http://download.eclipse.org/releases/2022-09 - - - jdt.ls.p2 - p2 - https://download.eclipse.org/jdtls/milestones/1.21.0/repository/ - - - jdt.ls.maven.snapshots - https://repo.eclipse.org/content/repositories/jdtls-snapshots/ - - - jdt.ls.maven.releases - https://repo.eclipse.org/content/repositories/jdtls-releases/ - - - - - cbi-release - https://repo.eclipse.org/content/repositories/cbi-releases/ - - - - - ${releases.repo.id} - Eclipse Releases Repository - ${releases.repo.url} - - - ${snapshots.repo.id} - Eclipse Snapshots Repository - ${snapshots.repo.url} - - - - - - org.eclipse.tycho - tycho-maven-plugin - ${tycho.version} - true - - - org.eclipse.tycho - target-platform-configuration - ${tycho.version} - - p2 - - - org.eclipse.jdt.ls - org.eclipse.jdt.ls.tp - ${jdt.ls.version} - - - consider - true - - - - - - - org.eclipse.tycho - tycho-packaging-plugin - ${tycho.version} - - yyyyMMdd-HHmm - - - - org.eclipse.tycho - tycho-surefire-plugin - ${tycho.version} - - false - -Xmx512m - - 60 - - - - - - - - platform-mac - - - mac - - - - - - org.eclipse.tycho - tycho-surefire-plugin - ${tycho.version} - - false - -Xmx512m -XstartOnFirstThread - - 60 - - - - - - - eclipse-sign - - - - org.eclipse.tycho - target-platform-configuration - ${tycho.version} - - - org.eclipse.cbi.maven.plugins - eclipse-jarsigner-plugin - 1.3.2 - - - sign - - sign - - - - - - - - - - org.eclipse.lsp4jakarta.jdt.core - org.eclipse.lsp4jakarta.jdt.test - org.eclipse.lsp4jakarta.jdt.site - - + + + 4.0.0 + org.eclipse.lsp4jakarta + jdt-parent + 0.2.0-SNAPSHOT + pom + Eclipse LSP4Jakarta JDT Plugin Parent + Eclipse LSP4Jakarta JDT Plugin Parent + + Eclipse LSP4Jakarta + https://github.com/eclipse/lsp4jakarta + + + scm:git:git@github.com:eclipse/lsp4jakarta.git + scm:git:git@github.com:eclipse/lsp4jakarta.git + git@github.com:eclipse/lsp4jakarta.git + HEAD + + + + EPL-2.0 + https://www.eclipse.org/legal/epl-2.0/ + Eclipse Public License 2.0 + + + + UTF-8 + UTF-8 + 17 + 17 + 3.0.3 + ${tycho.version} + scm:git:https://github.com/eclipse/lsp4mp + true + 1.21.0.20230316144047 + repo.eclipse.org + https://repo.eclipse.org/content/repositories/lsp4jakarta-releases/ + repo.eclipse.org + https://repo.eclipse.org/content/repositories/lsp4jakarta-snapshots/ + + + + 2023-06 + p2 + http://download.eclipse.org/releases/2023-06 + + + jdt.ls.p2 + p2 + https://download.eclipse.org/jdtls/milestones/1.21.0/repository/ + + + jdt.ls.maven.snapshots + https://repo.eclipse.org/content/repositories/jdtls-snapshots/ + + + jdt.ls.maven.releases + https://repo.eclipse.org/content/repositories/jdtls-releases/ + + + + + cbi-release + https://repo.eclipse.org/content/repositories/cbi-releases/ + + + + + ${releases.repo.id} + Eclipse Releases Repository + ${releases.repo.url} + + + ${snapshots.repo.id} + Eclipse Snapshots Repository + ${snapshots.repo.url} + + + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho.version} + true + + + org.eclipse.tycho + target-platform-configuration + ${tycho.version} + + p2 + + + org.eclipse.jdt.ls + org.eclipse.jdt.ls.tp + ${jdt.ls.version} + + + consider + true + + + + net.revelc.code.formatter + formatter-maven-plugin + 2.23.0 + + ${project.basedir}/../../resources/ide/config/eclipse-formatter-config.xml + LF + + + + + format + + + + + + + + + org.eclipse.tycho + tycho-packaging-plugin + ${tycho.version} + + yyyyMMdd-HHmm + + + + org.eclipse.tycho + tycho-surefire-plugin + ${tycho.version} + + false + -Xmx512m + + 60 + + + + + + + + platform-mac + + + mac + + + + + + org.eclipse.tycho + tycho-surefire-plugin + ${tycho.version} + + false + -Xmx512m -XstartOnFirstThread + + 60 + + + + + + + eclipse-sign + + + + org.eclipse.tycho + target-platform-configuration + ${tycho.version} + + + org.eclipse.cbi.maven.plugins + eclipse-jarsigner-plugin + 1.3.2 + + + sign + + sign + + + + + + + + + + org.eclipse.lsp4jakarta.jdt.core + org.eclipse.lsp4jakarta.jdt.site + org.eclipse.lsp4jakarta.jdt.test + + \ No newline at end of file diff --git a/jakarta.ls/pom.xml b/jakarta.ls/pom.xml index bd71b10d..795463bb 100644 --- a/jakarta.ls/pom.xml +++ b/jakarta.ls/pom.xml @@ -1,159 +1,131 @@ - - - 4.0.0 - - org.eclipse.lsp4jakarta - org.eclipse.lsp4jakarta.ls - 0.1.2-SNAPSHOT - - Eclipse LSP4Jakarta Language Server - Eclipse LSP4Jakarta Language Server - - - Eclipse LSP4Jakarta - https://github.com/eclipse/lsp4jakarta - - - - scm:git:git@github.com:eclipse/lsp4jakarta.git - scm:git:git@github.com:eclipse/lsp4jakarta.git - git@github.com:eclipse/lsp4jakarta.git - HEAD - - - - - EPL-2.0 - https://www.eclipse.org/legal/epl-2.0/ - Eclipse Public License 2.0 - - - - - UTF-8 - 3.0.3 - 17 - 17 - 0.17.0 - repo.eclipse.org - https://repo.eclipse.org/content/repositories/lsp4jakarta-releases/ - repo.eclipse.org - https://repo.eclipse.org/content/repositories/lsp4jakarta-snapshots/ - - - - - org.eclipse.lsp4j - org.eclipse.lsp4j - ${lsp4j.version} - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - ${lsp4j.version} - - - junit - junit - 4.11 - test - - - org.eclipse.lsp4mp - org.eclipse.lsp4mp.ls - 0.5.0 - uber - - - org.eclipse.lsp4j - org.eclipse.lsp4j - - - org.eclipse.lsp4j - org.eclipse.lsp4j.jsonrpc - - - - - - - - lsp4mp-releases - https://repo.eclipse.org/content/repositories/lsp4mp-releases/ - - true - - - false - - - - lsp4mp-snapshots - https://repo.eclipse.org/content/repositories/lsp4mp-snapshots/ - - false - - - true - - - - - - - cbi-release - https://repo.eclipse.org/content/repositories/cbi-releases/ - - - - - - - maven-assembly-plugin - 3.5.0 - - - make-assembly - package - - single - - - - - org.eclipse.lsp4jakarta.JakartaLanguageServerLauncher - - - - jar-with-dependencies - - - - - - - - - - org.eclipse.tycho - tycho-packaging-plugin - ${tycho.version} - - - - - - - - ${releases.repo.id} - Eclipse Releases Repository - ${releases.repo.url} - - - ${snapshots.repo.id} - Eclipse Snapshots Repository - ${snapshots.repo.url} - - - + + 4.0.0 + org.eclipse.lsp4jakarta + org.eclipse.lsp4jakarta.ls + 0.2.0-SNAPSHOT + Eclipse LSP4Jakarta Language Server + Eclipse LSP4Jakarta Language Server + + Eclipse LSP4Jakarta + https://github.com/eclipse/lsp4jakarta + + + scm:git:git@github.com:eclipse/lsp4jakarta.git + scm:git:git@github.com:eclipse/lsp4jakarta.git + git@github.com:eclipse/lsp4jakarta.git + HEAD + + + + EPL-2.0 + https://www.eclipse.org/legal/epl-2.0/ + Eclipse Public License 2.0 + + + + UTF-8 + UTF-8 + 3.0.3 + 17 + 17 + 0.21.0 + repo.eclipse.org + https://repo.eclipse.org/content/repositories/lsp4jakarta-releases/ + repo.eclipse.org + https://repo.eclipse.org/content/repositories/lsp4jakarta-snapshots/ + + + + org.eclipse.lsp4j + org.eclipse.lsp4j + ${lsp4j.version} + + + org.eclipse.lsp4j + org.eclipse.lsp4j.jsonrpc + ${lsp4j.version} + + + junit + junit + 4.11 + test + + + + com.google.guava + guava + 32.1.0-jre + + + org.eclipse.lsp4jakarta + org.eclipse.lsp4jakarta.jdt.core + 0.2.0-SNAPSHOT + + + + + cbi-release + https://repo.eclipse.org/content/repositories/cbi-releases/ + + + + + + maven-assembly-plugin + 3.5.0 + + + make-assembly + package + + single + + + + + org.eclipse.lsp4jakarta.ls.JakartaLanguageServerLauncher + + + + jar-with-dependencies + + + + + + + net.revelc.code.formatter + formatter-maven-plugin + 2.23.0 + + ${project.basedir}/../resources/ide/config/eclipse-formatter-config.xml + LF + + + + + format + + + + + + + + + ${releases.repo.id} + Eclipse Releases Repository + ${releases.repo.url} + + + ${snapshots.repo.id} + Eclipse Snapshots Repository + ${snapshots.repo.url} + + + \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaTextDocumentService.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaTextDocumentService.java deleted file mode 100644 index 79c81678..00000000 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaTextDocumentService.java +++ /dev/null @@ -1,259 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.CodeActionParams; -import org.eclipse.lsp4j.Command; -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.CompletionList; -import org.eclipse.lsp4j.CompletionParams; -import org.eclipse.lsp4j.Diagnostic; -import org.eclipse.lsp4j.DidChangeTextDocumentParams; -import org.eclipse.lsp4j.DidCloseTextDocumentParams; -import org.eclipse.lsp4j.DidOpenTextDocumentParams; -import org.eclipse.lsp4j.DidSaveTextDocumentParams; -import org.eclipse.lsp4j.Hover; -import org.eclipse.lsp4j.HoverParams; -import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.TextDocumentService; -import org.eclipse.lsp4jakarta.commons.JakartaClasspathParams; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextKind; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionResult; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; -import org.eclipse.lsp4jakarta.commons.snippets.SnippetRegistry; -import org.eclipse.lsp4jakarta.snippets.JavaSnippetCompletionContext; -import org.eclipse.lsp4jakarta.snippets.SnippetContextForJava; -import org.eclipse.lsp4mp.commons.DocumentFormat; -import org.eclipse.lsp4mp.ls.commons.BadLocationException; -import org.eclipse.lsp4mp.ls.commons.TextDocument; -import org.eclipse.lsp4mp.ls.commons.TextDocuments; - -public class JakartaTextDocumentService implements TextDocumentService { - - private static final Logger LOGGER = Logger.getLogger(JakartaTextDocumentService.class.getName()); - - private final JakartaLanguageServer jakartaLanguageServer; - - private SnippetRegistry snippetRegistry = new SnippetRegistry(); - - // Text document manager that maintains the contexts of the text documents - private final TextDocuments documents = new TextDocuments(); - - public JakartaTextDocumentService(JakartaLanguageServer jls) { - this.jakartaLanguageServer = jls; - } - - @Override - public void didOpen(DidOpenTextDocumentParams params) { - TextDocument document = documents.onDidOpenTextDocument(params); - String uri = document.getUri(); - triggerValidationFor(Arrays.asList(uri)); - } - - @Override - public void didChange(DidChangeTextDocumentParams params) { - TextDocument document = documents.onDidChangeTextDocument(params); - if (document != null) { - String uri = document.getUri(); - triggerValidationFor(Arrays.asList(uri)); - } - } - - @Override - public void didClose(DidCloseTextDocumentParams params) { - documents.onDidCloseTextDocument(params); - String uri = params.getTextDocument().getUri(); - // clear diagnostics - jakartaLanguageServer.getLanguageClient() - .publishDiagnostics(new PublishDiagnosticsParams(uri, new ArrayList())); - } - - @Override - public CompletableFuture, CompletionList>> completion(CompletionParams position) { - String uri = position.getTextDocument().getUri(); - TextDocument document = documents.get(uri); - // Async thread to query the JDT LS ext for snippet contexts on project's classpath - CompletableFuture> getSnippetContexts = CompletableFuture.supplyAsync(() -> { - // Get the list of snippet contexts to pass to the JDT LS ext - List snippetReg = snippetRegistry.getSnippets().stream().map(snippet -> { - return ((SnippetContextForJava) snippet.getContext()).getTypes().get(0); - }).collect(Collectors.toList()); - JakartaClasspathParams filterParams = new JakartaClasspathParams(uri, snippetReg); - try { - // Pass JakartaClasspathParams to IDE client, to be forwarded to the JDT LS ext - // Returns a CompletableFuture List of snippet context that are on the - // project's classpath - return jakartaLanguageServer.getLanguageClient() - .getContextBasedFilter(filterParams).get(); - } catch (Exception e) { - LOGGER.severe("Return LSP4Jakarta getContextBasedFilter() from client did not succeed: " + e.getMessage()); - return new ArrayList(); - } - }); - - // Async thread to query the JDT LS ext for cursor contexts in Java - JakartaJavaCompletionParams javaParams = new JakartaJavaCompletionParams(position.getTextDocument().getUri(), position.getPosition()); - CompletableFuture getCursorContext = CompletableFuture.supplyAsync(() -> { - try { - // Pass JakartaJavaCompletionParams to IDE client, to be forwarded to the JDT LS ext - // Returns a CompletableFuture JavaCursorContextResult of cursor context in the Java file - JavaCursorContextResult res = jakartaLanguageServer.getLanguageClient() - .getJavaCursorContext(javaParams).get(); - return res; - } catch (Exception e) { - LOGGER.severe("Return LSP4Jakarta getJavaCursorContext() from client did not succeed: " + e.getMessage()); - return new JavaCursorContextResult(JavaCursorContextKind.BEFORE_CLASS, ""); // error recovery - } - }); - - try { - int offset = document.offsetAt(position.getPosition()); - StringBuffer prefix = new StringBuffer(); - Range replaceRange = getReplaceRange(document, offset, prefix); - if (replaceRange != null) { - // Put list of CompletionItems in an Either and wrap as a CompletableFuture - return getCursorContext.thenCombineAsync(getSnippetContexts, (cursorContext, list) -> { - // Given the snippet contexts that are on the project's classpath, return the - // corresponding list of CompletionItems - if (cursorContext == null) { - LOGGER.severe("No Java cursor context provided, using default values to compute snippets."); - cursorContext = new JavaCursorContextResult(JavaCursorContextKind.BEFORE_CLASS, ""); // error recovery - } - return Either.forLeft( - snippetRegistry.getCompletionItem(replaceRange, "\n", true, list, cursorContext, prefix.toString())); - }); - } - } catch (BadLocationException e) { - LOGGER.severe("Failed to get completions: " + e.getMessage()); - } - return CompletableFuture.completedFuture(Either.forLeft(new ArrayList())); - } - - @Override - public void didSave(DidSaveTextDocumentParams params) { - // validate all opened java files - triggerValidationForAll(); - } - - /** - * Validate all opened Java files which belong to a MicroProfile project. - * - * @param projectURIs list of project URIs filter and null otherwise. - */ - private void triggerValidationForAll() { - List allDocs = documents.all().stream().map(doc -> doc.getUri()).collect(Collectors.toList()); - triggerValidationFor(allDocs); - } - - @Override - public CompletableFuture hover(HoverParams params) { - return CompletableFuture.completedFuture(null); - } - - @Override - public CompletableFuture>> codeAction(CodeActionParams params) { - // Prepare the JakartaJavaCodeActionParams - JakartaJavaCodeActionParams jakartaCodeActionParams = new JakartaJavaCodeActionParams(params); - // Pass the JakartaJavaCodeActionParams to IDE client, to be forwarded to the - // JDT LS ext - // Async thread to get the list of code actions from the JDT LS ext - return jakartaLanguageServer.getLanguageClient().getCodeAction(jakartaCodeActionParams) // - .thenApply(codeActions -> { - // Return the corresponding list of CodeActions, put in an Either and wrap as a - // CompletableFuture - return codeActions.stream().map(ca -> { - Either e = Either.forRight(ca); - return e; - }).collect(Collectors.toList()); - }); - } - - // diagnostic request - private void triggerValidationFor(List uris) { - if (uris.isEmpty()) { - return; - } - // Prepare the JakartaDiagnosticsParams - JakartaDiagnosticsParams javaParams = new JakartaDiagnosticsParams(uris); - // TODO: Use settings to see if markdown is supported - // boolean markdownSupported = - // sharedSettings.getHoverSettings().isContentFormatSupported(MarkupKind.MARKDOWN); - // if (markdownSupported) { - // javaParams.setDocumentFormat(DocumentFormat.Markdown); - // } - javaParams.setDocumentFormat(DocumentFormat.Markdown); - - // Async thread to query the JDT LS ext for Jakarta EE diagnostics - CompletableFuture.supplyAsync(() -> { - try { - // Pass the JakartaDiagnosticsParams to IDE client, to be forwarded to the JDT - // LS ext - return jakartaLanguageServer.getLanguageClient().getJavaDiagnostics(javaParams).get(); - } catch (Exception e) { - LOGGER.severe("Return LSP4Jakarta getJavaDiagnostics() from client did not succeed: " + e.getMessage()); - return new ArrayList(); - } - }).thenApply(jakartaDiagnostics -> { - // Publish the corresponding diagnostic items returned from the IDE client (from - // the JDT LS ext) - for (PublishDiagnosticsParams diagnostic : jakartaDiagnostics) { - jakartaLanguageServer.getLanguageClient().publishDiagnostics(diagnostic); - } - return jakartaDiagnostics; - }); - } - - protected void cleanDiagnostics() { - // clear existing diagnostics - documents.all().forEach(doc -> { - jakartaLanguageServer.getLanguageClient() - .publishDiagnostics(new PublishDiagnosticsParams(doc.getUri(), new ArrayList())); - }); - } - - private Range getReplaceRange(TextDocument document, int offset, StringBuffer prefix) throws BadLocationException { - String text = document.getText(); - if (offset < 0 || offset > text.length()) { - return null; - } - int start = offset; - if (offset != 0) { - // look for start position of "Range" - for (int i = offset - 1; i >= 0; i--) { - char ch = text.charAt(i); - if (!Character.isJavaIdentifierPart(ch)) { - break; - } else { - start = i; - prefix.insert(0, ch); - } - } - // ignore/leave all characters within same "identifier" after the offset - } - return new Range(document.positionAt(start), document.positionAt(offset)); - } -} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/api/JakartaLanguageClientAPI.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/api/JakartaLanguageClientAPI.java deleted file mode 100644 index 68f23ca3..00000000 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/api/JakartaLanguageClientAPI.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.api; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -import org.eclipse.lsp4j.CodeAction; -import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; -import org.eclipse.lsp4j.services.LanguageClient; -import org.eclipse.lsp4jakarta.commons.JakartaClasspathParams; -import org.eclipse.lsp4jakarta.commons.JakartaDiagnosticsParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; -import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionResult; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; - -/** - * API of the client consuming the Language Server for Jakarta EE. Used to send - * messages back to the client to ask for information about the Java project. - */ -public interface JakartaLanguageClientAPI extends LanguageClient { - - @JsonRequest("jakarta/java/diagnostics") - default CompletableFuture> getJavaDiagnostics(JakartaDiagnosticsParams javaParams) { - return CompletableFuture.completedFuture(null); - } - - /** - * @author Ankush Sharma - * @param classpathParams - * @return A List of Strings, each representing an item in the project classpath - * This method compares JavaProject classpath to the snippets contexts, - * returning a String for the snippets contexts that exist in the - * classpath and null for those that do not - */ - @JsonRequest("jakarta/java/classpath") - default CompletableFuture> getContextBasedFilter(JakartaClasspathParams classpathParams) { - return CompletableFuture.completedFuture(null); - } - - @JsonRequest("jakarta/java/cursorcontext") - default CompletableFuture getJavaCursorContext(JakartaJavaCompletionParams context) { - return CompletableFuture.completedFuture(null); - } - - @JsonRequest("jakarta/java/codeaction") - default CompletableFuture> getCodeAction(JakartaJavaCodeActionParams params) { - return CompletableFuture.completedFuture(null); - } -} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/ClasspathKind.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/ClasspathKind.java new file mode 100644 index 00000000..04ba9d3d --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/ClasspathKind.java @@ -0,0 +1,49 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +/** + * Classpath kind where application.properties is stored: + * + *
      + *
    • not in classpath
    • + *
    • in /java/main/src classpath
    • + *
    • in /java/main/test classpath
    • + *
    + * + * @author Angelo ZERR + * + */ +public enum ClasspathKind { + + NONE(1), SRC(2), TEST(3); + + private final int value; + + ClasspathKind(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static ClasspathKind forValue(int value) { + ClasspathKind[] allValues = ClasspathKind.values(); + if (value < 1 || value > allValues.length) + throw new IllegalArgumentException("Illegal enum value: " + value); + return allValues[value - 1]; + } + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/DocumentFormat.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/DocumentFormat.java new file mode 100644 index 00000000..69247c64 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/DocumentFormat.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +/** + * Reused from + * https://raw.githubusercontent.com/eclipse/lsp4mp/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/commons/DocumentFormat.java + * + * @author Angelo ZERR + */ +public enum DocumentFormat { + + PlainText(1), Markdown(2); + + private final int value; + + DocumentFormat(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static DocumentFormat forValue(int value) { + DocumentFormat[] allValues = DocumentFormat.values(); + if (value < 1 || value > allValues.length) + throw new IllegalArgumentException("Illegal enum value: " + value); + return allValues[value - 1]; + } + +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaClasspathParams.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaClasspathParams.java deleted file mode 100644 index e9508703..00000000 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaClasspathParams.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ -package org.eclipse.lsp4jakarta.commons; - -import java.util.List; - -public class JakartaClasspathParams { - - private String uri; - - private List snippetCtx; - - public JakartaClasspathParams() { - - } - - public JakartaClasspathParams(String uri, List snippetCtx) { - setUri(uri); - setSnippetCtx(snippetCtx); - } - - public void setUri(String uri) { - this.uri = uri; - } - - public String getUri() { - return this.uri; - } - - public void setSnippetCtx(List snippetCtx) { - this.snippetCtx = snippetCtx; - } - - public List getSnippetCtx() { - return this.snippetCtx; - } -} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaDiagnosticsParams.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaDiagnosticsParams.java deleted file mode 100644 index 5e4aeec4..00000000 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaDiagnosticsParams.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* IBM Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.commons; - -import org.eclipse.lsp4mp.commons.DocumentFormat; -import java.util.List; - -public class JakartaDiagnosticsParams { - - private List uris; - - private DocumentFormat documentFormat; - - public JakartaDiagnosticsParams() { - this(null); - } - - public JakartaDiagnosticsParams(List uris) { - setUris(uris); - } - - /** - * Returns the java file uris list. - * - * @return the java file uris list. - */ - public List getUris() { - return uris; - } - - /** - * Set the java file uris list. - * - * @param uris the java file uris list. - */ - public void setUris(List uris) { - this.uris = uris; - } - - public DocumentFormat getDocumentFormat() { - return documentFormat; - } - - public void setDocumentFormat(DocumentFormat documentFormat) { - this.documentFormat = documentFormat; - } -} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCodeActionParams.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCodeActionParams.java index a1b7e077..19141af2 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCodeActionParams.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCodeActionParams.java @@ -11,7 +11,6 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ - package org.eclipse.lsp4jakarta.commons; import org.eclipse.lsp4j.CodeActionContext; @@ -20,29 +19,30 @@ import org.eclipse.lsp4j.TextDocumentIdentifier; /** - * Java codeAction parameters. reused from - * https://github.com/eclipse/lsp4mp/blob/master/microprofile.jdt/org.eclipse.lsp4mp.jdt.core/src/main/java/org/eclipse/lsp4mp/commons/MicroProfileJavaCodeActionParams.java + * Jakarta Java codeAction parameters. * - * @author credit to Angelo ZERR + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/commons/MicroProfileJavaCodeActionParams.java * + * @author Angelo ZERR */ public class JakartaJavaCodeActionParams extends CodeActionParams { private boolean resourceOperationSupported; + private boolean commandConfigurationUpdateSupported; + + private boolean resolveSupported; + public JakartaJavaCodeActionParams() { super(); } public JakartaJavaCodeActionParams(final TextDocumentIdentifier textDocument, final Range range, - final CodeActionContext context) { + final CodeActionContext context) { super(textDocument, range, context); } - public JakartaJavaCodeActionParams(CodeActionParams params) { - super(params.getTextDocument(), params.getRange(), params.getContext()); - } - public String getUri() { return getTextDocument().getUri(); } @@ -55,4 +55,20 @@ public void setResourceOperationSupported(boolean resourceOperationSupported) { this.resourceOperationSupported = resourceOperationSupported; } -} \ No newline at end of file + public boolean isCommandConfigurationUpdateSupported() { + return commandConfigurationUpdateSupported; + } + + public void setCommandConfigurationUpdateSupported(boolean commandConfigurationUpdateSupported) { + this.commandConfigurationUpdateSupported = commandConfigurationUpdateSupported; + } + + public boolean isResolveSupported() { + return this.resolveSupported; + } + + public void setResolveSupported(boolean resolveSupported) { + this.resolveSupported = resolveSupported; + } + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionParams.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionParams.java index d91e5f25..fa02acf2 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionParams.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionParams.java @@ -14,63 +14,63 @@ *******************************************************************************/ package org.eclipse.lsp4jakarta.commons; - import org.eclipse.lsp4j.Position; /** - * Parameters that are passed on completion trigger for MicroProfile projects in Java files + * Parameters that are passed on completion trigger for Jakarta projects in Java + * files * * @author datho7561 */ public class JakartaJavaCompletionParams { - private String uri; - private Position position; + private String uri; + private Position position; - public JakartaJavaCompletionParams() { + public JakartaJavaCompletionParams() { - } + } - public JakartaJavaCompletionParams(String uri, Position position) { - this(); - setUri(uri); - setPosition(position); - } + public JakartaJavaCompletionParams(String uri, Position position) { + this(); + setUri(uri); + setPosition(position); + } - /** - * Returns the java file uri. - * - * @return the java file uri. - */ - public String getUri() { - return uri; - } + /** + * Returns the java file uri. + * + * @return the java file uri. + */ + public String getUri() { + return uri; + } - /** - * Set the java file uri. - * - * @param uri the java file uri. - */ - public void setUri(String uri) { - this.uri = uri; - } + /** + * Set the java file uri. + * + * @param uri the java file uri. + */ + public void setUri(String uri) { + this.uri = uri; + } - /** - * Returns the hover position - * - * @return the hover position - */ - public Position getPosition() { - return position; - } + /** + * Returns the hover position + * + * @return the hover position + */ + public Position getPosition() { + return position; + } - /** - * Sets the hover position - * - * @param position the hover position - */ - public void setPosition(Position position) { - this.position = position; - } + /** + * Sets the hover position + * + * @param position the hover position + */ + public void setPosition(Position position) { + this.position = position; + } } \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionResult.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionResult.java index 5596acc3..e5eb98d3 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionResult.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaCompletionResult.java @@ -20,39 +20,39 @@ */ public class JakartaJavaCompletionResult { - private CompletionList completionList; - private JavaCursorContextResult cursorContext; - - public JakartaJavaCompletionResult(CompletionList completionList, - JavaCursorContextResult cursorContext) { - this.completionList = completionList; - this.cursorContext = cursorContext; - } - - public JakartaJavaCompletionResult() { - this(null, null); - } - - /** - * Returns the list of completion items contributed by the Java language server - * component. - * - * @return the list of completion items contributed by the Java language server - * component - */ - public CompletionList getCompletionList() { - return completionList; - } - - /** - * Returns information on the context of the cursor in the Java file, calculated - * by the Java language server component. - * - * @return information on the context of the cursor in the Java file, calculated - * by the Java language server component - */ - public JavaCursorContextResult getCursorContext() { - return cursorContext; - } + private CompletionList completionList; + private JavaCursorContextResult cursorContext; + + public JakartaJavaCompletionResult(CompletionList completionList, + JavaCursorContextResult cursorContext) { + this.completionList = completionList; + this.cursorContext = cursorContext; + } + + public JakartaJavaCompletionResult() { + this(null, null); + } + + /** + * Returns the list of completion items contributed by the Java language server + * component. + * + * @return the list of completion items contributed by the Java language server + * component + */ + public CompletionList getCompletionList() { + return completionList; + } + + /** + * Returns information on the context of the cursor in the Java file, calculated + * by the Java language server component. + * + * @return information on the context of the cursor in the Java file, calculated + * by the Java language server component + */ + public JavaCursorContextResult getCursorContext() { + return cursorContext; + } } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfo.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfo.java new file mode 100644 index 00000000..8c470795 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfo.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +/** + * Java file information. + * + * @author Angelo ZERR + * + */ +public class JakartaJavaFileInfo { + + private String packageName; + + /** + * Returns the package name. + * + * @return the package name. + */ + public String getPackageName() { + return packageName; + } + + /** + * Set the package name. + * + * @param packageName the package name. + */ + public void setPackageName(String packageName) { + this.packageName = packageName; + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfoParams.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfoParams.java new file mode 100644 index 00000000..93b469b3 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaFileInfoParams.java @@ -0,0 +1,47 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +/** + * Jakarta Java file information parameters. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/commons/MicroProfileJavaFileInfoParams.java + * + * @author Angelo ZERR + * + */ +public class JakartaJavaFileInfoParams { + + private String uri; + + /** + * Returns the Java file uri. + * + * @return the Java file uri + */ + public String getUri() { + return uri; + } + + /** + * Set the Java file uri. + * + * @param uri the Java file uri. + */ + public void setUri(String uri) { + this.uri = uri; + } + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaProjectLabelsParams.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaProjectLabelsParams.java new file mode 100644 index 00000000..8e4804f3 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JakartaJavaProjectLabelsParams.java @@ -0,0 +1,74 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons; + +import java.util.List; + +/** + * Jakarta Java Project labels + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/commons/MicroProfileJavaProjectLabelsParams.java + * + * @author Angelo ZERR + * + */ +public class JakartaJavaProjectLabelsParams { + + private String uri; + + private List types; + + /** + * Returns the Java file uri. + * + * @return the Java file uri + */ + public String getUri() { + return uri; + } + + /** + * Set the Java file uri. + * + * @param uri the Java file uri. + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Returns the Java types list to check. + * + *

    + * If the owner Java project of the Java file URI contains some type in the + * classpath, it will return the type as label in + * {@link ProjectLabelInfoEntry#getLabels()} + *

    + * + * @return the Java types list to check + */ + public List getTypes() { + return types; + } + + /** + * Set the Java types list to check. + * + * @param types the Java types list to check. + */ + public void setTypes(List types) { + this.types = types; + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextKind.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextKind.java index d90cb925..1158ba9e 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextKind.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextKind.java @@ -18,217 +18,217 @@ */ public enum JavaCursorContextKind { - /** - * The cursor is in a file that does not have a root type declaration. - * - * eg. - *
    - * |
    - *
    - */ - IN_EMPTY_FILE(1), + /** + * The cursor is in a file that does not have a root type declaration. + * + * eg. + *
    + * |
    + *
    + */ + IN_EMPTY_FILE(1), - /** - * The cursor is before a type declaration body, either at the root of a file or - * within another class. The cursor is before any annotations on the type. - * - * eg. - * - *
    - * package org.acme;
    - *
    - * |
    - * @Inject
    - * public class MyClass {
    - *
    - * }
    - *
    - * - * eg. - * - *
    - * package org.acme;
    - *
    - * @Inject
    - * public class MyClass {
    - *
    - *     private int memberVariable;
    - *     |
    - *     @Inject
    - *     public static class MyChildClass {
    - *
    - *     }
    - * }
    - *
    - */ - BEFORE_CLASS(2), + /** + * The cursor is before a type declaration body, either at the root of a file or + * within another class. The cursor is before any annotations on the type. + * + * eg. + * + *
    + * package org.acme;
    + *
    + * |
    + * @Inject
    + * public class MyClass {
    + *
    + * }
    + *
    + * + * eg. + * + *
    + * package org.acme;
    + *
    + * @Inject
    + * public class MyClass {
    + *
    + *     private int memberVariable;
    + *     |
    + *     @Inject
    + *     public static class MyChildClass {
    + *
    + *     }
    + * }
    + *
    + */ + BEFORE_CLASS(2), - /** - * The cursor is in a type declaration body, and the next declaration in the - * body is a method declaration. The cursor is before any annotations on the - * method. - * - * eg. - * - *
    - * package org.acme;
    - *
    - * public class MyClass {
    - *     |
    - *     @Deprecated
    - *     public void myMethod() {
    - *     }
    - * }
    - *
    - */ - BEFORE_METHOD(3), + /** + * The cursor is in a type declaration body, and the next declaration in the + * body is a method declaration. The cursor is before any annotations on the + * method. + * + * eg. + * + *
    + * package org.acme;
    + *
    + * public class MyClass {
    + *     |
    + *     @Deprecated
    + *     public void myMethod() {
    + *     }
    + * }
    + *
    + */ + BEFORE_METHOD(3), - /** - * The cursor is in a type declaration body, and the next declaration in the - * body is a field declaration. The cursor is before any annotations on the - * field. - * - * eg. - * - *
    - * package org.acme;
    - *
    - * public class MyClass {
    - *     |
    - *     @Deprecated
    - *     public String myString;
    - * }
    - *
    - */ - BEFORE_FIELD(4), + /** + * The cursor is in a type declaration body, and the next declaration in the + * body is a field declaration. The cursor is before any annotations on the + * field. + * + * eg. + * + *
    + * package org.acme;
    + *
    + * public class MyClass {
    + *     |
    + *     @Deprecated
    + *     public String myString;
    + * }
    + *
    + */ + BEFORE_FIELD(4), - /** - * The cursor is before a type declaration body, either at the root of a file or - * within another class. The cursor is somewhere within the annotation - * declarations on the class. - * - * eg. - * - *
    - * package org.acme;
    - *
    - * @Inject|
    - * public class MyClass {
    - *
    - * }
    - *
    - * - * eg. - * - *
    - * package org.acme;
    - *
    - * @Inject
    - * public class MyClass {
    - *
    - *     private int memberVariable;
    - *
    - *     @Inject|
    - *     public static class MyChildClass {
    - *
    - *     }
    - * }
    - *
    - */ - IN_CLASS_ANNOTATIONS(5), + /** + * The cursor is before a type declaration body, either at the root of a file or + * within another class. The cursor is somewhere within the annotation + * declarations on the class. + * + * eg. + * + *
    + * package org.acme;
    + *
    + * @Inject|
    + * public class MyClass {
    + *
    + * }
    + *
    + * + * eg. + * + *
    + * package org.acme;
    + *
    + * @Inject
    + * public class MyClass {
    + *
    + *     private int memberVariable;
    + *
    + *     @Inject|
    + *     public static class MyChildClass {
    + *
    + *     }
    + * }
    + *
    + */ + IN_CLASS_ANNOTATIONS(5), - /** - * The cursor is in a type declaration body, and the next declaration in the - * body is a method declaration. The cursor is somewhere within the annotation - * declarations on the method. - * - * eg. - * - *
    - * package org.acme;
    - *
    - * public class MyClass {
    - *
    - *     @Deprecated|
    - *     public void myMethod() {
    - *     }
    - * }
    - *
    - */ - IN_METHOD_ANNOTATIONS(6), + /** + * The cursor is in a type declaration body, and the next declaration in the + * body is a method declaration. The cursor is somewhere within the annotation + * declarations on the method. + * + * eg. + * + *
    + * package org.acme;
    + *
    + * public class MyClass {
    + *
    + *     @Deprecated|
    + *     public void myMethod() {
    + *     }
    + * }
    + *
    + */ + IN_METHOD_ANNOTATIONS(6), - /** - * The cursor is in a type declaration body, and the next declaration in the - * body is a field declaration. The cursor is somewhere within the annotation - * declarations on the field. - * - * eg. - * - *
    - * package org.acme;
    - *
    - * public class MyClass {
    - *
    - *     @Deprecated|
    - *     public String myString;
    - * }
    - *
    - */ - IN_FIELD_ANNOTATIONS(7), + /** + * The cursor is in a type declaration body, and the next declaration in the + * body is a field declaration. The cursor is somewhere within the annotation + * declarations on the field. + * + * eg. + * + *
    + * package org.acme;
    + *
    + * public class MyClass {
    + *
    + *     @Deprecated|
    + *     public String myString;
    + * }
    + *
    + */ + IN_FIELD_ANNOTATIONS(7), - /** - * The cursor is in a type declaration body, after all the declarations for the - * type. - * - * eg. - * - *
    - * package org.acme;
    - *
    - * public class MyClass {
    - *
    - *     @Deprecated
    - *     public String myString;
    - *     |
    - * }
    - *
    - */ - IN_CLASS(8), + /** + * The cursor is in a type declaration body, after all the declarations for the + * type. + * + * eg. + * + *
    + * package org.acme;
    + *
    + * public class MyClass {
    + *
    + *     @Deprecated
    + *     public String myString;
    + *     |
    + * }
    + *
    + */ + IN_CLASS(8), - /** - * None of the above context apply. - * - * eg. - * - *
    - * package org.acme;
    - *
    - * public class MyClass {
    - *
    - *     @Deprecated
    - *     public void myMethod() {
    - *         |
    - *     }
    - * }
    - *
    - */ - NONE(2000); + /** + * None of the above context apply. + * + * eg. + * + *
    + * package org.acme;
    + *
    + * public class MyClass {
    + *
    + *     @Deprecated
    + *     public void myMethod() {
    + *         |
    + *     }
    + * }
    + *
    + */ + NONE(2000); - private final int value; + private final int value; - private JavaCursorContextKind(int value) { - this.value = value; - } + private JavaCursorContextKind(int value) { + this.value = value; + } - public int getValue() { - return value; - } + public int getValue() { + return value; + } - public static JavaCursorContextKind forValue(int value) { - JavaCursorContextKind[] allValues = JavaCursorContextKind.values(); - if (value < 1 || value > allValues.length) - throw new IllegalArgumentException("Illegal enum value: " + value); - return allValues[value - 1]; - } + public static JavaCursorContextKind forValue(int value) { + JavaCursorContextKind[] allValues = JavaCursorContextKind.values(); + if (value < 1 || value > allValues.length) + throw new IllegalArgumentException("Illegal enum value: " + value); + return allValues[value - 1]; + } } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextResult.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextResult.java index 199da341..504feb5d 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextResult.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/JavaCursorContextResult.java @@ -18,52 +18,52 @@ */ public class JavaCursorContextResult { - private JavaCursorContextKind kind; - private String prefix; + private JavaCursorContextKind kind; + private String prefix; - public JavaCursorContextResult(JavaCursorContextKind kind, String prefix) { - this.kind = kind; - this.prefix = prefix; - } + public JavaCursorContextResult(JavaCursorContextKind kind, String prefix) { + this.kind = kind; + this.prefix = prefix; + } - public JavaCursorContextResult() { - this(null, null); - } + public JavaCursorContextResult() { + this(null, null); + } - /** - * Returns the context of the cursor in the Java file or NONE if - * none of the contexts apply. - * - * For instance, it returns IN_METHOD_ANNOTATIONS if the cursor is - * in the list of annotations before a method declaration. - * - * @return the context of the cursor in the Java file - */ - public JavaCursorContextKind getKind() { - return kind; - } + /** + * Returns the context of the cursor in the Java file or NONE if + * none of the contexts apply. + * + * For instance, it returns IN_METHOD_ANNOTATIONS if the cursor is + * in the list of annotations before a method declaration. + * + * @return the context of the cursor in the Java file + */ + public JavaCursorContextKind getKind() { + return kind; + } - /** - * Returns the text content to the left of the cursor, up to the first whitespace. - * - * eg. - * - * - * public static| - * - * - * would return "static" - * - * - * | - * - * - * would return "" - * - *@return the text content to the left of the cursor, up to the first whitespace - */ - public String getPrefix() { - return prefix; - } + /** + * Returns the text content to the left of the cursor, up to the first whitespace. + * + * eg. + * + * + * public static| + * + * + * would return "static" + * + * + * | + * + * + * would return "" + * + * @return the text content to the left of the cursor, up to the first whitespace + */ + public String getPrefix() { + return prefix; + } } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/ProjectLabelInfoEntry.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/ProjectLabelInfoEntry.java index b35d4f61..47eb2c4a 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/ProjectLabelInfoEntry.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/ProjectLabelInfoEntry.java @@ -25,26 +25,38 @@ * */ public class ProjectLabelInfoEntry { - public static final ProjectLabelInfoEntry EMPTY_PROJECT_INFO = new ProjectLabelInfoEntry("", - Collections.emptyList()); + public static final ProjectLabelInfoEntry EMPTY_PROJECT_INFO = new ProjectLabelInfoEntry("", "", Collections.emptyList()); private final String uri; + private final String name; private final List labels; - public ProjectLabelInfoEntry(String uri, List labels) { + public ProjectLabelInfoEntry(String uri, String name, List labels) { this.uri = uri; + this.name = name; this.labels = labels; } /** * Returns the project uri * + * FIXME: on Linux, this is actually an absolute path and not a uri. + * * @return the project uri */ public String getUri() { return uri; } + /** + * Returns the name of the project + * + * @return The name of this project + */ + public String getName() { + return name; + } + /** * Returns the labels for the current project uri * @@ -61,6 +73,8 @@ public List getLabels() { * @return true if the project has the given label and false otherwise. */ public boolean hasLabel(String label) { + //boolean truth = true; + //return truth; return labels != null && labels.contains(label); } } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/SnippetRegistry.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/SnippetRegistry.java deleted file mode 100644 index 41365241..00000000 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/SnippetRegistry.java +++ /dev/null @@ -1,432 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020, 2022 Red Hat Inc. and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -* which is available at https://www.apache.org/licenses/LICENSE-2.0. -* -* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ - -package org.eclipse.lsp4jakarta.commons.snippets; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import org.eclipse.lsp4j.CompletionItem; -import org.eclipse.lsp4j.CompletionItemKind; -import org.eclipse.lsp4j.InsertTextFormat; -import org.eclipse.lsp4j.MarkupContent; -import org.eclipse.lsp4j.MarkupKind; -import org.eclipse.lsp4j.Range; -import org.eclipse.lsp4j.TextEdit; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; -import org.eclipse.lsp4jakarta.snippets.JakartaEESnippetRegistryLoader; -import org.eclipse.lsp4jakarta.snippets.SnippetContextForJava; -import org.eclipse.lsp4jakarta.utils.Messages; - -import com.google.gson.GsonBuilder; -import com.google.gson.JsonIOException; -import com.google.gson.JsonSyntaxException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; - -/** - * A registry that holds snippets using vscode snippet format - * - * @author Ankush Sharma, credit to Angelo ZERR - */ -public class SnippetRegistry { - - private static final String PACKAGE_NAME = "packagename"; - private static final String CLASS_NAME = "classname"; - private static final String[] RESOLVE_VARIABLES = { PACKAGE_NAME, CLASS_NAME }; - - List snippets; // Hold all snippets in this list - - /** - * Initialize the Snippet registry and create the array of Snippets - */ - public SnippetRegistry() { - snippets = new ArrayList<>(); - // Load all of the snippets into the registry - JakartaEESnippetRegistryLoader loader = new JakartaEESnippetRegistryLoader(); - try { - loader.load(this); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * Register the provided snippet - * - * @param snippet - */ - public void registerSnippet(Snippet snippet) { - snippets.add(snippet); - } - - /** - * Register snippets from a given JSON input stream - * - * @param json - * @throws IOException - */ - public void registerSnippets(InputStream in) throws IOException { - registerSnippets(in, null); - } - - /** - * Register the snippets from the given JSON stream with a context. - * - * @param json the JSON input stream which declares snippets with - * vscode snippet format. - * @param contextDeserializer the GSON context deserializer used to create Java - * context. - * @throws IOException - */ - public void registerSnippets(InputStream in, TypeAdapter> contextDeserializer) - throws IOException { - registerSnippets(new InputStreamReader(in, StandardCharsets.UTF_8.name()), contextDeserializer); - } - - /** - * Register the snippets from the given JSON reader. - * - * @param in the JSON reader which declares snippets with vscode snippet format. - * @throws IOException - */ - public void registerSnippets(Reader in) throws IOException { - registerSnippets(in, null); - } - - /** - * Register the snippets from the given JSON reader with a context. - * - * @param in the JSON reader which declares snippets with - * vscode snippet format. - * @param contextDeserializer the GSON context deserializer used to create Java - * context. - * @throws IOException - */ - public void registerSnippets(Reader in, TypeAdapter> contextDeserializer) - throws IOException { - // Read stream of tokens as JSON - JsonReader reader = new JsonReader(in); - reader.beginObject(); - while (reader.hasNext()) { - String name = reader.nextName(); - Snippet snippet = createSnippet(reader, contextDeserializer); - if (snippet.getDescription() == null) { - snippet.setDescription(name); - } - registerSnippet(snippet); - } - } - - /** - * Build a snippet from a given JsonReader - * - * @param reader - * @param contextDeserializer - * @return - * @throws JsonIOException - * @throws JsonSyntaxException - */ - private static Snippet createSnippet(JsonReader reader, - TypeAdapter> contextDeserializer) throws JsonIOException, JsonSyntaxException { - GsonBuilder builder = new GsonBuilder(); - builder.registerTypeAdapter(Snippet.class, new SnippetDeserializer(contextDeserializer)); - return builder.create().fromJson(reader, Snippet.class); - } - - /** - * Returns all snippets. - * - * @return all snippets. - */ - public List getSnippets() { - return snippets; - } - - /** - * Returns the snippet completion items according to the context filter. - * - * @param replaceRange the replace range. - * @param lineDelimiter the line delimiter. - * @param canSupportMarkdown true if markdown is supported to generate - * documentation and false otherwise. - * @param context the context filter. - * @param prefix completion prefix. - * @return the snippet completion items according to the context filter. - */ - public List getCompletionItem(final Range replaceRange, final String lineDelimiter, - boolean canSupportMarkdown, List context, JavaCursorContextResult cursorContext, String prefix) { - List snippets = getSnippets(); - Map values = new HashMap(); - int size = context.size(); - if (size == snippets.size() + 2) { // the last 2 strings are package name and class name - values.put(PACKAGE_NAME, context.get(size - 2)); - values.put(CLASS_NAME, context.get(size - 1)); - } - String filter = (prefix != null) ? prefix.toLowerCase() : null; - return snippets.stream() - // filter list based on cursor context - .filter(snippet -> - ((SnippetContextForJava) snippet.getContext()) - .snippetContentAppliesToContext(cursorContext)) - .map(snippet -> { - String label = snippet.getPrefixes().get(0); - if (context.get(snippets.indexOf(snippet)) == null - // in Eclipse, the filter is not working properly, have to add additional one - || (filter != null && filterLabel(filter, label.toLowerCase()) != true)) { - return null; - } - CompletionItem item = new CompletionItem(); - item.setLabel(label); - // item.setDetail(snippet.getDescription()); - item.setDetail(Messages.getMessage(snippet.getDescription())); - String insertText = getInsertText(snippet, false, lineDelimiter, values); - item.setKind(CompletionItemKind.Snippet); - item.setDocumentation( - Either.forRight(createDocumentation(snippet, canSupportMarkdown, lineDelimiter, values))); - item.setFilterText(label); - - TextEdit textEdit = new TextEdit(replaceRange, insertText); - item.setTextEdit(Either.forLeft(textEdit)); - item.setInsertTextFormat(InsertTextFormat.Snippet); - return item; - }).filter(completionItems -> completionItems != null).collect(Collectors.toList()); - } - - /** - * Returns all snippet completion items. This method does not take into account - * the current context such as ClassPath, or imports, etc... - * - * @param replaceRange the replace range. - * @param lineDelimeter the line delimeter - * @param canSupportMarkdown true if mardown is supported to generate - * documentation and false otherwise - * @return the snippet completion items irrespective of the current context. - */ - - public List getCompletionItemNoContext(final Range replaceRange, final String lineDelimeter, - boolean canSupportMarkdown) { - return getSnippets().stream().map(snippet -> { - // To filter by context, I just need to provide document contexts, and then - // perform a match and include or remove - // List snippetTypes = ((SnippetContextForJava) - // snippet.getContext()).getTypes(); - String label = snippet.getPrefixes().get(0); - CompletionItem item = new CompletionItem(); - item.setLabel(label); - item.setDetail(snippet.getDescription()); - String insertText = getInsertText(snippet, false, lineDelimeter, null); - - item.setKind(CompletionItemKind.Snippet); - item.setDocumentation( - Either.forRight(createDocumentation(snippet, canSupportMarkdown, lineDelimeter, null))); - item.setFilterText(label); - - TextEdit textEdit = new TextEdit(replaceRange, insertText); - item.setTextEdit(Either.forLeft(textEdit)); - item.setInsertTextFormat(InsertTextFormat.Snippet); - return item; - }).collect(Collectors.toList()); - } - - private static MarkupContent createDocumentation(Snippet snippet, boolean canSupportMarkdown, - String lineDelimiter, Map values) { - StringBuilder doc = new StringBuilder(); - if (canSupportMarkdown) { - doc.append(System.lineSeparator()); - doc.append("```"); - String scope = snippet.getScope(); - if (scope != null) { - doc.append(scope); - } - doc.append(System.lineSeparator()); - } - String insertText = getInsertText(snippet, true, lineDelimiter, values); - doc.append(insertText); - if (canSupportMarkdown) { - doc.append(System.lineSeparator()); - doc.append("```"); - doc.append(System.lineSeparator()); - } - return new MarkupContent(canSupportMarkdown ? MarkupKind.MARKDOWN : MarkupKind.PLAINTEXT, doc.toString()); - } - - private static String getInsertText(Snippet snippet, boolean replace, String lineDelimiter, - Map values) { - StringBuilder text = new StringBuilder(); - int i = 0; - List body = snippet.getBody(); - if (body != null) { - Map> foundVars = new HashMap>(); - for (String bodyLine : body) { - // resolve specific variables by values - if (values != null && values.size() > 0) { - foundVars.clear(); - // search for specific variables - getMatchedVariables(bodyLine, 0, RESOLVE_VARIABLES, foundVars); - if (foundVars.size() > 0) { // resolve specific variables by values - for (String key : foundVars.keySet()) { - String replacement = values.get(key); - if (replacement != null) { - Set vars = foundVars.get(key); - for (Iterator it = vars.iterator(); it.hasNext();) { - bodyLine = bodyLine.replace(it.next(), replacement); - } - } - } - } - } - if (i > 0) { - text.append(lineDelimiter); - } - if (replace) { - bodyLine = replace(bodyLine); - } - text.append(bodyLine); - i++; - } - } - return text.toString(); - } - - private static String replace(String line) { - return replace(line, 0, null); - } - - private static String replace(String line, int offset, StringBuilder newLine) { - int startExpr = line.indexOf("${", offset); - if (startExpr == -1) { - if (newLine == null) { - return line; - } - newLine.append(line.substring(offset, line.length())); - return newLine.toString(); - } - int endExpr = line.indexOf("}", startExpr); - if (endExpr == -1) { - // Should never occur - return line; - } - if (newLine == null) { - newLine = new StringBuilder(); - } - newLine.append(line.substring(offset, startExpr)); - // Parameter - int startParam = startExpr + 2; - int endParam = endExpr; - boolean startsWithNumber = true; - for (int i = startParam; i < endParam; i++) { - char ch = line.charAt(i); - if (Character.isDigit(ch)) { - startsWithNumber = true; - } else if (ch == ':') { - if (startsWithNumber) { - startParam = i + 1; - } - break; - } else if (ch == '|') { - if (startsWithNumber) { - startParam = i + 1; - int index = line.indexOf(',', startExpr); - if (index != -1) { - endParam = index; - } - } - break; - } else { - break; - } - } - newLine.append(line.substring(startParam, endParam)); - return replace(line, endExpr + 1, newLine); - } - - protected static String findExprBeforeAt(String text, int offset) { - if (offset < 0 || offset > text.length()) { - return null; - } - if (offset == 0) { - return ""; - } - StringBuilder expr = new StringBuilder(); - int i = offset - 1; - for (; i >= 0; i--) { - char ch = text.charAt(i); - if (Character.isWhitespace(ch)) { - break; - } else { - expr.insert(0, ch); - } - } - return expr.toString(); - } - - /** - * Get all matched variables from given string line. - * - * @param line - given string to search - * @param start - position/index to start the search from - * @param vars - searching variables - * @param matched - found variables - */ - private static void getMatchedVariables(String line, int start, String[] vars, - Map> matched) { - int idxS = line.indexOf("${", start); - if (idxS != -1) { - int idxE = line.indexOf('}', idxS); - if (idxE - 1 > idxS + 2) { - String varStr = line.substring(idxS + 2, idxE).trim().toLowerCase(); - Arrays.stream(vars).forEach(var -> { - if (varStr.endsWith(var) == true) { - if (matched.containsKey(var) != true) { - matched.put(var, new HashSet()); - } - matched.get(var).add(line.substring(idxS, idxE + 1)); - } - }); - getMatchedVariables(line, idxE, vars, matched); - } - } - } - - private boolean filterLabel(String filter, String label) { - boolean pass = true; - if (label.contains(filter) != true) { - char[] chars = filter.toCharArray(); - int start = 0; - for (char ch : chars) { - start = label.indexOf(ch, start); - if (start == -1) { - pass = false; - break; - } - start++; - } - } - return pass; - } -} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/utils/JSONUtility.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/utils/JSONUtility.java new file mode 100644 index 00000000..159a1260 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/utils/JSONUtility.java @@ -0,0 +1,97 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.commons.utils; + +import java.util.HashMap; + +import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler; +import org.eclipse.lsp4j.jsonrpc.json.adapters.EitherTypeAdapter; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; + +/** + * Utilities for working with JSON that has been converted to an Object using + * Gson. + */ +public class JSONUtility { + + private JSONUtility() {} + + private static final Gson LSP4J_GSON = new MessageJsonHandler(new HashMap<>()).getGson(); + + private static final Gson EITHER_GSON = new GsonBuilder() // + .registerTypeAdapterFactory(new EitherTypeAdapter.Factory()).create(); + + /** + * Converts the given Object to the given class using lsp4j's GSON logic. + * + * @param the class to convert the Object to + * @param object the object to convert + * @param clazz the class to convert the Object to + * @return the given Object converted to the given class using lsp4j's GSON + * logic + */ + public static T toModel(Object object, Class clazz) { + return toModel(getLsp4jGson(), object, clazz); + } + + /** + * Converts the given Object to the given class using the given GSON instance. + * + * @param the class to convert the Object to + * @param gson the gson instance to use to perform the conversion + * @param object the object to convert + * @param clazz the class to convert the Object to + * @return the given Object converted to the given class using the given GSON + * instance + */ + public static T toModel(Gson gson, Object object, Class clazz) { + if (object == null) { + return null; + } + if (clazz == null) { + throw new IllegalArgumentException("Class can not be null"); + } + if (object instanceof JsonElement) { + return gson.fromJson((JsonElement) object, clazz); + } + if (clazz.isInstance(object)) { + return clazz.cast(object); + } + // if nothing else works, try serializing and deserializing again + return gson.fromJson(gson.toJson(object), clazz); + } + + /** + * Returns a Gson instance configured similarly to the instance lsp4j uses. + * + * @return a Gson instance configured similarly to the instance lsp4j uses + */ + public static Gson getLsp4jGson() { + return LSP4J_GSON; + } + + /** + * Returns a Gson instance with most of the default options, but with the + * ability to parse {@code org.eclipse.lsp4j.Either}. + * + * @return a Gson instance with most of the default options, but with the + * ability to parse {@code org.eclipse.lsp4j.Either} + */ + public static Gson getEitherGson() { + return EITHER_GSON; + } +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/utils/StringUtils.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/utils/StringUtils.java new file mode 100644 index 00000000..d376a2a8 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/utils/StringUtils.java @@ -0,0 +1,149 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.lsp4jakarta.commons.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.StringTokenizer; + +/** + * This class is a copy/paste from + * https://github.com/spring-projects/spring-framework/blob/243f2890ee9e98c97c8dc7278f033def3bf33f86/spring-core/src/main/java/org/springframework/util/StringUtils.java + * which contains only method required by {@link AntPathMatcher}. + * + * @author Angelo ZERR + * + */ +public class StringUtils { + + private static final String[] EMPTY_STRING_ARRAY = {}; + + /** + * Check whether the given {@code String} contains actual text. + *

    + * More specifically, this method returns {@code true} if the {@code String} is + * not {@code null}, its length is greater than 0, and it contains at least one + * non-whitespace character. + * + * @param str the {@code String} to check (may be {@code null}) + * @return {@code true} if the {@code String} is not {@code null}, its length is + * greater than 0, and it does not contain whitespace only + * @see #hasText(CharSequence) + * @see #hasLength(String) + * @see Character#isWhitespace + */ + public static boolean hasText(String str) { + return (!isEmpty(str) && containsText(str)); + } + + public static boolean isEmpty(String value) { + return value == null || value.isEmpty(); + } + + private static boolean containsText(CharSequence str) { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return true; + } + } + return false; + } + + /** + * Tokenize the given {@code String} into a {@code String} array via a + * {@link StringTokenizer}. + *

    + * Trims tokens and omits empty tokens. + *

    + * The given {@code delimiters} string can consist of any number of delimiter + * characters. Each of those characters can be used to separate tokens. A + * delimiter is always a single character; for multi-character delimiters, + * consider using {@link #delimitedListToStringArray}. + * + * @param str the {@code String} to tokenize (potentially {@code null} or + * empty) + * @param delimiters the delimiter characters, assembled as a {@code String} + * (each of the characters is individually considered as a + * delimiter) + * @return an array of the tokens + * @see java.util.StringTokenizer + * @see String#trim() + * @see #delimitedListToStringArray + */ + public static String[] tokenizeToStringArray(String str, String delimiters) { + return tokenizeToStringArray(str, delimiters, true, true); + } + + /** + * Tokenize the given {@code String} into a {@code String} array via a + * {@link StringTokenizer}. + *

    + * The given {@code delimiters} string can consist of any number of delimiter + * characters. Each of those characters can be used to separate tokens. A + * delimiter is always a single character; for multi-character delimiters, + * consider using {@link #delimitedListToStringArray}. + * + * @param str the {@code String} to tokenize (potentially + * {@code null} or empty) + * @param delimiters the delimiter characters, assembled as a + * {@code String} (each of the characters is + * individually considered as a delimiter) + * @param trimTokens trim the tokens via {@link String#trim()} + * @param ignoreEmptyTokens omit empty tokens from the result array (only + * applies to tokens that are empty after trimming; + * StringTokenizer will not consider subsequent + * delimiters as token in the first place). + * @return an array of the tokens + * @see java.util.StringTokenizer + * @see String#trim() + * @see #delimitedListToStringArray + */ + public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, + boolean ignoreEmptyTokens) { + + if (str == null) { + return EMPTY_STRING_ARRAY; + } + + StringTokenizer st = new StringTokenizer(str, delimiters); + List tokens = new ArrayList<>(); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (trimTokens) { + token = token.trim(); + } + if (!ignoreEmptyTokens || token.length() > 0) { + tokens.add(token); + } + } + return toStringArray(tokens); + } + + /** + * Copy the given {@link Collection} into a {@code String} array. + *

    + * The {@code Collection} must contain {@code String} elements only. + * + * @param collection the {@code Collection} to copy (potentially {@code null} or + * empty) + * @return the resulting {@code String} array + */ + public static String[] toStringArray(Collection collection) { + return (!collection.isEmpty() ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY); + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaLanguageServer.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaLanguageServer.java similarity index 70% rename from jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaLanguageServer.java rename to jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaLanguageServer.java index 965c9616..d3d00ce0 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaLanguageServer.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaLanguageServer.java @@ -11,8 +11,9 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta; +package org.eclipse.lsp4jakarta.ls; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; @@ -26,10 +27,17 @@ import org.eclipse.lsp4j.services.LanguageServer; import org.eclipse.lsp4j.services.TextDocumentService; import org.eclipse.lsp4j.services.WorkspaceService; -import org.eclipse.lsp4jakarta.api.JakartaLanguageClientAPI; -import org.eclipse.lsp4mp.ls.commons.ParentProcessWatcher.ProcessLanguageServer; - -public class JakartaLanguageServer implements LanguageServer, ProcessLanguageServer { +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfo; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfoParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaProjectLabelsParams; +import org.eclipse.lsp4jakarta.commons.ProjectLabelInfoEntry; +import org.eclipse.lsp4jakarta.ls.api.JakartaJavaFileInfoProvider; +import org.eclipse.lsp4jakarta.ls.api.JakartaJavaProjectLabelsProvider; +import org.eclipse.lsp4jakarta.ls.api.JakartaLanguageClientAPI; +import org.eclipse.lsp4jakarta.ls.commons.ParentProcessWatcher.ProcessLanguageServer; +import org.eclipse.lsp4jakarta.ls.java.JakartaTextDocuments; + +public class JakartaLanguageServer implements LanguageServer, ProcessLanguageServer, JakartaJavaProjectLabelsProvider, JakartaJavaFileInfoProvider { private Integer parentProcessId; @@ -37,14 +45,16 @@ public class JakartaLanguageServer implements LanguageServer, ProcessLanguageSer private final WorkspaceService workspaceService; private final TextDocumentService textDocumentService; + private final JakartaTextDocuments javaDocuments; private JakartaLanguageClientAPI languageClient; public JakartaLanguageServer() { // Workspace service handles workspace settings changes and calls update // settings. + javaDocuments = new JakartaTextDocuments(this, this); workspaceService = new JakartaWorkspaceService(this); - textDocumentService = new JakartaTextDocumentService(this); + textDocumentService = new JakartaTextDocumentService(this, javaDocuments); } @Override @@ -56,7 +66,7 @@ public CompletableFuture initialize(InitializeParams params) { InitializeResult initializeResult = new InitializeResult(serverCapabilities); // Provide Completion Capability to the LS - initializeResult.getCapabilities().setCompletionProvider(new CompletionOptions()); + initializeResult.getCapabilities().setCompletionProvider(new CompletionOptions(false, null)); initializeResult.getCapabilities().setHoverProvider(true); initializeResult.getCapabilities().setCodeActionProvider(true); return CompletableFuture.completedFuture(initializeResult); @@ -110,4 +120,20 @@ public long getParentProcessId() { return parentProcessId != null ? parentProcessId : 0; } + @Override + public CompletableFuture getJavaProjectLabels( + JakartaJavaProjectLabelsParams javaParams) { + return getLanguageClient().getJavaProjectLabels(javaParams); + } + + @Override + public CompletableFuture> getAllJavaProjectLabels() { + return getLanguageClient().getAllJavaProjectLabels(); + } + + @Override + public CompletableFuture getJavaFileInfo(JakartaJavaFileInfoParams javaParams) { + return getLanguageClient().getJavaFileInfo(javaParams); + } + } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaLanguageServerLauncher.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaLanguageServerLauncher.java similarity index 72% rename from jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaLanguageServerLauncher.java rename to jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaLanguageServerLauncher.java index 5909cb1c..f7914ddb 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaLanguageServerLauncher.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaLanguageServerLauncher.java @@ -11,7 +11,7 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta; +package org.eclipse.lsp4jakarta.ls; import java.io.InputStream; import java.io.OutputStream; @@ -25,8 +25,8 @@ import org.eclipse.lsp4j.launch.LSPLauncher.Builder; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; -import org.eclipse.lsp4jakarta.api.JakartaLanguageClientAPI; -import org.eclipse.lsp4mp.ls.commons.ParentProcessWatcher; +import org.eclipse.lsp4jakarta.ls.api.JakartaLanguageClientAPI; +import org.eclipse.lsp4jakarta.ls.commons.ParentProcessWatcher; public class JakartaLanguageServerLauncher { public static void main(String[] args) { @@ -34,14 +34,14 @@ public static void main(String[] args) { Function wrapper; wrapper = it -> it; - if ("true".equals(System.getProperty("runAsync")) ) { + if ("true".equals(System.getProperty("runAsync"))) { wrapper = it -> msg -> CompletableFuture.runAsync(() -> it.consume(msg)); } if (!"false".equals(System.getProperty("watchParentProcess"))) { wrapper = new ParentProcessWatcher(server, wrapper); } Launcher launcher = createServerLauncher(server, System.in, System.out, - Executors.newCachedThreadPool()); + Executors.newCachedThreadPool()); server.setLanguageClient(launcher.getRemoteProxy()); launcher.startListening(); @@ -53,18 +53,17 @@ public static void main(String[] args) { * applied to the incoming and outgoing message streams so additional message * handling such as validation and tracing can be included. * - * @param server - the server that receives method calls from the - * remote client - * @param in - input stream to listen for incoming messages - * @param out - output stream to send outgoing messages + * @param server - the server that receives method calls from the + * remote client + * @param in - input stream to listen for incoming messages + * @param out - output stream to send outgoing messages * @param executorService - the executor service used to start threads - * @param wrapper - a function for plugging in additional message - * consumers + * @param wrapper - a function for plugging in additional message + * consumers */ public static Launcher createServerLauncher(LanguageServer server, InputStream in, OutputStream out, - ExecutorService executorService) { - return new Builder().setLocalService(server).setRemoteInterface(JakartaLanguageClientAPI.class) - .setInput(in).setOutput(out).setExecutorService(executorService).create(); + ExecutorService executorService) { + return new Builder().setLocalService(server).setRemoteInterface(JakartaLanguageClientAPI.class).setInput(in).setOutput(out).setExecutorService(executorService).create(); } } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaTextDocumentService.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaTextDocumentService.java new file mode 100644 index 00000000..6900c03f --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaTextDocumentService.java @@ -0,0 +1,269 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.ls; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.CodeActionParams; +import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.CompletionList; +import org.eclipse.lsp4j.CompletionParams; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DidChangeTextDocumentParams; +import org.eclipse.lsp4j.DidCloseTextDocumentParams; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; +import org.eclipse.lsp4j.DidSaveTextDocumentParams; +import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.HoverParams; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.eclipse.lsp4jakarta.commons.DocumentFormat; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionResult; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsSettings; +import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; +import org.eclipse.lsp4jakarta.ls.commons.BadLocationException; +import org.eclipse.lsp4jakarta.ls.commons.TextDocument; +import org.eclipse.lsp4jakarta.ls.commons.ValidatorDelayer; +import org.eclipse.lsp4jakarta.ls.java.JakartaTextDocuments; +import org.eclipse.lsp4jakarta.ls.java.JakartaTextDocuments.JakartaTextDocument; +import org.eclipse.lsp4jakarta.ls.java.JavaTextDocumentSnippetRegistry; +import org.eclipse.lsp4jakarta.snippets.JavaSnippetCompletionContext; +import org.eclipse.lsp4jakarta.snippets.SnippetContextForJava; + +public class JakartaTextDocumentService implements TextDocumentService { + + private static final Logger LOGGER = Logger.getLogger(JakartaTextDocumentService.class.getName()); + + private final JakartaLanguageServer jakartaLanguageServer; + + // Text document manager that maintains the contexts of the text documents + private final JakartaTextDocuments documents; + + private ValidatorDelayer validatorDelayer; + + public JakartaTextDocumentService(JakartaLanguageServer jls, JakartaTextDocuments jakartaTextDocuments) { + this.jakartaLanguageServer = jls; + this.documents = jakartaTextDocuments; + this.validatorDelayer = new ValidatorDelayer<>((javaTextDocument) -> { + triggerValidationFor(javaTextDocument); + }); + } + + @Override + public void didClose(DidCloseTextDocumentParams params) { + documents.onDidCloseTextDocument(params); + String uri = params.getTextDocument().getUri(); + // clear diagnostics + jakartaLanguageServer.getLanguageClient().publishDiagnostics(new PublishDiagnosticsParams(uri, new ArrayList())); + } + + @Override + public CompletableFuture, CompletionList>> completion(CompletionParams params) { + + JakartaTextDocument document = documents.get(params.getTextDocument().getUri()); + + return document.executeIfInJakartaProject((projectInfo, cancelChecker) -> { + JakartaJavaCompletionParams javaParams = new JakartaJavaCompletionParams(params.getTextDocument().getUri(), params.getPosition()); + + // get the completion capabilities from the java language server component + CompletableFuture javaParticipantCompletionsFuture = jakartaLanguageServer.getLanguageClient().getJavaCompletion(javaParams); + + // calculate params for Java snippets + Integer completionOffset = null; + try { + completionOffset = document.offsetAt(params.getPosition()); + } catch (BadLocationException e) { + // LOGGER.log(Level.SEVERE, "Error while getting java snippet completions", e); + return null; + } + + final Integer finalizedCompletionOffset = completionOffset; + boolean canSupportMarkdown = true; + boolean snippetsSupported = true; + + cancelChecker.checkCanceled(); + + return javaParticipantCompletionsFuture.thenApply((completionResult) -> { + cancelChecker.checkCanceled(); + + // We currently do not get any completion items from the JDT Extn layer - the + // completion + // list will be null, so we will new it up here to add the LS based snippets. + // Will we in the future? + CompletionList list = completionResult.getCompletionList(); + if (list == null) { + list = new CompletionList(); + } + + // We do get a cursorContext obj back from the JDT Extn layer - we will need + // that for snippet selection + JavaCursorContextResult cursorContext = completionResult.getCursorContext(); + + // calculate the snippet completion items based on the cursor context + JavaTextDocumentSnippetRegistry snippetRegistry = documents.getSnippetRegistry(); + List snippetCompletionItems = snippetRegistry.getCompletionItems( + document, finalizedCompletionOffset, canSupportMarkdown, + snippetsSupported, + (context, model) -> { + if (context != null + && context instanceof SnippetContextForJava) { + return ((SnippetContextForJava) context).isMatch(new JavaSnippetCompletionContext(projectInfo, cursorContext)); + } + return true; + }, projectInfo); + list.getItems().addAll(snippetCompletionItems); + + // This reduces the number of completion requests to the server. See: + // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion + list.setIsIncomplete(false); + return Either.forRight(list); + }); + + }, Either.forLeft(Collections.emptyList())); + } + + @Override + public CompletableFuture>> codeAction(CodeActionParams params) { + // Prepare the JakartaJavaCodeActionParams + JakartaJavaCodeActionParams jakartaCodeActionParams = new JakartaJavaCodeActionParams(); + jakartaCodeActionParams.setTextDocument(params.getTextDocument()); + jakartaCodeActionParams.setRange(params.getRange()); + jakartaCodeActionParams.setContext(params.getContext()); + + // TODO: Retrieve client capabilities, and pass it along in the parameter object: + // Investigate: + // jakartaCodeActionParams.setResourceOperationSupported(), + // jakartaCodeActionParams.setCommandConfigurationUpdateSupported(), + // jakartaCodeActionParams.etResolveSupported(). + + // Pass the JakartaJavaCodeActionParams to IDE client, to be forwarded to the + // JDT LS extension. + return jakartaLanguageServer.getLanguageClient().getJavaCodeAction(jakartaCodeActionParams) // + .thenApply(codeActions -> { + // Return the corresponding list of CodeActions, put in an Either and wrap as a + // CompletableFuture + return codeActions.stream().map(ca -> { + Either e = Either.forRight(ca); + return e; + }).collect(Collectors.toList()); + }); + } + + @Override + public CompletableFuture resolveCodeAction(CodeAction unresolved) { + return jakartaLanguageServer.getLanguageClient().resolveCodeAction(unresolved); + } + + @Override + public CompletableFuture hover(HoverParams params) { + return CompletableFuture.completedFuture(null); + } + + @Override + public void didOpen(DidOpenTextDocumentParams params) { + validate(documents.onDidOpenTextDocument(params), false); + } + + @Override + public void didChange(DidChangeTextDocumentParams params) { + validate(documents.onDidChangeTextDocument(params), true); + } + + @Override + public void didSave(DidSaveTextDocumentParams params) { + // validate all opened java files which belong to a Jakarta project + triggerValidationForAll(null); + } + + private void validate(JakartaTextDocument javaTextDocument, boolean delay) { + if (delay) { + validatorDelayer.validateWithDelay(javaTextDocument); + } else { + triggerValidationFor(javaTextDocument); + } + } + + /** + * Validate all opened Java files which belong to a Jakarta project. + * + * @param projectURIs list of project URIs filter and null otherwise. + */ + private void triggerValidationForAll(Set projectURIs) { + triggerValidationFor(documents.all().stream() // + .filter(document -> projectURIs == null || projectURIs.contains(document.getProjectURI())) // + .map(TextDocument::getUri) // + .collect(Collectors.toList())); + } + + /** + * Validate the given opened Java file. + * + * @param document the opened Java file. + */ + private void triggerValidationFor(JakartaTextDocument document) { + document.executeIfInJakartaProject((projectinfo, cancelChecker) -> { + String uri = document.getUri(); + triggerValidationFor(Arrays.asList(uri)); + return null; + }, null, true); + } + + /** + * Validate all given Java files uris. + * + * @param uris Java files uris to validate. + */ + private void triggerValidationFor(List uris) { + if (uris.isEmpty()) { + return; + } + + JakartaJavaDiagnosticsParams javaParams = new JakartaJavaDiagnosticsParams(uris, new JakartaJavaDiagnosticsSettings(null)); + + // TODO: Use settings to see if markdown format is supported, or remove it if not needed. + // Leave it hard coded for now. + // sharedSettings.getHoverSettings().isContentFormatSupported(MarkupKind.MARKDOWN); + javaParams.setDocumentFormat(DocumentFormat.Markdown); + + jakartaLanguageServer.getLanguageClient().getJavaDiagnostics(javaParams).thenApply(diagnostics -> { + if (diagnostics == null) { + return null; + } + for (PublishDiagnosticsParams diagnostic : diagnostics) { + jakartaLanguageServer.getLanguageClient().publishDiagnostics(diagnostic); + } + return null; + }); + } + + protected void cleanDiagnostics() { + // clear existing diagnostics + documents.all().forEach(doc -> { + jakartaLanguageServer.getLanguageClient().publishDiagnostics(new PublishDiagnosticsParams(doc.getUri(), new ArrayList())); + }); + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaWorkspaceService.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaWorkspaceService.java similarity index 97% rename from jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaWorkspaceService.java rename to jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaWorkspaceService.java index 1fab916f..43d8d98a 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/JakartaWorkspaceService.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/JakartaWorkspaceService.java @@ -11,7 +11,7 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta; +package org.eclipse.lsp4jakarta.ls; import org.eclipse.lsp4j.DidChangeConfigurationParams; import org.eclipse.lsp4j.DidChangeWatchedFilesParams; diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCodeActionProvider.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCodeActionProvider.java new file mode 100644 index 00000000..ff371bf4 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCodeActionProvider.java @@ -0,0 +1,31 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.api; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCodeActionParams; + +/** + * Jakarta code action provider. + * + */ +public interface JakartaJavaCodeActionProvider { + + @JsonRequest("jakarta/java/codeAction") + CompletableFuture> getJavaCodeAction(JakartaJavaCodeActionParams javaParams); + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCodeActionResolveProvider.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCodeActionResolveProvider.java new file mode 100644 index 00000000..707fd172 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCodeActionResolveProvider.java @@ -0,0 +1,28 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.api; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.CodeAction; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; + +/** + * Jakarta code action resolution (workspace edit) provider. + */ +public interface JakartaJavaCodeActionResolveProvider { + + @JsonRequest("jakarta/java/codeActionResolve") + CompletableFuture resolveCodeAction(CodeAction unresolved); + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCompletionProvider.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCompletionProvider.java new file mode 100644 index 00000000..372002f5 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaCompletionProvider.java @@ -0,0 +1,29 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.api; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaCompletionResult; + +/** + * Jakarta completion provider. + */ +public interface JakartaJavaCompletionProvider { + + @JsonRequest("jakarta/java/completion") + CompletableFuture getJavaCompletion(JakartaJavaCompletionParams javaParams); + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaDiagnosticsProvider.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaDiagnosticsProvider.java new file mode 100644 index 00000000..989fac7b --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaDiagnosticsProvider.java @@ -0,0 +1,29 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.api; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4jakarta.commons.JakartaJavaDiagnosticsParams; + +/** + * Jakarta Java diagnostics provider. + */ +public interface JakartaJavaDiagnosticsProvider { + + @JsonRequest("jakarta/java/diagnostics") + CompletableFuture> getJavaDiagnostics(JakartaJavaDiagnosticsParams javaParams); +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaFileInfoProvider.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaFileInfoProvider.java new file mode 100644 index 00000000..0ac5f084 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaFileInfoProvider.java @@ -0,0 +1,36 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.api; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfo; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfoParams; + +/** + * Jakarta Java file information provider. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/ls/api/MicroProfileJavaFileInfoProvider.java + * + * @author Angelo ZERR + * + */ +public interface JakartaJavaFileInfoProvider { + + @JsonRequest("jakarta/java/fileInfo") + CompletableFuture getJavaFileInfo(JakartaJavaFileInfoParams params); + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaProjectLabelsProvider.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaProjectLabelsProvider.java new file mode 100644 index 00000000..4e6804bc --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaJavaProjectLabelsProvider.java @@ -0,0 +1,32 @@ +/******************************************************************************* +* Copyright (c) 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.api; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4jakarta.commons.JakartaJavaProjectLabelsParams; +import org.eclipse.lsp4jakarta.commons.ProjectLabelInfoEntry; + +/** + * Jakarta project labels provider. + */ +public interface JakartaJavaProjectLabelsProvider { + + @JsonRequest("jakarta/java/projectLabels") + CompletableFuture getJavaProjectLabels(JakartaJavaProjectLabelsParams params); + + @JsonRequest("jakarta/java/workspaceLabels") + CompletableFuture> getAllJavaProjectLabels(); +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaLanguageClientAPI.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaLanguageClientAPI.java new file mode 100644 index 00000000..78a1a97e --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/api/JakartaLanguageClientAPI.java @@ -0,0 +1,22 @@ +/******************************************************************************* +* Copyright (c) 2020, 2023 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.ls.api; + +import org.eclipse.lsp4j.services.LanguageClient; + +/** + * API of the client consuming the Language Server for Jakarta EE. Used to send + * messages back to the client to ask for information about the Java project. + */ +public interface JakartaLanguageClientAPI extends LanguageClient, JakartaJavaCompletionProvider, JakartaJavaProjectLabelsProvider, JakartaJavaFileInfoProvider, JakartaJavaDiagnosticsProvider, JakartaJavaCodeActionProvider, JakartaJavaCodeActionResolveProvider {} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/BadLocationException.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/BadLocationException.java new file mode 100644 index 00000000..ce8d2594 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/BadLocationException.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +/** + * Indicates the attempt to access a non-existing position. The attempt has been + * performed on a text store such as a document or string. + *

    + * This class is not intended to be serialized. + *

    + * + * This class is a copy/paste of org.eclipse.jface.text.BadLocationException. + */ +public class BadLocationException extends Exception { + + /** + * Serial version UID for this class. + *

    + * Note: This class is not intended to be serialized. + *

    + * + * @since 3.1 + */ + private static final long serialVersionUID = 3257281452776370224L; + + /** + * Creates a new bad location exception. + */ + public BadLocationException() { + super(); + } + + /** + * Creates a new bad location exception. + * + * @param message the exception message + */ + public BadLocationException(String message) { + super(message); + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ILineTracker.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ILineTracker.java new file mode 100644 index 00000000..1cffa047 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ILineTracker.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; +//package org.eclipse.jface.text; + +import org.eclipse.lsp4j.Position; + +/** + * A line tracker maps character positions to line numbers and vice versa. + * Initially the line tracker is informed about its underlying text in order to + * initialize the mapping information. After that, the line tracker is informed + * about all changes of the underlying text allowing for incremental updates of + * the mapping information. It is the client's responsibility to actively inform + * the line tacker about text changes. For example, when using a line tracker in + * combination with a document the document controls the line tracker. + *

    + * In order to provide backward compatibility for clients of ILineTracker, extension + * interfaces are used to provide a means of evolution. The following extension interfaces + * exist: + *

      + *
    • {@link org.eclipse.jface.text.ILineTrackerExtension} since version 3.1 introducing the concept + * of rewrite sessions.
    • + *
    + *

    + * Clients may implement this interface or use the standard implementation + *

    + * {@link org.eclipse.jface.text.DefaultLineTracker}or + * {@link org.eclipse.jface.text.ConfigurableLineTracker}. + */ +public interface ILineTracker { + + /** + * Returns the line delimiter of the specified line. Returns null if the + * line is not closed with a line delimiter. + * + * @param line the line whose line delimiter is queried + * @return the line's delimiter or null if line does not have a delimiter + * @exception BadLocationException if the line number is invalid in this tracker's line structure + */ + String getLineDelimiter(int line) throws BadLocationException; + + /** + * Computes the number of lines in the given text. + * + * @param text the text whose number of lines should be computed + * @return the number of lines in the given text + */ + int computeNumberOfLines(String text); + + /** + * Returns the number of lines. + *

    + * Note that a document always has at least one line. + *

    + * + * @return the number of lines in this tracker's line structure + */ + int getNumberOfLines(); + + /** + * Returns the number of lines which are occupied by a given text range. + * + * @param offset the offset of the specified text range + * @param length the length of the specified text range + * @return the number of lines occupied by the specified range + * @exception BadLocationException if specified range is unknown to this tracker + */ + int getNumberOfLines(int offset, int length) throws BadLocationException; + + /** + * Returns the position of the first character of the specified line. + * + * @param line the line of interest + * @return offset of the first character of the line + * @exception BadLocationException if the line is unknown to this tracker + */ + int getLineOffset(int line) throws BadLocationException; + + /** + * Returns length of the specified line including the line's delimiter. + * + * @param line the line of interest + * @return the length of the line + * @exception BadLocationException if line is unknown to this tracker + */ + int getLineLength(int line) throws BadLocationException; + + /** + * Returns the line number the character at the given offset belongs to. + * + * @param offset the offset whose line number to be determined + * @return the number of the line the offset is on + * @exception BadLocationException if the offset is invalid in this tracker + */ + int getLineNumberOfOffset(int offset) throws BadLocationException; + + /** + * Returns a line description of the line at the given offset. + * The description contains the start offset and the length of the line + * excluding the line's delimiter. + * + * @param offset the offset whose line should be described + * @return a region describing the line + * @exception BadLocationException if offset is invalid in this tracker + */ + Line getLineInformationOfOffset(int offset) throws BadLocationException; + + /** + * Returns a line description of the given line. The description + * contains the start offset and the length of the line excluding the line's + * delimiter. + * + * @param line the line that should be described + * @return a region describing the line + * @exception BadLocationException if line is unknown to this tracker + */ + Line getLineInformation(int line) throws BadLocationException; + + /** + * Informs the line tracker about the specified change in the tracked text. + * + * @param offset the offset of the replaced text + * @param length the length of the replaced text + * @param text the substitution text + * @exception BadLocationException if specified range is unknown to this tracker + */ + void replace(int offset, int length, String text) throws BadLocationException; + + /** + * Sets the tracked text to the specified text. + * + * @param text the new tracked text + */ + void set(String text); + + Position getPositionAt(int position) throws BadLocationException; + + int getOffsetAt(Position position) throws BadLocationException; +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/Line.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/Line.java new file mode 100644 index 00000000..28078a1a --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/Line.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * This class is a copy/paste of org.eclipse.jface.text.Line + *******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +/** + * Describes a line as a particular number of characters beginning at + * a particular offset, consisting of a particular number of characters, + * and being closed with a particular line delimiter. + */ +final class Line /* implements IRegion */ { + + /** The offset of the line */ + public int offset; + /** The length of the line */ + public int length; + /** The delimiter of this line */ + public final String delimiter; + + /** + * Creates a new Line. + * + * @param offset the offset of the line + * @param end the last including character offset of the line + * @param delimiter the line's delimiter + */ + public Line(int offset, int end, String delimiter) { + this.offset = offset; + this.length = (end - offset) + 1; + this.delimiter = delimiter; + } + + /** + * Creates a new Line. + * + * @param offset the offset of the line + * @param length the length of the line + */ + public Line(int offset, int length) { + this.offset = offset; + this.length = length; + this.delimiter = null; + } + + //@Override + public int getOffset() { + return offset; + } + + //@Override + public int getLength() { + return length; + } + + @Override + public String toString() { + return "Line [offset: " + offset + ", length: " + length + ", delimiter: '" + delimiter + "']"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ListLineTracker.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ListLineTracker.java new file mode 100644 index 00000000..eaaaea18 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ListLineTracker.java @@ -0,0 +1,441 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * This class is a copy/paste of org.eclipse.jface.text.ListLineTracker + *******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.lsp4j.Position; + +/** + * Abstract, read-only implementation of ILineTracker. It lets the + * definition of line delimiters to subclasses. Assuming that '\n' is the only + * line delimiter, this abstract implementation defines the following line + * scheme: + *
      + *
    • "" -> [0,0] + *
    • "a" -> [0,1] + *
    • "\n" -> [0,1], [1,0] + *
    • "a\n" -> [0,2], [2,0] + *
    • "a\nb" -> [0,2], [2,1] + *
    • "a\nbc\n" -> [0,2], [2,3], [5,0] + *
    + * This class must be subclassed. + * + * @since 3.2 + */ +class ListLineTracker implements ILineTracker { + + /** The predefined delimiters of this tracker */ + public final static String[] DELIMITERS = { "\r", "\n", "\r\n" }; //$NON-NLS-3$ //$NON-NLS-1$ //$NON-NLS-2$ + /** A predefined delimiter information which is always reused as return value */ + private DelimiterInfo fDelimiterInfo = new DelimiterInfo(); + + /** The line information */ + private final List fLines = new ArrayList<>(); + /** The length of the tracked text */ + private int fTextLength; + + /** + * Combines the information of the occurrence of a line delimiter. + * delimiterIndex is the index where a line delimiter starts, + * whereas delimiterLength, indicates the length of the delimiter. + */ + protected static class DelimiterInfo { + public int delimiterIndex; + public int delimiterLength; + public String delimiter; + } + + /** + * Creates a new line tracker. + */ + protected ListLineTracker() {} + + /** + * Binary search for the line at a given offset. + * + * @param offset the offset whose line should be found + * @return the line of the offset + */ + private int findLine(int offset) { + + if (fLines.size() == 0) + return -1; + + int left = 0; + int right = fLines.size() - 1; + int mid = 0; + Line line = null; + + while (left < right) { + + mid = (left + right) / 2; + + line = fLines.get(mid); + if (offset < line.offset) { + if (left == mid) + right = left; + else + right = mid - 1; + } else if (offset > line.offset) { + if (right == mid) + left = right; + else + left = mid + 1; + } else if (offset == line.offset) { + left = right = mid; + } + } + + line = fLines.get(left); + if (line.offset > offset) + --left; + return left; + } + + @Override + public final Position getPositionAt(int offset) throws BadLocationException { + int lineNumber = getLineNumberOfOffset(offset); + int lines = fLines.size(); + int character = 0; + if (lines > 0) { + if (lineNumber == lines) { + Line l = fLines.get(lineNumber - 1); + character = offset - l.offset - l.length; + } else { + Line l = fLines.get(lineNumber); + character = offset - l.offset; + } + } + return new Position(lineNumber, character); + } + + /** + * Returns the number of lines covered by the specified text range. + * + * @param startLine the line where the text range starts + * @param offset the start offset of the text range + * @param length the length of the text range + * @return the number of lines covered by this text range + * @exception BadLocationException if range is undefined in this tracker + */ + private int getNumberOfLines(int startLine, int offset, int length) throws BadLocationException { + + if (length == 0) + return 1; + + int target = offset + length; + + Line l = fLines.get(startLine); + + if (l.delimiter == null) + return 1; + + if (l.offset + l.length > target) + return 1; + + if (l.offset + l.length == target) + return 2; + + return getLineNumberOfOffset(target) - startLine + 1; + } + + @Override + public final int getLineLength(int line) throws BadLocationException { + int lines = fLines.size(); + + if (line < 0 || line > lines) + throw new BadLocationException(); + + if (lines == 0 || lines == line) + return 0; + + Line l = fLines.get(line); + return l.length; + } + + @Override + public final int getLineNumberOfOffset(int position) throws BadLocationException { + if (position < 0) { + throw new BadLocationException("Negative offset : " + position); //$NON-NLS-1$ + } else if (position > fTextLength) { + throw new BadLocationException("Offset > length: " + position + " > " + fTextLength); //$NON-NLS-1$//$NON-NLS-2$ + } + + if (position == fTextLength) { + + int lastLine = fLines.size() - 1; + if (lastLine < 0) + return 0; + + Line l = fLines.get(lastLine); + return (l.delimiter != null ? lastLine + 1 : lastLine); + } + + return findLine(position); + } + + @Override + public int getOffsetAt(Position position) throws BadLocationException { + int line = position.getLine(); + int lines = fLines.size(); + + if (line < 0 || line > lines) + throw new BadLocationException("The line value, {" + line + "}, is out of bounds."); + + int lineOffset = -1; + int lineLength = -1; + if (lines == 0) { + lineOffset = 0; + lineLength = 0; + } else { + if (line == lines) { + Line l = fLines.get(line - 1); + lineOffset = l.offset + l.length; + lineLength = 0; + } else { + Line l = fLines.get(line); + lineOffset = l.offset; + lineLength = l.delimiter != null ? l.length - l.delimiter.length() : l.length; + } + } + int character = position.getCharacter(); + int offset = lineOffset + character; + int endLineOffset = lineOffset + lineLength; + if (offset > endLineOffset) + throw new BadLocationException("The character value, {" + character + "} of the line" + line + "}, is out of bounds."); + return offset; + } + + @Override + public final Line getLineInformationOfOffset(int position) throws BadLocationException { + if (position > fTextLength) + throw new BadLocationException("Offset > length: " + position + " > " + fTextLength); //$NON-NLS-1$//$NON-NLS-2$ + + if (position == fTextLength) { + int size = fLines.size(); + if (size == 0) + return new Line(0, 0); + Line l = fLines.get(size - 1); + return (l.delimiter != null ? new Line(fTextLength, 0) : new Line(fTextLength - l.length, l.length)); + } + + return getLineInformation(findLine(position)); + } + + @Override + public final Line getLineInformation(int line) throws BadLocationException { + int lines = fLines.size(); + + if (line < 0 || line > lines) + throw new BadLocationException("The line value, {" + line + "}, is out of bounds."); + + if (lines == 0) + return new Line(0, 0); + + if (line == lines) { + Line l = fLines.get(line - 1); + return new Line(l.offset + l.length, 0); + } + + Line l = fLines.get(line); + return (l.delimiter != null ? new Line(l.offset, l.length - l.delimiter.length()) : l); + } + + @Override + public final int getLineOffset(int line) throws BadLocationException { + int lines = fLines.size(); + + if (line < 0 || line > lines) + throw new BadLocationException("The line value, {" + line + "}, is out of bounds."); + + if (lines == 0) + return 0; + + if (line == lines) { + Line l = fLines.get(line - 1); + if (l.delimiter != null) + return l.offset + l.length; + throw new BadLocationException(); + } + + Line l = fLines.get(line); + return l.offset; + } + + @Override + public final int getNumberOfLines() { + int lines = fLines.size(); + + if (lines == 0) + return 1; + + Line l = fLines.get(lines - 1); + return (l.delimiter != null ? lines + 1 : lines); + } + + @Override + public final int getNumberOfLines(int position, int length) throws BadLocationException { + + if (position < 0 || position + length > fTextLength) + throw new BadLocationException(); + + if (length == 0) // optimization + return 1; + + return getNumberOfLines(getLineNumberOfOffset(position), position, length); + } + + @Override + public final int computeNumberOfLines(String text) { + int count = 0; + int start = 0; + DelimiterInfo delimiterInfo = nextDelimiterInfo(text, start); + while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) { + ++count; + start = delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength; + delimiterInfo = nextDelimiterInfo(text, start); + } + return count; + } + + public final String getLineDelimiter(int line) throws BadLocationException { + int lines = fLines.size(); + + if (line < 0 || line > lines) + throw new BadLocationException("The line value, {" + line + "}, is out of bounds."); + + if (lines == 0) + return null; + + if (line == lines) + return null; + + Line l = fLines.get(line); + return l.delimiter; + } + + /** + * Returns the information about the first delimiter found in the given text + * starting at the given offset. + * + * @param text the text to be searched + * @param offset the offset in the given text + * @return the information of the first found delimiter or null + */ + protected DelimiterInfo nextDelimiterInfo(String text, int offset) { + char ch; + int length = text.length(); + for (int i = offset; i < length; i++) { + + ch = text.charAt(i); + if (ch == '\r') { + + if (i + 1 < length) { + if (text.charAt(i + 1) == '\n') { + fDelimiterInfo.delimiter = DELIMITERS[2]; + fDelimiterInfo.delimiterIndex = i; + fDelimiterInfo.delimiterLength = 2; + return fDelimiterInfo; + } + } + + fDelimiterInfo.delimiter = DELIMITERS[0]; + fDelimiterInfo.delimiterIndex = i; + fDelimiterInfo.delimiterLength = 1; + return fDelimiterInfo; + + } else if (ch == '\n') { + + fDelimiterInfo.delimiter = DELIMITERS[1]; + fDelimiterInfo.delimiterIndex = i; + fDelimiterInfo.delimiterLength = 1; + return fDelimiterInfo; + } + } + + return null; + + } + + /** + * Creates the line structure for the given text. Newly created lines are + * inserted into the line structure starting at the given position. Returns the + * number of newly created lines. + * + * @param text the text for which to create a line structure + * @param insertPosition the position at which the newly created lines are + * inserted into the tracker's line structure + * @param offset the offset of all newly created lines + * @return the number of newly created lines + */ + private int createLines(String text, int insertPosition, int offset) { + + int count = 0; + int start = 0; + DelimiterInfo delimiterInfo = nextDelimiterInfo(text, 0); + + while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) { + + int index = delimiterInfo.delimiterIndex + (delimiterInfo.delimiterLength - 1); + + if (insertPosition + count >= fLines.size()) + fLines.add(new Line(offset + start, offset + index, delimiterInfo.delimiter)); + else + fLines.add(insertPosition + count, new Line(offset + start, offset + index, delimiterInfo.delimiter)); + + ++count; + start = index + 1; + delimiterInfo = nextDelimiterInfo(text, start); + } + + if (start < text.length()) { + if (insertPosition + count < fLines.size()) { + // there is a line below the current + Line l = fLines.get(insertPosition + count); + int delta = text.length() - start; + l.offset -= delta; + l.length += delta; + } else { + fLines.add(new Line(offset + start, offset + text.length() - 1, null)); + ++count; + } + } + + return count; + } + + @Override + public final void replace(int position, int length, String text) throws BadLocationException { + throw new UnsupportedOperationException(); + } + + @Override + public final void set(String text) { + fLines.clear(); + if (text != null) { + fTextLength = text.length(); + createLines(text, 0, 0); + } + } + + /** + * Returns the internal data structure, a {@link List} of {@link Line}s. Used + * only by {@link TreeLineTracker#TreeLineTracker(ListLineTracker)}. + * + * @return the internal list of lines. + */ + final List getLines() { + return fLines; + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ModelTextDocument.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ModelTextDocument.java new file mode 100644 index 00000000..929bc29e --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ModelTextDocument.java @@ -0,0 +1,111 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.util.concurrent.CancellationException; +import java.util.function.BiFunction; +import java.util.logging.Logger; + +import org.eclipse.lsp4j.TextDocumentItem; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; + +/** + * A {@link TextDocument} which is associate to a model loaded in async. + * + * @author Angelo ZERR + * + * @param the model type (ex : DOM Document) + */ +public class ModelTextDocument extends TextDocument { + + private static final Logger LOGGER = Logger.getLogger(ModelTextDocument.class.getName()); + + private final BiFunction parse; + + private T model; + + public ModelTextDocument(TextDocumentItem document, BiFunction parse) { + super(document); + this.parse = parse; + } + + public ModelTextDocument(String text, String uri, BiFunction parse) { + super(text, uri); + this.parse = parse; + } + + public T getExistingModel() { + return model; + } + + public T getModel() { + if (model == null) { + return getSynchronizedModel(); + } + return model; + } + + /** + * Return the existing parsed model synchronized with last version of the text + * document or parse the model. + * + * @return the existing parsed model synchronized with last version of the text + * document or parse the model. + */ + private synchronized T getSynchronizedModel() { + if (model != null) { + return model; + } + int version = super.getVersion(); + long start = System.currentTimeMillis(); + try { + LOGGER.fine("Start parsing of model with version '" + version); + // Stop of parse process can be done when completable future is canceled or when + // version of document changes + CancelChecker cancelChecker = new TextDocumentVersionChecker(this, version); + // parse the model + model = parse.apply(this, cancelChecker); + } catch (CancellationException e) { + LOGGER.fine("Stop parsing parsing of model with version '" + version + "' in " + + (System.currentTimeMillis() - start) + "ms"); + throw e; + } finally { + LOGGER.fine("End parse of model with version '" + version + "' in " + (System.currentTimeMillis() - start) + + "ms"); + } + return model; + } + + @Override + public void setText(String text) { + super.setText(text); + // text changed, mark the model as dirty + cancelModel(); + } + + @Override + public void setVersion(int version) { + super.setVersion(version); + // version changed, mark the model as dirty + cancelModel(); + } + + /** + * Mark the model as dirty + */ + private void cancelModel() { + model = null; + } + +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ModelTextDocuments.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ModelTextDocuments.java new file mode 100644 index 00000000..60d3ea55 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ModelTextDocuments.java @@ -0,0 +1,156 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; +import java.util.function.Function; + +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentItem; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures.FutureCancelChecker; + +/** + * The cache of {@link TextDocument} linked to a model. + * + * @author Angelo ZERR + * + * @param the model type (ex : DOM Document) + */ +public class ModelTextDocuments extends TextDocuments> { + + private final BiFunction parse; + + public ModelTextDocuments(BiFunction parse) { + this.parse = parse; + } + + @Override + public ModelTextDocument createDocument(TextDocumentItem document) { + ModelTextDocument doc = new ModelTextDocument(document, parse); + doc.setIncremental(isIncremental()); + return doc; + } + + /** + * Returns the model of the given text document Uri and null otherwise. + * + * @param uri the text document uri. + * + * @return the model of the given text document Uri and null otherwise. + */ + public T getExistingModel(TextDocumentIdentifier documentIdentifier) { + return getExistingModel(documentIdentifier.getUri()); + } + + /** + * Returns the model of the given text document Uri and null otherwise. + * + * @param uri the text document uri. + * + * @return the model of the given text document Uri and null otherwise. + */ + public T getExistingModel(String uri) { + ModelTextDocument document = get(uri); + if (document != null) { + return document.getExistingModel(); + } + return null; + } + + /** + * Returns the model of the given text document Uri and null otherwise. + * + * @param uri the text document uri. + * + * @return the model of the given text document Uri and null otherwise. + */ + public T getModel(TextDocumentIdentifier documentIdentifier) { + return getModel(documentIdentifier.getUri()); + } + + /** + * Returns the model of the given text document Uri and null otherwise. + * + * @param uri the text document uri. + * + * @return the model of the given text document Uri and null otherwise. + */ + public T getModel(String uri) { + ModelTextDocument document = get(uri); + if (document != null) { + return document.getModel(); + } + return null; + } + + /** + * Get or parse the model and apply the code function which expects the model. + * + * @param + * @param documentIdentifier the document identifier. + * @param code a bi function that accepts the parsed model and + * {@link CancelChecker} and returns the to be + * computed value + * @return the model for a given uri in a future and then apply the given + * function. + */ + public CompletableFuture computeModelAsync(TextDocumentIdentifier documentIdentifier, + BiFunction code) { + return CompletableFutures.computeAsync(cancelChecker -> { + // Get or parse the model. + T model = getModel(documentIdentifier); + if (model == null) { + return null; + } + cancelChecker.checkCanceled(); + // Apply the function code by using the parsed model. + return code.apply(model, cancelChecker); + }); + } + + /** + * Get or parse the model and apply the code function which expects the model. + * + * @param + * @param documentIdentifier the document identifier. + * @param code a bi function that accepts the parsed model and + * {@link CancelChecker} and returns as future the to + * be computed value + * @return the model for a given uri in a future and then apply the given + * function. + */ + public CompletableFuture computeModelAsyncCompose(TextDocumentIdentifier documentIdentifier, + BiFunction> code) { + return computeAsyncCompose(cancelChecker -> { + // Get or parse the model. + T model = getModel(documentIdentifier); + if (model == null) { + return null; + } + cancelChecker.checkCanceled(); + // Apply the function code by using the parsed model.{ + return code.apply(model, cancelChecker); + }); + } + + private static CompletableFuture computeAsyncCompose(Function> code) { + CompletableFuture start = new CompletableFuture<>(); + CompletableFuture result = start.thenComposeAsync(code); + start.complete(new FutureCancelChecker(result)); + return result; + } +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/MultiCancelChecker.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/MultiCancelChecker.java new file mode 100644 index 00000000..0077d31e --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/MultiCancelChecker.java @@ -0,0 +1,38 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import org.eclipse.lsp4j.jsonrpc.CancelChecker; + +/** + * Multiple cancel checker. + * + * @author Angelo ZERR + * + */ +public class MultiCancelChecker implements CancelChecker { + + private CancelChecker[] checkers; + + public MultiCancelChecker(CancelChecker... checkers) { + this.checkers = checkers; + } + + @Override + public void checkCanceled() { + for (CancelChecker cancelChecker : checkers) { + cancelChecker.checkCanceled(); + } + } +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ParentProcessWatcher.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ParentProcessWatcher.java new file mode 100644 index 00000000..6571c7ec --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ParentProcessWatcher.java @@ -0,0 +1,151 @@ +/******************************************************************************* +* Copyright (c) 2017 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.io.IOException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.lsp4j.jsonrpc.MessageConsumer; +import org.eclipse.lsp4j.services.LanguageServer; + +import com.google.common.io.Closeables; + +/** + * Watches the parent process PID and invokes exit if it is no longer available. + * This implementation waits for periods of inactivity to start querying the + * PIDs. + */ +public final class ParentProcessWatcher implements Runnable, Function { + + private static final Logger LOGGER = Logger.getLogger(ParentProcessWatcher.class.getName()); + private static final boolean isJava1x = System.getProperty("java.version").startsWith("1."); + private static final boolean isWindows = System.getProperty("os.name").toLowerCase().indexOf("win") >= 0; + + /** + * Exit code returned when XML Language Server is forced to exit. + */ + private static final int FORCED_EXIT_CODE = 1; + + private static final long INACTIVITY_DELAY_SECS = 30 * 1000; + private static final int POLL_DELAY_SECS = 10; + private volatile long lastActivityTime; + private final ProcessLanguageServer server; + private final Function wrapper; + private ScheduledFuture task; + private ScheduledExecutorService service; + + public interface ProcessLanguageServer extends LanguageServer { + + long getParentProcessId(); + + void exit(int exitCode); + } + + public ParentProcessWatcher(ProcessLanguageServer server, Function wrapper) { + this.server = server; + this.wrapper = wrapper; + service = Executors.newScheduledThreadPool(1); + task = service.scheduleWithFixedDelay(this, POLL_DELAY_SECS, POLL_DELAY_SECS, TimeUnit.SECONDS); + } + + @Override + public void run() { + if (!parentProcessStillRunning()) { + LOGGER.warning("Parent process stopped running, forcing server exit"); + task.cancel(true); + server.exit(FORCED_EXIT_CODE); + } + } + + /** + * Checks whether the parent process is still running. If not, then we assume it + * has crashed, and we have to terminate the Java Language Server. + * + * @return true if the parent process is still running + */ + private boolean parentProcessStillRunning() { + // Wait until parent process id is available + final long pid = server.getParentProcessId(); + if (pid == 0 || lastActivityTime > (System.currentTimeMillis() - INACTIVITY_DELAY_SECS)) { + return true; + } + String command; + if (isWindows) { + command = "cmd /c \"tasklist /FI \"PID eq " + pid + "\" | findstr " + pid + "\""; + } else { + command = "kill -0 " + pid; + } + Process process = null; + boolean finished = false; + try { + process = Runtime.getRuntime().exec(command); + finished = process.waitFor(POLL_DELAY_SECS, TimeUnit.SECONDS); + if (!finished) { + process.destroy(); + finished = process.waitFor(POLL_DELAY_SECS, TimeUnit.SECONDS); // wait for the process to stop + } + if (isWindows && finished && process.exitValue() > 1) { + // the tasklist command should return 0 (parent process exists) or 1 (parent + // process doesn't exist) + LOGGER.warning("The tasklist command: '" + command + "' returns " + process.exitValue()); + return true; + } + return !finished || process.exitValue() == 0; + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.WARNING, e.getMessage(), e); + return true; + } finally { + if (process != null) { + if (!finished) { + process.destroyForcibly(); + } + // Terminating or destroying the Process doesn't close the process handle on + // Windows. + // It is only closed when the Process object is garbage collected (in its + // finalize() method). + // On Windows, when the Java LS is idle, we need to explicitly request a GC, + // to prevent an accumulation of zombie processes, as finalize() will be called. + if (isWindows) { + // Java >= 9 doesn't close the handle when the process is garbage collected + // We need to close the opened streams + if (!isJava1x) { + Closeables.closeQuietly(process.getInputStream()); + Closeables.closeQuietly(process.getErrorStream()); + try { + Closeables.close(process.getOutputStream(), false); + } catch (IOException e) { + } + } + System.gc(); + } + } + } + + } + + @Override + public MessageConsumer apply(final MessageConsumer consumer) { + // inject our own consumer to refresh the timestamp + return message -> { + lastActivityTime = System.currentTimeMillis(); + wrapper.apply(consumer).consume(message); + }; + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocument.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocument.java new file mode 100644 index 00000000..d85a11f4 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocument.java @@ -0,0 +1,201 @@ +/******************************************************************************* +* Copyright (c) 2018 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextDocumentContentChangeEvent; +import org.eclipse.lsp4j.TextDocumentItem; + +/** + * Text document extends LSP4j {@link TextDocumentItem} to provide methods to + * retrieve position. + * + */ +public class TextDocument extends TextDocumentItem { + + private static final Logger LOGGER = Logger.getLogger(TextDocument.class.getName()); + + private final Object lock = new Object(); + + private static String DEFAULT_DELIMTER = System.lineSeparator(); + + private ILineTracker lineTracker; + + private boolean incremental; + + public TextDocument(TextDocumentItem document) { + this(document.getText(), document.getUri()); + super.setVersion(document.getVersion()); + super.setLanguageId(document.getLanguageId()); + } + + public TextDocument(String text, String uri) { + super.setUri(uri); + super.setText(text); + } + + public void setIncremental(boolean incremental) { + this.incremental = incremental; + // reset line tracker + lineTracker = null; + getLineTracker(); + } + + public boolean isIncremental() { + return incremental; + } + + public Position positionAt(int position) throws BadLocationException { + ILineTracker lineTracker = getLineTracker(); + return lineTracker.getPositionAt(position); + } + + public Range lineRangeAt(int position) throws BadLocationException { + ILineTracker lineTracker = getLineTracker(); + Line line = lineTracker.getLineInformationOfOffset(position); + int lineNumber = lineTracker.getLineNumberOfOffset(position); + return new Range(new Position(lineNumber, 0), new Position(lineNumber, line.length)); + } + + public int offsetAt(Position position) throws BadLocationException { + ILineTracker lineTracker = getLineTracker(); + return lineTracker.getOffsetAt(position); + } + + public String lineText(int lineNumber) throws BadLocationException { + ILineTracker lineTracker = getLineTracker(); + Line line = lineTracker.getLineInformation(lineNumber); + String text = super.getText(); + return text.substring(line.offset, line.offset + line.length); + } + + public String lineDelimiter(int lineNumber) throws BadLocationException { + ILineTracker lineTracker = getLineTracker(); + String lineDelimiter = lineTracker.getLineDelimiter(lineNumber); + if (lineDelimiter == null) { + if (lineTracker.getNumberOfLines() > 0) { + lineDelimiter = lineTracker.getLineInformation(0).delimiter; + } + } + if (lineDelimiter == null) { + lineDelimiter = DEFAULT_DELIMTER; + } + return lineDelimiter; + } + + public Range getWordRangeAt(int textOffset, Pattern wordDefinition) { + try { + Position pos = positionAt(textOffset); + ILineTracker lineTracker = getLineTracker(); + Line line = lineTracker.getLineInformation(pos.getLine()); + String text = super.getText(); + String lineText = text.substring(line.offset, textOffset); + int position = lineText.length(); + Matcher m = wordDefinition.matcher(lineText); + int currentPosition = 0; + while (currentPosition != position) { + if (m.find()) { + currentPosition = m.end(); + if (currentPosition == position) { + return new Range(new Position(pos.getLine(), m.start()), pos); + } + } else { + currentPosition++; + } + m.region(currentPosition, position); + } + return new Range(pos, pos); + } catch (BadLocationException e) { + return null; + } + } + + private ILineTracker getLineTracker() { + if (lineTracker == null) { + lineTracker = createLineTracker(); + } + return lineTracker; + } + + private synchronized ILineTracker createLineTracker() { + if (lineTracker != null) { + return lineTracker; + } + ILineTracker lineTracker = isIncremental() ? new TreeLineTracker(new ListLineTracker()) : new ListLineTracker(); + lineTracker.set(super.getText()); + return lineTracker; + } + + /** + * Update text of the document by using the changes and according the + * incremental support. + * + * @param changes the text document changes. + */ + public void update(List changes) { + if (changes.size() < 1) { + // no changes, ignore it. + return; + } + if (isIncremental()) { + try { + long start = System.currentTimeMillis(); + synchronized (lock) { + // Initialize buffer and line tracker from the current text document + StringBuilder buffer = new StringBuilder(getText()); + + // Loop for each changes and update the buffer + for (int i = 0; i < changes.size(); i++) { + + TextDocumentContentChangeEvent changeEvent = changes.get(i); + Range range = changeEvent.getRange(); + int length = 0; + + if (range != null) { + length = changeEvent.getRangeLength().intValue(); + } else { + // range is optional and if not given, the whole file content is replaced + length = buffer.length(); + range = new Range(positionAt(0), positionAt(length)); + } + String text = changeEvent.getText(); + int startOffset = offsetAt(range.getStart()); + buffer.replace(startOffset, startOffset + length, text); + lineTracker.replace(startOffset, length, text); + } + // Update the new text content from the updated buffer + setText(buffer.toString()); + } + LOGGER.fine("Text document content updated in " + (System.currentTimeMillis() - start) + "ms"); + } catch (BadLocationException e) { + // Should never occur. + } + } else { + // like vscode does, get the last changes + // see + // https://github.com/Microsoft/vscode-languageserver-node/blob/master/server/src/main.ts + TextDocumentContentChangeEvent last = changes.size() > 0 ? changes.get(changes.size() - 1) : null; + if (last != null) { + setText(last.getText()); + lineTracker.set(last.getText()); + } + } + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocumentVersionChecker.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocumentVersionChecker.java new file mode 100644 index 00000000..dae21e01 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocumentVersionChecker.java @@ -0,0 +1,47 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.util.concurrent.CancellationException; + +import org.eclipse.lsp4j.jsonrpc.CancelChecker; + +/** + * A {@link CancelChecker} implementation to throw a + * {@link CancellationException} when version of {@link TextDocument} changed. + * + * @author Angelo ZERR + * + */ +public class TextDocumentVersionChecker implements CancelChecker { + + private final TextDocument textDocument; + + private final int version; + + public TextDocumentVersionChecker(TextDocument textDocument, int version) { + this.textDocument = textDocument; + this.version = version; + } + + @Override + public void checkCanceled() { + if (textDocument.getVersion() != version) { + // the text document version has changed + throw new CancellationException("Text document version '" + version + "' has changed to version '" + + textDocument.getVersion() + "."); + } + } + +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocuments.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocuments.java new file mode 100644 index 00000000..d9e2c19d --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TextDocuments.java @@ -0,0 +1,128 @@ +/******************************************************************************* +* Copyright (c) 2018 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.lsp4j.DidChangeTextDocumentParams; +import org.eclipse.lsp4j.DidCloseTextDocumentParams; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentItem; +import org.eclipse.lsp4jakarta.ls.java.JakartaTextDocuments.JakartaTextDocument; + +/** + * A manager for simple text documents + */ +public class TextDocuments { + + private final Map documents; + + private boolean incremental = true; // default on + + public TextDocuments() { + documents = new HashMap<>(); + } + + /** + * Set the incremental support. + * + * @param incremental + */ + public void setIncremental(boolean incremental) { + this.incremental = incremental; + synchronized (documents) { + documents.values().forEach(document -> document.setIncremental(incremental)); + } + } + + /** + * Returns true if text document is managed in incremental mode and false + * otherwise. + * + * @return true if text document is managed in incremental mode and false + * otherwise. + */ + public boolean isIncremental() { + return incremental; + } + + /** + * Returns the document for the given URI. Returns undefined if the document is + * not mananged by this instance. + * + * @param uri The text document's URI to retrieve. + * @return the text document or `undefined`. + */ + public T get(String uri) { + synchronized (documents) { + return documents.get(uri); + } + } + + public T createDocument(TextDocumentItem document) { + TextDocument doc = new TextDocument(document); + doc.setIncremental(isIncremental()); + return (T) doc; + } + + public T onDidChangeTextDocument(DidChangeTextDocumentParams params) { + synchronized (documents) { + T document = getDocument(params.getTextDocument()); + if (document != null) { + document.setVersion(params.getTextDocument().getVersion()); + document.update(params.getContentChanges()); + return document; + } + } + return null; + } + + public T onDidOpenTextDocument(DidOpenTextDocumentParams params) { + TextDocumentItem item = params.getTextDocument(); + synchronized (documents) { + T document = createDocument(item); + documents.put(document.getUri(), document); + return document; + } + } + + public T onDidCloseTextDocument(DidCloseTextDocumentParams params) { + synchronized (documents) { + T document = getDocument(params.getTextDocument()); + if (document != null) { + documents.remove(params.getTextDocument().getUri()); + } + return document; + } + } + + private T getDocument(TextDocumentIdentifier identifier) { + return documents.get(identifier.getUri()); + } + + /** + * Returns the all opened documents. + * + * @return the all opened documents. + */ + public Collection all() { + synchronized (documents) { + return documents.values(); + } + } + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TreeLineTracker.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TreeLineTracker.java new file mode 100644 index 00000000..61a51114 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/TreeLineTracker.java @@ -0,0 +1,1448 @@ +/******************************************************************************* + * Copyright (c) 2005, 2015 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4jakarta.ls.commons.ListLineTracker.DelimiterInfo; + +/** + * Abstract implementation of ILineTracker. It lets the definition + * of line delimiters to subclasses. Assuming that '\n' is the only line + * delimiter, this abstract implementation defines the following line scheme: + *
      + *
    • "" -> [0,0] + *
    • "a" -> [0,1] + *
    • "\n" -> [0,1], [1,0] + *
    • "a\n" -> [0,2], [2,0] + *
    • "a\nb" -> [0,2], [2,1] + *
    • "a\nbc\n" -> [0,2], [2,3], [5,0] + *
    + *

    + * This class must be subclassed. + *

    + *

    + * Performance: The query operations perform in O(log n) + * where n is the number of lines in the document. The modification + * operations roughly perform in O(l * log n) where n is the + * number of lines in the document and l is the sum of the number of + * removed, added or modified lines. + *

    + * + * @since 3.2 + */ +public class TreeLineTracker implements ILineTracker { + + /** The predefined delimiters of this tracker */ + public final static String[] DELIMITERS = { "\r", "\n", "\r\n" }; //$NON-NLS-3$ //$NON-NLS-1$ //$NON-NLS-2$ + /** A predefined delimiter information which is always reused as return value */ + private DelimiterInfo fDelimiterInfo = new DelimiterInfo(); + + /* + * Differential Balanced Binary Tree + * + * Assumption: lines cannot overlap => there exists a total ordering of the + * lines by their offset, which is the same as the ordering by line number + * + * Base idea: store lines in a binary search tree - the key is the line number / + * line offset -> lookup_line is O(log n) -> lookup_offset is O(log n) - a + * change in a line somewhere will change any succeeding line numbers / line + * offsets -> replace is O(n) + * + * Differential tree: instead of storing the key (line number, line offset) + * directly, every node stores the difference between its key and its parent's + * key - the sort key is still the line number / line offset, but it remains + * "virtual" - inserting a node (a line) really increases the virtual key of all + * succeeding nodes (lines), but this fact will not be realized in the key + * information encoded in the nodes. -> any change only affects the nodes in the + * node's parent chain, although more bookkeeping has to be done when changing a + * node or balancing the tree -> replace is O(log n) -> line offsets and line + * numbers have to be computed when walking the tree from the root / from a node + * -> still O(log n) + * + * The balancing algorithm chosen does not depend on the differential tree + * property. An AVL tree implementation has been chosen for simplicity. + */ + + /* + * Turns assertions on/off. Don't make this a a debug option for performance + * reasons - this way the compiler can optimize the asserts away. + */ + private static final boolean ASSERT = false; + + /** + * The empty delimiter of the last line. The last line and only the last line + * must have this zero-length delimiter. + */ + private static final String NO_DELIM = ""; //$NON-NLS-1$ + + /** + * A node represents one line. Its character and line offsets are 0-based and + * relative to the subtree covered by the node. All nodes under the left subtree + * represent lines before, all nodes under the right subtree lines after the + * current node. + */ + private static final class Node { + Node(int length, String delimiter) { + this.length = length; + this.delimiter = delimiter; + } + + /** + * The line index in this node's line tree, or equivalently, the number of lines + * in the left subtree. + */ + int line; + /** + * The line offset in this node's line tree, or equivalently, the number of + * characters in the left subtree. + */ + int offset; + /** The number of characters in this line. */ + int length; + /** The line delimiter of this line, needed to answer the delimiter query. */ + String delimiter; + /** The parent node, null if this is the root node. */ + Node parent; + /** The left subtree, possibly null. */ + Node left; + /** The right subtree, possibly null. */ + Node right; + /** The balance factor. */ + byte balance; + + @Override + public final String toString() { + String bal; + switch (balance) { + case 0: + bal = "="; //$NON-NLS-1$ + break; + case 1: + bal = "+"; //$NON-NLS-1$ + break; + case 2: + bal = "++"; //$NON-NLS-1$ + break; + case -1: + bal = "-"; //$NON-NLS-1$ + break; + case -2: + bal = "--"; //$NON-NLS-1$ + break; + default: + bal = Byte.toString(balance); + } + return "[" + offset + "+" + pureLength() + "+" + delimiter.length() + "|" + line + "|" + bal + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + } + + /** + * Returns the pure (without the line delimiter) length of this line. + * + * @return the pure line length + */ + int pureLength() { + return length - delimiter.length(); + } + } + + /** + * The root node of the tree, never null. + */ + private Node fRoot = new Node(0, NO_DELIM); + + /** + * Creates a new line tracker. + */ + protected TreeLineTracker() {} + + /** + * Package visible constructor for creating a tree tracker from a list tracker. + * + * @param tracker the list line tracker + */ + public TreeLineTracker(ListLineTracker tracker) { + final List lines = tracker.getLines(); + final int n = lines.size(); + if (n == 0) + return; + + Line line = lines.get(0); + String delim = line.delimiter; + if (delim == null) + delim = NO_DELIM; + int length = line.length; + fRoot = new Node(length, delim); + Node node = fRoot; + + for (int i = 1; i < n; i++) { + line = lines.get(i); + delim = line.delimiter; + if (delim == null) + delim = NO_DELIM; + length = line.length; + node = insertAfter(node, length, delim); + } + + if (node.delimiter != NO_DELIM) + insertAfter(node, 0, NO_DELIM); + + if (ASSERT) + checkTree(); + } + + /** + * Returns the node (line) including a certain offset. If the offset is between + * two lines, the line starting at offset is returned. + *

    + * This means that for offsets smaller than the length, the following holds: + *

    + *

    + * line.offset <= offset < line.offset + offset.length. + *

    + *

    + * If offset is the document length, then this is true: + *

    + *

    + * offset= line.offset + line.length. + *

    + * + * @param offset a document offset + * @return the line starting at or containing offset + * @throws BadLocationException if the offset is invalid + */ + private Node nodeByOffset(final int offset) throws BadLocationException { + /* + * Works for any binary search tree. + */ + int remaining = offset; + Node node = fRoot; + while (true) { + if (node == null) + fail(offset); + + if (remaining < node.offset) { + node = node.left; + } else { + remaining -= node.offset; + if (remaining < node.length || remaining == node.length && node.right == null) { // last line + break; + } + remaining -= node.length; + node = node.right; + } + } + + return node; + } + + /** + * Returns the line number for the given offset. If the offset is between two + * lines, the line starting at offset is returned. The last line is + * returned if offset is equal to the document length. + * + * @param offset a document offset + * @return the line number starting at or containing offset + * @throws BadLocationException if the offset is invalid + */ + private int lineByOffset(final int offset) throws BadLocationException { + /* + * Works for any binary search tree. + */ + int remaining = offset; + Node node = fRoot; + int line = 0; + + while (true) { + if (node == null) + fail(offset); + + if (remaining < node.offset) { + node = node.left; + } else { + remaining -= node.offset; + line += node.line; + if (remaining < node.length || remaining == node.length && node.right == null) // last line + return line; + + remaining -= node.length; + line++; + node = node.right; + } + } + } + + /** + * Returns the node (line) with the given line number. Note that the last line + * is always incomplete, i.e. has the {@link #NO_DELIM} delimiter. + * + * @param line a line number + * @return the line with the given line number + * @throws BadLocationException if the line is invalid + */ + private Node nodeByLine(final int line) throws BadLocationException { + /* + * Works for any binary search tree. + */ + int remaining = line; + Node node = fRoot; + + while (true) { + if (node == null) + fail(line); + + if (remaining == node.line) + break; + if (remaining < node.line) { + node = node.left; + } else { + remaining -= node.line + 1; + node = node.right; + } + } + + return node; + } + + /** + * Returns the offset for the given line number. Note that the last line is + * always incomplete, i.e. has the {@link #NO_DELIM} delimiter. + * + * @param line a line number + * @return the line offset with the given line number + * @throws BadLocationException if the line is invalid + */ + private int offsetByLine(final int line) throws BadLocationException { + /* + * Works for any binary search tree. + */ + int remaining = line; + int offset = 0; + Node node = fRoot; + + while (true) { + if (node == null) + fail(line); + + if (remaining == node.line) + return offset + node.offset; + + if (remaining < node.line) { + node = node.left; + } else { + remaining -= node.line + 1; + offset += node.offset + node.length; + node = node.right; + } + } + } + + /** + * Left rotation - the given node is rotated down, its right child is rotated + * up, taking the previous structural position of node. + * + * @param node the node to rotate around + */ + private void rotateLeft(Node node) { + // if (ASSERT) Assert.isNotNull(node); + Node child = node.right; + // if (ASSERT) Assert.isNotNull(child); + boolean leftChild = node.parent == null || node == node.parent.left; + + // restructure + setChild(node.parent, child, leftChild); + + setChild(node, child.left, false); + setChild(child, node, true); + + // update relative info + // child becomes the new parent, its line and offset counts increase as the + // former parent + // moves under child's left subtree + child.line += node.line + 1; + child.offset += node.offset + node.length; + } + + /** + * Right rotation - the given node is rotated down, its left child is rotated + * up, taking the previous structural position of node. + * + * @param node the node to rotate around + */ + private void rotateRight(Node node) { + // if (ASSERT) Assert.isNotNull(node); + Node child = node.left; + // if (ASSERT) Assert.isNotNull(child); + boolean leftChild = node.parent == null || node == node.parent.left; + + setChild(node.parent, child, leftChild); + + setChild(node, child.right, true); + setChild(child, node, false); + + // update relative info + // node loses its left subtree, except for what it keeps in its new subtree + // this is exactly the amount in child + node.line -= child.line + 1; + node.offset -= child.offset + child.length; + } + + /** + * Helper method for moving a child, ensuring that parent pointers are set + * correctly. + * + * @param parent the new parent of child, null to + * replace the root node + * @param child the new child of parent, may be + * null + * @param isLeftChild true if child shall become + * parent's left child, false if it + * shall become parent's right child + */ + private void setChild(Node parent, Node child, boolean isLeftChild) { + if (parent == null) { + if (child == null) + fRoot = new Node(0, NO_DELIM); + else + fRoot = child; + } else { + if (isLeftChild) + parent.left = child; + else + parent.right = child; + } + if (child != null) + child.parent = parent; + } + + /** + * A left rotation around parent, whose structural position is + * replaced by node. + * + * @param node the node moving up and left + * @param parent the node moving left and down + */ + private void singleLeftRotation(Node node, Node parent) { + rotateLeft(parent); + node.balance = 0; + parent.balance = 0; + } + + /** + * A right rotation around parent, whose structural position is + * replaced by node. + * + * @param node the node moving up and right + * @param parent the node moving right and down + */ + private void singleRightRotation(Node node, Node parent) { + rotateRight(parent); + node.balance = 0; + parent.balance = 0; + } + + /** + * A double left rotation, first rotating right around node, then + * left around parent. + * + * @param node the node that will be rotated right + * @param parent the node moving left and down + */ + private void rightLeftRotation(Node node, Node parent) { + Node child = node.left; + rotateRight(node); + rotateLeft(parent); + if (child.balance == 1) { + node.balance = 0; + parent.balance = -1; + child.balance = 0; + } else if (child.balance == 0) { + node.balance = 0; + parent.balance = 0; + } else if (child.balance == -1) { + node.balance = 1; + parent.balance = 0; + child.balance = 0; + } + } + + /** + * A double right rotation, first rotating left around node, then + * right around parent. + * + * @param node the node that will be rotated left + * @param parent the node moving right and down + */ + private void leftRightRotation(Node node, Node parent) { + Node child = node.right; + rotateLeft(node); + rotateRight(parent); + if (child.balance == -1) { + node.balance = 0; + parent.balance = 1; + child.balance = 0; + } else if (child.balance == 0) { + node.balance = 0; + parent.balance = 0; + } else if (child.balance == 1) { + node.balance = -1; + parent.balance = 0; + child.balance = 0; + } + } + + /** + * Inserts a line with the given length and delimiter after node. + * + * @param node the predecessor of the inserted node + * @param length the line length of the inserted node + * @param delimiter the delimiter of the inserted node + * @return the inserted node + */ + private Node insertAfter(Node node, int length, String delimiter) { + /* + * An insertion really shifts the key of all succeeding nodes. Hence we insert + * the added node between node and the successor of node. The added node becomes + * either the right child of the predecessor node, or the left child of the + * successor node. + */ + Node added = new Node(length, delimiter); + + if (node.right == null) + setChild(node, added, false); + else + setChild(successorDown(node.right), added, true); + + // parent chain update + updateParentChain(added, length, 1); + updateParentBalanceAfterInsertion(added); + + return added; + } + + /** + * Updates the balance information in the parent chain of node until it reaches + * the root or finds a node whose balance violates the AVL constraint, which is + * the re-balanced. + * + * @param node the child of the first node that needs balance updating + */ + private void updateParentBalanceAfterInsertion(Node node) { + Node parent = node.parent; + while (parent != null) { + if (node == parent.left) + parent.balance--; + else + parent.balance++; + + switch (parent.balance) { + case 1: + case -1: + node = parent; + parent = node.parent; + continue; + case -2: + rebalanceAfterInsertionLeft(node); + break; + case 2: + rebalanceAfterInsertionRight(node); + break; + case 0: + break; + default: + // if (ASSERT) + // Assert.isTrue(false); + } + return; + } + } + + /** + * Re-balances a node whose parent has a double positive balance. + * + * @param node the node to re-balance + */ + private void rebalanceAfterInsertionRight(Node node) { + Node parent = node.parent; + if (node.balance == 1) { + singleLeftRotation(node, parent); + } else if (node.balance == -1) { + rightLeftRotation(node, parent); + } else if (ASSERT) { + // Assert.isTrue(false); + } + } + + /** + * Re-balances a node whose parent has a double negative balance. + * + * @param node the node to re-balance + */ + private void rebalanceAfterInsertionLeft(Node node) { + Node parent = node.parent; + if (node.balance == -1) { + singleRightRotation(node, parent); + } else if (node.balance == 1) { + leftRightRotation(node, parent); + } else if (ASSERT) { + // Assert.isTrue(false); + } + } + + @Override + public final void replace(int offset, int length, String text) throws BadLocationException { + if (ASSERT) + checkTree(); + + // Inlined nodeByOffset as we need both node and offset + int remaining = offset; + Node first = fRoot; + final int firstNodeOffset; + + while (true) { + if (first == null) + fail(offset); + + if (remaining < first.offset) { + first = first.left; + } else { + remaining -= first.offset; + if (remaining < first.length || remaining == first.length && first.right == null) { // last line + firstNodeOffset = offset - remaining; + break; + } + remaining -= first.length; + first = first.right; + } + } + // Inline nodeByOffset end + // if (ASSERT) Assert.isTrue(first != null); + + Node last; + if (offset + length < firstNodeOffset + first.length) + last = first; + else + last = nodeByOffset(offset + length); + // if (ASSERT) Assert.isTrue(last != null); + + int firstLineDelta = firstNodeOffset + first.length - offset; + if (first == last) + replaceInternal(first, text, length, firstLineDelta); + else + replaceFromTo(first, last, text, length, firstLineDelta); + + // if (ASSERT) checkTree(); + } + + /** + * Replace happening inside a single line. + * + * @param node the affected node + * @param text the added text + * @param length the replace length, < firstLineDelta + * @param firstLineDelta the number of characters from the replacement offset to + * the end of node > length + */ + private void replaceInternal(Node node, String text, int length, int firstLineDelta) { + // 1) modification on a single line + + DelimiterInfo info = text == null ? null : nextDelimiterInfo(text, 0); + + if (info == null || info.delimiter == null) { + // a) trivial case: insert into a single node, no line mangling + int added = text == null ? 0 : text.length(); + updateLength(node, added - length); + } else { + // b) more lines to add between two chunks of the first node + // remember what we split off the first line + int remainder = firstLineDelta - length; + String remDelim = node.delimiter; + + // join the first line with the first added + int consumed = info.delimiterIndex + info.delimiterLength; + int delta = consumed - firstLineDelta; + updateLength(node, delta); + node.delimiter = info.delimiter; + + // Inline addlines start + info = nextDelimiterInfo(text, consumed); + while (info != null) { + int lineLen = info.delimiterIndex - consumed + info.delimiterLength; + node = insertAfter(node, lineLen, info.delimiter); + consumed += lineLen; + info = nextDelimiterInfo(text, consumed); + } + // Inline addlines end + + // add remaining chunk merged with last (incomplete) additional line + insertAfter(node, remainder + text.length() - consumed, remDelim); + } + } + + /** + * Replace spanning from one node to another. + * + * @param node the first affected node + * @param last the last affected node + * @param text the added text + * @param length the replace length, >= firstLineDelta + * @param firstLineDelta the number of characters removed from the replacement + * offset to the end of node, <= + * length + */ + private void replaceFromTo(Node node, Node last, String text, int length, int firstLineDelta) { + // 2) modification covers several lines + + // delete intermediate nodes + // TODO could be further optimized: replace intermediate lines with intermediate + // added lines + // to reduce re-balancing + Node successor = successor(node); + while (successor != last) { + length -= successor.length; + Node toDelete = successor; + successor = successor(successor); + updateLength(toDelete, -toDelete.length); + } + + DelimiterInfo info = text == null ? null : nextDelimiterInfo(text, 0); + + if (info == null || info.delimiter == null) { + int added = text == null ? 0 : text.length(); + + // join the two lines if there are no lines added + join(node, last, added - length); + + } else { + + // join the first line with the first added + int consumed = info.delimiterIndex + info.delimiterLength; + updateLength(node, consumed - firstLineDelta); + node.delimiter = info.delimiter; + length -= firstLineDelta; + + // Inline addLines start + info = nextDelimiterInfo(text, consumed); + while (info != null) { + int lineLen = info.delimiterIndex - consumed + info.delimiterLength; + node = insertAfter(node, lineLen, info.delimiter); + consumed += lineLen; + info = nextDelimiterInfo(text, consumed); + } + // Inline addLines end + + updateLength(last, text.length() - consumed - length); + } + } + + /** + * Joins two consecutive node lines, additionally adjusting the resulting length + * of the combined line by delta. The first node gets deleted. + * + * @param one the first node to join + * @param two the second node to join + * @param delta the delta to apply to the remaining single node + */ + private void join(Node one, Node two, int delta) { + int oneLength = one.length; + updateLength(one, -oneLength); + updateLength(two, oneLength + delta); + } + + /** + * Adjusts the length of a node by delta, also adjusting the parent + * chain of node. If the node's length becomes zero and is not the + * last (incomplete) node, it is deleted after the update. + * + * @param node the node to adjust + * @param delta the character delta to add to the node's length + */ + private void updateLength(Node node, int delta) { + // if (ASSERT) Assert.isTrue(node.length + delta >= 0); + + // update the node itself + node.length += delta; + + // check deletion + final int lineDelta; + boolean delete = node.length == 0 && node.delimiter != NO_DELIM; + if (delete) + lineDelta = -1; + else + lineDelta = 0; + + // update parent chain + if (delta != 0 || lineDelta != 0) + updateParentChain(node, delta, lineDelta); + + if (delete) + delete(node); + } + + /** + * Updates the differential indices following the parent chain. All nodes from + * from.parent to the root are updated. + * + * @param node the child of the first node to update + * @param deltaLength the character delta + * @param deltaLines the line delta + */ + private void updateParentChain(Node node, int deltaLength, int deltaLines) { + updateParentChain(node, null, deltaLength, deltaLines); + } + + /** + * Updates the differential indices following the parent chain. All nodes from + * from.parent to to (exclusive) are updated. + * + * @param from the child of the first node to update + * @param to the first node not to update + * @param deltaLength the character delta + * @param deltaLines the line delta + */ + private void updateParentChain(Node from, Node to, int deltaLength, int deltaLines) { + Node parent = from.parent; + while (parent != to) { + // only update node if update comes from left subtree + if (from == parent.left) { + parent.offset += deltaLength; + parent.line += deltaLines; + } + from = parent; + parent = from.parent; + } + } + + /** + * Deletes a node from the tree, re-balancing it if necessary. The differential + * indices in the node's parent chain have to be updated in advance to calling + * this method. Generally, don't call delete directly, but call + * update_length(node, -node.length) to properly remove a node. + * + * @param node the node to delete. + */ + private void delete(Node node) { +// if (ASSERT) Assert.isTrue(node != null); +// if (ASSERT) Assert.isTrue(node.length == 0); + + Node parent = node.parent; + Node toUpdate; // the parent of the node that lost a child + boolean lostLeftChild; + boolean isLeftChild = parent == null || node == parent.left; + + if (node.left == null || node.right == null) { + // 1) node has one child at max - replace parent's pointer with the only child + // also handles the trivial case of no children + Node replacement = node.left == null ? node.right : node.left; + setChild(parent, replacement, isLeftChild); + toUpdate = parent; + lostLeftChild = isLeftChild; + // no updates to do - subtrees stay as they are + } else if (node.right.left == null) { + // 2a) node's right child has no left child - replace node with right child, + // giving node's + // left subtree to the right child + Node replacement = node.right; + setChild(parent, replacement, isLeftChild); + setChild(replacement, node.left, true); + replacement.line = node.line; + replacement.offset = node.offset; + replacement.balance = node.balance; + toUpdate = replacement; + lostLeftChild = false; +// } else if (node.left.right == null) { +// // 2b) symmetric case +// Node replacement= node.left; +// set_child(parent, replacement, isLeftChild); +// set_child(replacement, node.right, false); +// replacement.balance= node.balance; +// toUpdate= replacement; +// lostLeftChild= true; + } else { + // 3) hard case - replace node with its successor + Node successor = successor(node); + + // successor exists (otherwise node would not have right child, case 1) +// if (ASSERT) Assert.isNotNull(successor); +// // successor has no left child (a left child would be the real successor of node) +// if (ASSERT) Assert.isTrue(successor.left == null); +// if (ASSERT) Assert.isTrue(successor.line == 0); +// // successor is the left child of its parent (otherwise parent would be smaller and +// // hence the real successor) +// if (ASSERT) Assert.isTrue(successor == successor.parent.left); +// // successor is not a child of node (would have been covered by 2a) +// if (ASSERT) Assert.isTrue(successor.parent != node); + + toUpdate = successor.parent; + lostLeftChild = true; + + // update relative indices + updateParentChain(successor, node, -successor.length, -1); + + // delete successor from its current place - like 1) + setChild(toUpdate, successor.right, true); + + // move node's subtrees to its successor + setChild(successor, node.right, false); + setChild(successor, node.left, true); + + // replace node by successor in its parent + setChild(parent, successor, isLeftChild); + + // update the successor + successor.line = node.line; + successor.offset = node.offset; + successor.balance = node.balance; + } + + updateParentBalanceAfterDeletion(toUpdate, lostLeftChild); + } + + /** + * Updates the balance information in the parent chain of node. + * + * @param node the first node that needs balance updating + * @param wasLeftChild true if the deletion happened on + * node's left subtree, false if + * it occurred on node's right subtree + */ + private void updateParentBalanceAfterDeletion(Node node, boolean wasLeftChild) { + while (node != null) { + if (wasLeftChild) + node.balance++; + else + node.balance--; + + Node parent = node.parent; + if (parent != null) + wasLeftChild = node == parent.left; + + switch (node.balance) { + case 1: + case -1: + return; // done, no tree change + case -2: + if (rebalanceAfterDeletionRight(node.left)) + return; + break; // propagate up + case 2: + if (rebalanceAfterDeletionLeft(node.right)) + return; + break; // propagate up + case 0: + break; // propagate up + default: +// if (ASSERT) +// Assert.isTrue(false); + } + + node = parent; + } + } + + /** + * Re-balances a node whose parent has a double positive balance. + * + * @param node the node to re-balance + * @return true if the re-balancement leaves the height at + * node.parent constant, false if the height + * changed + */ + private boolean rebalanceAfterDeletionLeft(Node node) { + Node parent = node.parent; + if (node.balance == 1) { + singleLeftRotation(node, parent); + return false; + } else if (node.balance == -1) { + rightLeftRotation(node, parent); + return false; + } else if (node.balance == 0) { + rotateLeft(parent); + node.balance = -1; + parent.balance = 1; + return true; + } else { + // if (ASSERT) Assert.isTrue(false); + return true; + } + } + + /** + * Re-balances a node whose parent has a double negative balance. + * + * @param node the node to re-balance + * @return true if the re-balancement leaves the height at + * node.parent constant, false if the height + * changed + */ + private boolean rebalanceAfterDeletionRight(Node node) { + Node parent = node.parent; + if (node.balance == -1) { + singleRightRotation(node, parent); + return false; + } else if (node.balance == 1) { + leftRightRotation(node, parent); + return false; + } else if (node.balance == 0) { + rotateRight(parent); + node.balance = 1; + parent.balance = -1; + return true; + } else { + // if (ASSERT) Assert.isTrue(false); + return true; + } + } + + /** + * Returns the successor of a node, null if node is the last node. + * + * @param node a node + * @return the successor of node, null if there is + * none + */ + private Node successor(Node node) { + if (node.right != null) + return successorDown(node.right); + + return successorUp(node); + } + + /** + * Searches the successor of node in its parent chain. + * + * @param node a node + * @return the first node in node's parent chain that is reached + * from its left subtree, null if there is none + */ + private Node successorUp(final Node node) { + Node child = node; + Node parent = child.parent; + while (parent != null) { + if (child == parent.left) + return parent; + child = parent; + parent = child.parent; + } + // if (ASSERT) Assert.isTrue(node.delimiter == NO_DELIM); + return null; + } + + /** + * Searches the left-most node in a given subtree. + * + * @param node a node + * @return the left-most node in the given subtree + */ + private Node successorDown(Node node) { + Node child = node.left; + while (child != null) { + node = child; + child = node.left; + } + return node; + } + + /* miscellaneous */ + + /** + * Throws an exception. + * + * @param offset the illegal character or line offset that caused the exception + * @throws BadLocationException always + */ + private void fail(int offset) throws BadLocationException { + throw new BadLocationException(); + } + + /** + * Returns the information about the first delimiter found in the given text + * starting at the given offset. + * + * @param text the text to be searched + * @param offset the offset in the given text + * @return the information of the first found delimiter or null + */ + protected DelimiterInfo nextDelimiterInfo(String text, int offset) { + char ch; + int length = text.length(); + for (int i = offset; i < length; i++) { + + ch = text.charAt(i); + if (ch == '\r') { + + if (i + 1 < length) { + if (text.charAt(i + 1) == '\n') { + fDelimiterInfo.delimiter = DELIMITERS[2]; + fDelimiterInfo.delimiterIndex = i; + fDelimiterInfo.delimiterLength = 2; + return fDelimiterInfo; + } + } + + fDelimiterInfo.delimiter = DELIMITERS[0]; + fDelimiterInfo.delimiterIndex = i; + fDelimiterInfo.delimiterLength = 1; + return fDelimiterInfo; + + } else if (ch == '\n') { + + fDelimiterInfo.delimiter = DELIMITERS[1]; + fDelimiterInfo.delimiterIndex = i; + fDelimiterInfo.delimiterLength = 1; + return fDelimiterInfo; + } + } + + return null; + + } + + @Override + public final String getLineDelimiter(int line) throws BadLocationException { + Node node = nodeByLine(line); + return node.delimiter == NO_DELIM ? null : node.delimiter; + } + + @Override + public final int computeNumberOfLines(String text) { + int count = 0; + int start = 0; + DelimiterInfo delimiterInfo = nextDelimiterInfo(text, start); + while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) { + ++count; + start = delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength; + delimiterInfo = nextDelimiterInfo(text, start); + } + return count; + } + + @Override + public final int getNumberOfLines() { + // TODO track separately? + Node node = fRoot; + int lines = 0; + while (node != null) { + lines += node.line + 1; + node = node.right; + } + return lines; + } + + @Override + public final int getNumberOfLines(int offset, int length) throws BadLocationException { + if (length == 0) + return 1; + + int startLine = lineByOffset(offset); + int endLine = lineByOffset(offset + length); + + return endLine - startLine + 1; + } + + @Override + public final int getLineOffset(int line) throws BadLocationException { + return offsetByLine(line); + } + + @Override + public final int getLineLength(int line) throws BadLocationException { + Node node = nodeByLine(line); + return node.length; + } + + @Override + public final int getLineNumberOfOffset(int offset) throws BadLocationException { + return lineByOffset(offset); + } + + public final Position getPositionAt(int offset) throws BadLocationException { + Line l = getLineInformationOfOffset(offset); + int lineNumber = getLineNumberOfOffset(offset); + int character = offset - l.offset; + return new Position(lineNumber, character); + } + + @Override + public int getOffsetAt(Position position) throws BadLocationException { + int line = position.getLine(); + Line l = getLineInformation(line); + + if (line < 0/* || line > lines */) + throw new BadLocationException("The line value, {" + line + "}, is out of bounds."); + + int lineOffset = l.offset; + int lineLength = l.delimiter != null ? l.length - l.delimiter.length() : l.length; + +// int lineOffset = -1; +// int lineLength = -1; +// if (lines == 0) { +// lineOffset = 0; +// lineLength = 0; +// } else { +// if (line == lines) { +// Line l = fLines.get(line - 1); +// lineOffset = l.offset + l.length; +// lineLength = 0; +// } else { +// Line l = fLines.get(line); +// lineOffset = l.offset; +// lineLength = l.delimiter != null ? l.length - l.delimiter.length() : l.length; +// } +// } + int character = position.getCharacter(); + int offset = lineOffset + character; + int endLineOffset = lineOffset + lineLength; + if (offset > endLineOffset) + throw new BadLocationException("The character value, {" + character + "} of the line {" + line + "}, is out of bounds. Line length is {" + lineLength + "}"); + return offset; + + } + + @Override + public final Line getLineInformationOfOffset(final int offset) throws BadLocationException { + // Inline nodeByOffset start as we need both node and offset + int remaining = offset; + Node node = fRoot; + final int lineOffset; + + while (true) { + if (node == null) + fail(offset); + + if (remaining < node.offset) { + node = node.left; + } else { + remaining -= node.offset; + if (remaining < node.length || remaining == node.length && node.right == null) { // last line + lineOffset = offset - remaining; + break; + } + remaining -= node.length; + node = node.right; + } + } + // Inline nodeByOffset end + return new Line(lineOffset, node.pureLength()); + } + + @Override + public final Line getLineInformation(int line) throws BadLocationException { + try { + // Inline nodeByLine start + int remaining = line; + int offset = 0; + Node node = fRoot; + + while (true) { + if (node == null) + fail(line); + + if (remaining == node.line) { + offset += node.offset; + break; + } + if (remaining < node.line) { + node = node.left; + } else { + remaining -= node.line + 1; + offset += node.offset + node.length; + node = node.right; + } + } + // Inline nodeByLine end + return new Line(offset, node.pureLength()); + } catch (BadLocationException x) { + /* + * FIXME: this really strange behavior is mandated by the previous line tracker + * implementation and included here for compatibility. See + * LineTrackerTest3#testFunnyLastLineCompatibility(). + */ + if (line > 0 && line == getNumberOfLines()) { + line = line - 1; + // Inline nodeByLine start + int remaining = line; + int offset = 0; + Node node = fRoot; + + while (true) { + if (node == null) + fail(line); + + if (remaining == node.line) { + offset += node.offset; + break; + } + if (remaining < node.line) { + node = node.left; + } else { + remaining -= node.line + 1; + offset += node.offset + node.length; + node = node.right; + } + } + Node last = node; + // Inline nodeByLine end + if (last.length > 0) + return new Line(offset + last.length, 0); + } + throw x; + } + } + + @Override + public final void set(String text) { + fRoot = new Node(0, NO_DELIM); + try { + replace(0, 0, text); + } catch (BadLocationException x) { + throw new InternalError(); + } + } + + @Override + public String toString() { + int depth = computeDepth(fRoot); + int WIDTH = 30; + int leaves = (int) Math.pow(2, depth - 1); + int width = WIDTH * leaves; + String empty = "."; //$NON-NLS-1$ + + List roots = new LinkedList<>(); + roots.add(fRoot); + StringBuilder buf = new StringBuilder((width + 1) * depth); // see Bug 137688 + int indents = leaves; + char[] space = new char[leaves * WIDTH / 2]; + Arrays.fill(space, ' '); + for (int d = 0; d < depth; d++) { + // compute indent + indents /= 2; + int spaces = Math.max(0, indents * WIDTH - WIDTH / 2); + // print nodes + for (ListIterator it = roots.listIterator(); it.hasNext();) { + // pad before + buf.append(space, 0, spaces); + + Node node = it.next(); + String box; + // replace the node with its children + if (node == null) { + it.add(null); + box = empty; + } else { + it.set(node.left); + it.add(node.right); + box = node.toString(); + } + + // draw the node, pad to WIDTH + int pad_left = (WIDTH - box.length() + 1) / 2; + int pad_right = WIDTH - box.length() - pad_left; + buf.append(space, 0, pad_left); + buf.append(box); + buf.append(space, 0, pad_right); + + // pad after + buf.append(space, 0, spaces); + } + + buf.append('\n'); + } + + return buf.toString(); + } + + /** + * Recursively computes the depth of the tree. Only used by {@link #toString()}. + * + * @param root the subtree to compute the depth of, may be null + * @return the depth of the given tree, 0 if it is null + */ + private byte computeDepth(Node root) { + if (root == null) + return 0; + + return (byte) (Math.max(computeDepth(root.left), computeDepth(root.right)) + 1); + } + + /** + * Debug-only method that checks the tree structure and the differential + * offsets. + */ + private void checkTree() { + checkTreeStructure(fRoot); + + try { + checkTreeOffsets(nodeByOffset(0), new int[] { 0, 0 }, null); + } catch (BadLocationException x) { + throw new AssertionError(); + } + } + + /** + * Debug-only method that validates the tree structure below node. + * I.e. it checks whether all parent/child pointers are consistent and whether + * the AVL balance information is correct. + * + * @param node the node to validate + * @return the depth of the tree under node + */ + private byte checkTreeStructure(Node node) { + if (node == null) + return 0; + + byte leftDepth = checkTreeStructure(node.left); + byte rightDepth = checkTreeStructure(node.right); +// Assert.isTrue(node.balance == rightDepth - leftDepth); +// Assert.isTrue(node.left == null || node.left.parent == node); +// Assert.isTrue(node.right == null || node.right.parent == node); + + return (byte) (Math.max(rightDepth, leftDepth) + 1); + } + + /** + * Debug-only method that checks the differential offsets of the tree, starting + * at node and continuing until last. + * + * @param node the first Node to check, may be null + * @param offLen an array of length 2, with offLen[0] the expected + * offset of node and offLen[1] the + * expected line of node + * @param last the last Node to check, may be null + * @return an int[] of length 2, with the first element being the + * character length of node's subtree, and the second + * element the number of lines in node's subtree + */ + private int[] checkTreeOffsets(Node node, int[] offLen, Node last) { + if (node == last) + return offLen; + + // Assert.isTrue(node.offset == offLen[0]); + // Assert.isTrue(node.line == offLen[1]); + + if (node.right != null) { + int[] result = checkTreeOffsets(successorDown(node.right), new int[2], node); + offLen[0] += result[0]; + offLen[1] += result[1]; + } + + offLen[0] += node.length; + offLen[1]++; + return checkTreeOffsets(node.parent, offLen, last); + } +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ValidatorDelayer.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ValidatorDelayer.java new file mode 100644 index 00000000..cdae933c --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/ValidatorDelayer.java @@ -0,0 +1,97 @@ +/******************************************************************************* +* Copyright (c) 2022 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Validate a given document with delay. + * + * @param + */ +public class ValidatorDelayer { + + public static final long DEFAULT_VALIDATION_DELAY_MS = 500; + + private final ScheduledExecutorService executorService; + + private final Consumer validator; + + private final Map> pendingValidationRequests; + + private final long validationDelayMs; + + public ValidatorDelayer(Consumer validator) { + this(Executors.newScheduledThreadPool(2), validator, DEFAULT_VALIDATION_DELAY_MS); + } + + public ValidatorDelayer(ScheduledExecutorService executorService, Consumer validator, long validationDelayMs) { + this.executorService = executorService; + this.validator = validator; + this.pendingValidationRequests = new HashMap<>(); + this.validationDelayMs = validationDelayMs; + } + + /** + * Validate the given model document identified by the given uri with a delay. + * + * @param uri the document URI. + * @param document the document model to validate. + */ + public void validateWithDelay(T document) { + String uri = document.getUri(); + cleanPendingValidation(uri); + int version = document.getVersion(); + Future request = executorService.schedule(() -> { + synchronized (pendingValidationRequests) { + pendingValidationRequests.remove(uri); + } + if (version == document.getVersion()) { + validator.accept(document); + } + }, validationDelayMs, TimeUnit.MILLISECONDS); + synchronized (pendingValidationRequests) { + pendingValidationRequests.put(uri, request); + } + } + + public void cleanPendingValidation(String uri) { + synchronized (pendingValidationRequests) { + Future request = pendingValidationRequests.get(uri); + if (request != null) { + request.cancel(true); + pendingValidationRequests.remove(uri); + } + } + } + + /** + * Returns true if the document has a revalidation pending and false otherwise. + * + * @param uri the uri of the document to check + * + * @return true if the document has a revalidation pending and false otherwise + */ + public boolean isRevalidating(String uri) { + synchronized (pendingValidationRequests) { + return pendingValidationRequests.containsKey(uri); + } + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/ISnippetContext.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISnippetContext.java similarity index 71% rename from jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/ISnippetContext.java rename to jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISnippetContext.java index 7ec5099c..c970be20 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/ISnippetContext.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISnippetContext.java @@ -11,18 +11,17 @@ * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ - -package org.eclipse.lsp4jakarta.commons.snippets; +package org.eclipse.lsp4jakarta.ls.commons.snippets; /** - * Snippet context used to filter the snippet - * Reused from https://github.com/eclipse/lsp4mp/blob/master/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/ls/commons/snippets/ISnippetContext.java - * - * @param the value type waited by the snipper - * @author Ankush Sharma, credit to Angelo ZERR + * Snippet context used to filter the snippet. + * + * @author Angelo ZERR + * + * @param the value type waited by the snippet context. */ - public interface ISnippetContext { + /** * Return true if the given value match the snippet context and false otherwise. * diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/ISnippetRegistryLoader.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISnippetRegistryLoader.java similarity index 69% rename from jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/ISnippetRegistryLoader.java rename to jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISnippetRegistryLoader.java index 5b277c17..c489474a 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/ISnippetRegistryLoader.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISnippetRegistryLoader.java @@ -12,29 +12,30 @@ * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.commons.snippets; +package org.eclipse.lsp4jakarta.ls.commons.snippets; /** * Loader used to load snippets in a given registry for a language id - * Modified from https://github.com/eclipse/lsp4mp/blob/master/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/ls/commons/snippets/ISnippetRegistryLoader.java - * + * Modified from + * https://github.com/eclipse/lsp4mp/blob/master/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/ls/commons/snippets/ISnippetRegistryLoader.java + * * @author Ankush Sharma, credit to Angelo ZERR */ public interface ISnippetRegistryLoader { /** * Register a given snippet in the register - * + * * @param registry * @throws Exception */ void load(SnippetRegistry registry) throws Exception; - - /** - * Returns the language id and null otherwise. - * - * @return the language id and null otherwise. - */ - default String getLanguageId() { - return null; - } + + /** + * Returns the language id and null otherwise. + * + * @return the language id and null otherwise. + */ + default String getLanguageId() { + return null; + } } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISuffixPositionProvider.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISuffixPositionProvider.java new file mode 100644 index 00000000..52220617 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/ISuffixPositionProvider.java @@ -0,0 +1,35 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons.snippets; + +import org.eclipse.lsp4j.Position; + +/** + * Suffix position provider API. + * + * @author Angelo ZERR + * + */ +public interface ISuffixPositionProvider { + + /** + * Returns the suffix position provider of the given sufix and null + * otherwise. + * + * @param suffix + * @return the suffix position provider of the given sufix and null + * otherwise. + */ + Position findSuffixPosition(String suffix); +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/Snippet.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/Snippet.java similarity index 66% rename from jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/Snippet.java rename to jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/Snippet.java index 7ae94742..639c47a5 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/Snippet.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/Snippet.java @@ -12,23 +12,44 @@ * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.commons.snippets; +package org.eclipse.lsp4jakarta.ls.commons.snippets; import java.util.List; -import java.util.function.Predicate; +import java.util.Map; +import java.util.function.BiPredicate; /** - * Reused from https://github.com/eclipse/lsp4mp/blob/master/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/ls/commons/snippets/Snippet.java - * @author Ankush Sharma, credit to Angelo ZERR + * Snippet description (like vscode snippet). + * + * @author Angelo ZERR * */ public class Snippet { + + private String label; + private List prefixes; + + private String suffix; + private List body; + private String description; + private String scope; + + private String sortText; + private ISnippetContext context; + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + public List getPrefixes() { return prefixes; } @@ -37,6 +58,14 @@ public void setPrefixes(List prefixes) { this.prefixes = prefixes; } + public String getSuffix() { + return suffix; + } + + public void setSuffix(String suffix) { + this.suffix = suffix; + } + public List getBody() { return body; } @@ -61,6 +90,14 @@ public void setScope(String scope) { this.scope = scope; } + public String getSortText() { + return sortText; + } + + public void setSortText(String sortText) { + this.sortText = sortText; + } + public ISnippetContext getContext() { return context; } @@ -73,10 +110,12 @@ public boolean hasContext() { return getContext() != null; } - public boolean match(Predicate> contextFilter) { + public boolean match(BiPredicate, Map> contextFilter, + Map model) { if (!hasContext()) { return true; } - return contextFilter.test(getContext()); + return contextFilter.test(getContext(), model); } -} + +} \ No newline at end of file diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/SnippetDeserializer.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/SnippetDeserializer.java similarity index 96% rename from jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/SnippetDeserializer.java rename to jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/SnippetDeserializer.java index 7945cd8f..a51c5b0c 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/commons/snippets/SnippetDeserializer.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/SnippetDeserializer.java @@ -12,7 +12,7 @@ * Red Hat Inc. - initial API and implementation *******************************************************************************/ -package org.eclipse.lsp4jakarta.commons.snippets; +package org.eclipse.lsp4jakarta.ls.commons.snippets; import java.lang.reflect.Type; @@ -29,6 +29,7 @@ /** * Reused from https://github.com/eclipse/lsp4mp/blob/master/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/ls/commons/snippets/SnippetDeserializer.java + * * @author Ankush Sharma, credit to Angelo ZERR * */ @@ -46,8 +47,7 @@ public SnippetDeserializer(TypeAdapter> contextDese } @Override - public Snippet deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { + public Snippet deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { Snippet snippet = new Snippet(); JsonObject snippetObj = json.getAsJsonObject(); diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/SnippetRegistry.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/SnippetRegistry.java new file mode 100644 index 00000000..50b2c250 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/SnippetRegistry.java @@ -0,0 +1,395 @@ +/******************************************************************************* +* Copyright (c) 2020, 2022 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ + +package org.eclipse.lsp4jakarta.ls.commons.snippets; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.ServiceLoader; +import java.util.function.BiPredicate; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.CompletionItemKind; +import org.eclipse.lsp4j.InsertTextFormat; +import org.eclipse.lsp4j.MarkupContent; +import org.eclipse.lsp4j.MarkupKind; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4jakarta.commons.utils.StringUtils; + +import com.google.gson.GsonBuilder; +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; + +/** + * A registry for snippets which uses the same format than vscode snippet. + * + * @author Angelo ZERR + * + */ +public class SnippetRegistry { + + private static final Logger LOGGER = Logger.getLogger(SnippetRegistry.class.getName()); + + private final List snippets; + + public SnippetRegistry() { + this(null); + } + + public SnippetRegistry(String languageId) { + this(languageId, true); + } + + /** + * Snippet registry for a given language id. + * + * @param languageId the language id and null otherwise. + * @param loadDefault true if default snippets from SPI must be loaded and false + * otherwise. + */ + public SnippetRegistry(String languageId, boolean loadDefault) { + snippets = new ArrayList<>(); + // Load snippets from SPI + if (loadDefault) { + ServiceLoader loaders = ServiceLoader.load(ISnippetRegistryLoader.class); + loaders.forEach(loader -> { + if (Objects.equals(languageId, loader.getLanguageId())) { + try { + loader.load(this); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while consumming snippet loader " + loader.getClass().getName(), + e); + } + } + }); + } + } + + /** + * Register the given snippet. + * + * @param snippet the snippet to register. + */ + public void registerSnippet(Snippet snippet) { + snippets.add(snippet); + } + + /** + * Register the snippets from the given JSON input stream. + * + * @param in the JSON input stream which declares snippets with vscode snippet + * format. + * @throws IOException + */ + public void registerSnippets(InputStream in) throws IOException { + registerSnippets(in, null, null); + } + + /** + * Register the snippets from the given JSON stream with a context. + * + * @param in the JSON input stream which declares snippets with + * vscode snippet format. + * @param contextDeserializer the GSON context deserializer used to create Java + * context. + * @throws IOException + */ + public void registerSnippets(InputStream in, TypeAdapter> contextDeserializer) throws IOException { + registerSnippets(in, null, contextDeserializer); + } + + /** + * Register the snippets from the given JSON stream with a context. + * + * @param in the JSON input stream which declares snippets with + * vscode snippet format. + * @param defaultContext the default context. + * @throws IOException + */ + public void registerSnippets(InputStream in, ISnippetContext defaultContext) throws IOException { + registerSnippets(in, defaultContext, null); + } + + /** + * Register the snippets from the given JSON stream with a context. + * + * @param in the JSON input stream which declares snippets with + * vscode snippet format. + * @param defaultContext the default context. + * @param contextDeserializer the GSON context deserializer used to create Java + * context. + * @throws IOException + */ + public void registerSnippets(InputStream in, ISnippetContext defaultContext, + TypeAdapter> contextDeserializer) throws IOException { + registerSnippets(new InputStreamReader(in, StandardCharsets.UTF_8.name()), defaultContext, contextDeserializer); + } + + /** + * Register the snippets from the given JSON reader. + * + * @param in the JSON reader which declares snippets with vscode snippet format. + * @throws IOException + */ + public void registerSnippets(Reader in) throws IOException { + registerSnippets(in, null, null); + } + + /** + * Register the snippets from the given JSON reader with a context. + * + * @param in the JSON reader which declares snippets with + * vscode snippet format. + * @param contextDeserializer the GSON context deserializer used to create Java + * context. + * @throws IOException + */ + public void registerSnippets(Reader in, TypeAdapter> contextDeserializer) throws IOException { + registerSnippets(in, null, contextDeserializer); + } + + /** + * Register the snippets from the given JSON stream with a context. + * + * @param in the JSON reader which declares snippets with vscode + * snippet format. + * @param defaultContext the default context. + * @throws IOException + */ + public void registerSnippets(Reader in, ISnippetContext defaultContext) throws IOException { + registerSnippets(in, defaultContext, null); + } + + /** + * Register the snippets from the given JSON stream with a context. + * + * @param in the JSON reader which declares snippets with + * vscode snippet format. + * @param defaultContext the default context. + * @param contextDeserializer the GSON context deserializer used to create Java + * context. + * @throws IOException + */ + public void registerSnippets(Reader in, ISnippetContext defaultContext, + TypeAdapter> contextDeserializer) throws IOException { + JsonReader reader = new JsonReader(in); + reader.beginObject(); + while (reader.hasNext()) { + String name = reader.nextName(); + Snippet snippet = createSnippet(reader, contextDeserializer); + if (snippet.getDescription() == null) { + snippet.setDescription(name); + } + if (snippet.getContext() == null) { + snippet.setContext(defaultContext); + } + registerSnippet(snippet); + } + reader.endObject(); + } + + private static Snippet createSnippet(JsonReader reader, + TypeAdapter> contextDeserializer) throws JsonIOException, JsonSyntaxException { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(Snippet.class, new SnippetDeserializer(contextDeserializer)); + return builder.create().fromJson(reader, Snippet.class); + } + + /** + * Returns all snippets. + * + * @return all snippets. + */ + public List getSnippets() { + return snippets; + } + + /** + * Returns the snippet completion items according to the context filter. + * + * @param replaceRange the replace range. + * @param lineDelimiter the line delimiter. + * @param canSupportMarkdown true if markdown is supported to generate + * documentation and false otherwise. + * @param contextFilter the context filter. + * @param initialModel the initial model. + * @return the snippet completion items according to the context filter. + */ + public List getCompletionItems(Range replaceRange, String lineDelimiter, boolean canSupportMarkdown, + boolean snippetsSupported, BiPredicate, Map> contextFilter, + Map initialModel, ISuffixPositionProvider suffixProvider) { + if (replaceRange == null) { + return Collections.emptyList(); + } + final Map model = initialModel != null ? initialModel : new HashMap<>(); + return getSnippets().stream().filter(snippet -> { + return snippet.match(contextFilter, model); + }).map(snippet -> { + CompletionItem item = new CompletionItem(); + String prefix = snippet.getPrefixes().get(0); + String label = snippet.getLabel() != null ? snippet.getLabel() : prefix; + item.setLabel(label); + String insertText = getInsertText(snippet, model, snippetsSupported, lineDelimiter); + item.setKind(CompletionItemKind.Snippet); + item.setDocumentation( + Either.forRight(createDocumentation(snippet, model, canSupportMarkdown, lineDelimiter))); + item.setFilterText(prefix); + item.setDetail(snippet.getDescription()); + Range range = replaceRange; + if (!StringUtils.isEmpty(snippet.getSuffix()) && suffixProvider != null) { + Position end = suffixProvider.findSuffixPosition(snippet.getSuffix()); + if (end != null) { + range = new Range(replaceRange.getStart(), end); + } + } + item.setTextEdit(Either.forLeft(new TextEdit(range, insertText))); + item.setInsertTextFormat(InsertTextFormat.Snippet); + item.setSortText(snippet.getSortText()); + return item; + + }).collect(Collectors.toList()); + } + + private static MarkupContent createDocumentation(Snippet snippet, Map model, + boolean canSupportMarkdown, String lineDelimiter) { + StringBuilder doc = new StringBuilder(); + if (canSupportMarkdown) { + doc.append(System.lineSeparator()); + doc.append("```"); + String scope = snippet.getScope(); + if (scope != null) { + doc.append(scope); + } + doc.append(System.lineSeparator()); + } + String insertText = getInsertText(snippet, model, false, lineDelimiter); + doc.append(insertText); + if (canSupportMarkdown) { + doc.append(System.lineSeparator()); + doc.append("```"); + doc.append(System.lineSeparator()); + } + return new MarkupContent(canSupportMarkdown ? MarkupKind.MARKDOWN : MarkupKind.PLAINTEXT, doc.toString()); + } + + private static String getInsertText(Snippet snippet, Map model, boolean keepPlaceholders, + String lineDelimiter) { + StringBuilder text = new StringBuilder(); + int i = 0; + List body = snippet.getBody(); + if (body != null) { + for (String bodyLine : body) { + if (i > 0) { + text.append(lineDelimiter); + } + replacePlaceholders(bodyLine, 0, model, keepPlaceholders, text); + i++; + } + } + return text.toString(); + } + + /** + * Replace place holders (ex : ${name}) from the given line by + * using the given context model. + * + * @param line the line which can have some place holders. + * @param offset the start offset where the replace must be occured. + * @param model the context model. + * @param keepDollarVariable true if place holder (ex : ${name}) must be kept + * (ex : ${name}) or not (ex : name) + * @param newLine the replace line buffer result. + */ + private static void replacePlaceholders(String line, int offset, Map model, + boolean keepDollarVariable, StringBuilder newLine) { + int dollarIndex = line.indexOf("$", offset); + if (dollarIndex == -1 || dollarIndex == line.length() - 1) { + newLine.append(line.substring(offset, line.length())); + return; + } + char next = line.charAt(dollarIndex + 1); + if (Character.isDigit(next)) { + // ex: line = @RegistryType(type=$1) + if (!keepDollarVariable) { + newLine.append(line.substring(offset, dollarIndex)); + } + int lastDigitOffset = dollarIndex + 1; + while (line.length() < lastDigitOffset && Character.isDigit(line.charAt(lastDigitOffset))) { + lastDigitOffset++; + } + if (keepDollarVariable) { + newLine.append(line.substring(offset, lastDigitOffset)); + } + replacePlaceholders(line, lastDigitOffset, model, keepDollarVariable, newLine); + } else if (next == '{') { + int startExpr = dollarIndex; + int endExpr = line.indexOf("}", startExpr); + if (endExpr == -1) { + // Should never occur + return; + } + newLine.append(line.substring(offset, startExpr)); + // Parameter + int startParam = startExpr + 2; + int endParam = endExpr; + boolean onlyNumber = true; + for (int i = startParam; i < endParam; i++) { + char ch = line.charAt(i); + if (!Character.isDigit(ch)) { + onlyNumber = false; + if (ch == ':') { + startParam = i + 1; + break; + } else if (ch == '|') { + startParam = i + 1; + int index = line.indexOf(',', startExpr); + if (index != -1) { + endParam = index; + } + break; + } else { + break; + } + } + } + String paramName = line.substring(startParam, endParam); + if (model.containsKey(paramName)) { + paramName = model.get(paramName); + } else if (keepDollarVariable) { + paramName = line.substring(startExpr, endExpr + 1); + } + if (!(!keepDollarVariable && onlyNumber)) { + newLine.append(paramName); + } + replacePlaceholders(line, endExpr + 1, model, keepDollarVariable, newLine); + } + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/TextDocumentSnippetRegistry.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/TextDocumentSnippetRegistry.java new file mode 100644 index 00000000..07708245 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/commons/snippets/TextDocumentSnippetRegistry.java @@ -0,0 +1,121 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.commons.snippets; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.BiPredicate; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4jakarta.ls.commons.BadLocationException; +import org.eclipse.lsp4jakarta.ls.commons.TextDocument; + +/** + * Snippet registry which works with {@link TextDocument}. + * + * @author Angelo ZERR + * + */ +public class TextDocumentSnippetRegistry extends SnippetRegistry { + + private static final Logger LOGGER = Logger.getLogger(TextDocumentSnippetRegistry.class.getName()); + + public TextDocumentSnippetRegistry() { + this(null); + } + + public TextDocumentSnippetRegistry(String languageId, boolean loadDefault) { + super(languageId, loadDefault); + } + + public TextDocumentSnippetRegistry(String languageId) { + super(languageId, true); + } + + /** + * Returns the snippet completion items for the given completion offset and + * context filter. + * + * @param document the text document. + * @param completionOffset the completion offset. + * @param canSupportMarkdown true if markdown is supported to generate + * documentation and false otherwise. + * @param contextFilter the context filter. + * @param model the context model used to replace some place + * holder. + * @return the snippet completion items for the given completion offset and + * context filter. + */ + public List getCompletionItems(TextDocument document, int completionOffset, + boolean canSupportMarkdown, boolean snippetsSupported, + BiPredicate, Map> contextFilter, Map model) { + try { + String lineDelimiter = getLineDelimiter(document, completionOffset); + Range replaceRange = getReplaceRange(document, completionOffset); + return super.getCompletionItems(replaceRange, lineDelimiter, canSupportMarkdown, snippetsSupported, + contextFilter, model, null); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Error while computing snippet completion items", e); + return Collections.emptyList(); + } + } + + private static String getLineDelimiter(TextDocument document, int completionOffset) throws BadLocationException { + int lineNumber = document.positionAt(completionOffset).getLine(); + return document.lineDelimiter(lineNumber); + } + + public Range getReplaceRange(TextDocument document, int completionOffset) throws BadLocationException { + String expr = getExpr(document, completionOffset); + if (expr == null) { + return null; + } + int startOffset = completionOffset - expr.length(); + int endOffset = completionOffset; + return getReplaceRange(startOffset, endOffset, document); + } + + protected String getExpr(TextDocument document, int completionOffset) { + return findExprBeforeAt(document.getText(), completionOffset); + } + + private Range getReplaceRange(int replaceStart, int replaceEnd, TextDocument document) throws BadLocationException { + return new Range(document.positionAt(replaceStart), document.positionAt(replaceEnd)); + } + + private static String findExprBeforeAt(String text, int offset) { + if (offset < 0 || offset > text.length()) { + return null; + } + if (offset == 0) { + return ""; + } + StringBuilder expr = new StringBuilder(); + int i = offset - 1; + for (; i >= 0; i--) { + char ch = text.charAt(i); + if (Character.isWhitespace(ch)) { + break; + } else { + expr.insert(0, ch); + } + } + return expr.toString(); + } + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/java/JakartaTextDocuments.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/java/JakartaTextDocuments.java new file mode 100644 index 00000000..21ce5ab5 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/java/JakartaTextDocuments.java @@ -0,0 +1,392 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.java; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import org.eclipse.lsp4j.TextDocumentItem; +import org.eclipse.lsp4j.jsonrpc.CancelChecker; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures.FutureCancelChecker; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfo; +import org.eclipse.lsp4jakarta.commons.JakartaJavaFileInfoParams; +import org.eclipse.lsp4jakarta.commons.JakartaJavaProjectLabelsParams; +import org.eclipse.lsp4jakarta.commons.ProjectLabelInfoEntry; +import org.eclipse.lsp4jakarta.ls.api.JakartaJavaFileInfoProvider; +import org.eclipse.lsp4jakarta.ls.api.JakartaJavaProjectLabelsProvider; +import org.eclipse.lsp4jakarta.ls.commons.TextDocument; +import org.eclipse.lsp4jakarta.ls.commons.TextDocuments; +import org.eclipse.lsp4jakarta.ls.java.JakartaTextDocuments.JakartaTextDocument; + +/** + * Java Text documents registry which manages opened Java file. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/ls/java/JavaTextDocuments.java + * + * @author Angelo ZERR + * + */ +public class JakartaTextDocuments extends TextDocuments { + + private static final String JAKARTA_PROJECT_LABEL = "jakarta"; + + private static final Logger LOGGER = Logger.getLogger(JakartaTextDocuments.class.getName()); + + private static final ProjectLabelInfoEntry PROJECT_INFO_LOADING = new ProjectLabelInfoEntry(null, null, null); + + private final Map> documentCache; + + private final Map> projectCache; + + private final JakartaJavaProjectLabelsProvider projectInfoProvider; + + private final JakartaJavaFileInfoProvider fileInfoProvider; + + private JavaTextDocumentSnippetRegistry snippetRegistry; + + private boolean hasLoadedAllProjects = false; + + /** + * Opened Java file. + * + */ + public class JakartaTextDocument extends TextDocument { + + private String projectURI; + + private String packageName; + + private CompletableFuture fileInfoFuture; + + public JakartaTextDocument(TextDocumentItem document) { + super(document); + collectFileInfo(); + } + + /** + * Collect Java file information (ex : package name) from the JDT LS side. + */ + private void collectFileInfo() { + if (fileInfoProvider != null) { + if (fileInfoFuture == null || fileInfoFuture.isCancelled() + || fileInfoFuture.isCompletedExceptionally()) { + JakartaJavaFileInfoParams params = new JakartaJavaFileInfoParams(); + params.setUri(super.getUri()); + fileInfoFuture = fileInfoProvider.getJavaFileInfo(params); + } + JakartaJavaFileInfo fileInfo = fileInfoFuture.getNow(null); + if (fileInfo != null) { + setPackageName(fileInfo.getPackageName()); + } + } + } + + public String getProjectURI() { + return projectURI; + } + + public String getPackageName() { + if (packageName == null) { + collectFileInfo(); + } + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public void setProjectURI(String projectURI) { + this.projectURI = projectURI; + } + + /** + * Execute the given code only if the Java file belongs to a Jakarta project + * without waiting for the load of project information. + * + * @param the type to return. + * @param code the code to execute. + * @param defaultValue the default value to return if the Java file doesn't + * belong to a Jakarta project. + * @return the given code only if the Java file belongs to a Jakarta + * project without waiting for the load of project information. + */ + public CompletableFuture executeIfInJakartaProject( + BiFunction> code, T defaultValue) { + return executeIfInJakartaProject(code, defaultValue, false); + } + + /** + * Execute the given code only if the Java file belongs to a Jakarta + * project. + * + * @param the type to return. + * @param code the code to execute. + * @param defaultValue the default value to return if the Java file + * doesn't belong to a Jakarta project. + * @param waitForLoadingProjectInfo true if code to apply must be done when + * project information is loaded and false + * otherwise. + * @return the given code only if the Java file belongs to a Jakarta + * project. + */ + public CompletableFuture executeIfInJakartaProject( + BiFunction> code, T defaultValue, + boolean waitForLoadingProjectInfo) { + return computeAsyncCompose(cancelChecker -> { + CompletableFuture projectInfoFuture = getProjectInfo(this); + ProjectLabelInfoEntry projectInfo = projectInfoFuture.getNow(PROJECT_INFO_LOADING); + if (isProjectInfoLoading(projectInfo)) { + // The project information is loading. + if (!waitForLoadingProjectInfo) { + // don't wait the load of the project, apply the given code. + return executeIfInJakartaProject(null, code, defaultValue, cancelChecker); + } + // Wait the load of the project and apply the given code. + return projectInfoFuture.thenCompose(loadedProjectInfo -> { + return executeIfInJakartaProject(loadedProjectInfo, code, defaultValue, cancelChecker); + }); + } + // The project information is loaded, apply the given code + return executeIfInJakartaProject(projectInfo, code, defaultValue, cancelChecker); + }); + } + + private CompletableFuture executeIfInJakartaProject(ProjectLabelInfoEntry projectInfo, + BiFunction> code, T defaultValue, + CancelChecker cancelChecker) { + cancelChecker.checkCanceled(); + if (projectInfo == null || !isJakartaProject(projectInfo)) { + return CompletableFuture.completedFuture(defaultValue); + } + return code.apply(projectInfo, cancelChecker); + } + + /** + * Returns true if the Java file belongs to a Jakarta project and false + * otherwise. + * + * @return true if the Java file belongs to a Jakarta project and false + * otherwise. + */ + public boolean isInJakartaProject() { + ProjectLabelInfoEntry projectInfo = getProjectInfo(this).getNow(null); + return isJakartaProject(projectInfo); + } + } + + public JakartaTextDocuments(JakartaJavaProjectLabelsProvider projectInfoProvider, + JakartaJavaFileInfoProvider fileInfoProvider) { + this.projectInfoProvider = projectInfoProvider; + this.fileInfoProvider = fileInfoProvider; + this.documentCache = new ConcurrentHashMap<>(); + this.projectCache = new ConcurrentHashMap<>(); + } + + @Override + public JakartaTextDocument createDocument(TextDocumentItem document) { + JakartaTextDocument doc = new JakartaTextDocument(document); + doc.setIncremental(isIncremental()); + return doc; + } + + /** + * Returns as promise the Jakarta project information for the given java + * file document. + * + * @param document the java file document. + * @return as promise the Jakarta project information for the given java + * file document. + */ + private CompletableFuture getProjectInfo(JakartaTextDocument document) { + //TODO: revert this to getProjectInfoFromCache when/if ProjectLabelInfoEntry caching is re-enabled + return getProjectInfoFromClient(document). // + exceptionally(ex -> { + LOGGER.log(Level.WARNING, String.format( + "Error while getting ProjectLabelInfoEntry (classpath) for '%s'", document.getUri()), + ex); + return null; + }); + } + + CompletableFuture getProjectInfoFromClient(JakartaTextDocument document) { + String documentURI = document.getUri(); + // Search future which load project info + CompletableFuture projectInfo = null; + + // TODO: re-enable the retrieval of ProjectLabelInfoEntry objects from the caches? + + // We are not going to cache the ProjectLabelInfoEntry for right now - currently caching + // is breaking the classpath filtering behaviour - if the pom changes to restrict the classpath, caching + // prevents us from returning to the client to re-compute the classpath contents. + + // see getProjectInfoFromCache + + JakartaJavaProjectLabelsParams params = new JakartaJavaProjectLabelsParams(); + params.setUri(documentURI); + params.setTypes(getSnippetRegistry().getTypes()); + final CompletableFuture future = projectInfoProvider.getJavaProjectLabels(params); + ProjectLabelInfoEntry entry = null; + try { + // future.get() will definitely wait but forced me to re-write the code abit + entry = future.get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.log(Level.WARNING, String.format( + "Error while getting the CompletableFuture ProjectLabelInfoEntry object from client for '%s'", document.getUri()), + e); + } + + // TODO: re-enable caching of the ProjectLabelInfoEntry objects? + + // since we have effectively disabled cache lookups, and go to the client on every completion call + // (as we were doing in the previous code base) we will also disable the addition of + // a ProjectLabelInfoEntry into the caches + + // see getProjectInfoFromCache + + return future; + + } + + CompletableFuture getProjectInfoFromCache(JakartaTextDocument document) { + String projectURI = document.getProjectURI(); + String documentURI = document.getUri(); + // Search future which load project info in cache + CompletableFuture projectInfo = null; + + if (projectURI != null) { + // the java document has already been linked to a project URI, get future from + // the project cache. + projectInfo = projectCache.get(projectURI); + } else { + // get the current future for the given document URI + projectInfo = documentCache.get(documentURI); + } + if (projectInfo == null || projectInfo.isCancelled() || projectInfo.isCompletedExceptionally()) { + // not found in the cache, load the project info from the JDT LS Extension + JakartaJavaProjectLabelsParams params = new JakartaJavaProjectLabelsParams(); + params.setUri(documentURI); + params.setTypes(getSnippetRegistry().getTypes()); + final CompletableFuture future = projectInfoProvider.getJavaProjectLabels(params); + // >> changed this section starting here + ProjectLabelInfoEntry entry = null; + try { + // future.get() will definitely wait but forced me to re-write the code abit + entry = future.get(); + } catch (InterruptedException | ExecutionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if (entry != null) { + // project info with labels are get from the JDT LS + String newProjectURI = entry.getUri(); + // cache the project info in the project cache level. + projectCache.put(newProjectURI, future); + // update the project URI of the document to link it to a project URI + document.setProjectURI(newProjectURI); + // evict the document cache level. + documentCache.remove(documentURI); + } + // cache the future in the document level. + documentCache.put(documentURI, future); + return future; + } + + // Returns the cached project info + return projectInfo; + } + + /** + * Returns a list of all projects in the current workspace as a completable + * future + * + * Loads and caches any projects that have not yet been loaded into the cache. + * + * @returns a list of all projects in the current workspace as a completable + * future + */ + public CompletableFuture> getWorkspaceProjects() { + if (!hasLoadedAllProjects) { + return projectInfoProvider.getAllJavaProjectLabels() // + .thenApply(entries -> { + if (entries != null && entries.size() > 0) { + for (ProjectLabelInfoEntry entry : entries) { + if (entry != null) { + String newProjectURI = entry.getUri(); + if (!projectCache.containsKey(newProjectURI)) { + projectCache.put(newProjectURI, CompletableFuture.completedFuture(entry)); + } + } + } + } + hasLoadedAllProjects = true; + return entries; + }); + } + + // otherwise the list of all projects is cached + Collection> projectLabelFutures = projectCache.values(); + return CompletableFuture.allOf((CompletableFuture[]) projectLabelFutures.stream().toArray(CompletableFuture[]::new)) // + .thenApply(voidFuture -> { + return projectLabelFutures.stream() // + .map(futureProject -> { + return futureProject.getNow(null); + }) // + .filter(project -> project != null) // + .distinct() // + .collect(Collectors.toList()); + }); + } + + /** + * Returns true if the given project information has the "jakarta" label + * and false otherwise. + * + * @param projectInfo the project information. + * @return true if the given project information has the "jakarta" label + * and false otherwise. + */ + private static boolean isJakartaProject(ProjectLabelInfoEntry projectInfo) { + return projectInfo != null && projectInfo.hasLabel(JAKARTA_PROJECT_LABEL); + } + + public JavaTextDocumentSnippetRegistry getSnippetRegistry() { + if (snippetRegistry == null) { + snippetRegistry = new JavaTextDocumentSnippetRegistry(); + } + return snippetRegistry; + } + + private static CompletableFuture computeAsyncCompose(Function> code) { + CompletableFuture start = new CompletableFuture<>(); + CompletableFuture result = start.thenComposeAsync(code); + start.complete(new FutureCancelChecker(result)); + return result; + } + + private static boolean isProjectInfoLoading(ProjectLabelInfoEntry projectInfo) { + return PROJECT_INFO_LOADING == projectInfo; + } +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/java/JavaTextDocumentSnippetRegistry.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/java/JavaTextDocumentSnippetRegistry.java new file mode 100644 index 00000000..c7c2ec18 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/ls/java/JavaTextDocumentSnippetRegistry.java @@ -0,0 +1,177 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.ls.java; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiPredicate; + +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4jakarta.commons.ProjectLabelInfoEntry; +import org.eclipse.lsp4jakarta.commons.utils.StringUtils; +import org.eclipse.lsp4jakarta.ls.commons.BadLocationException; +import org.eclipse.lsp4jakarta.ls.commons.snippets.ISnippetContext; +import org.eclipse.lsp4jakarta.ls.commons.snippets.Snippet; +import org.eclipse.lsp4jakarta.ls.commons.snippets.TextDocumentSnippetRegistry; +import org.eclipse.lsp4jakarta.ls.java.JakartaTextDocuments.JakartaTextDocument; +import org.eclipse.lsp4jakarta.snippets.LanguageId; +import org.eclipse.lsp4jakarta.snippets.SnippetContextForJava; + +/** + * Java snippet registry. When a snippet is registered it replaces for the first + * line only the content 'package ${1:packagename};' to '${packagename}' in + * order to manage the case if packagename is empty (don't generate package) or + * not. + * + * Based on: + * https://github.com/eclipse/lsp4mp/blob/0.9.0/microprofile.ls/org.eclipse.lsp4mp.ls/src/main/java/org/eclipse/lsp4mp/ls/java/JavaTextDocumentSnippetRegistry.java + * + * @author Angelo ZERR + * + */ +public class JavaTextDocumentSnippetRegistry extends TextDocumentSnippetRegistry { + + private static final String PACKAGENAME_KEY = "packagename"; + private static final String EE_NAMESPACE_KEY = "ee-namespace"; + private static final String JAVAX_VALUE = "javax"; + private static final String JAKARTA_VALUE = "jakarta"; + + /** + * The type whose presence indicates that the jakarta namespace should be used. + */ + private static final String JAKARTA_FLAG_TYPE = "jakarta.ws.rs.GET"; + + private List types; + + public JavaTextDocumentSnippetRegistry() { + this(true); + } + + public JavaTextDocumentSnippetRegistry(boolean loadDefault) { + super(LanguageId.java.name(), loadDefault); + } + + /** + * Returns the all distinct types declared in context/type of each snippet. + * + * @return the all distinct types declared in context/type of each snippet. + */ + public List getTypes() { + if (types != null) { + return types; + } + types = collectTypes(); + return types; + } + + private synchronized List collectTypes() { + if (types != null) { + return types; + } + List types = new ArrayList<>(); + types.add(JAKARTA_FLAG_TYPE); + for (Snippet snippet : getSnippets()) { + if (snippet.getContext() != null && snippet.getContext() instanceof SnippetContextForJava) { + List snippetTypes = ((SnippetContextForJava) snippet.getContext()).getTypes(); + if (snippetTypes != null) { + for (String snippetType : snippetTypes) { + if (!types.contains(snippetType)) { + types.add(snippetType); + } + } + } + } + } + return types; + } + + @Override + public void registerSnippet(Snippet snippet) { + preprocessSnippetBody(snippet); + super.registerSnippet(snippet); + } + + /** + * Preprocess Snippet body for managing package name. + * + * @param snippet + */ + private void preprocessSnippetBody(Snippet snippet) { + List body = snippet.getBody(); + if (body.isEmpty()) { + return; + } + String firstLine = body.get(0); + if (firstLine.contains("${") && firstLine.contains(PACKAGENAME_KEY)) { + // Transform these 3 body lines: + // "package ${1:packagename};", + // "", + // "import jakarta.ws.rs.HEAD;", + + // to one line: + // ${packagename}import jakarta.ws.rs.HEAD; + + if (body.size() >= 2 && StringUtils.isEmpty(body.get(1))) { + // Remove the line "" + body.remove(1); + } + String line = ""; + if (body.size() >= 2) { + // Remove the line "import jakarta.ws.rs.HEAD;" + line = body.get(1); + body.remove(1); + } + // Update the line 0 to ${packagename}import + // jakarta.ws.rs.HEAD; + body.set(0, "${" + PACKAGENAME_KEY + "}" + line); + } + } + + public List getCompletionItems(JakartaTextDocument document, int completionOffset, + boolean canSupportMarkdown, boolean snippetsSupported, + BiPredicate, Map> contextFilter, ProjectLabelInfoEntry projectInfo) { + Map model = new HashMap<>(); + String packageStatement = ""; + String packageName = document.getPackageName(); + String lineDelimiter = System.lineSeparator(); + try { + lineDelimiter = document.lineDelimiter(0); + } catch (BadLocationException e) { + } + if (packageName == null) { + packageStatement = new StringBuilder("package ${1:packagename};")// + .append(lineDelimiter) // + .append(lineDelimiter) // + .toString(); + } else { + // fill package name to replace in the snippets + if (packageName.length() > 0) { + packageStatement = new StringBuilder("package ")// + .append(document.getPackageName()) // + .append(";") // + .append(lineDelimiter) // + .append(lineDelimiter) // + .toString(); + } + } + model.put(PACKAGENAME_KEY, packageStatement); + model.put(EE_NAMESPACE_KEY, + projectInfo.getLabels().contains(JavaTextDocumentSnippetRegistry.JAKARTA_FLAG_TYPE) ? JavaTextDocumentSnippetRegistry.JAKARTA_VALUE : JavaTextDocumentSnippetRegistry.JAVAX_VALUE); + return super.getCompletionItems(document, completionOffset, canSupportMarkdown, snippetsSupported, + contextFilter, model); + } + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/settings/JakartaCompletionCapabilities.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/settings/JakartaCompletionCapabilities.java new file mode 100644 index 00000000..0bbaa4b8 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/settings/JakartaCompletionCapabilities.java @@ -0,0 +1,88 @@ +/******************************************************************************* +* Copyright (c) 2019 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.settings; + +import org.eclipse.lsp4j.CompletionCapabilities; + +/** + * A wrapper around LSP {@link CompletionCapabilities}. + * + */ +public class JakartaCompletionCapabilities { + + private CompletionCapabilities completionCapabilities; + + public void setCapabilities(CompletionCapabilities completionCapabilities) { + this.completionCapabilities = completionCapabilities; + } + + public CompletionCapabilities getCompletionCapabilities() { + return completionCapabilities; + } + + /** + * Returns true if the client support snippet and + * false otherwise. + * + * @return true if the client support snippet and + * false otherwise. + */ + public boolean isCompletionSnippetsSupported() { + return completionCapabilities != null && completionCapabilities.getCompletionItem() != null + && completionCapabilities.getCompletionItem().getSnippetSupport() != null + && completionCapabilities.getCompletionItem().getSnippetSupport(); + } + + /** + * Returns true if the client support the given documentation + * format and false otherwise. + * + * @return true if the client support the given documentation + * format and false otherwise. + */ + public boolean isDocumentationFormatSupported(String documentationFormat) { + return completionCapabilities != null && completionCapabilities.getCompletionItem() != null + && completionCapabilities.getCompletionItem().getDocumentationFormat() != null + && completionCapabilities.getCompletionItem().getDocumentationFormat().contains(documentationFormat); + } + + /** + * Returns true if the client supports resolving the documentation property in + * completionItem/resolve and false otherwise. + * + * @return true if the client supports resolving the documentation property in + * completionItem/resolve and false otherwise + */ + public boolean isCompletionResolveDocumentationSupported() { + return completionCapabilities != null && completionCapabilities.getCompletionItem() != null + && completionCapabilities.getCompletionItem().getResolveSupport() != null + && completionCapabilities.getCompletionItem().getResolveSupport().getProperties() != null + && completionCapabilities.getCompletionItem().getResolveSupport().getProperties().contains("documentation"); + } + + /** + * Returns true if the client supports editRange in itemDefaults support and + * false otherwise. + * + * @param param the completion itemDefaults parameter to be checked + * @return true if the client supports editRange in itemDefaults support and + * false otherwise. + */ + public boolean isCompletionListItemDefaultsSupport(String param) { + return completionCapabilities != null && completionCapabilities.getCompletionList() != null + && completionCapabilities.getCompletionList().getItemDefaults() != null + && completionCapabilities.getCompletionList().getItemDefaults().contains(param); + } + +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/JakartaEESnippetRegistryLoader.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/JakartaEESnippetRegistryLoader.java index 0a11a178..2790bfc8 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/JakartaEESnippetRegistryLoader.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/JakartaEESnippetRegistryLoader.java @@ -15,12 +15,12 @@ import java.util.logging.Logger; -import org.eclipse.lsp4jakarta.commons.snippets.ISnippetRegistryLoader; -import org.eclipse.lsp4jakarta.commons.snippets.SnippetRegistry; +import org.eclipse.lsp4jakarta.ls.commons.snippets.ISnippetRegistryLoader; +import org.eclipse.lsp4jakarta.ls.commons.snippets.SnippetRegistry; /** * Loads in JakartaEE Specific Snippets - * + * * @author Ankush Sharma */ public class JakartaEESnippetRegistryLoader implements ISnippetRegistryLoader { @@ -30,21 +30,26 @@ public class JakartaEESnippetRegistryLoader implements ISnippetRegistryLoader { public void load(SnippetRegistry registry) throws Exception { LOGGER.info("Loading snippets into registry..."); registry.registerSnippets( - JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("restfulWs.json"), - SnippetContextForJava.TYPE_ADAPTER); + JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("restfulWs.json"), + SnippetContextForJava.TYPE_ADAPTER); registry.registerSnippets( - JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("servlet.json"), - SnippetContextForJava.TYPE_ADAPTER); + JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("servlet.json"), + SnippetContextForJava.TYPE_ADAPTER); registry.registerSnippets( - JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("persistence.json"), - SnippetContextForJava.TYPE_ADAPTER); + JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("persistence.json"), + SnippetContextForJava.TYPE_ADAPTER); registry.registerSnippets( - JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("bean-validation.json"), - SnippetContextForJava.TYPE_ADAPTER); + JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("bean-validation.json"), + SnippetContextForJava.TYPE_ADAPTER); registry.registerSnippets( - JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("transactions.json"), - SnippetContextForJava.TYPE_ADAPTER); + JakartaEESnippetRegistryLoader.class.getClassLoader().getResourceAsStream("transactions.json"), + SnippetContextForJava.TYPE_ADAPTER); } + @Override + public String getLanguageId() { + return LanguageId.java.name(); + } + } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/JavaSnippetCompletionContext.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/JavaSnippetCompletionContext.java index 3456320c..e3b5ca03 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/JavaSnippetCompletionContext.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/JavaSnippetCompletionContext.java @@ -22,33 +22,33 @@ */ public class JavaSnippetCompletionContext { - private ProjectLabelInfoEntry projectLabelInfoEntry; - private JavaCursorContextResult javaCursorContextResult; + private ProjectLabelInfoEntry projectLabelInfoEntry; + private JavaCursorContextResult javaCursorContextResult; - public JavaSnippetCompletionContext(ProjectLabelInfoEntry projectLabelInfoEntry, - JavaCursorContextResult javaCursorContextResult) { - this.projectLabelInfoEntry = projectLabelInfoEntry; - this.javaCursorContextResult = javaCursorContextResult; - } + public JavaSnippetCompletionContext(ProjectLabelInfoEntry projectLabelInfoEntry, + JavaCursorContextResult javaCursorContextResult) { + this.projectLabelInfoEntry = projectLabelInfoEntry; + this.javaCursorContextResult = javaCursorContextResult; + } - /** - * Returns the project label info entry for the file in which completion was - * triggered. - * - * @return the project label info entry for the file in which completion was - * triggered - */ - public ProjectLabelInfoEntry getProjectLabelInfoEntry() { - return projectLabelInfoEntry; - } + /** + * Returns the project label info entry for the file in which completion was + * triggered. + * + * @return the project label info entry for the file in which completion was + * triggered + */ + public ProjectLabelInfoEntry getProjectLabelInfoEntry() { + return projectLabelInfoEntry; + } - /** - * Returns the context related to the cursor location in the given document. - * - * @return the context related to the cursor location in the given document - */ - public JavaCursorContextResult getJavaCursorContextResult() { - return javaCursorContextResult; - } + /** + * Returns the context related to the cursor location in the given document. + * + * @return the context related to the cursor location in the given document + */ + public JavaCursorContextResult getJavaCursorContextResult() { + return javaCursorContextResult; + } } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/LanguageId.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/LanguageId.java new file mode 100644 index 00000000..ff7ab620 --- /dev/null +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/LanguageId.java @@ -0,0 +1,25 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +* which is available at https://www.apache.org/licenses/LICENSE-2.0. +* +* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package org.eclipse.lsp4jakarta.snippets; + +/** + * Snippet language id type. + * + * @author Angelo ZERR + * + */ +public enum LanguageId { + + java, properties; +} diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/SnippetContentType.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/SnippetContentType.java index 57a1b3a5..fe13902e 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/SnippetContentType.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/SnippetContentType.java @@ -20,16 +20,16 @@ */ public enum SnippetContentType { - @SerializedName("class") - CLASS, // + @SerializedName("class") + CLASS, // - @SerializedName("method") - METHOD, // + @SerializedName("method") + METHOD, // - @SerializedName("field") - FIELD, // + @SerializedName("field") + FIELD, // - @SerializedName("method-annotation") - METHOD_ANNOTATION; + @SerializedName("method-annotation") + METHOD_ANNOTATION; } diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/SnippetContextForJava.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/SnippetContextForJava.java index 148f7459..d433d1a5 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/SnippetContextForJava.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/snippets/SnippetContextForJava.java @@ -21,7 +21,7 @@ import org.eclipse.lsp4jakarta.commons.JavaCursorContextKind; import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; import org.eclipse.lsp4jakarta.commons.ProjectLabelInfoEntry; -import org.eclipse.lsp4jakarta.commons.snippets.ISnippetContext; +import org.eclipse.lsp4jakarta.ls.commons.snippets.ISnippetContext; import com.google.gson.Gson; import com.google.gson.TypeAdapter; @@ -38,18 +38,18 @@ public class SnippetContextForJava implements ISnippetContext { public static final TypeAdapter TYPE_ADAPTER = new SnippetContextForJavaAdapter(); - private static final Gson GSON = new Gson(); + private static final Gson GSON = new Gson(); private List types; - private SnippetContentType contentType; + private SnippetContentType contentType; public SnippetContextForJava(List types, SnippetContentType contentType) { - this.types = types; - this.contentType = contentType; - } + this.types = types; + this.contentType = contentType; + } - public SnippetContextForJava(List types) { - this(types, null); - } + public SnippetContextForJava(List types) { + this(types, null); + } public List getTypes() { return types; @@ -61,50 +61,50 @@ public boolean isMatch(JavaSnippetCompletionContext context) { return true; } - // Check types - boolean typeMatches = false; - if (context.getProjectLabelInfoEntry() != null) { - ProjectLabelInfoEntry label = context.getProjectLabelInfoEntry(); - if (types == null || types.isEmpty()) { - return typeMatches = true; - } else { - for (String type : types) { - if (label.hasLabel(type)) { - typeMatches = true; - break; - } - } - } - } else { - typeMatches = true; - } - - return typeMatches && snippetContentAppliesToContext(context.getJavaCursorContextResult()); - } - - public boolean snippetContentAppliesToContext(JavaCursorContextResult context) { - // content/context being null signals that the client doesn't support getting - // the completion context - if (contentType == null || context == null) { + // Check types + boolean typeMatches = false; + if (context.getProjectLabelInfoEntry() != null) { + ProjectLabelInfoEntry label = context.getProjectLabelInfoEntry(); + if (types == null || types.isEmpty()) { + return typeMatches = true; + } else { + for (String type : types) { + if (label.hasLabel(type)) { + typeMatches = true; + break; + } + } + } + } else { + typeMatches = true; + } + + return typeMatches && snippetContentAppliesToContext(context.getJavaCursorContextResult()); + } + + public boolean snippetContentAppliesToContext(JavaCursorContextResult context) { + // content/context being null signals that the client doesn't support getting + // the completion context + if (contentType == null || context == null) { return true; } JavaCursorContextKind kind = context.getKind(); - String prefix = context.getPrefix(); - boolean prefixMatchesAnnotation = prefix.startsWith("@"); - switch (contentType) { - case METHOD_ANNOTATION: - return prefixMatchesAnnotation && (kind == JavaCursorContextKind.BEFORE_METHOD - || kind == JavaCursorContextKind.IN_METHOD_ANNOTATIONS); - case CLASS: - return kind == JavaCursorContextKind.IN_EMPTY_FILE; - case METHOD: - return kind == JavaCursorContextKind.BEFORE_FIELD || kind == JavaCursorContextKind.BEFORE_METHOD - || kind == JavaCursorContextKind.IN_CLASS; - case FIELD: - return kind == JavaCursorContextKind.BEFORE_FIELD || kind == JavaCursorContextKind.BEFORE_METHOD - || kind == JavaCursorContextKind.IN_CLASS; - default: - return false; + String prefix = context.getPrefix(); + boolean prefixMatchesAnnotation = prefix.startsWith("@"); + switch (contentType) { + case METHOD_ANNOTATION: + return prefixMatchesAnnotation && (kind == JavaCursorContextKind.BEFORE_METHOD + || kind == JavaCursorContextKind.IN_METHOD_ANNOTATIONS); + case CLASS: + return kind == JavaCursorContextKind.IN_EMPTY_FILE; + case METHOD: + return kind == JavaCursorContextKind.BEFORE_FIELD || kind == JavaCursorContextKind.BEFORE_METHOD + || kind == JavaCursorContextKind.IN_CLASS; + case FIELD: + return kind == JavaCursorContextKind.BEFORE_FIELD || kind == JavaCursorContextKind.BEFORE_METHOD + || kind == JavaCursorContextKind.IN_CLASS; + default: + return false; } } @@ -118,32 +118,32 @@ public SnippetContextForJava read(final JsonReader in) throws IOException { } List types = new ArrayList<>(); - SnippetContentType contentType = null; + SnippetContentType contentType = null; in.beginObject(); while (in.hasNext()) { String name = in.nextName(); switch (name) { - case "type": - if (in.peek() == JsonToken.BEGIN_ARRAY) { - in.beginArray(); - while (in.peek() != JsonToken.END_ARRAY) { + case "type": + if (in.peek() == JsonToken.BEGIN_ARRAY) { + in.beginArray(); + while (in.peek() != JsonToken.END_ARRAY) { + types.add(in.nextString()); + } + in.endArray(); + } else { types.add(in.nextString()); } - in.endArray(); - } else { - types.add(in.nextString()); - } - break; - case "contentType": - String contentTypeString = in.nextString(); - contentType = GSON.fromJson(contentTypeString, SnippetContentType.class); - break; - default: - in.skipValue(); + break; + case "contentType": + String contentTypeString = in.nextString(); + contentType = GSON.fromJson(contentTypeString, SnippetContentType.class); + break; + default: + in.skipValue(); } } in.endObject(); - return new SnippetContextForJava(types, contentType); + return new SnippetContextForJava(types, contentType); } @Override diff --git a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/utils/Messages.java b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/utils/Messages.java index a04a2b6f..55837a82 100644 --- a/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/utils/Messages.java +++ b/jakarta.ls/src/main/java/org/eclipse/lsp4jakarta/utils/Messages.java @@ -1,50 +1,50 @@ -/******************************************************************************* -* Copyright (c) 2022 IBM Corporation and others. -* -* This program and the accompanying materials are made available under the -* terms of the Eclipse Public License v. 2.0 which is available at -* http://www.eclipse.org/legal/epl-2.0. -* -* SPDX-License-Identifier: EPL-2.0 -*******************************************************************************/ -package org.eclipse.lsp4jakarta.utils; - -import java.text.MessageFormat; -import java.util.Locale; -import java.util.ResourceBundle; - -public final class Messages { - -// private static final Logger LOGGER = Logger.getLogger(Messages.class.getName()); - - private static ResourceBundle resourceBundle = null; - - private static synchronized void initializeBundles() { - resourceBundle = ResourceBundle.getBundle("org.eclipse.lsp4jakarta.messages.messages", Locale.getDefault()); - } - - /** - * Returns message for the given key defined in resource bundle file. - * - * @param key the given key - * @param args replacements - * @return Returns message for the given key defined in resource bundle file - */ - public static String getMessage(String key, Object... args) { - if (resourceBundle == null) { - initializeBundles(); - } - String msg = null; - try { - msg = resourceBundle.getString(key); - if (msg != null && args != null && args.length > 0) { - msg = MessageFormat.format(msg, args); - } - } catch (Exception e) { - // do nothing for now, turn the logger on for missing keys once all messages are - // externalized - // LOGGER.info("Failed to get message for '" + key + "'"); - } - return (msg == null) ? key : msg; - } -} +/******************************************************************************* +* Copyright (c) 2022 IBM Corporation and others. +* +* This program and the accompanying materials are made available under the +* terms of the Eclipse Public License v. 2.0 which is available at +* http://www.eclipse.org/legal/epl-2.0. +* +* SPDX-License-Identifier: EPL-2.0 +*******************************************************************************/ +package org.eclipse.lsp4jakarta.utils; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.ResourceBundle; + +public final class Messages { + +// private static final Logger LOGGER = Logger.getLogger(Messages.class.getName()); + + private static ResourceBundle resourceBundle = null; + + private static synchronized void initializeBundles() { + resourceBundle = ResourceBundle.getBundle("org.eclipse.lsp4jakarta.messages.messages", Locale.getDefault()); + } + + /** + * Returns message for the given key defined in resource bundle file. + * + * @param key the given key + * @param args replacements + * @return Returns message for the given key defined in resource bundle file + */ + public static String getMessage(String key, Object... args) { + if (resourceBundle == null) { + initializeBundles(); + } + String msg = null; + try { + msg = resourceBundle.getString(key); + if (msg != null && args != null && args.length > 0) { + msg = MessageFormat.format(msg, args); + } + } catch (Exception e) { + // do nothing for now, turn the logger on for missing keys once all messages are + // externalized + // LOGGER.info("Failed to get message for '" + key + "'"); + } + return (msg == null) ? key : msg; + } +} diff --git a/jakarta.ls/src/main/resources/META-INF/services/org.eclipse.lsp4jakarta.ls.commons.snippets.ISnippetRegistryLoader b/jakarta.ls/src/main/resources/META-INF/services/org.eclipse.lsp4jakarta.ls.commons.snippets.ISnippetRegistryLoader new file mode 100644 index 00000000..50aa44ab --- /dev/null +++ b/jakarta.ls/src/main/resources/META-INF/services/org.eclipse.lsp4jakarta.ls.commons.snippets.ISnippetRegistryLoader @@ -0,0 +1 @@ +org.eclipse.lsp4jakarta.snippets.JakartaEESnippetRegistryLoader \ No newline at end of file diff --git a/jakarta.ls/src/test/java/org/eclipse/lsp4jakarta/snippets/JakartaSnippetRegistryTest.java b/jakarta.ls/src/test/java/org/eclipse/lsp4jakarta/snippets/JakartaSnippetRegistryTest.java index 5c39b8ba..ba26555c 100644 --- a/jakarta.ls/src/test/java/org/eclipse/lsp4jakarta/snippets/JakartaSnippetRegistryTest.java +++ b/jakarta.ls/src/test/java/org/eclipse/lsp4jakarta/snippets/JakartaSnippetRegistryTest.java @@ -23,9 +23,9 @@ import org.eclipse.lsp4jakarta.commons.JavaCursorContextKind; import org.eclipse.lsp4jakarta.commons.JavaCursorContextResult; import org.eclipse.lsp4jakarta.commons.ProjectLabelInfoEntry; -import org.eclipse.lsp4jakarta.commons.snippets.ISnippetContext; -import org.eclipse.lsp4jakarta.commons.snippets.Snippet; -import org.eclipse.lsp4jakarta.commons.snippets.SnippetRegistry; +import org.eclipse.lsp4jakarta.ls.commons.snippets.ISnippetContext; +import org.eclipse.lsp4jakarta.ls.commons.snippets.Snippet; +import org.eclipse.lsp4jakarta.ls.java.JavaTextDocumentSnippetRegistry; import org.junit.Test; /** @@ -34,175 +34,175 @@ public class JakartaSnippetRegistryTest { - SnippetRegistry registry = new SnippetRegistry(); - - @Test - public void haveJavaSnippets() { - assertFalse("Tests has no Jakarta Java snippets", registry.getSnippets().isEmpty()); - } - - /** - * Jakarta Bean Validation snippets - @Email - */ - @Test - public void beanValidationSnippetsTest() { - Optional beanValidationSnippet = findByPrefix("@Email", registry); - assertTrue("@Email Java snippet is not present in SnippetRegistry", beanValidationSnippet.isPresent()); - - snippetsContextTest(beanValidationSnippet, "jakarta.validation.constraints.Email", - JavaCursorContextKind.BEFORE_METHOD); - - } - - /** - * Jakarta Persistence snippets. - persist_context, persist_context_extended, - * persist_context_extended_unsync, persist_entity. - */ - @Test - public void persistenceSnippetsTest() { - Optional persistContextSnippet = findByPrefix("persist_context", registry); - assertTrue("persist_context Java snippet is not present in SnippetRegistry", persistContextSnippet.isPresent()); - - Optional persistContextExtendedSnippet = findByPrefix("persist_context_extended", registry); - assertTrue("persist_context_extended Java snippet is not present in SnippetRegistry", - persistContextExtendedSnippet.isPresent()); - - Optional persistContextExtendedunsyncSnippet = findByPrefix("persist_context_extended_unsync", - registry); - assertTrue("persist_context_extended_unsync Java snippet is not present in SnippetRegistry", - persistContextExtendedunsyncSnippet.isPresent()); - - Optional persistEntitySnippet = findByPrefix("persist_entity", registry); - assertTrue("persist_entity Java snippet is not present in SnippetRegistry", persistEntitySnippet.isPresent()); - - snippetsContextTest(persistContextSnippet, "jakarta.persistence.PersistenceContextType", - JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(persistContextExtendedSnippet, "jakarta.persistence.PersistenceContextType", - JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(persistContextExtendedunsyncSnippet, "jakarta.persistence.PersistenceContextType", - JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(persistEntitySnippet, "jakarta.persistence.Entity", JavaCursorContextKind.IN_EMPTY_FILE); - - } - - /** - * Jakarta RESTful Web Services snippets - rest_class, rest_get - * rest_post, rest_put, rest_delete, rest_head - */ - @Test - public void restfulWebServicesSnippetsPrefixTest() { - Optional restClassSnippet = findByPrefix("rest_class", registry); - assertTrue("rest_class Java snippet is not present in SnippetRegistry", restClassSnippet.isPresent()); - - Optional restGetSnippet = findByPrefix("rest_get", registry); - assertTrue("rest_get Java snippet is not present in SnippetRegistry", restGetSnippet.isPresent()); - - Optional restPostSnippet = findByPrefix("rest_post", registry); - assertTrue("rest_post Java snippet is not present in SnippetRegistry", restPostSnippet.isPresent()); - - Optional restPutSnippet = findByPrefix("rest_put", registry); - assertTrue("rest_put Java snippet is not present in SnippetRegistry", restPutSnippet.isPresent()); - - Optional restDeleteSnippet = findByPrefix("rest_delete", registry); - assertTrue("rest_delete Java snippet is not present in SnippetRegistry", restDeleteSnippet.isPresent()); - - Optional restHeadSnippet = findByPrefix("rest_head", registry); - assertTrue("rest_head Java snippet is not present in SnippetRegistry", restHeadSnippet.isPresent()); - - snippetsContextTest(restClassSnippet, "jakarta.ws.rs.GET", JavaCursorContextKind.IN_EMPTY_FILE); - snippetsContextTest(restGetSnippet, "jakarta.ws.rs.GET", JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(restPostSnippet, "jakarta.ws.rs.POST", JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(restPutSnippet, "jakarta.ws.rs.PUT", JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(restDeleteSnippet, "jakarta.ws.rs.DELETE", JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(restHeadSnippet, "jakarta.ws.rs.HEAD", JavaCursorContextKind.BEFORE_METHOD); - - } - - /** - * Jakarta Servlet snippets - servlet_generic, servlet_doget - * servlet_dopost, servlet_webfilter - */ - @Test - public void ServletSnippetsTest() { - Optional servletGenericSnippet = findByPrefix("servlet_generic", registry); - assertTrue("servlet_generic Java snippet is not present in SnippetRegistry", servletGenericSnippet.isPresent()); - - Optional servletDoGetSnippet = findByPrefix("servlet_doget", registry); - assertTrue("servlet_doget Java snippet is not present in SnippetRegistry", servletDoGetSnippet.isPresent()); - - Optional servletDoPostSnippet = findByPrefix("servlet_dopost", registry); - assertTrue("servlet_dopost Java snippet is not present in SnippetRegistry", servletDoPostSnippet.isPresent()); - - Optional servletWebFilterSnippet = findByPrefix("servlet_webfilter", registry); - assertTrue("servlet_webfilter Java snippet is not present in SnippetRegistry", - servletWebFilterSnippet.isPresent()); - - snippetsContextTest(servletGenericSnippet, "jakarta.servlet.GenericServlet", - JavaCursorContextKind.IN_EMPTY_FILE); - snippetsContextTest(servletDoGetSnippet, "jakarta.servlet.http.HttpServlet", - JavaCursorContextKind.IN_EMPTY_FILE); - snippetsContextTest(servletDoPostSnippet, "jakarta.servlet.http.HttpServlet", - JavaCursorContextKind.IN_EMPTY_FILE); - snippetsContextTest(servletWebFilterSnippet, "jakarta.servlet.Filter", JavaCursorContextKind.IN_EMPTY_FILE); - - } - - /** - * Jakarta Transactions snippets - tx_user_inject, - * tx_user_jndi, @ Transactional - */ - @Test - public void TransactionsSnippetsTest() { - Optional txUserInjectSnippet = findByPrefix("tx_user_inject", registry); - assertTrue("tx_user_inject Java snippet is not present in SnippetRegistry", txUserInjectSnippet.isPresent()); - - Optional txUserJndiSnippet = findByPrefix("tx_user_jndi", registry); - assertTrue("tx_user_jndi Java snippet is not present in SnippetRegistry", txUserJndiSnippet.isPresent()); - - Optional transactionalSnippet = findByPrefix("@Transactional", registry); - assertTrue("@Transactional Java snippet is not present in SnippetRegistry", transactionalSnippet.isPresent()); - - snippetsContextTest(txUserInjectSnippet, "jakarta.transaction.UserTransaction", - JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(txUserJndiSnippet, "jakarta.transaction.UserTransaction", - JavaCursorContextKind.BEFORE_METHOD); - snippetsContextTest(transactionalSnippet, "jakarta.transaction.Transactional", - JavaCursorContextKind.IN_METHOD_ANNOTATIONS); - - } - - // Verify whether the snippet is present in the registry. - private static Optional findByPrefix(String prefix, SnippetRegistry registry) { - return registry.getSnippets().stream().filter(snippet -> snippet.getPrefixes().contains(prefix)).findFirst(); - } - - // Verify whether the snippet context is present in the registry. - private void snippetsContextTest(Optional snippet, String contextType, - JavaCursorContextKind javaCursorContextKind) { - - ISnippetContext context = snippet.get().getContext(); - assertNotNull(snippet.get().getPrefixes() + " snippet context is null", context); - assertTrue(snippet.get().getPrefixes() + " snippet context is not a Java context", - context instanceof SnippetContextForJava); - - ProjectLabelInfoEntry projectInfo = new ProjectLabelInfoEntry("", new ArrayList<>()); - boolean match = ((SnippetContextForJava) context).isMatch(context(projectInfo, javaCursorContextKind)); - assertFalse("Project should not have " + contextType + " type", match); - - ProjectLabelInfoEntry projectInfo1 = new ProjectLabelInfoEntry("", Arrays.asList(contextType)); - boolean match1 = ((SnippetContextForJava) context).isMatch(context(projectInfo1, javaCursorContextKind, - snippet.get().getPrefixes().isEmpty() ? "" : snippet.get().getPrefixes().get(0))); - assertTrue("Project has no " + contextType + " type", match1); - - } - - private static JavaSnippetCompletionContext context(ProjectLabelInfoEntry projectInfo, - JavaCursorContextKind javaCursorContext) { - return context(projectInfo, javaCursorContext, ""); - } - - private static JavaSnippetCompletionContext context(ProjectLabelInfoEntry projectInfo, - JavaCursorContextKind javaCursorContext, String prefix) { - return new JavaSnippetCompletionContext(projectInfo, new JavaCursorContextResult(javaCursorContext, prefix)); - } + JavaTextDocumentSnippetRegistry registry = new JavaTextDocumentSnippetRegistry(); + + @Test + public void haveJavaSnippets() { + assertFalse("Tests has no Jakarta Java snippets", registry.getSnippets().isEmpty()); + } + + /** + * Jakarta Bean Validation snippets - @Email + */ + @Test + public void beanValidationSnippetsTest() { + Optional beanValidationSnippet = findByPrefix("@Email", registry); + assertTrue("@Email Java snippet is not present in SnippetRegistry", beanValidationSnippet.isPresent()); + + snippetsContextTest(beanValidationSnippet, "jakarta.validation.constraints.Email", + JavaCursorContextKind.BEFORE_METHOD); + + } + + /** + * Jakarta Persistence snippets. - persist_context, persist_context_extended, + * persist_context_extended_unsync, persist_entity. + */ + @Test + public void persistenceSnippetsTest() { + Optional persistContextSnippet = findByPrefix("persist_context", registry); + assertTrue("persist_context Java snippet is not present in SnippetRegistry", persistContextSnippet.isPresent()); + + Optional persistContextExtendedSnippet = findByPrefix("persist_context_extended", registry); + assertTrue("persist_context_extended Java snippet is not present in SnippetRegistry", + persistContextExtendedSnippet.isPresent()); + + Optional persistContextExtendedunsyncSnippet = findByPrefix("persist_context_extended_unsync", + registry); + assertTrue("persist_context_extended_unsync Java snippet is not present in SnippetRegistry", + persistContextExtendedunsyncSnippet.isPresent()); + + Optional persistEntitySnippet = findByPrefix("persist_entity", registry); + assertTrue("persist_entity Java snippet is not present in SnippetRegistry", persistEntitySnippet.isPresent()); + + snippetsContextTest(persistContextSnippet, "jakarta.persistence.PersistenceContextType", + JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(persistContextExtendedSnippet, "jakarta.persistence.PersistenceContextType", + JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(persistContextExtendedunsyncSnippet, "jakarta.persistence.PersistenceContextType", + JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(persistEntitySnippet, "jakarta.persistence.Entity", JavaCursorContextKind.IN_EMPTY_FILE); + + } + + /** + * Jakarta RESTful Web Services snippets - rest_class, rest_get + * rest_post, rest_put, rest_delete, rest_head + */ + @Test + public void restfulWebServicesSnippetsPrefixTest() { + Optional restClassSnippet = findByPrefix("rest_class", registry); + assertTrue("rest_class Java snippet is not present in SnippetRegistry", restClassSnippet.isPresent()); + + Optional restGetSnippet = findByPrefix("rest_get", registry); + assertTrue("rest_get Java snippet is not present in SnippetRegistry", restGetSnippet.isPresent()); + + Optional restPostSnippet = findByPrefix("rest_post", registry); + assertTrue("rest_post Java snippet is not present in SnippetRegistry", restPostSnippet.isPresent()); + + Optional restPutSnippet = findByPrefix("rest_put", registry); + assertTrue("rest_put Java snippet is not present in SnippetRegistry", restPutSnippet.isPresent()); + + Optional restDeleteSnippet = findByPrefix("rest_delete", registry); + assertTrue("rest_delete Java snippet is not present in SnippetRegistry", restDeleteSnippet.isPresent()); + + Optional restHeadSnippet = findByPrefix("rest_head", registry); + assertTrue("rest_head Java snippet is not present in SnippetRegistry", restHeadSnippet.isPresent()); + + snippetsContextTest(restClassSnippet, "jakarta.ws.rs.GET", JavaCursorContextKind.IN_EMPTY_FILE); + snippetsContextTest(restGetSnippet, "jakarta.ws.rs.GET", JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(restPostSnippet, "jakarta.ws.rs.POST", JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(restPutSnippet, "jakarta.ws.rs.PUT", JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(restDeleteSnippet, "jakarta.ws.rs.DELETE", JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(restHeadSnippet, "jakarta.ws.rs.HEAD", JavaCursorContextKind.BEFORE_METHOD); + + } + + /** + * Jakarta Servlet snippets - servlet_generic, servlet_doget + * servlet_dopost, servlet_webfilter + */ + @Test + public void ServletSnippetsTest() { + Optional servletGenericSnippet = findByPrefix("servlet_generic", registry); + assertTrue("servlet_generic Java snippet is not present in SnippetRegistry", servletGenericSnippet.isPresent()); + + Optional servletDoGetSnippet = findByPrefix("servlet_doget", registry); + assertTrue("servlet_doget Java snippet is not present in SnippetRegistry", servletDoGetSnippet.isPresent()); + + Optional servletDoPostSnippet = findByPrefix("servlet_dopost", registry); + assertTrue("servlet_dopost Java snippet is not present in SnippetRegistry", servletDoPostSnippet.isPresent()); + + Optional servletWebFilterSnippet = findByPrefix("servlet_webfilter", registry); + assertTrue("servlet_webfilter Java snippet is not present in SnippetRegistry", + servletWebFilterSnippet.isPresent()); + + snippetsContextTest(servletGenericSnippet, "jakarta.servlet.GenericServlet", + JavaCursorContextKind.IN_EMPTY_FILE); + snippetsContextTest(servletDoGetSnippet, "jakarta.servlet.http.HttpServlet", + JavaCursorContextKind.IN_EMPTY_FILE); + snippetsContextTest(servletDoPostSnippet, "jakarta.servlet.http.HttpServlet", + JavaCursorContextKind.IN_EMPTY_FILE); + snippetsContextTest(servletWebFilterSnippet, "jakarta.servlet.Filter", JavaCursorContextKind.IN_EMPTY_FILE); + + } + + /** + * Jakarta Transactions snippets - tx_user_inject, + * tx_user_jndi, @ Transactional + */ + @Test + public void TransactionsSnippetsTest() { + Optional txUserInjectSnippet = findByPrefix("tx_user_inject", registry); + assertTrue("tx_user_inject Java snippet is not present in SnippetRegistry", txUserInjectSnippet.isPresent()); + + Optional txUserJndiSnippet = findByPrefix("tx_user_jndi", registry); + assertTrue("tx_user_jndi Java snippet is not present in SnippetRegistry", txUserJndiSnippet.isPresent()); + + Optional transactionalSnippet = findByPrefix("@Transactional", registry); + assertTrue("@Transactional Java snippet is not present in SnippetRegistry", transactionalSnippet.isPresent()); + + snippetsContextTest(txUserInjectSnippet, "jakarta.transaction.UserTransaction", + JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(txUserJndiSnippet, "jakarta.transaction.UserTransaction", + JavaCursorContextKind.BEFORE_METHOD); + snippetsContextTest(transactionalSnippet, "jakarta.transaction.Transactional", + JavaCursorContextKind.IN_METHOD_ANNOTATIONS); + + } + + // Verify whether the snippet is present in the registry. + private static Optional findByPrefix(String prefix, JavaTextDocumentSnippetRegistry registry) { + return registry.getSnippets().stream().filter(snippet -> snippet.getPrefixes().contains(prefix)).findFirst(); + } + + // Verify whether the snippet context is present in the registry. + private void snippetsContextTest(Optional snippet, String contextType, + JavaCursorContextKind javaCursorContextKind) { + + ISnippetContext context = snippet.get().getContext(); + assertNotNull(snippet.get().getPrefixes() + " snippet context is null", context); + assertTrue(snippet.get().getPrefixes() + " snippet context is not a Java context", + context instanceof SnippetContextForJava); + + ProjectLabelInfoEntry projectInfo = new ProjectLabelInfoEntry("", null, new ArrayList<>()); + boolean match = ((SnippetContextForJava) context).isMatch(context(projectInfo, javaCursorContextKind)); + assertFalse("Project should not have " + contextType + " type", match); + + ProjectLabelInfoEntry projectInfo1 = new ProjectLabelInfoEntry("", null, Arrays.asList(contextType)); + boolean match1 = ((SnippetContextForJava) context).isMatch(context(projectInfo1, javaCursorContextKind, + snippet.get().getPrefixes().isEmpty() ? "" : snippet.get().getPrefixes().get(0))); + assertTrue("Project has no " + contextType + " type", match1); + + } + + private static JavaSnippetCompletionContext context(ProjectLabelInfoEntry projectInfo, + JavaCursorContextKind javaCursorContext) { + return context(projectInfo, javaCursorContext, ""); + } + + private static JavaSnippetCompletionContext context(ProjectLabelInfoEntry projectInfo, + JavaCursorContextKind javaCursorContext, String prefix) { + return new JavaSnippetCompletionContext(projectInfo, new JavaCursorContextResult(javaCursorContext, prefix)); + } } diff --git a/resources/ide/config/eclipse-formatter-config.xml b/resources/ide/config/eclipse-formatter-config.xml new file mode 100644 index 00000000..4767fa72 --- /dev/null +++ b/resources/ide/config/eclipse-formatter-config.xml @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +