blob: 2200e6fc15c93607482154ae761fdc44b0477318 [file] [log] [blame]
/*
* Copyright 2000-2014 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.util.xml.highlighting;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.XmlSuppressableInspectionTool;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.*;
import com.intellij.util.xml.reflect.AbstractDomChildrenDescription;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* @author Dmitry Avdeev
* @see com.intellij.util.xml.highlighting.BasicDomElementsInspection
*/
public abstract class DomElementsInspection<T extends DomElement> extends XmlSuppressableInspectionTool {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.highlighting.DomElementsInspection");
private final Set<Class<? extends T>> myDomClasses;
public DomElementsInspection(Class<? extends T> domClass, @NotNull Class<? extends T>... additionalClasses) {
myDomClasses = new THashSet<Class<? extends T>>(Arrays.asList(additionalClasses));
myDomClasses.add(domClass);
}
/**
* This method is called internally in {@link DomElementAnnotationsManager#checkFileElement(com.intellij.util.xml.DomFileElement, DomElementsInspection, boolean)}
* it should add some problems to the annotation holder. The default implementation performs recursive tree traversal, and calls
* {@link #checkDomElement(com.intellij.util.xml.DomElement, DomElementAnnotationHolder, DomHighlightingHelper)} for each element.
* @param domFileElement file element to check
* @param holder the place to store problems
*/
public void checkFileElement(DomFileElement<T> domFileElement, final DomElementAnnotationHolder holder) {
final DomHighlightingHelper helper =
DomElementAnnotationsManager.getInstance(domFileElement.getManager().getProject()).getHighlightingHelper();
final Consumer<DomElement> consumer = new Consumer<DomElement>() {
@Override
public void consume(final DomElement element) {
checkChildren(element, this);
checkDomElement(element, holder, helper);
}
};
consumer.consume(domFileElement.getRootElement());
}
@SuppressWarnings({"MethodMayBeStatic"})
protected void checkChildren(final DomElement element, Consumer<DomElement> visitor) {
final XmlElement xmlElement = element.getXmlElement();
if (xmlElement instanceof XmlTag) {
for (final DomElement child : DomUtil.getDefinedChildren(element, true, true)) {
final XmlElement element1 = child.getXmlElement();
if (element1 == null) {
LOG.error("child=" + child + " of class " + child.getClass() + "; parent=" + element);
}
if (element1.isPhysical()) {
visitor.consume(child);
}
}
for (final AbstractDomChildrenDescription description : element.getGenericInfo().getChildrenDescriptions()) {
if (description.getAnnotation(Required.class) != null) {
for (final DomElement child : description.getValues(element)) {
if (!DomUtil.hasXml(child)) {
visitor.consume(child);
}
}
}
}
}
}
/**
* @return the classes passed earlier to the constructor
*/
public final Set<Class<? extends T>> getDomClasses() {
return myDomClasses;
}
/**
* Not intended to be overridden or called by implementors.
* Override {@link #checkFileElement(com.intellij.util.xml.DomFileElement, DomElementAnnotationHolder)} (which is preferred) or
* {@link #checkDomElement(com.intellij.util.xml.DomElement, DomElementAnnotationHolder, DomHighlightingHelper)} instead.
*/
@Override
@Nullable
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (file instanceof XmlFile && (file.isPhysical() || ApplicationManager.getApplication().isUnitTestMode())) {
for (Class<? extends T> domClass: myDomClasses) {
final DomFileElement<? extends T> fileElement = DomManager.getDomManager(file.getProject()).getFileElement((XmlFile)file, domClass);
if (fileElement != null) {
//noinspection unchecked
return checkDomFile((DomFileElement<T>)fileElement, manager, isOnTheFly);
}
}
}
return null;
}
@Override
@NotNull
public HighlightDisplayLevel getDefaultLevel() {
return HighlightDisplayLevel.ERROR;
}
@Override
public boolean isEnabledByDefault() {
return true;
}
/**
* not intended to be overridden or called by implementors
*/
@Nullable
protected ProblemDescriptor[] checkDomFile(@NotNull final DomFileElement<T> domFileElement,
@NotNull final InspectionManager manager,
@SuppressWarnings("UnusedParameters") final boolean isOnTheFly) {
final DomElementAnnotationsManager annotationsManager = DomElementAnnotationsManager.getInstance(manager.getProject());
final List<DomElementProblemDescriptor> list = annotationsManager.checkFileElement(domFileElement, this, isOnTheFly);
if (list.isEmpty()) return ProblemDescriptor.EMPTY_ARRAY;
List<ProblemDescriptor> problems =
ContainerUtil.concat(list, new Function<DomElementProblemDescriptor, Collection<? extends ProblemDescriptor>>() {
@Override
public Collection<ProblemDescriptor> fun(final DomElementProblemDescriptor s) {
return annotationsManager.createProblemDescriptors(manager, s);
}
});
return problems.toArray(new ProblemDescriptor[problems.size()]);
}
/**
* Check particular DOM element for problems. The inspection implementor should focus on this method.
* The default implementation throws {@link UnsupportedOperationException}.
* See {@link com.intellij.util.xml.highlighting.BasicDomElementsInspection}
* @param element element to check
* @param holder a place to add problems to
* @param helper helper object
*/
protected void checkDomElement(DomElement element, DomElementAnnotationHolder holder, DomHighlightingHelper helper) {
throw new UnsupportedOperationException("checkDomElement() is not implemented in " + getClass().getName());
}
}