package fr.adrienbrault.idea.symfony2plugin.routing.inspection; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlToken; import com.intellij.util.containers.ContainerUtil; import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils; import fr.adrienbrault.idea.symfony2plugin.util.psi.PsiElementAssertUtil; import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper; import org.jetbrains.annotations.NotNull; import org.jetbrains.yaml.psi.YAMLKeyValue; /** * @author Daniel Espendiller <daniel@espendiller.net> */ public class RouteSettingDeprecatedInspection extends LocalInspectionTool { @NotNull public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, boolean isOnTheFly) { if(!Symfony2ProjectComponent.isEnabled(holder.getProject())) { return super.buildVisitor(holder, isOnTheFly); } return new PsiElementVisitor() { @Override public void visitElement(PsiElement element) { if(element instanceof XmlAttributeValue) { registerAttributeRequirementProblem(holder, (XmlAttributeValue) element, "_method"); registerAttributeRequirementProblem(holder, (XmlAttributeValue) element, "_scheme"); } else if(element instanceof XmlAttribute) { registerRoutePatternProblem(holder, (XmlAttribute) element); } else if(element instanceof YAMLKeyValue) { registerYmlRoutePatternProblem(holder, (YAMLKeyValue) element); } super.visitElement(element); } }; } private void registerYmlRoutePatternProblem(@NotNull ProblemsHolder holder, @NotNull YAMLKeyValue element) { String s = PsiElementUtils.trimQuote(element.getKeyText()); if("pattern".equals(s) && YamlHelper.isRoutingFile(element.getContainingFile())) { // pattern: foo holder.registerProblem(element.getKey(), "Pattern is deprecated; use path instead", ProblemHighlightType.LIKE_DEPRECATED); } else if(("_method".equals(s) || "_scheme".equals(s)) && YamlHelper.isRoutingFile(element.getContainingFile())) { // requirements: { _method: 'foo', '_scheme': 'foo' } YAMLKeyValue parentOfType = PsiTreeUtil.getParentOfType(element, YAMLKeyValue.class); if(parentOfType != null && "requirements".equals(parentOfType.getKeyText())) { holder.registerProblem(element.getKey(), String.format("The '%s' requirement is deprecated", s), ProblemHighlightType.LIKE_DEPRECATED); } } } private void registerRoutePatternProblem(@NotNull ProblemsHolder holder, @NotNull XmlAttribute xmlAttribute) { if(!"pattern".equals(xmlAttribute.getName())) { return; } XmlTag xmlTagRoute = PsiElementAssertUtil.getParentOfTypeWithNameOrNull(xmlAttribute, XmlTag.class, "route"); if(xmlTagRoute != null && xmlAttribute.getFirstChild() != null) { holder.registerProblem(xmlAttribute.getFirstChild(), "Pattern is deprecated; use path instead", ProblemHighlightType.LIKE_DEPRECATED); } } private void registerAttributeRequirementProblem(@NotNull ProblemsHolder holder, @NotNull XmlAttributeValue xmlAttributeValue, @NotNull final String requirementAttribute) { if(!xmlAttributeValue.getValue().equals(requirementAttribute)) { return; } XmlAttribute xmlAttributeKey = PsiElementAssertUtil.getParentOfTypeWithNameOrNull(xmlAttributeValue, XmlAttribute.class, "key"); if(xmlAttributeKey != null) { XmlTag xmlTagDefault = PsiElementAssertUtil.getParentOfTypeWithNameOrNull(xmlAttributeKey, XmlTag.class, "requirement"); if(xmlTagDefault != null) { XmlTag xmlTagRoute = PsiElementAssertUtil.getParentOfTypeWithNameOrNull(xmlTagDefault, XmlTag.class, "route"); if(xmlTagRoute != null) { // attach to attribute token only we dont want " or ' char included PsiElement target = findAttributeValueToken(xmlAttributeValue, requirementAttribute); holder.registerProblem(target != null ? target : xmlAttributeValue, String.format("The '%s' requirement is deprecated", requirementAttribute), ProblemHighlightType.LIKE_DEPRECATED); } } } } /** * Find child token which stores value * * XmlToken: "'" * XmlToken: "attributeText" * XmlToken: "'" */ private PsiElement findAttributeValueToken(@NotNull XmlAttributeValue xmlAttributeValue, @NotNull final String attributeText) { return ContainerUtil.find(xmlAttributeValue.getChildren(), psiElement -> psiElement instanceof XmlToken && attributeText.equals(psiElement.getText()) ); } }