Skip to content

Commit

Permalink
Merge pull request #726 from aparnamichael/issue511_-ImplementInterfa…
Browse files Browse the repository at this point in the history
…ceProposal_quickfix

Fixes#511  Transform the quick fixes which use ImplementInterfaceProposal to implement the code action participant extension point.
  • Loading branch information
aparnamichael authored May 14, 2024
2 parents 4301f68 + c733b6a commit 596c531
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 49 deletions.
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-di#RemoveInject"
implementationClass="io.openliberty.tools.intellij.lsp4jakarta.lsp4ij.codeAction.proposal.quickfix.RemoveInjectAnnotationQuickFix"/>

<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

0 comments on commit 596c531

Please sign in to comment.