update copyright
[fedora-idea.git] / xml / dom-impl / src / com / intellij / util / xml / highlighting / DomElementAnnotationsManagerImpl.java
blob38363c0cc833e05368cc37e17ab4c444735f3e0c
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.util.xml.highlighting;
19 import com.intellij.codeInsight.daemon.HighlightDisplayKey;
20 import com.intellij.codeInspection.InspectionManager;
21 import com.intellij.codeInspection.InspectionProfile;
22 import com.intellij.codeInspection.InspectionProfileEntry;
23 import com.intellij.codeInspection.ProblemDescriptor;
24 import com.intellij.codeInspection.ex.LocalInspectionToolWrapper;
25 import com.intellij.lang.annotation.HighlightSeverity;
26 import com.intellij.openapi.Disposable;
27 import com.intellij.openapi.diagnostic.Logger;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.roots.ProjectRootManager;
30 import com.intellij.openapi.util.Disposer;
31 import com.intellij.openapi.util.Key;
32 import com.intellij.openapi.util.ModificationTracker;
33 import com.intellij.profile.Profile;
34 import com.intellij.profile.ProfileChangeAdapter;
35 import com.intellij.profile.codeInspection.InspectionProfileManager;
36 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
37 import com.intellij.psi.util.CachedValue;
38 import com.intellij.psi.util.CachedValueProvider;
39 import com.intellij.psi.util.CachedValuesManager;
40 import com.intellij.psi.util.PsiModificationTracker;
41 import com.intellij.psi.xml.XmlFile;
42 import com.intellij.psi.xml.XmlTag;
43 import com.intellij.util.EventDispatcher;
44 import com.intellij.util.SmartList;
45 import com.intellij.util.containers.ContainerUtil;
46 import com.intellij.util.xml.DomElement;
47 import com.intellij.util.xml.DomFileElement;
48 import com.intellij.util.xml.DomUtil;
49 import gnu.trove.THashMap;
50 import org.jetbrains.annotations.NotNull;
51 import org.jetbrains.annotations.Nullable;
53 import java.util.ArrayList;
54 import java.util.Collections;
55 import java.util.List;
56 import java.util.Map;
58 public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManager {
59 public static final Object LOCK = new Object();
61 private static final Logger LOG = Logger.getInstance("#com.intellij.util.xml.highlighting.DomElementAnnotationsManagerImpl");
62 private static final Key<DomElementsProblemsHolderImpl> DOM_PROBLEM_HOLDER_KEY = Key.create("DomProblemHolder");
63 private static final Key<CachedValue<Boolean>> CACHED_VALUE_KEY = Key.create("DomProblemHolderCachedValue");
64 private final EventDispatcher<DomHighlightingListener> myDispatcher = EventDispatcher.create(DomHighlightingListener.class);
66 private final Map<Class, List<DomElementsAnnotator>> myClass2Annotator = new THashMap<Class, List<DomElementsAnnotator>>();
68 private static final DomElementsProblemsHolder EMPTY_PROBLEMS_HOLDER = new DomElementsProblemsHolder() {
69 @NotNull
70 public List<DomElementProblemDescriptor> getProblems(DomElement domElement) {
71 return Collections.emptyList();
74 public List<DomElementProblemDescriptor> getProblems(final DomElement domElement, boolean includeXmlProblems) {
75 return Collections.emptyList();
78 public List<DomElementProblemDescriptor> getProblems(final DomElement domElement,
79 final boolean includeXmlProblems,
80 final boolean withChildren) {
81 return Collections.emptyList();
84 public List<DomElementProblemDescriptor> getProblems(DomElement domElement,
85 final boolean includeXmlProblems,
86 final boolean withChildren,
87 HighlightSeverity minSeverity) {
88 return Collections.emptyList();
91 public List<DomElementProblemDescriptor> getProblems(DomElement domElement, final boolean withChildren, HighlightSeverity minSeverity) {
92 return Collections.emptyList();
95 public List<DomElementProblemDescriptor> getAllProblems() {
96 return Collections.emptyList();
99 public List<DomElementProblemDescriptor> getAllProblems(@NotNull DomElementsInspection inspection) {
100 return Collections.emptyList();
103 public boolean isInspectionCompleted(@NotNull final DomElementsInspection inspectionClass) {
104 return false;
108 private final DomHighlightingHelperImpl myHighlightingHelper = new DomHighlightingHelperImpl(this);
109 private final ModificationTracker myModificationTracker;
110 private final ProjectRootManager myProjectRootManager;
111 private final CachedValuesManager myCachedValuesManager;
112 private long myModificationCount;
114 public DomElementAnnotationsManagerImpl(Project project, final InspectionProfileManager inspectionProfileManager, ProjectRootManager projectRootManager,
115 final CachedValuesManager cachedValuesManager) {
116 myCachedValuesManager = cachedValuesManager;
117 myProjectRootManager = projectRootManager;
118 myModificationTracker = new ModificationTracker() {
119 public long getModificationCount() {
120 return myModificationCount;
123 final ProfileChangeAdapter profileChangeAdapter = new ProfileChangeAdapter() {
124 public void profileActivated(Profile oldProfile, Profile profile) {
125 dropAnnotationsCache();
128 public void profileChanged(Profile profile) {
129 dropAnnotationsCache();
132 inspectionProfileManager.addProfileChangeListener(profileChangeAdapter, project);
133 Disposer.register(project, new Disposable() {
134 public void dispose() {
135 inspectionProfileManager.removeProfileChangeListener(profileChangeAdapter);
140 public void dropAnnotationsCache() {
141 myModificationCount++;
144 public final List<DomElementProblemDescriptor> appendProblems(@NotNull DomFileElement element, @NotNull DomElementAnnotationHolder annotationHolder, Class<? extends DomElementsInspection> inspectionClass) {
145 final DomElementAnnotationHolderImpl holderImpl = (DomElementAnnotationHolderImpl)annotationHolder;
146 synchronized (LOCK) {
147 final DomElementsProblemsHolderImpl holder = _getOrCreateProblemsHolder(element);
148 holder.appendProblems(holderImpl, inspectionClass);
150 myDispatcher.getMulticaster().highlightingFinished(element);
151 return Collections.unmodifiableList(holderImpl);
154 private DomElementsProblemsHolderImpl _getOrCreateProblemsHolder(final DomFileElement element) {
155 DomElementsProblemsHolderImpl holder;
156 final DomElement rootElement = element.getRootElement();
157 final XmlTag rootTag = rootElement.getXmlTag();
158 if (rootTag == null) return new DomElementsProblemsHolderImpl(element);
160 holder = rootTag.getUserData(DOM_PROBLEM_HOLDER_KEY);
161 if (isHolderOutdated(element.getFile()) || holder == null) {
162 holder = new DomElementsProblemsHolderImpl(element);
163 rootTag.putUserData(DOM_PROBLEM_HOLDER_KEY, holder);
164 final CachedValue<Boolean> cachedValue = myCachedValuesManager.createCachedValue(new CachedValueProvider<Boolean>() {
165 public Result<Boolean> compute() {
166 return new Result<Boolean>(Boolean.FALSE, element, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myModificationTracker, myProjectRootManager);
168 }, false);
169 cachedValue.getValue();
170 element.getFile().putUserData(CACHED_VALUE_KEY, cachedValue);
172 return holder;
175 public static boolean isHolderUpToDate(DomElement element) {
176 synchronized (LOCK) {
177 return !isHolderOutdated(DomUtil.getFile(element));
181 public static void outdateProblemHolder(final DomElement element) {
182 synchronized (LOCK) {
183 DomUtil.getFile(element).putUserData(CACHED_VALUE_KEY, null);
187 private static boolean isHolderOutdated(final XmlFile file) {
188 final CachedValue<Boolean> cachedValue = file.getUserData(CACHED_VALUE_KEY);
189 return cachedValue == null || !cachedValue.hasUpToDateValue();
192 @NotNull
193 public DomElementsProblemsHolder getProblemHolder(DomElement element) {
194 if (element == null || !element.isValid()) return EMPTY_PROBLEMS_HOLDER;
195 final DomFileElement<DomElement> fileElement = DomUtil.getFileElement(element);
197 synchronized (LOCK) {
198 final XmlTag tag = fileElement.getRootElement().getXmlTag();
199 if (tag != null) {
200 final DomElementsProblemsHolder readyHolder = tag.getUserData(DOM_PROBLEM_HOLDER_KEY);
201 if (readyHolder != null) {
202 return readyHolder;
205 return EMPTY_PROBLEMS_HOLDER;
209 @NotNull
210 public DomElementsProblemsHolder getCachedProblemHolder(DomElement element) {
211 return getProblemHolder(element);
214 public void annotate(final DomElement element, final DomElementAnnotationHolder holder, final Class rootClass) {
215 final List<DomElementsAnnotator> list = getAnnotators(rootClass);
216 if (list != null) {
217 for (DomElementsAnnotator annotator : list) {
218 annotator.annotate(element, holder);
224 public final void registerDomElementsAnnotator(DomElementsAnnotator annotator, Class<? extends DomElement> aClass) {
225 getOrCreateAnnotators(aClass).add(annotator);
228 private List<DomElementsAnnotator> getOrCreateAnnotators(final Class aClass) {
229 List<DomElementsAnnotator> annotators = getAnnotators(aClass);
230 if (annotators == null) {
231 myClass2Annotator.put(aClass, annotators = new ArrayList<DomElementsAnnotator>());
233 return annotators;
236 @Nullable
237 private List<DomElementsAnnotator> getAnnotators(final Class aClass) {
238 return myClass2Annotator.get(aClass);
241 public List<ProblemDescriptor> createProblemDescriptors(final InspectionManager manager, DomElementProblemDescriptor problemDescriptor) {
242 return ContainerUtil.createMaybeSingletonList(DomElementsHighlightingUtil.createProblemDescriptors(manager, problemDescriptor));
245 public boolean isHighlightingFinished(final DomElement[] domElements) {
246 for (final DomElement domElement : domElements) {
247 if (getHighlightStatus(domElement) != DomHighlightStatus.INSPECTIONS_FINISHED) {
248 return false;
251 return true;
254 public void addHighlightingListener(DomHighlightingListener listener, Disposable parentDisposable) {
255 myDispatcher.addListener(listener, parentDisposable);
258 public DomHighlightingHelper getHighlightingHelper() {
259 return myHighlightingHelper;
262 @NotNull
263 public <T extends DomElement> List<DomElementProblemDescriptor> checkFileElement(@NotNull final DomFileElement<T> domFileElement,
264 @NotNull final DomElementsInspection<T> inspection) {
265 final DomElementsProblemsHolder problemHolder = getProblemHolder(domFileElement);
266 if (isHolderUpToDate(domFileElement) && problemHolder.isInspectionCompleted(inspection)) {
267 return problemHolder.getAllProblems(inspection);
270 final DomElementAnnotationHolder holder = new DomElementAnnotationHolderImpl();
271 inspection.checkFileElement(domFileElement, holder);
272 return appendProblems(domFileElement, holder, inspection.getClass());
275 public List<DomElementsInspection> getSuitableDomInspections(final DomFileElement fileElement, boolean enabledOnly) {
276 Class rootType = fileElement.getRootElementClass();
277 final InspectionProfile profile = getInspectionProfile(fileElement);
278 final List<DomElementsInspection> inspections = new SmartList<DomElementsInspection>();
279 for (final InspectionProfileEntry profileEntry : profile.getInspectionTools(fileElement.getFile())) {
280 if (!enabledOnly || profile.isToolEnabled(HighlightDisplayKey.find(profileEntry.getShortName()), fileElement.getFile())) {
281 ContainerUtil.addIfNotNull(getSuitableInspection(profileEntry, rootType), inspections);
284 return inspections;
287 protected InspectionProfile getInspectionProfile(final DomFileElement fileElement) {
288 return InspectionProjectProfileManager.getInstance(fileElement.getManager().getProject()).getInspectionProfile();
291 @Nullable
292 private static DomElementsInspection getSuitableInspection(InspectionProfileEntry entry, Class rootType) {
293 if (entry instanceof LocalInspectionToolWrapper) {
294 return getSuitableInspection(((LocalInspectionToolWrapper)entry).getTool(), rootType);
297 if (entry instanceof DomElementsInspection) {
298 if (((DomElementsInspection)entry).getDomClasses().contains(rootType)) {
299 return (DomElementsInspection) entry;
302 return null;
305 @Nullable public <T extends DomElement> DomElementsInspection<T> getMockInspection(DomFileElement<T> root) {
306 if (root.getFileDescription().isAutomaticHighlightingEnabled()) {
307 return new MockAnnotatingDomInspection<T>(root.getRootElementClass());
309 if (getSuitableDomInspections(root, false).isEmpty()) {
310 return new MockDomInspection<T>(root.getRootElementClass());
313 return null;
316 private static boolean areInspectionsFinished(DomElementsProblemsHolderImpl holder, final List<DomElementsInspection> suitableInspections) {
317 for (final DomElementsInspection inspection : suitableInspections) {
318 if (!holder.isInspectionCompleted(inspection)) {
319 return false;
322 return true;
325 @NotNull
326 public DomHighlightStatus getHighlightStatus(final DomElement element) {
327 synchronized (LOCK) {
328 final DomFileElement<DomElement> root = DomUtil.getFileElement(element);
329 if (!isHolderOutdated(root.getFile())) {
330 final DomElementsProblemsHolder holder = getProblemHolder(element);
331 if (holder instanceof DomElementsProblemsHolderImpl) {
332 DomElementsProblemsHolderImpl holderImpl = (DomElementsProblemsHolderImpl)holder;
333 final List<DomElementsInspection> suitableInspections = getSuitableDomInspections(root, true);
334 final DomElementsInspection mockInspection = getMockInspection(root);
335 final boolean annotatorsFinished = mockInspection == null || holderImpl.isInspectionCompleted(mockInspection);
336 final boolean inspectionsFinished = areInspectionsFinished(holderImpl, suitableInspections);
337 if (annotatorsFinished) {
338 if (suitableInspections.isEmpty() || inspectionsFinished) return DomHighlightStatus.INSPECTIONS_FINISHED;
339 return DomHighlightStatus.ANNOTATORS_FINISHED;
343 return DomHighlightStatus.NONE;