diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 8413b924237f..099b9b23dfb4 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -78,6 +78,13 @@ enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.idea.devkit.inspections.InspectionMappingConsistencyInspection"/> + diff --git a/plugins/devkit/resources/inspectionDescriptions/ExtensionPointBeanClass.html b/plugins/devkit/resources/inspectionDescriptions/ExtensionPointBeanClass.html new file mode 100644 index 000000000000..ec105a2ee396 --- /dev/null +++ b/plugins/devkit/resources/inspectionDescriptions/ExtensionPointBeanClass.html @@ -0,0 +1,6 @@ + + +Ensures that an <extensionPoint> tag has <with> subtags specifying types for all class fields. + + + \ No newline at end of file diff --git a/plugins/devkit/src/dom/ExtensionPoint.java b/plugins/devkit/src/dom/ExtensionPoint.java index c05fbf8b8c27..1c25bd2e5ebd 100644 --- a/plugins/devkit/src/dom/ExtensionPoint.java +++ b/plugins/devkit/src/dom/ExtensionPoint.java @@ -56,4 +56,6 @@ public interface ExtensionPoint extends DomElement { @NotNull @SubTagList("with") List getWithElements(); + + With addWith(); } diff --git a/plugins/devkit/src/dom/impl/PluginFieldNameConverter.java b/plugins/devkit/src/dom/impl/PluginFieldNameConverter.java index 5515db6d1bd4..37ce15267102 100644 --- a/plugins/devkit/src/dom/impl/PluginFieldNameConverter.java +++ b/plugins/devkit/src/dom/impl/PluginFieldNameConverter.java @@ -68,21 +68,25 @@ public class PluginFieldNameConverter extends ResolvingConverter { } private static PsiField findFieldByAttributeValue(PsiClass psiClass, @NotNull String attrNameToFind) { - final PsiConstantEvaluationHelper evalHelper = JavaPsiFacade.getInstance(psiClass.getProject()).getConstantEvaluationHelper(); for (PsiField psiField : psiClass.getAllFields()) { - final PsiMethod getter = PropertyUtils.findGetterForField(psiField); - final PsiMethod setter = PropertyUtils.findSetterForField(psiField); - final PsiAnnotation attrAnno = ExtensionDomExtender.findAnnotation(Attribute.class, psiField, getter, setter); - if (attrAnno != null) { - final String attrName = ExtensionDomExtender.getStringAttribute(attrAnno, "value", evalHelper); - if (attrNameToFind.equals(attrName)) { - return psiField; - } + if (attrNameToFind.equals(getAttributeAnnotationValue(psiField))) { + return psiField; } } return null; } + public static String getAttributeAnnotationValue(PsiField psiField) { + final PsiConstantEvaluationHelper evalHelper = JavaPsiFacade.getInstance(psiField.getProject()).getConstantEvaluationHelper(); + final PsiMethod getter = PropertyUtils.findGetterForField(psiField); + final PsiMethod setter = PropertyUtils.findSetterForField(psiField); + final PsiAnnotation attrAnno = ExtensionDomExtender.findAnnotation(Attribute.class, psiField, getter, setter); + if (attrAnno != null) { + return ExtensionDomExtender.getStringAttribute(attrAnno, "value", evalHelper); + } + return null; + } + @Nullable @Override public String toString(@Nullable PsiField field, ConvertContext context) { diff --git a/plugins/devkit/src/inspections/ExtensionPointBeanClassInspection.java b/plugins/devkit/src/inspections/ExtensionPointBeanClassInspection.java new file mode 100644 index 000000000000..c232170d6a5a --- /dev/null +++ b/plugins/devkit/src/inspections/ExtensionPointBeanClassInspection.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.devkit.inspections; + +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiField; +import com.intellij.psi.XmlElementVisitor; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.devkit.dom.ExtensionPoint; +import org.jetbrains.idea.devkit.dom.impl.ExtensionDomExtender; +import org.jetbrains.idea.devkit.inspections.quickfix.AddWithTagFix; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yole + */ +public class ExtensionPointBeanClassInspection extends DevKitInspectionBase { + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { + return new XmlElementVisitor() { + @Override + public void visitXmlTag(XmlTag tag) { + DomElement element = DomUtil.getDomElement(tag); + if (element instanceof ExtensionPoint) { + ExtensionPoint extensionPoint = (ExtensionPoint)element; + if (extensionPoint.getWithElements().isEmpty() && !collectMissingWithTags(extensionPoint).isEmpty()) { + holder.registerProblem(tag, + " does not have tags to specify the types of class fields", + new AddWithTagFix()); + } + } + } + }; + } + + public static List collectMissingWithTags(ExtensionPoint element) { + final List result = new ArrayList(); + PsiClass beanClass = element.getBeanClass().getValue(); + if (beanClass != null) { + for (PsiField field : beanClass.getAllFields()) { + if (ExtensionDomExtender.isClassField(field.getName()) && + ExtensionDomExtender.findWithElement(element.getWithElements(), field) == null) { + result.add(field); + } + } + } + return result; + } +} diff --git a/plugins/devkit/src/inspections/quickfix/AddWithTagFix.java b/plugins/devkit/src/inspections/quickfix/AddWithTagFix.java new file mode 100644 index 000000000000..138c2a4ce19e --- /dev/null +++ b/plugins/devkit/src/inspections/quickfix/AddWithTagFix.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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 org.jetbrains.idea.devkit.inspections.quickfix; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiField; +import com.intellij.util.PsiNavigateUtil; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.devkit.dom.ExtensionPoint; +import org.jetbrains.idea.devkit.dom.With; +import org.jetbrains.idea.devkit.dom.impl.PluginFieldNameConverter; +import org.jetbrains.idea.devkit.inspections.ExtensionPointBeanClassInspection; + +import java.util.List; + +/** + * @author yole + */ +public class AddWithTagFix implements LocalQuickFix { + @NotNull + @Override + public String getName() { + return "Add tag"; + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + DomElement element = DomUtil.getDomElement(descriptor.getPsiElement()); + if (!(element instanceof ExtensionPoint)) { + return; + } + ExtensionPoint extensionPoint = (ExtensionPoint)element; + List fields = ExtensionPointBeanClassInspection.collectMissingWithTags(extensionPoint); + PsiElement navTarget = null; + for (PsiField field : fields) { + String attributeName = PluginFieldNameConverter.getAttributeAnnotationValue(field); + if (attributeName == null) { + attributeName = field.getName(); + } + With with = extensionPoint.addWith(); + with.getAttribute().setStringValue(attributeName); + with.getImplements().setStringValue(""); + if (navTarget == null) { + navTarget = with.getImplements().getXmlAttributeValue(); + } + } + if (navTarget != null) { + PsiNavigateUtil.navigate(navTarget); + } + } +}