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
;
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() {
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
) {
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
);
169 cachedValue
.getValue();
170 element
.getFile().putUserData(CACHED_VALUE_KEY
, cachedValue
);
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();
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();
200 final DomElementsProblemsHolder readyHolder
= tag
.getUserData(DOM_PROBLEM_HOLDER_KEY
);
201 if (readyHolder
!= null) {
205 return EMPTY_PROBLEMS_HOLDER
;
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
);
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
>());
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
) {
254 public void addHighlightingListener(DomHighlightingListener listener
, Disposable parentDisposable
) {
255 myDispatcher
.addListener(listener
, parentDisposable
);
258 public DomHighlightingHelper
getHighlightingHelper() {
259 return myHighlightingHelper
;
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
);
287 protected InspectionProfile
getInspectionProfile(final DomFileElement fileElement
) {
288 return InspectionProjectProfileManager
.getInstance(fileElement
.getManager().getProject()).getInspectionProfile();
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
;
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());
316 private static boolean areInspectionsFinished(DomElementsProblemsHolderImpl holder
, final List
<DomElementsInspection
> suitableInspections
) {
317 for (final DomElementsInspection inspection
: suitableInspections
) {
318 if (!holder
.isInspectionCompleted(inspection
)) {
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
;