/* * Copyright 2000-2010 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.android.inspections; import com.android.SdkConstants; import com.android.resources.ResourceType; import com.intellij.codeInspection.*; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.XmlRecursiveElementVisitor; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlChildRole; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.util.containers.HashSet; import com.intellij.util.xml.DomFileDescription; import org.jetbrains.android.dom.AndroidAnyAttributeDescriptor; import org.jetbrains.android.dom.AndroidResourceDomFileDescription; import org.jetbrains.android.dom.AndroidXmlTagDescriptor; import org.jetbrains.android.dom.manifest.ManifestDomFileDescription; import org.jetbrains.android.dom.xml.AndroidXmlResourcesUtil; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; /** * @author Eugene.Kudelevsky */ public class AndroidUnknownAttributeInspection extends LocalInspectionTool { private static volatile Set<String> ourSupportedResourceTypes; @Nls @NotNull @Override public String getGroupDisplayName() { return AndroidBundle.message("android.inspections.group.name"); } @Nls @NotNull @Override public String getDisplayName() { return AndroidBundle.message("android.inspections.unknown.attribute.name"); } @NotNull @Override public String getShortName() { return "AndroidUnknownAttribute"; } @Override public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { if (!(file instanceof XmlFile)) { return ProblemDescriptor.EMPTY_ARRAY; } AndroidFacet facet = AndroidFacet.getInstance(file); if (facet == null) { return ProblemDescriptor.EMPTY_ARRAY; } if (isMyFile(facet, (XmlFile)file)) { MyVisitor visitor = new MyVisitor(manager, isOnTheFly); file.accept(visitor); return visitor.myResult.toArray(new ProblemDescriptor[visitor.myResult.size()]); } return ProblemDescriptor.EMPTY_ARRAY; } static boolean isMyFile(@NotNull AndroidFacet facet, XmlFile file) { String resourceType = facet.getLocalResourceManager().getFileResourceType(file); if (resourceType != null) { if (ourSupportedResourceTypes == null) { ourSupportedResourceTypes = new HashSet<String>(); for (DomFileDescription description : DomFileDescription.EP_NAME.getExtensions()) { if (description instanceof AndroidResourceDomFileDescription) { String[] resourceTypes = ((AndroidResourceDomFileDescription)description).getResourceTypes(); Collections.addAll(ourSupportedResourceTypes, resourceTypes); } } } if (!ourSupportedResourceTypes.contains(resourceType)) { return false; } if (ResourceType.XML.getName().equals(resourceType)) { final XmlTag rootTag = file.getRootTag(); return rootTag != null && AndroidXmlResourcesUtil.isSupportedRootTag(facet, rootTag.getName()); } return true; } return ManifestDomFileDescription.isManifestFile(file, facet); } private static class MyVisitor extends XmlRecursiveElementVisitor { private final InspectionManager myInspectionManager; private final boolean myOnTheFly; final List<ProblemDescriptor> myResult = new ArrayList<ProblemDescriptor>(); private MyVisitor(InspectionManager inspectionManager, boolean onTheFly) { myInspectionManager = inspectionManager; myOnTheFly = onTheFly; } @Override public void visitXmlAttribute(XmlAttribute attribute) { if (!"xmlns".equals(attribute.getNamespacePrefix())) { String namespace = attribute.getNamespace(); if (SdkConstants.NS_RESOURCES.equals(namespace) || namespace.isEmpty()) { final XmlTag tag = attribute.getParent(); if (tag != null && tag.getDescriptor() instanceof AndroidXmlTagDescriptor && attribute.getDescriptor() instanceof AndroidAnyAttributeDescriptor) { final ASTNode node = attribute.getNode(); assert node != null; ASTNode nameNode = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(node); final PsiElement nameElement = nameNode != null ? nameNode.getPsi() : null; if (nameElement != null) { myResult.add(myInspectionManager.createProblemDescriptor(nameElement, AndroidBundle .message("android.inspections.unknown.attribute.message", attribute.getName()), myOnTheFly, LocalQuickFix.EMPTY_ARRAY, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)); } } } } } } }