/* * Copyright 2015 The authors * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.struts2.annotators; import com.intellij.codeInsight.daemon.LineMarkerInfo; import com.intellij.codeInsight.daemon.LineMarkerProviderDescriptor; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.xml.XmlTag; import com.intellij.struts2.StrutsBundle; import com.intellij.struts2.StrutsConstants; import com.intellij.struts2.StrutsIcons; import com.intellij.struts2.dom.struts.action.Action; import com.intellij.struts2.dom.struts.model.StrutsManager; import com.intellij.struts2.dom.struts.model.StrutsModel; import com.intellij.struts2.facet.StrutsFacet; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; import icons.Struts2Icons; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.Arrays; import java.util.Collection; import java.util.List; /** * Annotates custom tags with "action" attribute. * * @author Yann Cébron */ public class JspActionAnnotator extends LineMarkerProviderDescriptor { @NonNls private static final String ACTION_ATTRIBUTE_NAME = "action"; @NonNls private static final String[] TAGS_WITH_ACTION_ATTRIBUTE = new String[]{"action", "form", "reset", "submit", "url"}; private static final NullableFunction<Action, PsiMethod> ACTION_METHOD_FUNCTION = action -> action.searchActionMethod(); @Override public String getId() { return "JspActionAnnotator"; } @NotNull @Override public String getName() { return "Action (JSP)"; } @Nullable @Override public Icon getIcon() { return Struts2Icons.Action; } @Override public LineMarkerInfo getLineMarkerInfo(@NotNull final PsiElement psiElement) { return null; } @Override public void collectSlowLineMarkers(@NotNull final List<PsiElement> psiElements, @NotNull final Collection<LineMarkerInfo> lineMarkerInfos) { if (psiElements.isEmpty()) { return; } for (final PsiElement element : psiElements) { annotate(element, lineMarkerInfos); } } private static void annotate(@NotNull final PsiElement element, @NotNull final Collection<LineMarkerInfo> lineMarkerInfos) { if (!(element instanceof XmlTag)) { return; } final XmlTag xmlTag = (XmlTag) element; // any of our tags? final String tagName = xmlTag.getLocalName(); if (Arrays.binarySearch(TAGS_WITH_ACTION_ATTRIBUTE, tagName) < 0) { return; } if (!Comparing.equal(xmlTag.getNamespace(), StrutsConstants.TAGLIB_STRUTS_UI_URI)) { return; } // short exit when Struts 2 facet not present final Module module = ModuleUtilCore.findModuleForPsiElement(element); if (module == null) { return; } if (StrutsFacet.getInstance(module) == null) { return; } // special case for <action> final String actionPath = Comparing.equal(tagName, ACTION_ATTRIBUTE_NAME) ? xmlTag.getAttributeValue("name") : xmlTag.getAttributeValue(ACTION_ATTRIBUTE_NAME); if (actionPath == null) { return; } final StrutsModel strutsModel = StrutsManager.getInstance(element.getProject()).getCombinedModel(module); if (strutsModel == null) { return; } final String namespace = xmlTag.getAttributeValue("namespace"); final List<Action> actions = strutsModel.findActionsByName(actionPath, namespace); if (actions.isEmpty()) { return; } // resolve to action method should be exactly 1 final NavigationGutterIconBuilder<PsiElement> gutterIconBuilder = NavigationGutterIconBuilder.create(StrutsIcons.ACTION_CLASS). setAlignment(GutterIconRenderer.Alignment.LEFT). setTooltipText(StrutsBundle.message("annotators.jsp.goto.action.method")). setEmptyPopupText(StrutsBundle.message("annotators.jsp.goto.action.method.not.found")). setTargets(new NotNullLazyValue<Collection<? extends PsiElement>>() { @NotNull protected Collection<PsiMethod> compute() { return ContainerUtil.mapNotNull(actions, ACTION_METHOD_FUNCTION); } }); lineMarkerInfos.add(gutterIconBuilder.createLineMarkerInfo(xmlTag)); } }