Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes#511 Transform the quick fixes which use ImplementInterfaceProposal to implement the code action participant extension point. #726

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2023 Red Hat Inc. and others.
* Copyright (c) 2020, 2024 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
Expand All @@ -16,15 +16,27 @@

import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.JDTUtils;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.Messages;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.ExtendedCodeAction;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.IJavaCodeActionParticipant;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.JavaCodeActionContext;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.JavaCodeActionResolveContext;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.corrections.proposal.ChangeCorrectionProposal;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.corrections.proposal.ImplementInterfaceProposal;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4mp.commons.CodeActionResolveData;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* QuickFix for fixing HttpServlet extension error by providing the code actions
Expand All @@ -36,27 +48,48 @@
* @author Credit to Angelo ZERR
*
*/
public class FilterImplementationQuickFix {
public class FilterImplementationQuickFix implements IJavaCodeActionParticipant {
private static final Logger LOGGER = Logger.getLogger(FilterImplementationQuickFix.class.getName());

@Override
public String getParticipantId() {
return FilterImplementationQuickFix.class.getName();
}

public List<? extends CodeAction> getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic) {
List<CodeAction> codeActions = new ArrayList<>();
PsiElement node = context.getCoveredNode();
PsiClass parentType = getBinding(node);
if (parentType != null) {
// Create code action
// interface
ChangeCorrectionProposal proposal = new ImplementInterfaceProposal(
null, parentType, context.getASTRoot(),
"jakarta.servlet.Filter", 0, context.getSource().getCompilationUnit());
CodeAction codeAction = context.convertToCodeAction(proposal, diagnostic);

if (codeAction != null) {
codeActions.add(codeAction);
}
String title = Messages.getMessage("LetClassImplement",
parentType.getName(),
ServletConstants.FILTER);
codeActions.add(JDTUtils.createCodeAction(context, diagnostic, title, getParticipantId()));
}
return codeActions;
}

@Override
public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) {

final CodeAction toResolve = context.getUnresolved();
final PsiElement node = context.getCoveredNode();
final PsiClass parentType = getBinding(node);

assert parentType != null;
ChangeCorrectionProposal proposal = new ImplementInterfaceProposal(
context.getCompilationUnit(), parentType, context.getASTRoot(),
"jakarta.servlet.Filter", 0, context.getSource().getCompilationUnit());
try {
WorkspaceEdit we = context.convertToWorkspaceEdit(proposal);
toResolve.setEdit(we);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Unable to create workspace edit for code action to filter implementation", e);
}
return toResolve;
}


