| /* |
| * 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 com.intellij.lang.ant.dom; |
| |
| import com.intellij.codeInspection.LocalQuickFix; |
| import com.intellij.codeInspection.ProblemHighlightType; |
| import com.intellij.lang.ant.AntBundle; |
| import com.intellij.lang.ant.AntSupport; |
| import com.intellij.lang.ant.quickfix.AntChangeContextLocalFix; |
| import com.intellij.lang.ant.quickfix.AntCreatePropertyFix; |
| import com.intellij.lang.ant.quickfix.AntCreateTargetFix; |
| import com.intellij.lang.ant.validation.AntInspection; |
| import com.intellij.lang.properties.psi.PropertiesFile; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiPolyVariantReference; |
| import com.intellij.psi.PsiReference; |
| import com.intellij.psi.xml.XmlElement; |
| import com.intellij.util.SmartList; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.HashSet; |
| import com.intellij.util.xml.DomElement; |
| import com.intellij.util.xml.DomUtil; |
| import com.intellij.util.xml.GenericDomValue; |
| import com.intellij.util.xml.highlighting.DomElementAnnotationHolder; |
| import com.intellij.util.xml.highlighting.DomHighlightingHelper; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| public class AntResolveInspection extends AntInspection { |
| |
| public static final String SHORT_NAME = "AntResolveInspection"; |
| |
| @NotNull |
| public String getDisplayName() { |
| return "Ant references resolve problems"; |
| } |
| |
| @NotNull |
| public String getShortName() { |
| return SHORT_NAME; |
| } |
| |
| protected void checkDomElement(DomElement element, DomElementAnnotationHolder holder, DomHighlightingHelper helper) { |
| if (element instanceof GenericDomValue) { |
| final XmlElement valueElement = DomUtil.getValueElement(((GenericDomValue)element)); |
| if (valueElement != null) { |
| checkReferences(valueElement, holder, element); |
| } |
| } |
| else if (element instanceof AntDomTypeDef) { |
| final AntDomTypeDef typeDef = (AntDomTypeDef)element; |
| final List<String> errors = typeDef.getErrorDescriptions(); |
| if (!errors.isEmpty()) { |
| final StringBuilder builder = new StringBuilder(); |
| builder.append(AntBundle.message("failed.to.load.types")).append(":"); |
| for (String error : errors) { |
| builder.append("\n").append(error); |
| } |
| holder.createProblem(typeDef, builder.toString()); |
| } |
| } |
| else if (element instanceof AntDomCustomElement) { |
| final AntDomCustomElement custom = (AntDomCustomElement)element; |
| if (custom.getDefinitionClass() == null) { |
| final AntDomNamedElement declaringElement = custom.getDeclaringElement(); |
| if (declaringElement instanceof AntDomTypeDef) { |
| String failedMessage = AntBundle.message("using.definition.which.type.failed.to.load"); |
| final String error = custom.getLoadError(); |
| if (error != null) { |
| failedMessage = failedMessage + ": " + error; |
| } |
| holder.createProblem(custom, failedMessage); |
| } |
| } |
| } |
| } |
| |
| private static void checkReferences(final XmlElement xmlElement, final @NonNls DomElementAnnotationHolder holder, DomElement domElement) { |
| if (xmlElement == null) { |
| return; |
| } |
| Set<PsiReference> processed = null; |
| Collection<PropertiesFile> propertyFiles = null; // to be initialized lazily |
| for (final PsiReference ref : xmlElement.getReferences()) { |
| if (!(ref instanceof AntDomReference)) { |
| continue; |
| } |
| final AntDomReference antDomRef = (AntDomReference)ref; |
| if (antDomRef.shouldBeSkippedByAnnotator()) { |
| continue; |
| } |
| if (processed != null && processed.contains(ref)) { |
| continue; |
| } |
| |
| if (!isResolvable(ref)) { |
| final List<LocalQuickFix> quickFixList = new SmartList<LocalQuickFix>(); |
| quickFixList.add(new AntChangeContextLocalFix()); |
| |
| if (ref instanceof AntDomPropertyReference) { |
| final String canonicalText = ref.getCanonicalText(); |
| quickFixList.add(new AntCreatePropertyFix(canonicalText, null)); |
| final PsiFile containingFile = xmlElement.getContainingFile(); |
| if (containingFile != null) { |
| if (propertyFiles == null) { |
| propertyFiles = getPropertyFiles(AntSupport.getAntDomProject(containingFile), xmlElement); |
| } |
| for (PropertiesFile propertyFile : propertyFiles) { |
| quickFixList.add(new AntCreatePropertyFix(canonicalText, propertyFile)); |
| } |
| } |
| } |
| else if (ref instanceof AntDomTargetReference) { |
| quickFixList.add(new AntCreateTargetFix(ref.getCanonicalText())); |
| } |
| |
| holder.createProblem( |
| domElement, |
| ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, |
| antDomRef.getUnresolvedMessagePattern(), |
| ref.getRangeInElement(), |
| quickFixList.toArray((new LocalQuickFix[quickFixList.size()])) |
| ); |
| |
| if (ref instanceof AntDomFileReference) { |
| if (processed == null) { |
| processed = new HashSet<PsiReference>(); |
| } |
| ContainerUtil.addAll(processed, ((AntDomFileReference)ref).getFileReferenceSet().getAllReferences()); |
| } |
| } |
| } |
| } |
| |
| private static boolean isResolvable(PsiReference ref) { |
| if (ref.resolve() != null) { |
| return true; |
| } |
| if (ref instanceof PsiPolyVariantReference) { |
| return ((PsiPolyVariantReference)ref).multiResolve(false).length > 0; |
| } |
| return false; |
| } |
| |
| @NotNull |
| private static Collection<PropertiesFile> getPropertyFiles(@Nullable AntDomProject antDomProject, @NotNull XmlElement stopElement) { |
| if (antDomProject == null) { |
| return Collections.emptyList(); |
| } |
| final Set<PropertiesFile> files = new java.util.HashSet<PropertiesFile>(); |
| final int stopOffset = stopElement.getTextOffset(); |
| |
| for (Iterator<AntDomElement> iterator = antDomProject.getAntChildrenIterator(); iterator.hasNext(); ) { |
| AntDomElement child = iterator.next(); |
| final XmlElement xmlElement = child.getXmlElement(); |
| if (xmlElement != null && xmlElement.getTextOffset() >= stopOffset) { |
| break; // no need to offer to add properties to files that are imported after the property reference |
| } |
| if (child instanceof AntDomProperty) { |
| final AntDomProperty property = (AntDomProperty)child; |
| final PropertiesFile file = property.getPropertiesFile(); |
| if (file != null) { |
| files.add(file); |
| } |
| } |
| } |
| return files; |
| } |
| |
| } |