This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts-intellij-plugin.git


The following commit(s) were added to refs/heads/main by this push:
     new acc0caa  Struts 7 action detection + @StrutsParameter quick-fix (#110)
acc0caa is described below

commit acc0caa48b8664ef46497d28b1faa8229d87ae00
Author: Lukasz Lenart <[email protected]>
AuthorDate: Thu Jul 2 16:58:40 2026 +0200

    Struts 7 action detection + @StrutsParameter quick-fix (#110)
    
    * fix(inspection): detect Struts 7 actions via modern Action interface
    
    Recognize actions implementing org.apache.struts2.action.Action (Struts 7+)
    in addition to the legacy com.opensymphony.xwork2.Action, and check the
    action interface independently of the Convention plugin. Only the *Action
    naming convention remains gated behind Convention plugin presence.
    
    This lets the @StrutsParameter inspection cover convention/interface-based
    actions on Struts 7, where the legacy XWork Action interface no longer 
exists.
    
    Co-authored-by: Cursor <[email protected]>
    
    * feat(inspection): add "Annotate with @StrutsParameter" quick-fix
    
    Offer a one-click fix on the missing-annotation warning that inserts
    @StrutsParameter on the flagged field or setter and shortens the import.
    
    Co-authored-by: Cursor <[email protected]>
    
    ---------
    
    Co-authored-by: Cursor <[email protected]>
---
 CHANGELOG.md                                       |  2 +
 .../java/com/intellij/struts2/StrutsConstants.java |  6 ++
 .../struts2/inspection/StrutsActionClassUtil.java  | 16 +++--
 .../StrutsParameterAnnotationInspection.java       | 33 ++++++++-
 .../resources/messages/Struts2Bundle.properties    |  1 +
 .../StrutsParameterAnnotationInspectionTest.java   | 80 ++++++++++++++++++++++
 6 files changed, 131 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8a1a56b..99ae7c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 ### Added
 
 - Add Java inspection for Struts action setters and public fields missing 
`@StrutsParameter` when annotation-based parameter binding is required
+- Add "Annotate with `@StrutsParameter`" quick-fix for the missing-annotation 
inspection
 - Diagram tab auto-refreshes when `struts.xml` is edited (same file, active 
tab) and on tab activation after Text edits 
([#97](https://github.com/apache/struts-intellij-plugin/issues/97))
 
 ### Removed
@@ -15,6 +16,7 @@
 
 ### Fixed
 
+- Recognize Struts 7 actions implementing `org.apache.struts2.action.Action` 
(and detect the action interface independently of the Convention plugin) so the 
`@StrutsParameter` inspection also covers convention/interface-based actions
 - Update Struts 7.2.1 metadata support: add current constants and completion 
values for annotation-required parameters, chaining annotation checks, 
`html5`/`css_xhtml` themes, `jakarta-stream` multipart parsing, and modern 
web.xml Struts filters
 - Fix false "Cannot resolve symbol" errors for namespace-relative JSP result 
paths (e.g. `WEB-INF/upload.jsp` without leading slash)
 - Fix path completion inside `<result>` tags by restoring correct 
`FileReferenceSet` construction for IntelliJ 2026.1
diff --git a/src/main/java/com/intellij/struts2/StrutsConstants.java 
b/src/main/java/com/intellij/struts2/StrutsConstants.java
index 26903c9..cb0c81d 100644
--- a/src/main/java/com/intellij/struts2/StrutsConstants.java
+++ b/src/main/java/com/intellij/struts2/StrutsConstants.java
@@ -40,6 +40,12 @@ public final class StrutsConstants {
   @NonNls
   public static final String XWORK_ACTION_CLASS = 
"com.opensymphony.xwork2.Action";
 
+  /**
+   * Modern Struts action interface (Struts 7+), replacing the legacy {@link 
#XWORK_ACTION_CLASS}.
+   */
+  @NonNls
+  public static final String STRUTS_ACTION_CLASS = 
"org.apache.struts2.action.Action";
+
   @NonNls
   public static final String STRUTS_PARAMETER_ANNOTATION =
       "org.apache.struts2.interceptor.parameter.StrutsParameter";
diff --git 
a/src/main/java/com/intellij/struts2/inspection/StrutsActionClassUtil.java 
b/src/main/java/com/intellij/struts2/inspection/StrutsActionClassUtil.java
index 2f2b2f2..3e1d46f 100644
--- a/src/main/java/com/intellij/struts2/inspection/StrutsActionClassUtil.java
+++ b/src/main/java/com/intellij/struts2/inspection/StrutsActionClassUtil.java
@@ -55,16 +55,20 @@ final class StrutsActionClassUtil {
       return true;
     }
 
-    if (!isConventionPluginPresent(psiClass)) {
-      return false;
+    // Implementing the Struts action interface marks it as an action, 
regardless of the Convention plugin.
+    // Struts 7+ uses org.apache.struts2.action.Action; earlier versions use 
com.opensymphony.xwork2.Action.
+    if (InheritanceUtil.isInheritor(psiClass, 
StrutsConstants.STRUTS_ACTION_CLASS) ||
+        InheritanceUtil.isInheritor(psiClass, 
StrutsConstants.XWORK_ACTION_CLASS)) {
+      return true;
     }
 
-    final String className = psiClass.getName();
-    if (className != null && StringUtil.endsWith(className, "Action")) {
-      return true;
+    // The *Action naming convention only applies when the Convention plugin 
is present.
+    if (isConventionPluginPresent(psiClass)) {
+      final String className = psiClass.getName();
+      return className != null && StringUtil.endsWith(className, "Action");
     }
 
-    return InheritanceUtil.isInheritor(psiClass, 
StrutsConstants.XWORK_ACTION_CLASS);
+    return false;
   }
 
   private static boolean isConcretePublicClass(@Nullable PsiClass psiClass) {
diff --git 
a/src/main/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspection.java
 
b/src/main/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspection.java
index 7a113b5..010cd35 100644
--- 
a/src/main/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspection.java
+++ 
b/src/main/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspection.java
@@ -15,15 +15,24 @@
 package com.intellij.struts2.inspection;
 
 import com.intellij.codeInspection.LocalInspectionTool;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
 import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.project.Project;
 import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiAnnotation;
 import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiElementVisitor;
 import com.intellij.psi.PsiField;
 import com.intellij.psi.PsiIdentifier;
 import com.intellij.psi.PsiJavaFile;
 import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiModifierList;
+import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import com.intellij.psi.util.PsiTreeUtil;
 import com.intellij.struts2.StrutsBundle;
+import com.intellij.struts2.StrutsConstants;
 import com.intellij.struts2.facet.StrutsFacet;
 import org.jetbrains.annotations.NotNull;
 
@@ -66,8 +75,30 @@ public final class StrutsParameterAnnotationInspection 
extends LocalInspectionTo
         }
 
         holder.registerProblem(identifier,
-                               
StrutsBundle.message("inspections.struts.parameter.annotation.message"));
+                               
StrutsBundle.message("inspections.struts.parameter.annotation.message"),
+                               new AddStrutsParameterAnnotationFix());
       }
     };
   }
+
+  private static final class AddStrutsParameterAnnotationFix implements 
LocalQuickFix {
+    @Override
+    public @NotNull String getFamilyName() {
+      return 
StrutsBundle.message("inspections.struts.parameter.annotation.fix");
+    }
+
+    @Override
+    public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor 
descriptor) {
+      final PsiModifierListOwner owner =
+        PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), 
PsiModifierListOwner.class);
+      final PsiModifierList modifierList = owner != null ? 
owner.getModifierList() : null;
+      if (modifierList == null ||
+          
modifierList.hasAnnotation(StrutsConstants.STRUTS_PARAMETER_ANNOTATION)) {
+        return;
+      }
+
+      final PsiAnnotation annotation = 
modifierList.addAnnotation(StrutsConstants.STRUTS_PARAMETER_ANNOTATION);
+      
JavaCodeStyleManager.getInstance(project).shortenClassReferences(annotation);
+    }
+  }
 }