private PsiClass getBinding(PsiElement node) {
return PsiTreeUtil.getParentOfType(node, PsiClass.class);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2023 Red Hat Inc. and others.
* Copyright (c) 2020, 2024 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
Expand All @@ -17,15 +17,25 @@

import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.JDTUtils;
import io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.Messages;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.ExtendedCodeAction;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.IJavaCodeActionParticipant;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.JavaCodeActionContext;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.codeaction.JavaCodeActionResolveContext;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.corrections.proposal.ChangeCorrectionProposal;
import io.openliberty.tools.intellij.lsp4mp4ij.psi.core.java.corrections.proposal.ImplementInterfaceProposal;
import org.eclipse.lsp4j.CodeAction;
import org.eclipse.lsp4j.CodeActionKind;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4mp.commons.CodeActionResolveData;

import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* QuickFix for fixing HttpServlet extension error by providing the code actions
Expand All @@ -38,48 +48,69 @@
*
*/

public class ListenerImplementationQuickFix {
public class ListenerImplementationQuickFix implements IJavaCodeActionParticipant {

private static final Logger LOGGER = Logger.getLogger(ListenerImplementationQuickFix.class.getName());

private final static String INTERFACE_NAME_KEY = "interface";

@Override
public String getParticipantId() {
return ListenerImplementationQuickFix.class.getName();
}

public List<? extends CodeAction> getCodeActions(JavaCodeActionContext context, Diagnostic diagnostic) {
List<CodeAction> codeActions = new ArrayList<>();
// Create code action
// interface

setUpCodeAction(codeActions, diagnostic, context, ServletConstants.SERVLET_CONTEXT_LISTENER,
"jakarta.servlet.ServletContextListener");
setUpCodeAction(codeActions, diagnostic, context,
ServletConstants.SERVLET_CONTEXT_ATTRIBUTE_LISTENER,
"jakarta.servlet.ServletContextAttributeListener");
setUpCodeAction(codeActions, diagnostic, context, ServletConstants.SERVLET_REQUEST_LISTENER,
"jakarta.servlet.ServletRequestListener");
setUpCodeAction(codeActions, diagnostic, context,
ServletConstants.SERVLET_REQUEST_ATTRIBUTE_LISTENER,
"jakarta.servlet.ServletRequestAttributeListener");
setUpCodeAction(codeActions, diagnostic, context, ServletConstants.HTTP_SESSION_LISTENER,
"jakarta.servlet.http.HttpSessionListener");
setUpCodeAction(codeActions, diagnostic, context,
ServletConstants.HTTP_SESSION_ATTRIBUTE_LISTENER,
"jakarta.servlet.http.HttpSessionAttributeListener");
setUpCodeAction(codeActions, diagnostic, context, ServletConstants.HTTP_SESSION_ID_LISTENER,
"jakarta.servlet.http.HttpSessionIdListener");
PsiElement node = context.getCoveredNode();
PsiClass parentType = getBinding(node);

String[] listenerConstants = {
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
};

for (String interfaceType : listenerConstants) {
Map<String, Object> extendedData = new HashMap<>();
extendedData.put(INTERFACE_NAME_KEY, interfaceType);
String title = getLabel(interfaceType, parentType);
codeActions.add(JDTUtils.createCodeAction(context, diagnostic, title, getParticipantId(), extendedData));
}

return codeActions;
}

@Override
public CodeAction resolveCodeAction(JavaCodeActionResolveContext context) {
final CodeAction toResolve = context.getUnresolved();
final PsiElement node = context.getCoveredNode();
final PsiClass parentType = getBinding(node);
CodeActionResolveData data = (CodeActionResolveData) toResolve.getData();
String interfaceType = (String) data.getExtendedDataEntry(INTERFACE_NAME_KEY);

assert parentType != null;
ChangeCorrectionProposal proposal = new ImplementInterfaceProposal(
context.getCompilationUnit(), parentType, context.getASTRoot(),
interfaceType, 0, context.getSource().getCompilationUnit());
try {
WorkspaceEdit we = context.convertToWorkspaceEdit(proposal);
toResolve.setEdit(we);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Unable to create workspace edit for code action to listener implementation", e);
}
return toResolve;
}

private PsiClass getBinding(PsiElement node) {
return PsiTreeUtil.getParentOfType(node, PsiClass.class);
}

private void setUpCodeAction(List<CodeAction> codeActions, Diagnostic diagnostic, JavaCodeActionContext sourceContext,
String interfaceName, String interfaceType) {
JavaCodeActionContext targetContext = sourceContext.copy();
PsiElement node = targetContext.getCoveredNode(); // find covered node in the new context
PsiClass targetType = getBinding(node);
if (targetType != null) {
ChangeCorrectionProposal proposal = new ImplementInterfaceProposal(
null, targetType, targetContext.getASTRoot(), interfaceType, 0,
sourceContext.getCompilationUnit());
CodeAction codeAction = targetContext.convertToCodeAction(proposal, diagnostic);
codeActions.add(codeAction);
}
private static String getLabel(String fqAnnotation, PsiClass parentType) {
String annotationName = fqAnnotation.substring(fqAnnotation.lastIndexOf('.') + 1, fqAnnotation.length());
return Messages.getMessage("LetClassImplement", parentType.getName(), annotationName);
}
}
12 changes: 11 additions & 1 deletion src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@
<javaCodeActionParticipant kind="source"
group="mp"
implementationClass="io.openliberty.tools.intellij.lsp4mp4ij.psi.internal.openapi.java.MicroProfileGenerateOpenAPIOperation"/>

<javaCodeActionParticipant kind="quickfix"
group="jakarta"
targetDiagnostic="jakarta-servlet#ExtendHttpServlet"
Expand Down Expand Up @@ -389,6 +389,16 @@
targetDiagnostic="jakarta-cdi#InvalidScopeDecl"
implementationClass="io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.cdi.ScopeDeclarationQuickFix"/>

<javaCodeActionParticipant kind="quickfix"
group="jakarta"
targetDiagnostic="jakarta-servlet#ImplementListener"
implementationClass="io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.servlet.ListenerImplementationQuickFix"/>

<javaCodeActionParticipant kind="quickfix"
group="jakarta"
targetDiagnostic="jakarta-servlet#ImplementFilter"
implementationClass="io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.servlet.FilterImplementationQuickFix"/>

<configSourceProvider
implementation="io.openliberty.tools.intellij.lsp4mp4ij.psi.internal.core.providers.MicroProfileConfigSourceProvider"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,104 @@ public void RemoveDuplicateWebFilterAttributes() throws Exception {
CodeAction ca2 = JakartaForJavaAssert.ca(uri, "Remove the `value` attribute from @WebFilter", d, te2);
JakartaForJavaAssert.assertJavaCodeAction(codeActionParams, utils, ca1, ca2);
}

@Test
public void implementFilter() throws Exception {
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
IPsiUtils utils = PsiUtilsLSImpl.getInstance(myProject);

VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
+ "/src/main/java/io/openliberty/sample/jakarta/servlet/DontImplementFilter.java");
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();

JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams();
diagnosticsParams.setUris(Arrays.asList(uri));

Diagnostic d = JakartaForJavaAssert.d(5, 13, 32, "Annotated classes with @WebFilter must implement the Filter interface.",
DiagnosticSeverity.Error, "jakarta-servlet", "ImplementFilter");

JakartaForJavaAssert.assertJavaDiagnostics(diagnosticsParams, utils, d);

//TODO: this condition will be enabled when all quickfixes are refactored.
// if (CHECK_CODE_ACTIONS) {
// test associated quick-fix code action
JakartaJavaCodeActionParams codeActionParams = JakartaForJavaAssert.createCodeActionParams(uri, d);
String newText = "package io.openliberty.sample.jakarta.servlet;" +
"\n\nimport jakarta.servlet.Filter;\nimport jakarta.servlet.annotation.WebFilter;" +
"\n\n@WebFilter(urlPatterns = {\"/filter\"})\npublic class DontImplementFilter implements " +
"Filter {\n\n}";
TextEdit te = JakartaForJavaAssert.te(0, 0, 7, 1, newText);

CodeAction ca = JakartaForJavaAssert.ca(uri, "Let 'DontImplementFilter' implement 'Filter'", d, te);
JakartaForJavaAssert.assertJavaCodeAction(codeActionParams, utils, ca);
// }
}

@Test
public void implementListener() throws Exception {
Module module = createMavenModule(new File("src/test/resources/projects/maven/jakarta-sample"));
IPsiUtils utils = PsiUtilsLSImpl.getInstance(myProject);

VirtualFile javaFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(ModuleUtilCore.getModuleDirPath(module)
+ "/src/main/java/io/openliberty/sample/jakarta/servlet/DontImplementListener.java");
String uri = VfsUtilCore.virtualToIoFile(javaFile).toURI().toString();

JakartaDiagnosticsParams diagnosticsParams = new JakartaDiagnosticsParams();
diagnosticsParams.setUris(Arrays.asList(uri));

Diagnostic d = JakartaForJavaAssert.d(5, 13, 34, "Annotated classes with @WebListener must implement one or more of the following interfaces: ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener, HttpSessionListener, HttpSessionAttributeListener, or HttpSessionIdListener.",
DiagnosticSeverity.Error, "jakarta-servlet", "ImplementListener");

JakartaForJavaAssert.assertJavaDiagnostics(diagnosticsParams, utils, d);

//TODO: this condition will be enabled when all quickfixes are refactored.
// if (CHECK_CODE_ACTIONS) {
// test associated quick-fix code action
JakartaJavaCodeActionParams codeActionParams = JakartaForJavaAssert.createCodeActionParams(uri, d);

String newText = "package io.openliberty.sample.jakarta.servlet;\n\nimport jakarta.servlet.annotation." +
"WebListener;\nimport jakarta.servlet.http.HttpSessionAttributeListener;\n\n@WebListener\n" +
"public class DontImplementListener implements HttpSessionAttributeListener {\n\n}";
TextEdit te1 = JakartaForJavaAssert.te(0, 0, 7, 1, newText);
CodeAction ca1 = JakartaForJavaAssert.ca(uri, "Let 'DontImplementListener' implement 'HttpSessionAttributeListener'", d, te1);

newText = "package io.openliberty.sample.jakarta.servlet;\n\nimport jakarta.servlet.annotation." +
"WebListener;\nimport jakarta.servlet.http.HttpSessionIdListener;\n\n@WebListener\n" +
"public class DontImplementListener implements HttpSessionIdListener {\n\n}";
TextEdit te2 = JakartaForJavaAssert.te(0, 0, 7, 1, newText);
CodeAction ca2 = JakartaForJavaAssert.ca(uri, "Let 'DontImplementListener' implement 'HttpSessionIdListener'", d, te2);

newText = "package io.openliberty.sample.jakarta.servlet;\n\nimport jakarta.servlet.annotation." +
"WebListener;\nimport jakarta.servlet.http.HttpSessionListener;\n\n@WebListener\npublic" +
" class DontImplementListener implements HttpSessionListener {\n\n}";
TextEdit te3 = JakartaForJavaAssert.te(0, 0, 7, 1, newText);
CodeAction ca3 = JakartaForJavaAssert.ca(uri, "Let 'DontImplementListener' implement 'HttpSessionListener'", d, te3);

newText = "package io.openliberty.sample.jakarta.servlet;\n\nimport jakarta.servlet." +
"ServletContextAttributeListener;\nimport jakarta.servlet.annotation.WebListener;\n\n" +
"@WebListener\npublic class DontImplementListener implements ServletContextAttributeListener {\n\n}";
TextEdit te4 = JakartaForJavaAssert.te(0, 0, 7, 1, newText);
CodeAction ca4 = JakartaForJavaAssert.ca(uri, "Let 'DontImplementListener' implement 'ServletContextAttributeListener'", d, te4);

newText = "package io.openliberty.sample.jakarta.servlet;\n\nimport jakarta.servlet.ServletContextListener;" +
"\nimport jakarta.servlet.annotation.WebListener;\n\n@WebListener\npublic class DontImplementListener" +
" implements ServletContextListener {\n\n}";
TextEdit te5 = JakartaForJavaAssert.te(0, 0, 7, 1, newText);
CodeAction ca5 = JakartaForJavaAssert.ca(uri, "Let 'DontImplementListener' implement 'ServletContextListener'", d, te5);

newText = "package io.openliberty.sample.jakarta.servlet;\n\nimport jakarta.servlet." +
"ServletRequestAttributeListener;\nimport jakarta.servlet.annotation.WebListener;\n\n" +
"@WebListener\npublic class DontImplementListener implements ServletRequestAttributeListener {\n\n}";
TextEdit te6 = JakartaForJavaAssert.te(0, 0, 7, 1, newText);
CodeAction ca6 = JakartaForJavaAssert.ca(uri, "Let 'DontImplementListener' implement 'ServletRequestAttributeListener'", d, te6);

newText = "package io.openliberty.sample.jakarta.servlet;\n\nimport jakarta.servlet.ServletRequestListener;" +
"\nimport jakarta.servlet.annotation.WebListener;\n\n@WebListener\npublic class DontImplementListener" +
" implements ServletRequestListener {\n\n}";
TextEdit te7 = JakartaForJavaAssert.te(0, 0, 7, 1, newText);
CodeAction ca7 = JakartaForJavaAssert.ca(uri, "Let 'DontImplementListener' implement 'ServletRequestListener'", d, te7);

JakartaForJavaAssert.assertJavaCodeAction(codeActionParams, utils, ca1, ca2, ca3, ca4, ca5, ca6, ca7);
}
// }
}
Loading
Loading