| /* |
| * 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.codeInsight.daemon.impl.SeverityRegistrar; |
| import com.intellij.lang.annotation.Annotation; |
| import com.intellij.lang.annotation.HighlightSeverity; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.Factory; |
| import com.intellij.util.Function; |
| import com.intellij.util.SmartList; |
| import com.intellij.util.containers.ConcurrentHashMap; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.xml.DomElement; |
| import com.intellij.util.xml.DomElementVisitor; |
| import com.intellij.util.xml.DomFileElement; |
| import com.intellij.util.xml.DomUtil; |
| import gnu.trove.THashMap; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| public class DomElementsProblemsHolderImpl implements DomElementsProblemsHolder { |
| private final Map<DomElement, Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>>> myCachedErrors = |
| new ConcurrentHashMap<DomElement, Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>>>(); |
| private final Map<DomElement, Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>>> myCachedChildrenErrors = |
| new ConcurrentHashMap<DomElement, Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>>>(); |
| private final List<Annotation> myAnnotations = new ArrayList<Annotation>(); |
| |
| private final Function<DomElement, List<DomElementProblemDescriptor>> myDomProblemsGetter = |
| new Function<DomElement, List<DomElementProblemDescriptor>>() { |
| @Override |
| public List<DomElementProblemDescriptor> fun(final DomElement s) { |
| final Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>> map = myCachedErrors.get(s); |
| return map != null ? ContainerUtil.concat(map.values()) : Collections.<DomElementProblemDescriptor>emptyList(); |
| } |
| }; |
| |
| private final DomFileElement myElement; |
| |
| private static final Factory<Map<Class<? extends DomElementsInspection>,List<DomElementProblemDescriptor>>> CONCURRENT_HASH_MAP_FACTORY = new Factory<Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>>>() { |
| @Override |
| public Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>> create() { |
| return new ConcurrentHashMap<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>>(); |
| } |
| }; |
| private static final Factory<List<DomElementProblemDescriptor>> SMART_LIST_FACTORY = new Factory<List<DomElementProblemDescriptor>>() { |
| @Override |
| public List<DomElementProblemDescriptor> create() { |
| return new SmartList<DomElementProblemDescriptor>(); |
| } |
| }; |
| private final Set<Class<? extends DomElementsInspection>> myPassedInspections = new THashSet<Class<? extends DomElementsInspection>>(); |
| |
| public DomElementsProblemsHolderImpl(final DomFileElement element) { |
| myElement = element; |
| } |
| |
| public final void appendProblems(final DomElementAnnotationHolderImpl holder, final Class<? extends DomElementsInspection> inspectionClass) { |
| if (isInspectionCompleted(inspectionClass)) return; |
| |
| for (final DomElementProblemDescriptor descriptor : holder) { |
| addProblem(descriptor, inspectionClass); |
| } |
| myAnnotations.addAll(holder.getAnnotations()); |
| myPassedInspections.add(inspectionClass); |
| } |
| |
| @Override |
| public final boolean isInspectionCompleted(@NotNull final DomElementsInspection inspection) { |
| return isInspectionCompleted(inspection.getClass()); |
| } |
| |
| public final boolean isInspectionCompleted(final Class<? extends DomElementsInspection> inspectionClass) { |
| synchronized (DomElementAnnotationsManagerImpl.LOCK) { |
| return myPassedInspections.contains(inspectionClass); |
| } |
| } |
| |
| public final List<Annotation> getAnnotations() { |
| return myAnnotations; |
| } |
| |
| public final void addProblem(final DomElementProblemDescriptor descriptor, final Class<? extends DomElementsInspection> inspection) { |
| ContainerUtil.getOrCreate(ContainerUtil.getOrCreate(myCachedErrors, descriptor.getDomElement(), CONCURRENT_HASH_MAP_FACTORY), inspection, |
| SMART_LIST_FACTORY).add(descriptor); |
| myCachedChildrenErrors.clear(); |
| } |
| |
| @Override |
| @NotNull |
| public synchronized List<DomElementProblemDescriptor> getProblems(DomElement domElement) { |
| if (domElement == null || !domElement.isValid()) return Collections.emptyList(); |
| return myDomProblemsGetter.fun(domElement); |
| } |
| |
| @Override |
| public List<DomElementProblemDescriptor> getProblems(final DomElement domElement, boolean includeXmlProblems) { |
| return getProblems(domElement); |
| } |
| |
| @Override |
| public List<DomElementProblemDescriptor> getProblems(final DomElement domElement, |
| final boolean includeXmlProblems, |
| final boolean withChildren) { |
| if (!withChildren || domElement == null || !domElement.isValid()) { |
| return getProblems(domElement); |
| } |
| |
| return ContainerUtil.concat(getProblemsMap(domElement).values()); |
| } |
| |
| @Override |
| public List<DomElementProblemDescriptor> getProblems(DomElement domElement, |
| final boolean includeXmlProblems, |
| final boolean withChildren, |
| final HighlightSeverity minSeverity) { |
| return getProblems(domElement, withChildren, minSeverity); |
| } |
| |
| @Override |
| public List<DomElementProblemDescriptor> getProblems(final DomElement domElement, final boolean withChildren, final HighlightSeverity minSeverity) { |
| return ContainerUtil.findAll(getProblems(domElement, true, withChildren), new Condition<DomElementProblemDescriptor>() { |
| @Override |
| public boolean value(final DomElementProblemDescriptor object) { |
| return SeverityRegistrar.getSeverityRegistrar(domElement.getManager().getProject()).compare(object.getHighlightSeverity(), minSeverity) >= 0; |
| } |
| }); |
| |
| } |
| |
| @NotNull |
| private Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>> getProblemsMap(final DomElement domElement) { |
| final Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>> map = myCachedChildrenErrors.get(domElement); |
| if (map != null) { |
| return map; |
| } |
| |
| final Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>> problems = new THashMap<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>>(); |
| if (domElement == myElement) { |
| for (Map<Class<? extends DomElementsInspection>, List<DomElementProblemDescriptor>> listMap : myCachedErrors.values()) { |
| mergeMaps(problems, listMap); |
| } |
| } else { |
| mergeMaps(problems, myCachedErrors.get(domElement)); |
| if (DomUtil.hasXml(domElement)) { |
| domElement.acceptChildren(new DomElementVisitor() { |
| @Override |
| public void visitDomElement(DomElement element) { |
| mergeMaps(problems, getProblemsMap(element)); |
| } |
| }); |
| } |
| } |
| |
| myCachedChildrenErrors.put(domElement, problems); |
| return problems; |
| } |
| |
| private static <T> void mergeMaps(final Map<T, List<DomElementProblemDescriptor>> accumulator, @Nullable final Map<T, List<DomElementProblemDescriptor>> toAdd) { |
| if (toAdd == null) return; |
| for (final Map.Entry<T, List<DomElementProblemDescriptor>> entry : toAdd.entrySet()) { |
| ContainerUtil.getOrCreate(accumulator, entry.getKey(), SMART_LIST_FACTORY).addAll(entry.getValue()); |
| } |
| } |
| |
| @Override |
| public List<DomElementProblemDescriptor> getAllProblems() { |
| return getProblems(myElement, false, true); |
| } |
| |
| @Override |
| public List<DomElementProblemDescriptor> getAllProblems(@NotNull DomElementsInspection inspection) { |
| if (!myElement.isValid()) { |
| return Collections.emptyList(); |
| } |
| final List<DomElementProblemDescriptor> list = getProblemsMap(myElement).get(inspection.getClass()); |
| return list != null ? new ArrayList<DomElementProblemDescriptor>(list) : Collections.<DomElementProblemDescriptor>emptyList(); |
| } |
| } |