diff --git a/src/main/resources/messages/Struts2Bundle.properties 
b/src/main/resources/messages/Struts2Bundle.properties
index b9064e7..39b3afd 100644
--- a/src/main/resources/messages/Struts2Bundle.properties
+++ b/src/main/resources/messages/Struts2Bundle.properties
@@ -66,6 +66,7 @@ inspections.hardcoded.action.url.display.name=Hardcoded 
action URL
 
 inspections.struts.parameter.annotation.display.name=Missing StrutsParameter 
annotation
 inspections.struts.parameter.annotation.message=Parameter injection requires 
@StrutsParameter when struts.parameters.requireAnnotations is enabled
+inspections.struts.parameter.annotation.fix=Annotate with @StrutsParameter
 
 intentions.family.name=Struts 2
 
diff --git 
a/src/test/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspectionTest.java
 
b/src/test/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspectionTest.java
index ff6f0c5..401bb1b 100644
--- 
a/src/test/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspectionTest.java
+++ 
b/src/test/java/com/intellij/struts2/inspection/StrutsParameterAnnotationInspectionTest.java
@@ -14,6 +14,7 @@
  */
 package com.intellij.struts2.inspection;
 
+import com.intellij.codeInsight.intention.IntentionAction;
 import com.intellij.codeInspection.InspectionProfileEntry;
 import com.intellij.psi.PsiFile;
 import com.intellij.struts2.BasicLightHighlightingTestCase;
