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 194d391  fix: resolve 21 critical nullability/NPE warnings from Qodana 
CI (#51)
194d391 is described below

commit 194d39166ca6605148495fe2cda334b178b49970
Author: Lukasz Lenart <[email protected]>
AuthorDate: Tue Feb 24 15:25:52 2026 +0100

    fix: resolve 21 critical nullability/NPE warnings from Qodana CI (#51)
    
    Add null guards and fix nullable annotations across 15 files to prevent
    potential runtime NPE crashes identified by Qodana static analysis:
    
    - ResultNode: replace FileTypes.UNKNOWN.getIcon() with 
AllIcons.FileTypes.Unknown
    - Struts2GlobalVariableProvider (freemarker): null-guard action.getXmlTag()
    - ActionPathResultContributor: null-guard action.getXmlTag() in 
getVariants()
    - ActionChainOrRedirectResultContributor: same pattern in getVariants()
    - GoToActionSymbolProvider: null-guard action.getXmlTag() in addItems()
    - ResultActionPropertyReferenceProvider: null-guard result.getXmlTag(), 
simplify dead Math.max()
    - StrutsConstantValueReference: null-guard getErrorMessage() return
    - CreateValidationXmlIntention: null-guard action.getName().getStringValue()
    - ExtendableClassConverterSpringContributor: null-guard 
getPossibleSubClasses()
    - ConstantValueConverterImpl: null-guard domElement.getXmlTag()
    - HardcodedActionUrlInspection: assert buildTag result non-null
    - ValidatorManagerImpl: null-guard clazz.getName()
    - GotoRelatedActionProvider: null-guard pathReference.resolve()
    - StrutsConventionImplicitUsageProvider: null-guard psiClass.getName()
    - StrutsDataModel: fix @Nullable to @NotNull on createGroupNodeRealizer()
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-authored-by: Claude <[email protected]>
---
 .../StrutsConventionImplicitUsageProvider.java     | 119 ++++----
 .../constant/ConstantValueConverterImpl.java       |  33 +--
 .../ActionChainOrRedirectResultContributor.java    | 186 ++++++-------
 .../impl/path/ActionPathResultContributor.java     | 242 ++++++++---------
 .../dom/validator/ValidatorManagerImpl.java        | 238 ++++++++--------
 .../freemarker/Struts2GlobalVariableProvider.java  | 205 +++++++-------
 .../gotosymbol/GoToActionSymbolProvider.java       |  68 ++---
 .../gotosymbol/GotoRelatedActionProvider.java      | 100 +++----
 .../intellij/struts2/graph/StrutsDataModel.java    | 262 +++++++++---------
 .../intellij/struts2/graph/beans/ResultNode.java   |  48 ++--
 .../code/CreateValidationXmlIntention.java         | 300 ++++++++++-----------
 .../inspection/HardcodedActionUrlInspection.java   |   1 +
 .../ResultActionPropertyReferenceProvider.java     | 148 +++++-----
 .../web/StrutsConstantValueReference.java          | 223 +++++++--------
 .../ExtendableClassConverterSpringContributor.java | 199 +++++++-------
 15 files changed, 1199 insertions(+), 1173 deletions(-)

diff --git 
a/src/main/java/com/intellij/struts2/annotators/StrutsConventionImplicitUsageProvider.java
 
b/src/main/java/com/intellij/struts2/annotators/StrutsConventionImplicitUsageProvider.java
index 35d5ca4..ba54aa4 100644
--- 
a/src/main/java/com/intellij/struts2/annotators/StrutsConventionImplicitUsageProvider.java
+++ 
b/src/main/java/com/intellij/struts2/annotators/StrutsConventionImplicitUsageProvider.java
@@ -29,76 +29,77 @@ import org.jetbrains.annotations.Nullable;
  */
 public class StrutsConventionImplicitUsageProvider implements 
ImplicitUsageProvider {
 
-  @Override
-  public boolean isImplicitUsage(@NotNull PsiElement element) {
-    if (element instanceof PsiClass) {
-      return isConventionActionClass((PsiClass)element);
+    @Override
+    public boolean isImplicitUsage(@NotNull PsiElement element) {
+        if (element instanceof PsiClass) {
+            return isConventionActionClass((PsiClass) element);
+        }
+
+        if (element instanceof PsiMethod psiMethod) {
+            if (!checkMethod(psiMethod)) {
+                return false;
+            }
+
+            return isAnnotatedWithAction(psiMethod) ||
+                    isConventionActionClass(psiMethod.getContainingClass());
+        }
+        return false;
     }
 
-    if (element instanceof PsiMethod psiMethod) {
-      if (!checkMethod(psiMethod)) {
+    @Override
+    public boolean isImplicitRead(@NotNull PsiElement element) {
         return false;
-      }
-
-      return isAnnotatedWithAction(psiMethod) ||
-             isConventionActionClass(psiMethod.getContainingClass());
-    }
-    return false;
-  }
-
-  @Override
-  public boolean isImplicitRead(@NotNull PsiElement element) {
-    return false;
-  }
-
-  @Override
-  public boolean isImplicitWrite(@NotNull PsiElement element) {
-    return false;
-  }
-
-  private static boolean checkMethod(PsiMethod psiMethod) {
-    return psiMethod.hasModifierProperty(PsiModifier.PUBLIC) &&
-           !psiMethod.isConstructor() &&
-           !psiMethod.hasModifierProperty(PsiModifier.STATIC) &&
-           !psiMethod.hasModifierProperty(PsiModifier.ABSTRACT);
-  }
-
-  private static boolean isAnnotatedWithAction(PsiModifierListOwner 
psiModifierListOwner) {
-    return AnnotationUtil.isAnnotated(psiModifierListOwner, 
StrutsConventionConstants.ACTION, 0);
-  }
-
-  private static boolean isConventionActionClass(@Nullable PsiClass psiClass) {
-    if (psiClass == null ||
-        psiClass.isInterface() ||
-        psiClass.isEnum() ||
-        psiClass.isAnnotationType() ||
-        !psiClass.hasModifierProperty(PsiModifier.PUBLIC) ||
-        psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
-      return false;
     }
 
-    if (isAnnotatedWithAction(psiClass)) {
-      return true;
+    @Override
+    public boolean isImplicitWrite(@NotNull PsiElement element) {
+        return false;
     }
 
-    if (AnnotationUtil.isAnnotated(psiClass, 
StrutsConventionConstants.ACTIONS, 0)) {
-      return true;
+    private static boolean checkMethod(PsiMethod psiMethod) {
+        return psiMethod.hasModifierProperty(PsiModifier.PUBLIC) &&
+                !psiMethod.isConstructor() &&
+                !psiMethod.hasModifierProperty(PsiModifier.STATIC) &&
+                !psiMethod.hasModifierProperty(PsiModifier.ABSTRACT);
     }
 
-    if (!isConventionPluginPresent(psiClass)) {
-      return false;
+    private static boolean isAnnotatedWithAction(PsiModifierListOwner 
psiModifierListOwner) {
+        return AnnotationUtil.isAnnotated(psiModifierListOwner, 
StrutsConventionConstants.ACTION, 0);
     }
 
-    if (StringUtil.endsWith(psiClass.getName(), "Action")) {
-      return true;
+    private static boolean isConventionActionClass(@Nullable PsiClass 
psiClass) {
+        if (psiClass == null ||
+                psiClass.isInterface() ||
+                psiClass.isEnum() ||
+                psiClass.isAnnotationType() ||
+                !psiClass.hasModifierProperty(PsiModifier.PUBLIC) ||
+                psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
+            return false;
+        }
+
+        if (isAnnotatedWithAction(psiClass)) {
+            return true;
+        }
+
+        if (AnnotationUtil.isAnnotated(psiClass, 
StrutsConventionConstants.ACTIONS, 0)) {
+            return true;
+        }
+
+        if (!isConventionPluginPresent(psiClass)) {
+            return false;
+        }
+
+        final String className = psiClass.getName();
+        if (className != null && StringUtil.endsWith(className, "Action")) {
+            return true;
+        }
+
+        return InheritanceUtil.isInheritor(psiClass, 
StrutsConstants.XWORK_ACTION_CLASS);
     }
 
-    return InheritanceUtil.isInheritor(psiClass, 
StrutsConstants.XWORK_ACTION_CLASS);
-  }
-
-  private static boolean isConventionPluginPresent(PsiElement element) {
-    final PsiClass conventionService = 
JavaPsiFacade.getInstance(element.getProject()).
-      findClass(StrutsConventionConstants.CONVENTIONS_SERVICE, 
element.getResolveScope());
-    return conventionService != null;
-  }
+    private static boolean isConventionPluginPresent(PsiElement element) {
+        final PsiClass conventionService = 
JavaPsiFacade.getInstance(element.getProject()).
+                findClass(StrutsConventionConstants.CONVENTIONS_SERVICE, 
element.getResolveScope());
+        return conventionService != null;
+    }
 }
diff --git 
a/src/main/java/com/intellij/struts2/dom/struts/constant/ConstantValueConverterImpl.java
 
b/src/main/java/com/intellij/struts2/dom/struts/constant/ConstantValueConverterImpl.java
index 30bb89c..a6a20a1 100644
--- 
a/src/main/java/com/intellij/struts2/dom/struts/constant/ConstantValueConverterImpl.java
+++ 
b/src/main/java/com/intellij/struts2/dom/struts/constant/ConstantValueConverterImpl.java
@@ -29,21 +29,24 @@ import org.jetbrains.annotations.Nullable;
  */
 public class ConstantValueConverterImpl extends ConstantValueConverter {
 
-  @Override
-  @Nullable
-  public Converter<?> getConverter(@NotNull final GenericDomValue domElement) {
-    final Constant constant = (Constant) domElement.getParent();
-    assert constant != null;
-
-    final String constantName = constant.getName().getStringValue();
-    if (StringUtil.isEmpty(constantName)) {
-      return null;
+    @Override
+    @Nullable
+    public Converter<?> getConverter(@NotNull final GenericDomValue 
domElement) {
+        final Constant constant = (Constant) domElement.getParent();
+        assert constant != null;
+
+        final String constantName = constant.getName().getStringValue();
+        if (StringUtil.isEmpty(constantName)) {
+            return null;
+        }
+
+        final XmlTag xmlTag = domElement.getXmlTag();
+        if (xmlTag == null) {
+            return null;
+        }
+        final StrutsConstantManager constantManager = 
StrutsConstantManager.getInstance(xmlTag.getProject());
+
+        return constantManager.findConverter(xmlTag, 
StrutsConstantKey.create(constantName));
     }
 
-    final XmlTag xmlTag = domElement.getXmlTag();
-    final StrutsConstantManager constantManager = 
StrutsConstantManager.getInstance(xmlTag.getProject());
-
-    return constantManager.findConverter(xmlTag, 
StrutsConstantKey.create(constantName));
-  }
-
 }
\ No newline at end of file
diff --git 
a/src/main/java/com/intellij/struts2/dom/struts/impl/path/ActionChainOrRedirectResultContributor.java
 
b/src/main/java/com/intellij/struts2/dom/struts/impl/path/ActionChainOrRedirectResultContributor.java
index 9f6f869..6117607 100644
--- 
a/src/main/java/com/intellij/struts2/dom/struts/impl/path/ActionChainOrRedirectResultContributor.java
+++ 
b/src/main/java/com/intellij/struts2/dom/struts/impl/path/ActionChainOrRedirectResultContributor.java
@@ -45,111 +45,113 @@ import java.util.Objects;
  */
 public class ActionChainOrRedirectResultContributor extends 
StrutsResultContributor {
 
-  @Override
-  public boolean matchesResultType(@NotNull @NonNls final String resultType) {
-    return ResultTypeResolver.isChainOrRedirectType(resultType);
-  }
-
-  @Override
-  public boolean createReferences(@NotNull final PsiElement psiElement,
-                                  final @NotNull List<PsiReference> references,
-                                  final boolean soft) {
-    final StrutsModel model = 
StrutsManager.getInstance(psiElement.getProject())
-      .getModelByFile((XmlFile) psiElement.getContainingFile());
-    if (model == null) {
-      return false;
+    @Override
+    public boolean matchesResultType(@NotNull @NonNls final String resultType) 
{
+        return ResultTypeResolver.isChainOrRedirectType(resultType);
     }
 
-    final String currentPackage = getNamespace(psiElement);
-    if (currentPackage == null) {
-      return false;
-    }
+    @Override
+    public boolean createReferences(@NotNull final PsiElement psiElement,
+                                    final @NotNull List<PsiReference> 
references,
+                                    final boolean soft) {
+        final StrutsModel model = 
StrutsManager.getInstance(psiElement.getProject())
+                .getModelByFile((XmlFile) psiElement.getContainingFile());
+        if (model == null) {
+            return false;
+        }
 
-    final PsiReference chainReference = new ActionChainReference((XmlTag) 
psiElement, currentPackage, model);
-    references.add(chainReference);
-    return true;
-  }
+        final String currentPackage = getNamespace(psiElement);
+        if (currentPackage == null) {
+            return false;
+        }
 
-  @Override
-  @Nullable
-  public PathReference getPathReference(@NotNull final String path, @NotNull 
final PsiElement element) {
-    return createDefaultPathReference(path, element, Struts2Icons.Action);
-  }
+        final PsiReference chainReference = new ActionChainReference((XmlTag) 
psiElement, currentPackage, model);
+        references.add(chainReference);
+        return true;
+    }
 
+    @Override
+    @Nullable
+    public PathReference getPathReference(@NotNull final String path, @NotNull 
final PsiElement element) {
+        return createDefaultPathReference(path, element, Struts2Icons.Action);
+    }
 
-  private static final class ActionChainReference extends 
PsiReferenceBase<XmlTag> implements EmptyResolveMessageProvider {
 
-    private final String currentPackage;
-    private final StrutsModel model;
+    private static final class ActionChainReference extends 
PsiReferenceBase<XmlTag> implements EmptyResolveMessageProvider {
 
-    private ActionChainReference(final XmlTag psiElement,
-                                 final String currentPackage,
-                                 final StrutsModel model) {
-      super(psiElement, true);
-      this.currentPackage = currentPackage;
-      this.model = model;
-    }
+        private final String currentPackage;
+        private final StrutsModel model;
 
-    @Override
-    public PsiElement resolve() {
-      final XmlTagValue tagValue = myElement.getValue();
-      final String path = PathReference.trimPath(tagValue.getText());
-
-      // use given namespace or current if none given
-      final int namespacePrefixIndex = path.lastIndexOf("/");
-      final String namespace;
-      if (namespacePrefixIndex != -1) {
-        namespace = path.substring(0, namespacePrefixIndex);
-      } else {
-        namespace = currentPackage;
-      }
-
-      final String strippedPath = path.substring(namespacePrefixIndex != -1 ? 
namespacePrefixIndex + 1 : 0);
-      final List<Action> actions = model.findActionsByName(strippedPath, 
namespace);
-      if (actions.size() == 1) {
-        final Action action = actions.get(0);
-        return action.getXmlTag();
-      }
-
-      return null;
-    }
+        private ActionChainReference(final XmlTag psiElement,
+                                     final String currentPackage,
+                                     final StrutsModel model) {
+            super(psiElement, true);
+            this.currentPackage = currentPackage;
+            this.model = model;
+        }
 
-    @Override
-    public Object @NotNull [] getVariants() {
-      final List<Action> allActions = model.getActionsForNamespace(null);
-      final List<LookupElementBuilder> variants = new 
ArrayList<>(allActions.size());
-      for (final Action action : allActions) {
-        final String actionPath = action.getName().getStringValue();
-        if (actionPath != null) {
-          final boolean isInCurrentPackage = 
Objects.equals(action.getNamespace(), currentPackage);
-
-          // prepend package-name if not default ("/") or "current" package
-          final String actionNamespace = action.getNamespace();
-          final String fullPath;
-          if (!Objects.equals(actionNamespace, 
StrutsPackage.DEFAULT_NAMESPACE) &&
-              !isInCurrentPackage) {
-            fullPath = actionNamespace + "/" + actionPath;
-          } else {
-            fullPath = actionPath;
-          }
-
-          final LookupElementBuilder builder = 
LookupElementBuilder.create(action.getXmlTag(), fullPath)
-            .withBoldness(isInCurrentPackage)
-            .withIcon(Struts2Icons.Action)
-            .withTypeText(action.getNamespace());
-          variants.add(builder);
+        @Override
+        public PsiElement resolve() {
+            final XmlTagValue tagValue = myElement.getValue();
+            final String path = PathReference.trimPath(tagValue.getText());
+
+            // use given namespace or current if none given
+            final int namespacePrefixIndex = path.lastIndexOf("/");
+            final String namespace;
+            if (namespacePrefixIndex != -1) {
+                namespace = path.substring(0, namespacePrefixIndex);
+            } else {
+                namespace = currentPackage;
+            }
+
+            final String strippedPath = path.substring(namespacePrefixIndex != 
-1 ? namespacePrefixIndex + 1 : 0);
+            final List<Action> actions = model.findActionsByName(strippedPath, 
namespace);
+            if (actions.size() == 1) {
+                final Action action = actions.get(0);
+                return action.getXmlTag();
+            }
+
+            return null;
         }
-      }
 
-      return ArrayUtil.toObjectArray(variants);
-    }
+        @Override
+        public Object @NotNull [] getVariants() {
+            final List<Action> allActions = model.getActionsForNamespace(null);
+            final List<LookupElementBuilder> variants = new 
ArrayList<>(allActions.size());
+            for (final Action action : allActions) {
+                final String actionPath = action.getName().getStringValue();
+                if (actionPath != null) {
+                    final boolean isInCurrentPackage = 
Objects.equals(action.getNamespace(), currentPackage);
+
+                    // prepend package-name if not default ("/") or "current" 
package
+                    final String actionNamespace = action.getNamespace();
+                    final String fullPath;
+                    if (!Objects.equals(actionNamespace, 
StrutsPackage.DEFAULT_NAMESPACE) &&
+                            !isInCurrentPackage) {
+                        fullPath = actionNamespace + "/" + actionPath;
+                    } else {
+                        fullPath = actionPath;
+                    }
+
+                    final XmlTag actionTag = action.getXmlTag();
+                    if (actionTag == null) continue;
+                    final LookupElementBuilder builder = 
LookupElementBuilder.create(actionTag, fullPath)
+                            .withBoldness(isInCurrentPackage)
+                            .withIcon(Struts2Icons.Action)
+                            .withTypeText(action.getNamespace());
+                    variants.add(builder);
+                }
+            }
+
+            return ArrayUtil.toObjectArray(variants);
+        }
 
-    @NotNull
-    @Override
-    public String getUnresolvedMessagePattern() {
-      return "Cannot resolve Action '" + getValue() + "'";
-    }
+        @NotNull
+        @Override
+        public String getUnresolvedMessagePattern() {
+            return "Cannot resolve Action '" + getValue() + "'";
+        }
 
-  }
+    }
 
 }
diff --git 
a/src/main/java/com/intellij/struts2/dom/struts/impl/path/ActionPathResultContributor.java
 
b/src/main/java/com/intellij/struts2/dom/struts/impl/path/ActionPathResultContributor.java
index dad762b..0e1ac24 100644
--- 
a/src/main/java/com/intellij/struts2/dom/struts/impl/path/ActionPathResultContributor.java
+++ 
b/src/main/java/com/intellij/struts2/dom/struts/impl/path/ActionPathResultContributor.java
@@ -49,142 +49,144 @@ import java.util.Objects;
  */
 public class ActionPathResultContributor extends StrutsResultContributor {
 
-  @Override
-  public boolean matchesResultType(@NotNull @NonNls final String resultType) {
-    return ResultTypeResolver.isDispatchType(resultType);
-  }
-
-  @Override
-  public boolean createReferences(@NotNull final PsiElement psiElement,
-                                  final @NotNull List<PsiReference> references,
-                                  final boolean soft) {
-    final StrutsModel model = 
StrutsManager.getInstance(psiElement.getProject())
-                                           .getModelByFile((XmlFile) 
psiElement.getContainingFile());
-    if (model == null) {
-      return false;
+    @Override
+    public boolean matchesResultType(@NotNull @NonNls final String resultType) 
{
+        return ResultTypeResolver.isDispatchType(resultType);
     }
 
-    final String currentPackage = getNamespace(psiElement);
-    if (currentPackage == null) {
-      return false;
-    }
+    @Override
+    public boolean createReferences(@NotNull final PsiElement psiElement,
+                                    final @NotNull List<PsiReference> 
references,
+                                    final boolean soft) {
+        final StrutsModel model = 
StrutsManager.getInstance(psiElement.getProject())
+                .getModelByFile((XmlFile) psiElement.getContainingFile());
+        if (model == null) {
+            return false;
+        }
 
-    final TextRange rangeInElement = 
ElementManipulators.getValueTextRange(psiElement);
-    final String fullPath = psiElement.getText();
-    final String trimmedPath = rangeInElement.substring(fullPath);
-    final TextRange trimmedPathRange = 
TextRange.from(rangeInElement.getStartOffset(),
-                                                      
PathReference.trimPath(trimmedPath).length());
+        final String currentPackage = getNamespace(psiElement);
+        if (currentPackage == null) {
+            return false;
+        }
 
-    final PsiReference actionReference = new ActionPathReference((XmlTag) 
psiElement,
-                                                                 
trimmedPathRange,
-                                                                 
currentPackage,
-                                                                 model);
+        final TextRange rangeInElement = 
ElementManipulators.getValueTextRange(psiElement);
+        final String fullPath = psiElement.getText();
+        final String trimmedPath = rangeInElement.substring(fullPath);
+        final TextRange trimmedPathRange = 
TextRange.from(rangeInElement.getStartOffset(),
+                PathReference.trimPath(trimmedPath).length());
 
-    references.add(actionReference);
-    return false;
-  }
+        final PsiReference actionReference = new ActionPathReference((XmlTag) 
psiElement,
+                trimmedPathRange,
+                currentPackage,
+                model);
 
-  @Override
-  @Nullable
-  public PathReference getPathReference(@NotNull final String path, @NotNull 
final PsiElement element) {
-    return createDefaultPathReference(path, element, Struts2Icons.Action);
-  }
+        references.add(actionReference);
+        return false;
+    }
 
+    @Override
+    @Nullable
+    public PathReference getPathReference(@NotNull final String path, @NotNull 
final PsiElement element) {
+        return createDefaultPathReference(path, element, Struts2Icons.Action);
+    }
 
-  private static final class ActionPathReference extends 
PsiReferenceBase<XmlTag> {
 
-    private final String currentPackage;
-    private final StrutsModel model;
+    private static final class ActionPathReference extends 
PsiReferenceBase<XmlTag> {
 
-    private ActionPathReference(final XmlTag psiElement,
-                                final TextRange textRange,
-                                final String currentPackage,
-                                final StrutsModel model) {
-      super(psiElement, textRange, true);
-      this.currentPackage = currentPackage;
-      this.model = model;
-    }
+        private final String currentPackage;
+        private final StrutsModel model;
 
-    private List<String> getActionExtensions() {
-      return StrutsConstantHelper.getActionExtensions(myElement);
-    }
+        private ActionPathReference(final XmlTag psiElement,
+                                    final TextRange textRange,
+                                    final String currentPackage,
+                                    final StrutsModel model) {
+            super(psiElement, textRange, true);
+            this.currentPackage = currentPackage;
+            this.model = model;
+        }
 
-    @Override
-    public PsiElement resolve() {
-      final String path = getCanonicalText();
-
-      int extensionIndex = -1;
-      for (final String actionExtension : getActionExtensions()) {
-        extensionIndex = path.lastIndexOf(actionExtension);
-        if (extensionIndex != -1) {
-          break;
+        private List<String> getActionExtensions() {
+            return StrutsConstantHelper.getActionExtensions(myElement);
         }
-      }
-      if (extensionIndex == -1) {
-        return null;
-      }
-
-      // use given namespace or current if none given
-      final int namespacePrefixIndex = path.lastIndexOf("/");
-      final String namespace;
-      if (namespacePrefixIndex != -1) {
-        namespace = path.substring(0, namespacePrefixIndex);
-      } else {
-        namespace = currentPackage;
-      }
-
-      // "/XX/" behind ".extension" --> not parseable
-      if (namespacePrefixIndex > extensionIndex) {
-        return null;
-      }
-
-      final String strippedPath = path.substring(namespacePrefixIndex != -1 ? 
namespacePrefixIndex + 1 : 0,
-                                                 extensionIndex);
-      final List<Action> actions = model.findActionsByName(strippedPath, 
namespace);
-      if (actions.size() == 1) {
-        final Action action = actions.get(0);
-        return action.getXmlTag();
-      }
-
-      return null;
-    }
 
-    @Override
-    public Object @NotNull [] getVariants() {
-      final List<String> extensions = getActionExtensions();
-      if (extensions.isEmpty()) {
-        return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
-      }
-
-      final String firstExtension = extensions.get(0);
-
-      final List<Action> allActions = model.getActionsForNamespace(null);
-      final List<LookupElementBuilder> variants = new 
ArrayList<>(allActions.size());
-      for (final Action action : allActions) {
-        final String actionPath = action.getName().getStringValue();
-        if (actionPath != null) {
-          final boolean isInCurrentPackage = 
Objects.equals(action.getNamespace(), currentPackage);
-
-          // prepend package-name if not default ("/") or "current" package
-          final String actionNamespace = action.getNamespace();
-          final String fullPath;
-          if (!Objects.equals(actionNamespace, 
StrutsPackage.DEFAULT_NAMESPACE) &&
-              !isInCurrentPackage) {
-            fullPath = actionNamespace + "/" + actionPath + firstExtension;
-          } else {
-            fullPath = actionPath + firstExtension;
-          }
-
-          final LookupElementBuilder builder = 
LookupElementBuilder.create(action.getXmlTag(), fullPath)
-                                                                   
.withBoldness(isInCurrentPackage)
-                                                                   
.withIcon(Struts2Icons.Action)
-                                                                   
.withTypeText(action.getNamespace());
-          variants.add(builder);
+        @Override
+        public PsiElement resolve() {
+            final String path = getCanonicalText();
+
+            int extensionIndex = -1;
+            for (final String actionExtension : getActionExtensions()) {
+                extensionIndex = path.lastIndexOf(actionExtension);
+                if (extensionIndex != -1) {
+                    break;
+                }
+            }
+            if (extensionIndex == -1) {
+                return null;
+            }
+
+            // use given namespace or current if none given
+            final int namespacePrefixIndex = path.lastIndexOf("/");
+            final String namespace;
+            if (namespacePrefixIndex != -1) {
+                namespace = path.substring(0, namespacePrefixIndex);
+            } else {
+                namespace = currentPackage;
+            }
+
+            // "/XX/" behind ".extension" --> not parseable
+            if (namespacePrefixIndex > extensionIndex) {
+                return null;
+            }
+
+            final String strippedPath = path.substring(namespacePrefixIndex != 
-1 ? namespacePrefixIndex + 1 : 0,
+                    extensionIndex);
+            final List<Action> actions = model.findActionsByName(strippedPath, 
namespace);
+            if (actions.size() == 1) {
+                final Action action = actions.get(0);
+                return action.getXmlTag();
+            }
+
+            return null;
         }
-      }
 
-      return ArrayUtil.toObjectArray(variants);
+        @Override
+        public Object @NotNull [] getVariants() {
+            final List<String> extensions = getActionExtensions();
+            if (extensions.isEmpty()) {
+                return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
+            }
+
+            final String firstExtension = extensions.get(0);
+
+            final List<Action> allActions = model.getActionsForNamespace(null);
+            final List<LookupElementBuilder> variants = new 
ArrayList<>(allActions.size());
+            for (final Action action : allActions) {
+                final String actionPath = action.getName().getStringValue();
+                if (actionPath != null) {
+                    final boolean isInCurrentPackage = 
Objects.equals(action.getNamespace(), currentPackage);
+
+                    // prepend package-name if not default ("/") or "current" 
package
+                    final String actionNamespace = action.getNamespace();
+                    final String fullPath;
+                    if (!Objects.equals(actionNamespace, 
StrutsPackage.DEFAULT_NAMESPACE) &&
+                            !isInCurrentPackage) {
+                        fullPath = actionNamespace + "/" + actionPath + 
firstExtension;
+                    } else {
+                        fullPath = actionPath + firstExtension;
+                    }
+
+                    final XmlTag actionTag = action.getXmlTag();
+                    if (actionTag == null) continue;
+                    final LookupElementBuilder builder = 
LookupElementBuilder.create(actionTag, fullPath)
+                            .withBoldness(isInCurrentPackage)
+                            .withIcon(Struts2Icons.Action)
+                            .withTypeText(action.getNamespace());
+                    variants.add(builder);
+                }
+            }
+
+            return ArrayUtil.toObjectArray(variants);
+        }
     }
-  }
 
 }
diff --git 
a/src/main/java/com/intellij/struts2/dom/validator/ValidatorManagerImpl.java 
b/src/main/java/com/intellij/struts2/dom/validator/ValidatorManagerImpl.java
index 539787c..933877b 100644
--- a/src/main/java/com/intellij/struts2/dom/validator/ValidatorManagerImpl.java
+++ b/src/main/java/com/intellij/struts2/dom/validator/ValidatorManagerImpl.java
@@ -39,10 +39,12 @@ import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.xml.DomFileElement;
 import com.intellij.util.xml.DomManager;
 import com.intellij.util.xml.DomService;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -52,144 +54,148 @@ import org.jetbrains.annotations.Nullable;
  */
 public class ValidatorManagerImpl extends ValidatorManager {
 
-  @NonNls
-  private static final String VALIDATORS_XML = "validators.xml";
-
-  @NonNls
-  private static final String VALIDATORS_DEFAULT_XML = "default.xml";
+    @NonNls
+    private static final String VALIDATORS_XML = "validators.xml";
 
-  @Override
-  public boolean isValidatorsFile(@NotNull final XmlFile xmlFile) {
-    return 
DomManager.getDomManager(xmlFile.getProject()).getFileElement(xmlFile, 
Validators.class) != null;
-  }
+    @NonNls
+    private static final String VALIDATORS_DEFAULT_XML = "default.xml";
 
-  @Nullable
-  private static DomFileElement<ValidatorsConfig> 
getValidatorsConfigFileElement(@NotNull final XmlFile xmlFile) {
-    return 
DomManager.getDomManager(xmlFile.getProject()).getFileElement(xmlFile, 
ValidatorsConfig.class);
-  }
-
-  @Override
-  public boolean isCustomValidatorConfigFile(@NotNull final PsiFile psiFile) {
-    return !Objects.equals(psiFile.getName(), VALIDATORS_DEFAULT_XML);
-  }
-
-  @Override
-  public List<ValidatorConfig> getValidators(@NotNull final Module module) {
-    final XmlFile validatorsFile = getValidatorConfigFile(module);
-    if (validatorsFile == null) {
-      return Collections.emptyList();
+    @Override
+    public boolean isValidatorsFile(@NotNull final XmlFile xmlFile) {
+        return 
DomManager.getDomManager(xmlFile.getProject()).getFileElement(xmlFile, 
Validators.class) != null;
     }
 
-    final DomFileElement<ValidatorsConfig> validatorsConfigElement = 
getValidatorsConfigFileElement(validatorsFile);
-    if (validatorsConfigElement == null) {
-      return Collections.emptyList();
+    @Nullable
+    private static DomFileElement<ValidatorsConfig> 
getValidatorsConfigFileElement(@NotNull final XmlFile xmlFile) {
+        return 
DomManager.getDomManager(xmlFile.getProject()).getFileElement(xmlFile, 
ValidatorsConfig.class);
     }
 
-    final List<ValidatorConfig> validatorConfigs = 
validatorsConfigElement.getRootElement().getValidatorConfigs();
-    if (!isCustomValidatorConfigFile(validatorsFile)) {
-      return validatorConfigs;
+    @Override
+    public boolean isCustomValidatorConfigFile(@NotNull final PsiFile psiFile) 
{
+        return !Objects.equals(psiFile.getName(), VALIDATORS_DEFAULT_XML);
     }
 
-    // add validators from default.xml for Struts > 2.0.8
-    final String version = StrutsVersionDetector.detectStrutsVersion(module);
-    if (StringUtil.compareVersionNumbers(version, "2.0.8") == 1) {
-      final XmlFile defaultValidatorFile = findDefaultValidatorsFile(module);
-      if (defaultValidatorFile != null) {
-        final DomFileElement<ValidatorsConfig> fileElement = 
getValidatorsConfigFileElement(defaultValidatorFile);
-        if (fileElement == null) {
-          return validatorConfigs;
+    @Override
+    public List<ValidatorConfig> getValidators(@NotNull final Module module) {
+        final XmlFile validatorsFile = getValidatorConfigFile(module);
+        if (validatorsFile == null) {
+            return Collections.emptyList();
+        }
+
+        final DomFileElement<ValidatorsConfig> validatorsConfigElement = 
getValidatorsConfigFileElement(validatorsFile);
+        if (validatorsConfigElement == null) {
+            return Collections.emptyList();
         }
 
-        final List<ValidatorConfig> defaultValidators = 
fileElement.getRootElement().getValidatorConfigs();
+        final List<ValidatorConfig> validatorConfigs = 
validatorsConfigElement.getRootElement().getValidatorConfigs();
+        if (!isCustomValidatorConfigFile(validatorsFile)) {
+            return validatorConfigs;
+        }
 
-        final List<ValidatorConfig> allValidatorConfigs = new 
ArrayList<>(defaultValidators);
-        allValidatorConfigs.addAll(validatorConfigs); // custom overrides 
defaults
-        return allValidatorConfigs;
-      }
-    }
+        // add validators from default.xml for Struts > 2.0.8
+        final String version = 
StrutsVersionDetector.detectStrutsVersion(module);
+        if (StringUtil.compareVersionNumbers(version, "2.0.8") == 1) {
+            final XmlFile defaultValidatorFile = 
findDefaultValidatorsFile(module);
+            if (defaultValidatorFile != null) {
+                final DomFileElement<ValidatorsConfig> fileElement = 
getValidatorsConfigFileElement(defaultValidatorFile);
+                if (fileElement == null) {
+                    return validatorConfigs;
+                }
+
+                final List<ValidatorConfig> defaultValidators = 
fileElement.getRootElement().getValidatorConfigs();
+
+                final List<ValidatorConfig> allValidatorConfigs = new 
ArrayList<>(defaultValidators);
+                allValidatorConfigs.addAll(validatorConfigs); // custom 
overrides defaults
+                return allValidatorConfigs;
+            }
+        }
 
-    return validatorConfigs;
-  }
-
-  @Override
-  @Nullable
-  public XmlFile getValidatorConfigFile(@NotNull final Module module) {
-    final Project project = module.getProject();
-    final PsiManager psiManager = PsiManager.getInstance(project);
-
-    final VirtualFile validatorsVirtualFile =
-        ResourceFileUtil.findResourceFileInScope(VALIDATORS_XML, project,
-                                                 
GlobalSearchScope.moduleRuntimeScope(module, false));
-
-    if (validatorsVirtualFile != null) {
-      final PsiFile file = psiManager.findFile(validatorsVirtualFile);
-      if (file != null &&
-          getValidatorsConfigFileElement((XmlFile) file) != null) {
-        return (XmlFile) file;
-      }
+        return validatorConfigs;
     }
 
-    return findDefaultValidatorsFile(module);
-  }
+    @Override
+    @Nullable
+    public XmlFile getValidatorConfigFile(@NotNull final Module module) {
+        final Project project = module.getProject();
+        final PsiManager psiManager = PsiManager.getInstance(project);
+
+        final VirtualFile validatorsVirtualFile =
+                ResourceFileUtil.findResourceFileInScope(VALIDATORS_XML, 
project,
+                        GlobalSearchScope.moduleRuntimeScope(module, false));
+
+        if (validatorsVirtualFile != null) {
+            final PsiFile file = psiManager.findFile(validatorsVirtualFile);
+            if (file != null &&
+                    getValidatorsConfigFileElement((XmlFile) file) != null) {
+                return (XmlFile) file;
+            }
+        }
 
-  @NotNull
-  @Override
-  public List<XmlFile> findValidationFilesFor(@NotNull final PsiClass clazz) {
-    final PsiFile psiFile = clazz.getContainingFile().getOriginalFile();
-    final PsiDirectory containingDirectory = psiFile.getContainingDirectory();
-    if (containingDirectory == null) {
-      return Collections.emptyList();
+        return findDefaultValidatorsFile(module);
     }
 
-    final PsiPackage containingPackage = 
JavaDirectoryService.getInstance().getPackage(containingDirectory);
-    if (containingPackage == null) {
-      return Collections.emptyList();
-    }
+    @NotNull
+    @Override
+    public List<XmlFile> findValidationFilesFor(@NotNull final PsiClass clazz) 
{
+        final PsiFile psiFile = clazz.getContainingFile().getOriginalFile();
+        final PsiDirectory containingDirectory = 
psiFile.getContainingDirectory();
+        if (containingDirectory == null) {
+            return Collections.emptyList();
+        }
 
-    final PackageScope searchScope = new PackageScope(containingPackage, 
false, true);
-    final List<DomFileElement<Validators>> validationRoots =
-        DomService.getInstance().getFileElements(Validators.class, 
clazz.getProject(), searchScope);
-
-    final List<DomFileElement<Validators>> filtered =
-        ContainerUtil.filter(validationRoots, validatorDomFileElement -> {
-          final String fileName = validatorDomFileElement.getFile().getName();
-          return StringUtil.startsWith(fileName, clazz.getName());
-        });
-
-    return ContainerUtil.map(filtered, DomFileElement::getFile);
-  }
-
-  /**
-   * Find {@code com/opensymphony/xwork2/validator/validators/default.xml} 
from {@code xwork.jar}.
-   *
-   * @param module Current module.
-   * @return {@code null} if not found.
-   */
-  @Nullable
-  private static XmlFile findDefaultValidatorsFile(final Module module) {
-    final Project project = module.getProject();
-
-    final PsiClass emailValidatorClass = 
JavaPsiFacade.getInstance(project).findClass(
-        "com.opensymphony.xwork2.validator.validators.EmailValidator",
-        GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, 
false));
-    if (emailValidatorClass == null) {
-      return null;
-    }
+        final PsiPackage containingPackage = 
JavaDirectoryService.getInstance().getPackage(containingDirectory);
+        if (containingPackage == null) {
+            return Collections.emptyList();
+        }
+
+        final PackageScope searchScope = new PackageScope(containingPackage, 
false, true);
+        final List<DomFileElement<Validators>> validationRoots =
+                DomService.getInstance().getFileElements(Validators.class, 
clazz.getProject(), searchScope);
+
+        final String className = clazz.getName();
+        if (className == null) {
+            return Collections.emptyList();
+        }
+        final List<DomFileElement<Validators>> filtered =
+                ContainerUtil.filter(validationRoots, validatorDomFileElement 
-> {
+                    final String fileName = 
validatorDomFileElement.getFile().getName();
+                    return StringUtil.startsWith(fileName, className);
+                });
 
-    final VirtualFile file = PsiUtilCore.getVirtualFile(emailValidatorClass);
-    if (file == null ||
-        file.getFileSystem() != JarFileSystem.getInstance()) {
-      return null;
+        return ContainerUtil.map(filtered, DomFileElement::getFile);
     }
 
-    // go up one level to ../validators/
-    final VirtualFile parent = file.getParent();
-    assert parent != null : "error walking up to parent from 
EmailValidator.class, xwork JAR file=" + file;
+    /**
+     * Find {@code com/opensymphony/xwork2/validator/validators/default.xml} 
from {@code xwork.jar}.
+     *
+     * @param module Current module.
+     * @return {@code null} if not found.
+     */
+    @Nullable
+    private static XmlFile findDefaultValidatorsFile(final Module module) {
+        final Project project = module.getProject();
+
+        final PsiClass emailValidatorClass = 
JavaPsiFacade.getInstance(project).findClass(
+                "com.opensymphony.xwork2.validator.validators.EmailValidator",
+                
GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, false));
+        if (emailValidatorClass == null) {
+            return null;
+        }
+
+        final VirtualFile file = 
PsiUtilCore.getVirtualFile(emailValidatorClass);
+        if (file == null ||
+                file.getFileSystem() != JarFileSystem.getInstance()) {
+            return null;
+        }
 
-    final VirtualFile vfDefaultXml = parent.findChild(VALIDATORS_DEFAULT_XML);
-    assert vfDefaultXml != null : "VF for default.xml null, parent=" + parent;
+        // go up one level to ../validators/
+        final VirtualFile parent = file.getParent();
+        assert parent != null : "error walking up to parent from 
EmailValidator.class, xwork JAR file=" + file;
 
-    return (XmlFile) PsiManager.getInstance(project).findFile(vfDefaultXml);
-  }
+        final VirtualFile vfDefaultXml = 
parent.findChild(VALIDATORS_DEFAULT_XML);
+        assert vfDefaultXml != null : "VF for default.xml null, parent=" + 
parent;
+
+        return (XmlFile) 
PsiManager.getInstance(project).findFile(vfDefaultXml);
+    }
 
 }
\ No newline at end of file
diff --git 
a/src/main/java/com/intellij/struts2/freemarker/Struts2GlobalVariableProvider.java
 
b/src/main/java/com/intellij/struts2/freemarker/Struts2GlobalVariableProvider.java
index ba748f1..f83f985 100644
--- 
a/src/main/java/com/intellij/struts2/freemarker/Struts2GlobalVariableProvider.java
+++ 
b/src/main/java/com/intellij/struts2/freemarker/Struts2GlobalVariableProvider.java
@@ -34,6 +34,7 @@ import com.intellij.jsp.JspManager;
 import com.intellij.psi.util.PsiTypesUtil;
 import com.intellij.psi.xml.XmlDocument;
 import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
 import com.intellij.struts2.StrutsConstants;
 import com.intellij.struts2.StrutsIcons;
 import com.intellij.struts2.dom.struts.action.Action;
@@ -54,122 +55,124 @@ import java.util.Collections;
 import java.util.List;
 
 final class Struts2GlobalVariableProvider extends FtlGlobalVariableProvider {
-  @Override
-  @NotNull
-  public List<? extends FtlVariable> getGlobalVariables(final FtlFile file) {
-    final Module module = ModuleUtilCore.findModuleForPsiElement(file);
-    if (module == null) {
-      return Collections.emptyList();
-    }
+    @Override
+    @NotNull
+    public List<? extends FtlVariable> getGlobalVariables(final FtlFile file) {
+        final Module module = ModuleUtilCore.findModuleForPsiElement(file);
+        if (module == null) {
+            return Collections.emptyList();
+        }
 
-    if (StrutsFacet.getInstance(module) == null) {
-      return Collections.emptyList();
-    }
+        if (StrutsFacet.getInstance(module) == null) {
+            return Collections.emptyList();
+        }
 
-    final List<FtlVariable> result = new ArrayList<>();
-    result.add(new MyFtlLightVariable("stack", file, (FtlType) null));
-    result.add(new MyFtlLightVariable("response", file, 
WebCommonClassNames.JAVAX_HTTP_SERVLET_RESPONSE));
-    result.add(new MyFtlLightVariable("res", file, 
WebCommonClassNames.JAVAX_HTTP_SERVLET_RESPONSE));
-    result.add(new MyFtlLightVariable("request", file, 
WebCommonClassNames.JAVAX_HTTP_SERVLET_REQUEST));
-    result.add(new MyFtlLightVariable("req", file, 
WebCommonClassNames.JAVAX_HTTP_SERVLET_REQUEST));
-    result.add(new MyFtlLightVariable("session", file, 
WebCommonClassNames.JAVAX_HTTP_SESSION));
-    result.add(new MyFtlLightVariable("application", file, 
WebCommonClassNames.JAVAX_SERVLET_CONTEXT));
-    result.add(new MyFtlLightVariable("base", file, 
CommonClassNames.JAVA_LANG_STRING));
-
-    installTaglibSupport(result, module,
-                         StrutsConstants.TAGLIB_STRUTS_UI_URI, 
StrutsConstants.TAGLIB_STRUTS_UI_PREFIX);
-    installTaglibSupport(result, module,
-                         StrutsConstants.TAGLIB_JQUERY_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_PLUGIN_PREFIX);
-    installTaglibSupport(result, module,
-                         StrutsConstants.TAGLIB_JQUERY_RICHTEXT_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_RICHTEXT_PLUGIN_PREFIX);
-    installTaglibSupport(result, module,
-                         StrutsConstants.TAGLIB_JQUERY_CHART_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_CHART_PLUGIN_PREFIX);
-    installTaglibSupport(result, module,
-                         StrutsConstants.TAGLIB_JQUERY_TREE_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_TREE_PLUGIN_PREFIX);
-    installTaglibSupport(result, module,
-                         StrutsConstants.TAGLIB_JQUERY_GRID_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_GRID_PLUGIN_PREFIX);
-    installTaglibSupport(result, module,
-                         StrutsConstants.TAGLIB_JQUERY_MOBILE_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_MOBILE_PLUGIN_PREFIX);
-    installTaglibSupport(result, module,
-                         StrutsConstants.TAGLIB_BOOTSTRAP_PLUGIN_URI, 
StrutsConstants.TAGLIB_BOOTSTRAP_PLUGIN_PREFIX);
-
-    final Processor<Action> processor = action -> {
-      final PsiClass actionClass = action.searchActionClass();
-      if (actionClass != null) {
-        for (final Result result1 : action.getResults()) {
-          final ResultType resultType = result1.getEffectiveResultType();
-          if (resultType != null &&
-              
FreeMarkerStrutsResultContributor.FREEMARKER.equals(resultType.getName().getStringValue()))
 {
-            final PathReference reference = result1.getValue();
-            final PsiElement target = reference == null ? null : 
reference.resolve();
-            if (target != null &&
-                (file.getManager().areElementsEquivalent(file, target) ||
-                 
file.getManager().areElementsEquivalent(file.getOriginalFile(), target))) {
-              final PsiClassType actionType = 
PsiTypesUtil.getClassType(actionClass);
-              final FtlPsiType ftlPsiType = FtlPsiType.wrap(actionType);
-              result.add(new MyFtlLightVariable("", action.getXmlTag(), 
ftlPsiType));
-              result.add(new MyFtlLightVariable("action", action.getXmlTag(), 
ftlPsiType));
-              return false; // stop after first match
+        final List<FtlVariable> result = new ArrayList<>();
+        result.add(new MyFtlLightVariable("stack", file, (FtlType) null));
+        result.add(new MyFtlLightVariable("response", file, 
WebCommonClassNames.JAVAX_HTTP_SERVLET_RESPONSE));
+        result.add(new MyFtlLightVariable("res", file, 
WebCommonClassNames.JAVAX_HTTP_SERVLET_RESPONSE));
+        result.add(new MyFtlLightVariable("request", file, 
WebCommonClassNames.JAVAX_HTTP_SERVLET_REQUEST));
+        result.add(new MyFtlLightVariable("req", file, 
WebCommonClassNames.JAVAX_HTTP_SERVLET_REQUEST));
+        result.add(new MyFtlLightVariable("session", file, 
WebCommonClassNames.JAVAX_HTTP_SESSION));
+        result.add(new MyFtlLightVariable("application", file, 
WebCommonClassNames.JAVAX_SERVLET_CONTEXT));
+        result.add(new MyFtlLightVariable("base", file, 
CommonClassNames.JAVA_LANG_STRING));
+
+        installTaglibSupport(result, module,
+                StrutsConstants.TAGLIB_STRUTS_UI_URI, 
StrutsConstants.TAGLIB_STRUTS_UI_PREFIX);
+        installTaglibSupport(result, module,
+                StrutsConstants.TAGLIB_JQUERY_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_PLUGIN_PREFIX);
+        installTaglibSupport(result, module,
+                StrutsConstants.TAGLIB_JQUERY_RICHTEXT_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_RICHTEXT_PLUGIN_PREFIX);
+        installTaglibSupport(result, module,
+                StrutsConstants.TAGLIB_JQUERY_CHART_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_CHART_PLUGIN_PREFIX);
+        installTaglibSupport(result, module,
+                StrutsConstants.TAGLIB_JQUERY_TREE_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_TREE_PLUGIN_PREFIX);
+        installTaglibSupport(result, module,
+                StrutsConstants.TAGLIB_JQUERY_GRID_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_GRID_PLUGIN_PREFIX);
+        installTaglibSupport(result, module,
+                StrutsConstants.TAGLIB_JQUERY_MOBILE_PLUGIN_URI, 
StrutsConstants.TAGLIB_JQUERY_MOBILE_PLUGIN_PREFIX);
+        installTaglibSupport(result, module,
+                StrutsConstants.TAGLIB_BOOTSTRAP_PLUGIN_URI, 
StrutsConstants.TAGLIB_BOOTSTRAP_PLUGIN_PREFIX);
+
+        final Processor<Action> processor = action -> {
+            final PsiClass actionClass = action.searchActionClass();
+            if (actionClass != null) {
+                for (final Result result1 : action.getResults()) {
+                    final ResultType resultType = 
result1.getEffectiveResultType();
+                    if (resultType != null &&
+                            
FreeMarkerStrutsResultContributor.FREEMARKER.equals(resultType.getName().getStringValue()))
 {
+                        final PathReference reference = result1.getValue();
+                        final PsiElement target = reference == null ? null : 
reference.resolve();
+                        if (target != null &&
+                                (file.getManager().areElementsEquivalent(file, 
target) ||
+                                        
file.getManager().areElementsEquivalent(file.getOriginalFile(), target))) {
+                            final XmlTag actionTag = action.getXmlTag();
+                            if (actionTag == null) return false;
+                            final PsiClassType actionType = 
PsiTypesUtil.getClassType(actionClass);
+                            final FtlPsiType ftlPsiType = 
FtlPsiType.wrap(actionType);
+                            result.add(new MyFtlLightVariable("", actionTag, 
ftlPsiType));
+                            result.add(new MyFtlLightVariable("action", 
actionTag, ftlPsiType));
+                            return false; // stop after first match
+                        }
+                    }
+                }
             }
-          }
-        }
-      }
 
-      return true;
-    };
+            return true;
+        };
 
-    for (final StrutsModel model : 
StrutsManager.getInstance(file.getProject()).getAllModels(module)) {
-      model.processActions(processor);
-    }
-    return result;
-  }
-
-  private static void installTaglibSupport(@NotNull final List<FtlVariable> 
result,
-                                           @NotNull final Module module,
-                                           @NotNull @NonNls final String 
taglibUri,
-                                           @NotNull @NonNls final String 
taglibPrefix) {
-    final XmlFile xmlFile = 
JspManager.getInstance(module.getProject()).getTldFileByUri(taglibUri, module, 
null);
-    if (xmlFile == null) {
-      return;
+        for (final StrutsModel model : 
StrutsManager.getInstance(file.getProject()).getAllModels(module)) {
+            model.processActions(processor);
+        }
+        return result;
     }
 
-    final XmlDocument document = xmlFile.getDocument();
-    if (document == null) {
-      return;
-    }
+    private static void installTaglibSupport(@NotNull final List<FtlVariable> 
result,
+                                             @NotNull final Module module,
+                                             @NotNull @NonNls final String 
taglibUri,
+                                             @NotNull @NonNls final String 
taglibPrefix) {
+        final XmlFile xmlFile = 
JspManager.getInstance(module.getProject()).getTldFileByUri(taglibUri, module, 
null);
+        if (xmlFile == null) {
+            return;
+        }
 
-    final XmlNSDescriptor descriptor = (XmlNSDescriptor) 
document.getMetaData();
-    if (descriptor == null) {
-      return;
-    }
+        final XmlDocument document = xmlFile.getDocument();
+        if (document == null) {
+            return;
+        }
 
-    PsiElement declaration = descriptor.getDeclaration();
-    if (declaration == null) {
-      declaration = xmlFile;
-    }
+        final XmlNSDescriptor descriptor = (XmlNSDescriptor) 
document.getMetaData();
+        if (descriptor == null) {
+            return;
+        }
 
-    result.add(new MyFtlLightVariable(taglibPrefix, declaration, new 
FtlXmlNamespaceType(descriptor)));
-  }
+        PsiElement declaration = descriptor.getDeclaration();
+        if (declaration == null) {
+            declaration = xmlFile;
+        }
 
+        result.add(new MyFtlLightVariable(taglibPrefix, declaration, new 
FtlXmlNamespaceType(descriptor)));
+    }
 
-  private static final class MyFtlLightVariable extends FtlLightVariable {
 
-    private MyFtlLightVariable(@NotNull @NonNls final String name,
-                               @NotNull final PsiElement parent,
-                               @Nullable final FtlType type) {
-      super(name, parent, type);
-    }
+    private static final class MyFtlLightVariable extends FtlLightVariable {
 
-    private MyFtlLightVariable(@NotNull @NonNls final String name,
-                               @NotNull final PsiElement parent,
-                               @NotNull @NonNls final String psiType) {
-      super(name, parent, psiType);
-    }
+        private MyFtlLightVariable(@NotNull @NonNls final String name,
+                                   @NotNull final PsiElement parent,
+                                   @Nullable final FtlType type) {
+            super(name, parent, type);
+        }
 
-    @Override
-    public Icon getIcon(final boolean open) {
-      return StrutsIcons.STRUTS_VARIABLE;
+        private MyFtlLightVariable(@NotNull @NonNls final String name,
+                                   @NotNull final PsiElement parent,
+                                   @NotNull @NonNls final String psiType) {
+            super(name, parent, psiType);
+        }
+
+        @Override
+        public Icon getIcon(final boolean open) {
+            return StrutsIcons.STRUTS_VARIABLE;
+        }
     }
-  }
 
 }
\ No newline at end of file
diff --git 
a/src/main/java/com/intellij/struts2/gotosymbol/GoToActionSymbolProvider.java 
b/src/main/java/com/intellij/struts2/gotosymbol/GoToActionSymbolProvider.java
index 08a8f2a..6a7bd91 100644
--- 
a/src/main/java/com/intellij/struts2/gotosymbol/GoToActionSymbolProvider.java
+++ 
b/src/main/java/com/intellij/struts2/gotosymbol/GoToActionSymbolProvider.java
@@ -20,6 +20,7 @@ import com.intellij.navigation.NavigationItem;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.project.Project;
 import com.intellij.struts2.Struts2Icons;
+import com.intellij.psi.xml.XmlTag;
 import com.intellij.struts2.dom.struts.action.Action;
 import com.intellij.struts2.dom.struts.model.StrutsManager;
 import com.intellij.struts2.dom.struts.model.StrutsModel;
@@ -38,44 +39,47 @@ import java.util.Set;
  */
 public class GoToActionSymbolProvider extends GoToSymbolProvider {
 
-  @Override
-  protected boolean acceptModule(final Module module) {
-    return StrutsFacet.getInstance(module) != null;
-  }
-  @NotNull
-  @Override
-  protected Collection<Module> calcAcceptableModules(@NotNull Project project) 
{
-    return 
ProjectFacetManager.getInstance(project).getModulesWithFacet(StrutsFacet.FACET_TYPE_ID);
-  }
+    @Override
+    protected boolean acceptModule(final Module module) {
+        return StrutsFacet.getInstance(module) != null;
+    }
 
-  @Override
-  protected void addNames(@NotNull final Module module, final Set<String> 
result) {
-    final StrutsModel strutsModel = 
StrutsManager.getInstance(module.getProject()).getCombinedModel(module);
-    if (strutsModel == null) {
-      return;
+    @NotNull
+    @Override
+    protected Collection<Module> calcAcceptableModules(@NotNull Project 
project) {
+        return 
ProjectFacetManager.getInstance(project).getModulesWithFacet(StrutsFacet.FACET_TYPE_ID);
     }
 
-    strutsModel.processActions(action -> {
-      result.add(action.getName().getStringValue());
-      return true;
-    });
-  }
+    @Override
+    protected void addNames(@NotNull final Module module, final Set<String> 
result) {
+        final StrutsModel strutsModel = 
StrutsManager.getInstance(module.getProject()).getCombinedModel(module);
+        if (strutsModel == null) {
+            return;
+        }
 
-  @Override
-  protected void addItems(@NotNull final Module module, final String name, 
final List<NavigationItem> result) {
-    final StrutsModel strutsModel = 
StrutsManager.getInstance(module.getProject()).getCombinedModel(module);
-    if (strutsModel == null) {
-      return;
+        strutsModel.processActions(action -> {
+            result.add(action.getName().getStringValue());
+            return true;
+        });
     }
 
-    final List<Action> actions = strutsModel.findActionsByName(name, null);
-    for (final Action action : actions) {
-      final NavigationItem item = createNavigationItem(action.getXmlTag(),
-                                                       
action.getName().getStringValue() +
-                                                       " [" + 
action.getNamespace() + "]",
-                                                       Struts2Icons.Action);
-      result.add(item);
+    @Override
+    protected void addItems(@NotNull final Module module, final String name, 
final List<NavigationItem> result) {
+        final StrutsModel strutsModel = 
StrutsManager.getInstance(module.getProject()).getCombinedModel(module);
+        if (strutsModel == null) {
+            return;
+        }
+
+        final List<Action> actions = strutsModel.findActionsByName(name, null);
+        for (final Action action : actions) {
+            final XmlTag tag = action.getXmlTag();
+            if (tag == null) continue;
+            final NavigationItem item = createNavigationItem(tag,
+                    action.getName().getStringValue() +
+                            " [" + action.getNamespace() + "]",
+                    Struts2Icons.Action);
+            result.add(item);
+        }
     }
-  }
 
 }
diff --git 
a/src/main/java/com/intellij/struts2/gotosymbol/GotoRelatedActionProvider.java 
b/src/main/java/com/intellij/struts2/gotosymbol/GotoRelatedActionProvider.java
index df9646a..03a543f 100644
--- 
a/src/main/java/com/intellij/struts2/gotosymbol/GotoRelatedActionProvider.java
+++ 
b/src/main/java/com/intellij/struts2/gotosymbol/GotoRelatedActionProvider.java
@@ -39,69 +39,69 @@ import java.util.*;
  * @author Yann C&eacute;bron
  */
 final class GotoRelatedActionProvider extends GotoRelatedProvider {
-  // TODO restrict to "realistic" results
-  private static final Set<String> SUPPORTED_EXTENSIONS = 
CollectionFactory.createFilePathSet(Arrays.asList("ftl", "htm", "html", "jsp", 
"jspx", "txt", "vm"));
+    // TODO restrict to "realistic" results
+    private static final Set<String> SUPPORTED_EXTENSIONS = 
CollectionFactory.createFilePathSet(Arrays.asList("ftl", "htm", "html", "jsp", 
"jspx", "txt", "vm"));
 
-  @NotNull
-  @Override
-  public List<? extends GotoRelatedItem> getItems(@NotNull final PsiElement 
psiElement) {
-    PsiFile psiFile = psiElement.getContainingFile();
-    if (psiFile == null) return Collections.emptyList();
+    @NotNull
+    @Override
+    public List<? extends GotoRelatedItem> getItems(@NotNull final PsiElement 
psiElement) {
+        PsiFile psiFile = psiElement.getContainingFile();
+        if (psiFile == null) return Collections.emptyList();
 
-    final PsiFile containingFile = psiFile.getOriginalFile();
-    final String filename = containingFile.getName();
+        final PsiFile containingFile = psiFile.getOriginalFile();
+        final String filename = containingFile.getName();
 
-    final String extension = FileUtilRt.getExtension(filename);
-    if (!SUPPORTED_EXTENSIONS.contains(extension)) {
-      return Collections.emptyList();
-    }
+        final String extension = FileUtilRt.getExtension(filename);
+        if (!SUPPORTED_EXTENSIONS.contains(extension)) {
+            return Collections.emptyList();
+        }
 
-    final StrutsManager strutsManager = 
StrutsManager.getInstance(psiElement.getProject());
-    final StrutsModel strutsModel = strutsManager.getCombinedModel(psiElement);
-    if (strutsModel == null) {
-      return Collections.emptyList();
-    }
+        final StrutsManager strutsManager = 
StrutsManager.getInstance(psiElement.getProject());
+        final StrutsModel strutsModel = 
strutsManager.getCombinedModel(psiElement);
+        if (strutsModel == null) {
+            return Collections.emptyList();
+        }
 
-    final List<PsiFile> allFiles = 
containingFile.getViewProvider().getAllFiles();
+        final List<PsiFile> allFiles = 
containingFile.getViewProvider().getAllFiles();
 
-    final Set<Action> actions = new HashSet<>();
-    final List<GotoRelatedItem> items = new ArrayList<>();
-    strutsModel.processActions(action -> {
-      for (final Result result : action.getResults()) {
+        final Set<Action> actions = new HashSet<>();
+        final List<GotoRelatedItem> items = new ArrayList<>();
+        strutsModel.processActions(action -> {
+            for (final Result result : action.getResults()) {
 
-        final PathReference pathReference = result.getValue();
-        if (pathReference == null) {
-          continue;
-        }
+                final PathReference pathReference = result.getValue();
+                if (pathReference == null) {
+                    continue;
+                }
 
-        final String path = pathReference.getPath();
-        if (!path.endsWith(filename)) {
-          continue;
-        }
+                final String path = pathReference.getPath();
+                if (!path.endsWith(filename)) {
+                    continue;
+                }
 
-        final PsiElement resolve = pathReference.resolve();
-        if (ContainerUtil.find(allFiles, resolve) == null) {
-          continue;
-        }
+                final PsiElement resolve = pathReference.resolve();
+                if (resolve == null || ContainerUtil.find(allFiles, resolve) 
== null) {
+                    continue;
+                }
 
-        if (!actions.contains(action)) {
-          items.add(new DomGotoRelatedItem(action));
-          actions.add(action);
-        }
+                if (!actions.contains(action)) {
+                    items.add(new DomGotoRelatedItem(action));
+                    actions.add(action);
+                }
 
-        final PsiClass actionClass = action.searchActionClass();
-        if (actionClass == null) {
-          continue;
-        }
+                final PsiClass actionClass = action.searchActionClass();
+                if (actionClass == null) {
+                    continue;
+                }
 
-        final PsiMethod actionMethod = action.searchActionMethod();
-        items.add(new GotoRelatedItem(actionMethod != null ? actionMethod : 
actionClass));
-      }
+                final PsiMethod actionMethod = action.searchActionMethod();
+                items.add(new GotoRelatedItem(actionMethod != null ? 
actionMethod : actionClass));
+            }
 
-      return true;
-    });
+            return true;
+        });
 
-    return items;
-  }
+        return items;
+    }
 
 }
\ No newline at end of file
diff --git a/src/main/java/com/intellij/struts2/graph/StrutsDataModel.java 
b/src/main/java/com/intellij/struts2/graph/StrutsDataModel.java
index 4c633c0..86bdbb8 100644
--- a/src/main/java/com/intellij/struts2/graph/StrutsDataModel.java
+++ b/src/main/java/com/intellij/struts2/graph/StrutsDataModel.java
@@ -46,163 +46,163 @@ import java.util.*;
  */
 public class StrutsDataModel extends GraphDataModel<BasicStrutsNode, 
BasicStrutsEdge> {
 
-  private final Set<BasicStrutsNode> myNodes = new HashSet<>();
-  private final Set<BasicStrutsEdge> myEdges = new HashSet<>();
-
-  private final Map<PsiFile, NodesGroup> myGroups = new HashMap<>();
-
-  private final Project myProject;
-  private final XmlFile myFile;
-
-  @NonNls
-  private static final String UNKNOWN = "???";
-
-  public StrutsDataModel(final XmlFile file) {
-    myFile = file;
-    myProject = file.getProject();
-  }
-
-  @Override
-  @NotNull
-  public Collection<BasicStrutsNode> getNodes() {
-    refreshDataModel();
-    return myNodes;
-  }
-
-  @Override
-  @NotNull
-  public Collection<BasicStrutsEdge> getEdges() {
-    return myEdges;
-  }
-
-  @Override
-  @NotNull
-  public BasicStrutsNode getSourceNode(final BasicStrutsEdge edge) {
-    return edge.getSource();
-  }
-
-  @Override
-  @NotNull
-  public BasicStrutsNode getTargetNode(final BasicStrutsEdge edge) {
-    return edge.getTarget();
-  }
-
-  @Override
-  @NotNull
-  public String getNodeName(final BasicStrutsNode node) {
-    return node.getName();
-  }
-
-  @Override
-  @NotNull
-  public String getEdgeName(final BasicStrutsEdge edge) {
-    return edge.getName();
-  }
-
-  @Override
-  public BasicStrutsEdge createEdge(@NotNull final BasicStrutsNode from, 
@NotNull final BasicStrutsNode to) {
-    return null;
-  }
-
-  @Override
-  public void dispose() {
-  }
-
-  private void refreshDataModel() {
-    myNodes.clear();
-    myEdges.clear();
-    updateDataModel();
-  }
-
-  @Override
-  public NodeGroupDescriptor getGroup(final BasicStrutsNode basicStrutsNode) {
-    if (isGroupElements()) {
-      final XmlElement xmlElement = 
basicStrutsNode.getIdentifyingElement().getXmlElement();
-      assert xmlElement != null;
-      return myGroups.get(xmlElement.getContainingFile());
+    private final Set<BasicStrutsNode> myNodes = new HashSet<>();
+    private final Set<BasicStrutsEdge> myEdges = new HashSet<>();
+
+    private final Map<PsiFile, NodesGroup> myGroups = new HashMap<>();
+
+    private final Project myProject;
+    private final XmlFile myFile;
+
+    @NonNls
+    private static final String UNKNOWN = "???";
+
+    public StrutsDataModel(final XmlFile file) {
+        myFile = file;
+        myProject = file.getProject();
     }
 
-    return super.getGroup(basicStrutsNode);
-  }
+    @Override
+    @NotNull
+    public Collection<BasicStrutsNode> getNodes() {
+        refreshDataModel();
+        return myNodes;
+    }
 
-  private void addNode(final BasicStrutsNode node) {
-    if (!node.getIdentifyingElement().isValid()) {
-      return;
+    @Override
+    @NotNull
+    public Collection<BasicStrutsEdge> getEdges() {
+        return myEdges;
     }
 
-    myNodes.add(node);
+    @Override
+    @NotNull
+    public BasicStrutsNode getSourceNode(final BasicStrutsEdge edge) {
+        return edge.getSource();
+    }
 
-    if (isGroupElements()) {
-      final XmlElement element = node.getIdentifyingElement().getXmlElement();
-      assert element != null;
-      final PsiFile file = element.getContainingFile();
-      if (file != null && !myGroups.containsKey(file)) {
-        final String name = file.getName();
+    @Override
+    @NotNull
+    public BasicStrutsNode getTargetNode(final BasicStrutsEdge edge) {
+        return edge.getTarget();
+    }
 
-        final BasicNodesGroup group = new BasicNodesGroup(name) {
+    @Override
+    @NotNull
+    public String getNodeName(final BasicStrutsNode node) {
+        return node.getName();
+    }
 
-          @Override
-          public @Nullable GroupNodeRealizer createGroupNodeRealizer() {
-            final GroupNodeRealizer groupNodeRealizer = 
super.createGroupNodeRealizer();
-            assert groupNodeRealizer != null;
+    @Override
+    @NotNull
+    public String getEdgeName(final BasicStrutsEdge edge) {
+        return edge.getName();
+    }
 
-            final NodeLabel nodeLabel = groupNodeRealizer.getLabel();
-            nodeLabel.setText("      " + getGroupName());
-            nodeLabel.setModel(NodeLabel.INTERNAL);
-            nodeLabel.setPosition(NodeLabel.TOP_RIGHT);
+    @Override
+    public BasicStrutsEdge createEdge(@NotNull final BasicStrutsNode from, 
@NotNull final BasicStrutsNode to) {
+        return null;
+    }
 
-            return groupNodeRealizer;
-          }
-        };
+    @Override
+    public void dispose() {
+    }
 
-        // collapse all other files
-        group.setClosed(file != myFile);
+    private void refreshDataModel() {
+        myNodes.clear();
+        myEdges.clear();
+        updateDataModel();
+    }
 
-        myGroups.put(file, group);
+    @Override
+    public NodeGroupDescriptor getGroup(final BasicStrutsNode basicStrutsNode) 
{
+        if (isGroupElements()) {
+            final XmlElement xmlElement = 
basicStrutsNode.getIdentifyingElement().getXmlElement();
+            assert xmlElement != null;
+            return myGroups.get(xmlElement.getContainingFile());
+        }
 
-      }
+        return super.getGroup(basicStrutsNode);
     }
-  }
 
-  // TODO configurable?
-  private boolean isGroupElements() {
-    return true;
-  }
+    private void addNode(final BasicStrutsNode node) {
+        if (!node.getIdentifyingElement().isValid()) {
+            return;
+        }
+
+        myNodes.add(node);
 
-  private void addEdge(final BasicStrutsEdge edge) {
-    if (!edge.getSource().getIdentifyingElement().isValid() ||
-        !edge.getTarget().getIdentifyingElement().isValid()) {
-      return;
+        if (isGroupElements()) {
+            final XmlElement element = 
node.getIdentifyingElement().getXmlElement();
+            assert element != null;
+            final PsiFile file = element.getContainingFile();
+            if (file != null && !myGroups.containsKey(file)) {
+                final String name = file.getName();
+
+                final BasicNodesGroup group = new BasicNodesGroup(name) {
+
+                    @Override
+                    public @NotNull GroupNodeRealizer 
createGroupNodeRealizer() {
+                        final GroupNodeRealizer groupNodeRealizer = 
super.createGroupNodeRealizer();
+                        assert groupNodeRealizer != null;
+
+                        final NodeLabel nodeLabel = 
groupNodeRealizer.getLabel();
+                        nodeLabel.setText("      " + getGroupName());
+                        nodeLabel.setModel(NodeLabel.INTERNAL);
+                        nodeLabel.setPosition(NodeLabel.TOP_RIGHT);
+
+                        return groupNodeRealizer;
+                    }
+                };
+
+                // collapse all other files
+                group.setClosed(file != myFile);
+
+                myGroups.put(file, group);
+
+            }
+        }
     }
 
-    myEdges.add(edge);
-  }
+    // TODO configurable?
+    private boolean isGroupElements() {
+        return true;
+    }
+
+    private void addEdge(final BasicStrutsEdge edge) {
+        if (!edge.getSource().getIdentifyingElement().isValid() ||
+                !edge.getTarget().getIdentifyingElement().isValid()) {
+            return;
+        }
 
-  private void updateDataModel() {
-    final StrutsModel model = 
StrutsManager.getInstance(myProject).getModelByFile(myFile);
-    if (model == null) {
-      return;
+        myEdges.add(edge);
     }
 
-    for (final StrutsPackage strutsPackage : model.getStrutsPackages()) {
-      for (final Action action : strutsPackage.getActions()) {
-        final ActionNode actionNode = new ActionNode(action, 
action.getName().getStringValue());
-        addNode(actionNode);
+    private void updateDataModel() {
+        final StrutsModel model = 
StrutsManager.getInstance(myProject).getModelByFile(myFile);
+        if (model == null) {
+            return;
+        }
 
-        for (final Result result : action.getResults()) {
-          final PathReference pathReference = result.getValue();
-          final String path = pathReference != null ? pathReference.getPath() 
: UNKNOWN;
+        for (final StrutsPackage strutsPackage : model.getStrutsPackages()) {
+            for (final Action action : strutsPackage.getActions()) {
+                final ActionNode actionNode = new ActionNode(action, 
action.getName().getStringValue());
+                addNode(actionNode);
 
-          final ResultNode resultNode = new ResultNode(result, path);
-          addNode(resultNode);
+                for (final Result result : action.getResults()) {
+                    final PathReference pathReference = result.getValue();
+                    final String path = pathReference != null ? 
pathReference.getPath() : UNKNOWN;
 
-          final String resultName = result.getName().getStringValue();
-          addEdge(new BasicStrutsEdge(actionNode, resultNode, resultName != 
null ? resultName : Result.DEFAULT_NAME));
+                    final ResultNode resultNode = new ResultNode(result, path);
+                    addNode(resultNode);
+
+                    final String resultName = 
result.getName().getStringValue();
+                    addEdge(new BasicStrutsEdge(actionNode, resultNode, 
resultName != null ? resultName : Result.DEFAULT_NAME));
+                }
+
+            }
         }
 
-      }
     }
 
-  }
-
 }
\ No newline at end of file
diff --git a/src/main/java/com/intellij/struts2/graph/beans/ResultNode.java 
b/src/main/java/com/intellij/struts2/graph/beans/ResultNode.java
index 612c2bc..c13ab9d 100644
--- a/src/main/java/com/intellij/struts2/graph/beans/ResultNode.java
+++ b/src/main/java/com/intellij/struts2/graph/beans/ResultNode.java
@@ -14,7 +14,7 @@
  */
 package com.intellij.struts2.graph.beans;
 
-import com.intellij.openapi.fileTypes.FileTypes;
+import com.intellij.icons.AllIcons;
 import com.intellij.openapi.paths.PathReference;
 import com.intellij.struts2.dom.struts.action.Result;
 import org.jetbrains.annotations.NotNull;
@@ -28,32 +28,32 @@ import javax.swing.*;
  */
 public class ResultNode extends BasicStrutsNode<Result> {
 
-  private static final Icon UNKNOWN_RESULT_ICON = FileTypes.UNKNOWN.getIcon();
+    private static final Icon UNKNOWN_RESULT_ICON = AllIcons.FileTypes.Unknown;
 
-  public ResultNode(@NotNull final Result identifyingElement,
-                    @NotNull final String path) {
-    super(identifyingElement, path);
-  }
-
-  @Override
-  @NotNull
-  public Icon getIcon() {
-    final Result result = getIdentifyingElement();
-    if (!result.isValid()) {
-      return UNKNOWN_RESULT_ICON;
-    }
-
-    final PathReference pathReference = result.getValue();
-    if (pathReference == null) {
-      return UNKNOWN_RESULT_ICON;
+    public ResultNode(@NotNull final Result identifyingElement,
+                      @NotNull final String path) {
+        super(identifyingElement, path);
     }
 
-    if (pathReference.resolve() == null) {
-      return UNKNOWN_RESULT_ICON;
+    @Override
+    @NotNull
+    public Icon getIcon() {
+        final Result result = getIdentifyingElement();
+        if (!result.isValid()) {
+            return UNKNOWN_RESULT_ICON;
+        }
+
+        final PathReference pathReference = result.getValue();
+        if (pathReference == null) {
+            return UNKNOWN_RESULT_ICON;
+        }
+
+        if (pathReference.resolve() == null) {
+            return UNKNOWN_RESULT_ICON;
+        }
+
+        final Icon pathReferenceIcon = pathReference.getIcon();
+        return pathReferenceIcon != null ? pathReferenceIcon : 
UNKNOWN_RESULT_ICON;
     }
 
-    final Icon pathReferenceIcon = pathReference.getIcon();
-    return pathReferenceIcon != null ? pathReferenceIcon : UNKNOWN_RESULT_ICON;
-  }
-
 }
\ No newline at end of file
diff --git 
a/src/main/java/com/intellij/struts2/intentions/code/CreateValidationXmlIntention.java
 
b/src/main/java/com/intellij/struts2/intentions/code/CreateValidationXmlIntention.java
index 5106af0..fe9b023 100644
--- 
a/src/main/java/com/intellij/struts2/intentions/code/CreateValidationXmlIntention.java
+++ 
b/src/main/java/com/intellij/struts2/intentions/code/CreateValidationXmlIntention.java
@@ -62,175 +62,175 @@ import java.util.List;
  */
 public class CreateValidationXmlIntention extends 
PsiElementBaseIntentionAction implements Iconable {
 
-  @NotNull
-  @Override
-  public String getText() {
-    return "Create validation.xml";
-  }
-
-  @Override
-  public boolean isAvailable(@NotNull final Project project,
-                             final Editor editor,
-                             @NotNull final PsiElement psiElement) {
-    final PsiClass clazz = findActionClass(psiElement);
-    if (clazz == null) {
-      return false;
+    @NotNull
+    @Override
+    public String getText() {
+        return "Create validation.xml";
     }
 
-    // short exit if Struts Facet not present
-    final Module module = ModuleUtilCore.findModuleForPsiElement(clazz);
-    if (module == null ||
-        StrutsFacet.getInstance(module) == null) {
-      return false;
-    }
+    @Override
+    public boolean isAvailable(@NotNull final Project project,
+                               final Editor editor,
+                               @NotNull final PsiElement psiElement) {
+        final PsiClass clazz = findActionClass(psiElement);
+        if (clazz == null) {
+            return false;
+        }
 
-    final List<Action> actions = getActionsForClazz(project, clazz, module);
-    if (actions.isEmpty()) {
-      return false;
-    }
+        // short exit if Struts Facet not present
+        final Module module = ModuleUtilCore.findModuleForPsiElement(clazz);
+        if (module == null ||
+                StrutsFacet.getInstance(module) == null) {
+            return false;
+        }
 
-    final List<XmlFile> files = 
ValidatorManager.getInstance(psiElement.getProject()).findValidationFilesFor(clazz);
-    return files.isEmpty() ||
-           files.size() != actions.size();
-  }
+        final List<Action> actions = getActionsForClazz(project, clazz, 
module);
+        if (actions.isEmpty()) {
+            return false;
+        }
 
-  private static List<Action> getActionsForClazz(final Project project, final 
PsiClass clazz, final Module module) {
-    final StrutsModel model = 
StrutsManager.getInstance(project).getCombinedModel(module);
-    if (model == null || !model.isActionClass(clazz)) {
-      return Collections.emptyList();
+        final List<XmlFile> files = 
ValidatorManager.getInstance(psiElement.getProject()).findValidationFilesFor(clazz);
+        return files.isEmpty() ||
+                files.size() != actions.size();
     }
 
-    return model.findActionsByClass(clazz);
-  }
-
-  @NotNull
-  @Override
-  public String getFamilyName() {
-    return getText();
-  }
-
-  @Override
-  public Icon getIcon(final int flags) {
-    return Struts2Icons.Action;
-  }
-
-  @Override
-  public void invoke(@NotNull final Project project,
-                     final Editor editor,
-                     @NotNull final PsiElement element) throws 
IncorrectOperationException {
-    final PsiClass actionClass = findActionClass(element);
-    assert actionClass != null : element;
-
-    final List<Action> filteredActions = 
getActionsWithoutValidation(actionClass);
-    if (filteredActions.size() > 1) {
-      final ListPopupStep<Action> step =
-        new BaseListPopupStep<>("Choose action mapping", filteredActions) {
-
-          @Override
-          public Icon getIconFor(final Action value) {
-            return Struts2Icons.Action;
-          }
-
-          @NotNull
-          @Override
-          public String getTextFor(final Action value) {
-            return value.getName().getStringValue() + " (" + 
value.getMethod().getStringValue() + ")";
-          }
-
-          @Override
-          public PopupStep onChosen(final Action selectedValue, final boolean 
finalChoice) {
-            final String path = selectedValue.getName().getStringValue();
-            WriteCommandAction.writeCommandAction(project).run(() -> 
createValidationXml(project, actionClass, path));
-            return FINAL_CHOICE;
-          }
-        };
-      
JBPopupFactory.getInstance().createListPopup(step).showInBestPositionFor(editor);
-      return;
+    private static List<Action> getActionsForClazz(final Project project, 
final PsiClass clazz, final Module module) {
+        final StrutsModel model = 
StrutsManager.getInstance(project).getCombinedModel(module);
+        if (model == null || !model.isActionClass(clazz)) {
+            return Collections.emptyList();
+        }
+
+        return model.findActionsByClass(clazz);
     }
 
-    createValidationXml(project, actionClass, 
filteredActions.get(0).getName().getStringValue());
-  }
-
-  private static void createValidationXml(final Project project,
-                                          final PsiClass actionClass,
-                                          @Nullable final String path) {
-    final PsiManager manager = PsiManager.getInstance(project);
-    final String actionClassQualifiedName = actionClass.getQualifiedName();
-    assert actionClassQualifiedName != null;
-
-    final PackageWrapper targetPackage =
-      new PackageWrapper(manager, 
StringUtil.getPackageName(actionClassQualifiedName));
-
-    final Module module = ModuleUtilCore.findModuleForPsiElement(actionClass);
-    assert module != null;
-    final List<VirtualFile> sourceRoots = 
ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.PRODUCTION);
-    final VirtualFile sourceRoot = sourceRoots.size() == 1 ? 
sourceRoots.get(0) :
-                                   
CommonMoveClassesOrPackagesUtil.chooseSourceRoot(targetPackage,
-                                                                               
     sourceRoots,
-                                                                               
     manager.findDirectory(sourceRoots.get(0)));
-    if (sourceRoot == null) {
-      return;
+    @NotNull
+    @Override
+    public String getFamilyName() {
+        return getText();
     }
 
-    final PsiDirectory directory = manager.findDirectory(sourceRoot);
-    assert directory != null : sourceRoot.getPresentableUrl();
+    @Override
+    public Icon getIcon(final int flags) {
+        return Struts2Icons.Action;
+    }
 
-    final FileTemplateManager templateManager = 
FileTemplateManager.getInstance(project);
-    final FileTemplate validationTemplate = 
templateManager.getJ2eeTemplate(StrutsFileTemplateGroupDescriptorFactory.VALIDATION_XML);
+    @Override
+    public void invoke(@NotNull final Project project,
+                       final Editor editor,
+                       @NotNull final PsiElement element) throws 
IncorrectOperationException {
+        final PsiClass actionClass = findActionClass(element);
+        assert actionClass != null : element;
+
+        final List<Action> filteredActions = 
getActionsWithoutValidation(actionClass);
+        if (filteredActions.size() > 1) {
+            final ListPopupStep<Action> step =
+                    new BaseListPopupStep<>("Choose action mapping", 
filteredActions) {
+
+                        @Override
+                        public Icon getIconFor(final Action value) {
+                            return Struts2Icons.Action;
+                        }
+
+                        @NotNull
+                        @Override
+                        public String getTextFor(final Action value) {
+                            return value.getName().getStringValue() + " (" + 
value.getMethod().getStringValue() + ")";
+                        }
+
+                        @Override
+                        public PopupStep onChosen(final Action selectedValue, 
final boolean finalChoice) {
+                            final String path = 
selectedValue.getName().getStringValue();
+                            
WriteCommandAction.writeCommandAction(project).run(() -> 
createValidationXml(project, actionClass, path));
+                            return FINAL_CHOICE;
+                        }
+                    };
+            
JBPopupFactory.getInstance().createListPopup(step).showInBestPositionFor(editor);
+            return;
+        }
 
-    final PsiDirectory packageDirectoryInSourceRoot = 
CommonJavaRefactoringUtil.createPackageDirectoryInSourceRoot(targetPackage, 
sourceRoot);
-    try {
-      final String filename =
-        path == null ? actionClass.getName() + "-validation.xml" : 
actionClass.getName() + "-" + path + "-validation.xml";
-      final PsiElement psiElement = 
FileTemplateUtil.createFromTemplate(validationTemplate, filename, null, 
packageDirectoryInSourceRoot);
-      NavigationUtil.activateFileWithPsiElement(psiElement, true);
+        createValidationXml(project, actionClass, 
filteredActions.get(0).getName().getStringValue());
     }
-    catch (Exception e) {
-      throw new IncorrectOperationException("error creating validation.xml", 
(Throwable)e);
-    }
-  }
-
-  private static List<Action> getActionsWithoutValidation(final PsiClass 
actionClass) {
-    final Project project = actionClass.getProject();
-    final List<Action> actions = getActionsForClazz(project,
-                                                    actionClass,
-                                                    
ModuleUtilCore.findModuleForPsiElement(actionClass));
-
-    final List<XmlFile> files = 
ValidatorManager.getInstance(project).findValidationFilesFor(actionClass);
-    return ContainerUtil.filter(actions, action -> {
-      final String path = action.getName().getStringValue();
-      for (final XmlFile file : files) {
-        if (file.getName().contains(path)) {
-          return false;
+
+    private static void createValidationXml(final Project project,
+                                            final PsiClass actionClass,
+                                            @Nullable final String path) {
+        final PsiManager manager = PsiManager.getInstance(project);
+        final String actionClassQualifiedName = actionClass.getQualifiedName();
+        assert actionClassQualifiedName != null;
+
+        final PackageWrapper targetPackage =
+                new PackageWrapper(manager, 
StringUtil.getPackageName(actionClassQualifiedName));
+
+        final Module module = 
ModuleUtilCore.findModuleForPsiElement(actionClass);
+        assert module != null;
+        final List<VirtualFile> sourceRoots = 
ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.PRODUCTION);
+        final VirtualFile sourceRoot = sourceRoots.size() == 1 ? 
sourceRoots.get(0) :
+                CommonMoveClassesOrPackagesUtil.chooseSourceRoot(targetPackage,
+                        sourceRoots,
+                        manager.findDirectory(sourceRoots.get(0)));
+        if (sourceRoot == null) {
+            return;
         }
-      }
-      return true;
-    });
-  }
-
-  @Nullable
-  private static PsiClass findActionClass(final PsiElement psiElement) {
-    if (!(psiElement instanceof PsiIdentifier)) {
-      return null;
-    }
 
-    final PsiElement parent = psiElement.getParent();
-    if (!(parent instanceof PsiClass clazz)) {
-      return null;
-    }
+        final PsiDirectory directory = manager.findDirectory(sourceRoot);
+        assert directory != null : sourceRoot.getPresentableUrl();
+
+        final FileTemplateManager templateManager = 
FileTemplateManager.getInstance(project);
+        final FileTemplate validationTemplate = 
templateManager.getJ2eeTemplate(StrutsFileTemplateGroupDescriptorFactory.VALIDATION_XML);
 
-    if (clazz.getNameIdentifier() != psiElement) {
-      return null;
+        final PsiDirectory packageDirectoryInSourceRoot = 
CommonJavaRefactoringUtil.createPackageDirectoryInSourceRoot(targetPackage, 
sourceRoot);
+        try {
+            final String filename =
+                    path == null ? actionClass.getName() + "-validation.xml" : 
actionClass.getName() + "-" + path + "-validation.xml";
+            final PsiElement psiElement = 
FileTemplateUtil.createFromTemplate(validationTemplate, filename, null, 
packageDirectoryInSourceRoot);
+            NavigationUtil.activateFileWithPsiElement(psiElement, true);
+        } catch (Exception e) {
+            throw new IncorrectOperationException("error creating 
validation.xml", (Throwable) e);
+        }
     }
 
-    // do not run on non-public, abstract classes or interfaces
-    if (clazz.isInterface() ||
-        clazz.isAnnotationType() ||
-        !clazz.hasModifierProperty(PsiModifier.PUBLIC) ||
-        clazz.hasModifierProperty(PsiModifier.ABSTRACT)) {
-      return null;
+    private static List<Action> getActionsWithoutValidation(final PsiClass 
actionClass) {
+        final Project project = actionClass.getProject();
+        final List<Action> actions = getActionsForClazz(project,
+                actionClass,
+                ModuleUtilCore.findModuleForPsiElement(actionClass));
+
+        final List<XmlFile> files = 
ValidatorManager.getInstance(project).findValidationFilesFor(actionClass);
+        return ContainerUtil.filter(actions, action -> {
+            final String path = action.getName().getStringValue();
+            if (path == null) return true;
+            for (final XmlFile file : files) {
+                if (file.getName().contains(path)) {
+                    return false;
+                }
+            }
+            return true;
+        });
     }
 
-    return clazz;
-  }
+    @Nullable
+    private static PsiClass findActionClass(final PsiElement psiElement) {
+        if (!(psiElement instanceof PsiIdentifier)) {
+            return null;
+        }
+
+        final PsiElement parent = psiElement.getParent();
+        if (!(parent instanceof PsiClass clazz)) {
+            return null;
+        }
+
+        if (clazz.getNameIdentifier() != psiElement) {
+            return null;
+        }
+
+        // do not run on non-public, abstract classes or interfaces
+        if (clazz.isInterface() ||
+                clazz.isAnnotationType() ||
+                !clazz.hasModifierProperty(PsiModifier.PUBLIC) ||
+                clazz.hasModifierProperty(PsiModifier.ABSTRACT)) {
+            return null;
+        }
+
+        return clazz;
+    }
 }
diff --git 
a/src/main/java/com/intellij/struts2/jsp/inspection/HardcodedActionUrlInspection.java
 
b/src/main/java/com/intellij/struts2/jsp/inspection/HardcodedActionUrlInspection.java
index 7af9d1a..9ffed32 100644
--- 
a/src/main/java/com/intellij/struts2/jsp/inspection/HardcodedActionUrlInspection.java
+++ 
b/src/main/java/com/intellij/struts2/jsp/inspection/HardcodedActionUrlInspection.java
@@ -203,6 +203,7 @@ public class HardcodedActionUrlInspection extends 
XmlSuppressableInspectionTool
             while (indent.length() < start - lineStart) indent += " ";
 
             Pair<String, String> tag_var = buildTag(prefix, url, indent, 
inline, myActionExtension);
+            assert tag_var != null;
             String tag = tag_var.getFirst();
             String var = tag_var.getSecond();
 
diff --git 
a/src/main/java/com/intellij/struts2/reference/ResultActionPropertyReferenceProvider.java
 
b/src/main/java/com/intellij/struts2/reference/ResultActionPropertyReferenceProvider.java
index 76a2bfd..655b50e 100644
--- 
a/src/main/java/com/intellij/struts2/reference/ResultActionPropertyReferenceProvider.java
+++ 
b/src/main/java/com/intellij/struts2/reference/ResultActionPropertyReferenceProvider.java
@@ -18,6 +18,7 @@ package com.intellij.struts2.reference;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.*;
+import com.intellij.psi.xml.XmlTag;
 import com.intellij.struts2.dom.struts.action.Action;
 import com.intellij.struts2.dom.struts.action.Result;
 import com.intellij.struts2.reference.common.BeanPropertyPathReference;
@@ -34,77 +35,80 @@ import org.jetbrains.annotations.NotNull;
  */
 public class ResultActionPropertyReferenceProvider extends 
PsiReferenceProvider {
 
-  private static final String EXPRESSION_START = "${";
-  private static final String EXPRESSION_END = "}";
-
-  @Override
-  public PsiReference @NotNull [] getReferencesByElement(@NotNull final 
PsiElement psiElement,
-                                                         @NotNull final 
ProcessingContext processingContext) {
-    final Result result = (Result)DomUtil.getDomElement(psiElement);
-    assert result != null : psiElement.getText();
-    final Action action = result.getParentOfType(Action.class, true);
-    assert action != null : psiElement.getText();
-    final PsiClass actionClass = action.searchActionClass();
-    if (actionClass == null) {
-      return PsiReference.EMPTY_ARRAY;
+    private static final String EXPRESSION_START = "${";
+    private static final String EXPRESSION_END = "}";
+
+    @Override
+    public PsiReference @NotNull [] getReferencesByElement(@NotNull final 
PsiElement psiElement,
+                                                           @NotNull final 
ProcessingContext processingContext) {
+        final Result result = (Result) DomUtil.getDomElement(psiElement);
+        assert result != null : psiElement.getText();
+        final Action action = result.getParentOfType(Action.class, true);
+        assert action != null : psiElement.getText();
+        final PsiClass actionClass = action.searchActionClass();
+        if (actionClass == null) {
+            return PsiReference.EMPTY_ARRAY;
+        }
+
+        final XmlTag xmlTag = result.getXmlTag();
+        if (xmlTag == null) {
+            return PsiReference.EMPTY_ARRAY;
+        }
+        final int tagValueStartOffset = 
ElementManipulators.getOffsetInElement(xmlTag);
+        PsiReference[] references = new PsiReference[1];
+
+        final String stringValue = result.getStringValue();
+        if (!StringUtil.isNotEmpty(stringValue)) {
+            return PsiReference.EMPTY_ARRAY;
+        }
+
+        final String resultText = StringUtil.replace(stringValue, "&", 
"&amp;");
+        final int lastExpressionEnd = resultText.length();
+
+        int startOffset = 0;
+        while (startOffset < lastExpressionEnd) {
+            startOffset = resultText.indexOf(EXPRESSION_START, startOffset);
+            if (startOffset == -1) {
+                break;
+            }
+
+            startOffset += EXPRESSION_START.length();
+            final int closingBraceIdx = resultText.indexOf(EXPRESSION_END, 
startOffset);
+            final int length = (closingBraceIdx != -1 ? closingBraceIdx : 
resultText.length()) - startOffset;
+
+            final String expressionString = resultText.substring(startOffset, 
startOffset + length);
+
+            // we only "fake" OGNL here, skip method call expressions for now
+            if (StringUtil.containsChar(expressionString, '(')) {
+                continue;
+            }
+
+            final BeanPropertyPathReferenceSet propertyPathReferenceSet =
+                    new BeanPropertyPathReferenceSet(expressionString,
+                            psiElement,
+                            startOffset,
+                            '.',
+                            actionClass,
+                            true) {
+
+                        // CTOR creates references eagerly, so we have to 
subclass here
+                        @Override
+                        public boolean isSoft() {
+                            return false;
+                        }
+
+                        @NotNull
+                        @Override
+                        protected BeanPropertyPathReference 
createReference(final TextRange range, final int index) {
+                            final TextRange shift = 
TextRange.from(range.getStartOffset() + tagValueStartOffset,
+                                    range.getLength()); // shift range to 
XmlTag value range
+                            return createBeanPropertyPathReference(shift, 
index);
+                        }
+                    };
+
+            references = ArrayUtil.mergeArrays(references, 
propertyPathReferenceSet.getPsiReferences());
+        }
+
+        return references;
     }
-
-    final int tagValueStartOffset = 
ElementManipulators.getOffsetInElement(result.getXmlTag());
-    PsiReference[] references = new PsiReference[1];
-
-    final String stringValue = result.getStringValue();
-    if (!StringUtil.isNotEmpty(stringValue)) {
-      return PsiReference.EMPTY_ARRAY;
-    }
-
-    final String resultText = StringUtil.replace(stringValue, "&", "&amp;");
-    final int lastExpressionEnd = Math.max(resultText.length(),         // 
missing '}'
-                                           
resultText.lastIndexOf(EXPRESSION_START));
-
-    int startOffset = 0;
-    while (startOffset < lastExpressionEnd) {
-      startOffset = resultText.indexOf(EXPRESSION_START, startOffset);
-      if (startOffset == -1) {
-        break;
-      }
-
-      startOffset += EXPRESSION_START.length();
-      final int closingBraceIdx = resultText.indexOf(EXPRESSION_END, 
startOffset);
-      final int length = (closingBraceIdx != -1 ? closingBraceIdx : 
resultText.length()) - startOffset;
-
-      final String expressionString = resultText.substring(startOffset, 
startOffset + length);
-
-      // we only "fake" OGNL here, skip method call expressions for now
-      if (StringUtil.containsChar(expressionString, '(')) {
-        continue;
-      }
-
-      final BeanPropertyPathReferenceSet propertyPathReferenceSet =
-        new BeanPropertyPathReferenceSet(expressionString,
-                                         psiElement,
-                                         startOffset,
-                                         '.',
-                                         actionClass,
-                                         true) {
-
-          // CTOR creates references eagerly, so we have to subclass here
-          @Override
-          public boolean isSoft() {
-            return false;
-          }
-
-          @NotNull
-          @Override
-          protected BeanPropertyPathReference createReference(final TextRange 
range, final int index) {
-            final TextRange shift = TextRange.from(range.getStartOffset() + 
tagValueStartOffset,
-                                                   range.getLength()); // 
shift range to XmlTag value range
-            return createBeanPropertyPathReference(shift, index);
-          }
-        };
-
-      references = ArrayUtil.mergeArrays(references, 
propertyPathReferenceSet.getPsiReferences());
-    }
-
-    return references;
-  }
 }
\ No newline at end of file
diff --git 
a/src/main/java/com/intellij/struts2/reference/web/StrutsConstantValueReference.java
 
b/src/main/java/com/intellij/struts2/reference/web/StrutsConstantValueReference.java
index 45ff9b6..ce2e587 100644
--- 
a/src/main/java/com/intellij/struts2/reference/web/StrutsConstantValueReference.java
+++ 
b/src/main/java/com/intellij/struts2/reference/web/StrutsConstantValueReference.java
@@ -43,138 +43,139 @@ import java.util.*;
  */
 class StrutsConstantValueReference extends PsiReferenceBase<XmlTag> implements 
EmptyResolveMessageProvider {
 
-  @Nullable
-  private final Pair<DomElement, Converter> elementConverterPair;
-
-  StrutsConstantValueReference(@NotNull final XmlTag xmlTag) {
-    super(xmlTag, false);
-    elementConverterPair = getElementConverterPair();
-  }
-
-  @Override
-  public PsiElement resolve() {
-    if (elementConverterPair == null) {
-      return myElement;
-    }
-
-    final Converter converter = elementConverterPair.getSecond();
-    final ConvertContext convertContext = 
ConvertContextFactory.createConvertContext(elementConverterPair.first);
+    @Nullable
+    private final Pair<DomElement, Converter> elementConverterPair;
 
-    // additional variants (String only)
-    if (converter instanceof ResolvingConverter) {
-      final Set additionalVariants = ((ResolvingConverter<?>) 
converter).getAdditionalVariants(convertContext);
-      if (additionalVariants.contains(getValue())) {
-        return myElement;
-      }
+    StrutsConstantValueReference(@NotNull final XmlTag xmlTag) {
+        super(xmlTag, false);
+        elementConverterPair = getElementConverterPair();
     }
 
-    // "normal" reference
-    final Object resolveObject = converter.fromString(getValue(), 
convertContext);
-    if (resolveObject == null) {
-      return null;
-    }
+    @Override
+    public PsiElement resolve() {
+        if (elementConverterPair == null) {
+            return myElement;
+        }
 
-    // DomElement
-    if (resolveObject instanceof DomElement) {
-      return ((DomElement) resolveObject).getXmlTag();
-    }
+        final Converter converter = elementConverterPair.getSecond();
+        final ConvertContext convertContext = 
ConvertContextFactory.createConvertContext(elementConverterPair.first);
 
-    // fake self-reference (e.g. String value from Converter)
-    if (!(resolveObject instanceof PsiElement)) {
-      return myElement;
-    }
+        // additional variants (String only)
+        if (converter instanceof ResolvingConverter) {
+            final Set additionalVariants = ((ResolvingConverter<?>) 
converter).getAdditionalVariants(convertContext);
+            if (additionalVariants.contains(getValue())) {
+                return myElement;
+            }
+        }
 
-    // "real" reference
-    return (PsiElement) resolveObject;
-  }
+        // "normal" reference
+        final Object resolveObject = converter.fromString(getValue(), 
convertContext);
+        if (resolveObject == null) {
+            return null;
+        }
 
-  @Override
-  @NotNull
-  public String getUnresolvedMessagePattern() {
-    assert elementConverterPair != null;
+        // DomElement
+        if (resolveObject instanceof DomElement) {
+            return ((DomElement) resolveObject).getXmlTag();
+        }
 
-    return elementConverterPair.second
-        .getErrorMessage(getValue(), 
ConvertContextFactory.createConvertContext(elementConverterPair.first));
-  }
+        // fake self-reference (e.g. String value from Converter)
+        if (!(resolveObject instanceof PsiElement)) {
+            return myElement;
+        }
 
-  @Override
-  @SuppressWarnings({"unchecked"})
-  public Object @NotNull [] getVariants() {
-    if (elementConverterPair == null) {
-      return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
+        // "real" reference
+        return (PsiElement) resolveObject;
     }
 
-    final Converter converter = elementConverterPair.second;
-    if (!(converter instanceof ResolvingConverter resolvingConverter)) {
-      return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
+    @Override
+    @NotNull
+    public String getUnresolvedMessagePattern() {
+        assert elementConverterPair != null;
+
+        final String msg = elementConverterPair.second
+                .getErrorMessage(getValue(), 
ConvertContextFactory.createConvertContext(elementConverterPair.first));
+        return msg != null ? msg : "Cannot resolve '" + getValue() + "'";
     }
 
-    // merge "normal" + additional variants
-    final DomElement paramValueElement = elementConverterPair.first;
-    final ConvertContext convertContext = 
ConvertContextFactory.createConvertContext(paramValueElement);
+    @Override
+    @SuppressWarnings({"unchecked"})
+    public Object @NotNull [] getVariants() {
+        if (elementConverterPair == null) {
+            return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
+        }
 
-    // wrap explicitly for empty list
-    final Collection converterVariants = new 
ArrayList(resolvingConverter.getVariants(convertContext));
+        final Converter converter = elementConverterPair.second;
+        if (!(converter instanceof ResolvingConverter resolvingConverter)) {
+            return ArrayUtilRt.EMPTY_OBJECT_ARRAY;
+        }
 
-    final Collection variants;
-    if (!converterVariants.isEmpty() &&
-        converterVariants.iterator().next() instanceof DomElement) {
-      variants = 
Arrays.asList(ElementPresentationManager.getInstance().createVariants(converterVariants));
-    } else {
-      variants = converterVariants;
-    }
+        // merge "normal" + additional variants
+        final DomElement paramValueElement = elementConverterPair.first;
+        final ConvertContext convertContext = 
ConvertContextFactory.createConvertContext(paramValueElement);
+
+        // wrap explicitly for empty list
+        final Collection converterVariants = new 
ArrayList(resolvingConverter.getVariants(convertContext));
 
-    variants.addAll(resolvingConverter.getAdditionalVariants(convertContext));
-
-    // add custom created references
-    if (resolvingConverter instanceof CustomReferenceConverter) {
-      final PsiReference[] references = ((CustomReferenceConverter) 
resolvingConverter).
-          createReferences((GenericDomValue) paramValueElement,
-                           myElement,
-                           convertContext);
-      for (final PsiReference customReference : references) {
-        if (customReference instanceof JavaClassReference javaClassReference) {
-          @NotNull List<String> names = javaClassReference.getSuperClasses();
-          PsiElement context = javaClassReference.getCompletionContext();
-          if (!names.isEmpty() && context instanceof PsiPackage) {
-            javaClassReference.processSubclassVariants((PsiPackage)context, 
ArrayUtil.toStringArray(names), element -> variants.add(element));
-            continue;
-          }
+        final Collection variants;
+        if (!converterVariants.isEmpty() &&
+                converterVariants.iterator().next() instanceof DomElement) {
+            variants = 
Arrays.asList(ElementPresentationManager.getInstance().createVariants(converterVariants));
+        } else {
+            variants = converterVariants;
+        }
+
+        
variants.addAll(resolvingConverter.getAdditionalVariants(convertContext));
+
+        // add custom created references
+        if (resolvingConverter instanceof CustomReferenceConverter) {
+            final PsiReference[] references = ((CustomReferenceConverter) 
resolvingConverter).
+                    createReferences((GenericDomValue) paramValueElement,
+                            myElement,
+                            convertContext);
+            for (final PsiReference customReference : references) {
+                if (customReference instanceof JavaClassReference 
javaClassReference) {
+                    @NotNull List<String> names = 
javaClassReference.getSuperClasses();
+                    PsiElement context = 
javaClassReference.getCompletionContext();
+                    if (!names.isEmpty() && context instanceof PsiPackage) {
+                        
javaClassReference.processSubclassVariants((PsiPackage) context, 
ArrayUtil.toStringArray(names), element -> variants.add(element));
+                        continue;
+                    }
+                }
+                Collections.addAll(variants, customReference.getVariants());
+            }
         }
-        Collections.addAll(variants, customReference.getVariants());
-      }
-    }
 
-    return ArrayUtil.toObjectArray(variants);
-  }
-
-  /**
-   * Gets the DomElement and corresponding converter.
-   *
-   * @return {@code null} on errors or if one of both could not be resolved.
-   */
-  @Nullable
-  private Pair<DomElement, Converter> getElementConverterPair() {
-    final DomElement paramValueElement = DomUtil.getDomElement(myElement);
-    assert paramValueElement != null;
-
-    final DomElement domElement = paramValueElement.getParent();
-    assert domElement instanceof CommonParamValue;
-
-    final CommonParamValue initParamElement = (CommonParamValue) domElement;
-    final String paramName = initParamElement.getParamName().getStringValue();
-    if (StringUtil.isEmpty(paramName)) {
-      return null;
+        return ArrayUtil.toObjectArray(variants);
     }
 
-    final StrutsConstantManager constantManager = 
StrutsConstantManager.getInstance(myElement.getProject());
+    /**
+     * Gets the DomElement and corresponding converter.
+     *
+     * @return {@code null} on errors or if one of both could not be resolved.
+     */
+    @Nullable
+    private Pair<DomElement, Converter> getElementConverterPair() {
+        final DomElement paramValueElement = DomUtil.getDomElement(myElement);
+        assert paramValueElement != null;
+
+        final DomElement domElement = paramValueElement.getParent();
+        assert domElement instanceof CommonParamValue;
+
+        final CommonParamValue initParamElement = (CommonParamValue) 
domElement;
+        final String paramName = 
initParamElement.getParamName().getStringValue();
+        if (StringUtil.isEmpty(paramName)) {
+            return null;
+        }
 
-    final Converter converter = constantManager.findConverter(myElement, 
StrutsConstantKey.create(paramName));
-    if (converter == null) {
-      return null;
-    }
+        final StrutsConstantManager constantManager = 
StrutsConstantManager.getInstance(myElement.getProject());
 
-    return Pair.create(paramValueElement, converter);
-  }
+        final Converter converter = constantManager.findConverter(myElement, 
StrutsConstantKey.create(paramName));
+        if (converter == null) {
+            return null;
+        }
+
+        return Pair.create(paramValueElement, converter);
+    }
 
 }
\ No newline at end of file
diff --git 
a/src/main/java/com/intellij/struts2/spring/ExtendableClassConverterSpringContributor.java
 
b/src/main/java/com/intellij/struts2/spring/ExtendableClassConverterSpringContributor.java
index 53effd6..06ee8ef 100644
--- 
a/src/main/java/com/intellij/struts2/spring/ExtendableClassConverterSpringContributor.java
+++ 
b/src/main/java/com/intellij/struts2/spring/ExtendableClassConverterSpringContributor.java
@@ -46,120 +46,119 @@ import java.util.stream.Collectors;
  * @author Yann C&eacute;bron
  */
 final class ExtendableClassConverterSpringContributor
-  extends ExtendableClassConverter.ExtendableClassConverterContributor {
-
-  @Override
-  @NotNull
-  public String getTypeName() {
-    return StrutsBundle.message("dom.extendable.class.converter.type.spring");
-  }
-
-  /**
-   * Checks if struts2-spring-plugin is present in current module.
-   *
-   * @param convertContext Current context.
-   * @return true if yes.
-   */
-  @Override
-  public boolean isSuitable(@NotNull final ConvertContext convertContext) {
-    if (!SpringCommonUtils.isSpringConfigured(convertContext.getModule())) 
return false;
-
-    return DomJavaUtil.findClass(StrutsConstants.SPRING_OBJECT_FACTORY_CLASS, 
convertContext.getInvocationElement()) != null;
-  }
-
-  @Override
-  public PsiReference @NotNull [] getReferences(@NotNull final ConvertContext 
convertContext,
-                                                @NotNull final PsiElement 
psiElement,
-                                                @NotNull final ExtendClass 
extendClass) {
-    return new PsiReference[]{new 
SpringBeanReference((XmlAttributeValue)psiElement,
-                                                      
convertContext.getModule(),
-                                                      extendClass)};
-  }
-
-
-  // TODO provide QuickFix to create Spring bean?
-  private static final class SpringBeanReference extends 
PsiReferenceBase<XmlAttributeValue> {
-
-    private final Module module;
-    private final ExtendClass extendClass;
-
-    private SpringBeanReference(final XmlAttributeValue element,
-                                final Module module,
-                                final ExtendClass extendClass) {
-      super(element, true);
-      this.module = module;
-      this.extendClass = extendClass;
-    }
+        extends ExtendableClassConverter.ExtendableClassConverterContributor {
 
+    @Override
     @NotNull
-    private SpringModel getSpringModel() {
-      return 
SpringManager.getInstance(module.getProject()).getCombinedModel(module);
+    public String getTypeName() {
+        return 
StrutsBundle.message("dom.extendable.class.converter.type.spring");
     }
 
+    /**
+     * Checks if struts2-spring-plugin is present in current module.
+     *
+     * @param convertContext Current context.
+     * @return true if yes.
+     */
     @Override
-    public PsiElement resolve() {
-      final String beanName = myElement.getValue();
-      if (StringUtil.isEmpty(beanName)) {
-        return null;
-      }
-
-      final SpringModel springModel = getSpringModel();
-      final SpringBeanPointer<?>  springBean = 
SpringModelSearchers.findBean(springModel, beanName);
-      if (springBean == null) {
-        return null;
-      }
-
-      if (springBean.isAbstract()) {
-        return null;
-      }
-
-      return springBean.getBeanClass();
+    public boolean isSuitable(@NotNull final ConvertContext convertContext) {
+        if (!SpringCommonUtils.isSpringConfigured(convertContext.getModule())) 
return false;
+
+        return 
DomJavaUtil.findClass(StrutsConstants.SPRING_OBJECT_FACTORY_CLASS, 
convertContext.getInvocationElement()) != null;
     }
 
     @Override
-    public Object @NotNull [] getVariants() {
-      final SpringModel springModel = getSpringModel();
+    public PsiReference @NotNull [] getReferences(@NotNull final 
ConvertContext convertContext,
+                                                  @NotNull final PsiElement 
psiElement,
+                                                  @NotNull final ExtendClass 
extendClass) {
+        return new PsiReference[]{new SpringBeanReference((XmlAttributeValue) 
psiElement,
+                convertContext.getModule(),
+                extendClass)};
+    }
 
-      final @Nullable Set<PsiClass> subClasses = getPossibleSubClasses();
 
-      final Collection<SpringBeanPointer<?>> list = new ArrayList<>();
-      if (subClasses.size() > 0) {
-        for (PsiClass subClass : subClasses) {
-          list.addAll(SpringModelSearchers.findBeans(springModel, 
SpringModelSearchParameters.byClass(subClass).withInheritors()));
+    // TODO provide QuickFix to create Spring bean?
+    private static final class SpringBeanReference extends 
PsiReferenceBase<XmlAttributeValue> {
+
+        private final Module module;
+        private final ExtendClass extendClass;
+
+        private SpringBeanReference(final XmlAttributeValue element,
+                                    final Module module,
+                                    final ExtendClass extendClass) {
+            super(element, true);
+            this.module = module;
+            this.extendClass = extendClass;
         }
-      }
-      else {
-        list.addAll(springModel.getAllCommonBeans());
-      }
-
-      final List<LookupElement> variants = new ArrayList<>(list.size());
-      for (final SpringBeanPointer<?>  bean : list) {
-        if (bean.isAbstract()) {
-          continue;
+
+        @NotNull
+        private SpringModel getSpringModel() {
+            return 
SpringManager.getInstance(module.getProject()).getCombinedModel(module);
         }
 
-        ContainerUtil.addIfNotNull(variants, 
SpringConverterUtil.createCompletionVariant(bean));
-      }
+        @Override
+        public PsiElement resolve() {
+            final String beanName = myElement.getValue();
+            if (StringUtil.isEmpty(beanName)) {
+                return null;
+            }
 
-      return variants.toArray(LookupElement.EMPTY_ARRAY);
-    }
+            final SpringModel springModel = getSpringModel();
+            final SpringBeanPointer<?> springBean = 
SpringModelSearchers.findBean(springModel, beanName);
+            if (springBean == null) {
+                return null;
+            }
 
-    /**
-     * Determines a possible subclass for the current reference element.
-     *
-     * @return Subclass the Spring bean reference must implement or {@code 
null} if no subclass defined.
-     */
-    @Nullable
-    private Set<PsiClass> getPossibleSubClasses() {
-      final String[] subClassName = extendClass.value();
-      if (subClassName.length == 0) {
-        return null;
-      }
-      return Arrays.stream(subClassName)
-        .map(s -> JavaPsiFacade.getInstance(module.getProject())
-        .findClass(s, 
GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, false)))
-        .filter(Objects::nonNull)
-        .collect(Collectors.toSet());
+            if (springBean.isAbstract()) {
+                return null;
+            }
+
+            return springBean.getBeanClass();
+        }
+
+        @Override
+        public Object @NotNull [] getVariants() {
+            final SpringModel springModel = getSpringModel();
+
+            final @Nullable Set<PsiClass> subClasses = getPossibleSubClasses();
+
+            final Collection<SpringBeanPointer<?>> list = new ArrayList<>();
+            if (subClasses != null && !subClasses.isEmpty()) {
+                for (PsiClass subClass : subClasses) {
+                    list.addAll(SpringModelSearchers.findBeans(springModel, 
SpringModelSearchParameters.byClass(subClass).withInheritors()));
+                }
+            } else {
+                list.addAll(springModel.getAllCommonBeans());
+            }
+
+            final List<LookupElement> variants = new ArrayList<>(list.size());
+            for (final SpringBeanPointer<?> bean : list) {
+                if (bean.isAbstract()) {
+                    continue;
+                }
+
+                ContainerUtil.addIfNotNull(variants, 
SpringConverterUtil.createCompletionVariant(bean));
+            }
+
+            return variants.toArray(LookupElement.EMPTY_ARRAY);
+        }
+
+        /**
+         * Determines a possible subclass for the current reference element.
+         *
+         * @return Subclass the Spring bean reference must implement or {@code 
null} if no subclass defined.
+         */
+        @Nullable
+        private Set<PsiClass> getPossibleSubClasses() {
+            final String[] subClassName = extendClass.value();
+            if (subClassName.length == 0) {
+                return null;
+            }
+            return Arrays.stream(subClassName)
+                    .map(s -> JavaPsiFacade.getInstance(module.getProject())
+                            .findClass(s, 
GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, false)))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toSet());
+        }
     }
-  }
 }
\ No newline at end of file

Reply via email to