@@ -52,6 +53,32 @@ public class StrutsParameterAnnotationInspectionTest extends 
BasicLightHighlight
     myFixture.checkHighlighting();
   }
 
+  public void testWarnsInActionImplementingModernActionInterface() {
+    configureStrutsParameterAnnotation();
+    configureModernActionInterface();
+    createStrutsFileSetFromFixture("struts.xml", 
"struts-require-annotations.xml");
+
+    configureFileForHighlighting("test/ModernController.java", """
+      package test;
+
+      import org.apache.struts2.action.Action;
+
+      public class ModernController implements Action {
+        public String <warning descr="%s">username</warning>;
+
+        public void <warning descr="%s">setPassword</warning>(String password) 
{
+        }
+
+        @Override
+        public String execute() {
+          return SUCCESS;
+        }
+      }
+      """.formatted(WARNING, WARNING));
+
+    myFixture.checkHighlighting();
+  }
+
   public void testDoesNotWarnAboutAnnotatedMembers() {
     configureStrutsParameterAnnotation();
     createStrutsFileSetFromFixture("struts.xml", 
"struts-require-annotations.xml");
@@ -144,6 +171,47 @@ public class StrutsParameterAnnotationInspectionTest 
extends BasicLightHighlight
     myFixture.checkHighlighting();
   }
 
+  public void testQuickFixAnnotatesInjectableMember() {
+    configureStrutsParameterAnnotation();
+    configureModernActionInterface();
+    createStrutsFileSetFromFixture("struts.xml", 
"struts-require-annotations.xml");
+
+    myFixture.configureByText("ModernController.java", """
+      package test;
+
+      import org.apache.struts2.action.Action;
+
+      public class ModernController implements Action {
+        public String user<caret>name;
+
+        @Override
+        public String execute() {
+          return SUCCESS;
+        }
+      }
+      """);
+
+    final IntentionAction fix = myFixture.findSingleIntention("Annotate with 
@StrutsParameter");
+    myFixture.launchAction(fix);
+
+    myFixture.checkResult("""
+      package test;
+
+      import org.apache.struts2.action.Action;
+      import org.apache.struts2.interceptor.parameter.StrutsParameter;
+
+      public class ModernController implements Action {
+        @StrutsParameter
+        public String username;
+
+        @Override
+        public String execute() {
+          return SUCCESS;
+        }
+      }
+      """);
+  }
+
   private void configureStrutsParameterAnnotation() {
     
myFixture.addFileToProject("org/apache/struts2/interceptor/parameter/StrutsParameter.java",
 """
       package org.apache.struts2.interceptor.parameter;
@@ -154,6 +222,18 @@ public class StrutsParameterAnnotationInspectionTest 
extends BasicLightHighlight
       """);
   }
 
+  private void configureModernActionInterface() {
+    myFixture.addFileToProject("org/apache/struts2/action/Action.java", """
+      package org.apache.struts2.action;
+
+      public interface Action {
+        String SUCCESS = "success";
+
+        String execute() throws Exception;
+      }
+      """);
+  }
+
   private void configureFileForHighlighting(@NotNull String path, @NotNull 
String text) {
     final PsiFile psiFile = myFixture.addFileToProject(path, text);
     myFixture.configureFromExistingVirtualFile(psiFile.getVirtualFile());

Reply via email to