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.
16 package com
.intellij
.xml
.util
;
18 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.XmlHighlightVisitor
;
19 import com
.intellij
.lang
.Language
;
20 import com
.intellij
.openapi
.util
.Key
;
21 import com
.intellij
.openapi
.util
.Pair
;
22 import com
.intellij
.openapi
.util
.UserDataCache
;
23 import com
.intellij
.psi
.*;
24 import com
.intellij
.psi
.impl
.source
.resolve
.reference
.impl
.providers
.IdReferenceProvider
;
25 import com
.intellij
.psi
.templateLanguages
.OuterLanguageElement
;
26 import com
.intellij
.psi
.util
.CachedValue
;
27 import com
.intellij
.psi
.util
.CachedValueProvider
;
28 import com
.intellij
.psi
.util
.CachedValuesManager
;
29 import com
.intellij
.psi
.util
.PsiTreeUtil
;
30 import com
.intellij
.psi
.xml
.*;
31 import com
.intellij
.xml
.XmlAttributeDescriptor
;
32 import com
.intellij
.xml
.XmlElementDescriptor
;
33 import org
.jetbrains
.annotations
.NotNull
;
34 import org
.jetbrains
.annotations
.Nullable
;
41 public class XmlRefCountHolder
{
42 private static final Key
<CachedValue
<XmlRefCountHolder
>> xmlRefCountHolderKey
= Key
.create("xml ref count holder");
44 private final static UserDataCache
<CachedValue
<XmlRefCountHolder
>, XmlFile
, Object
> CACHE
=
45 new UserDataCache
<CachedValue
<XmlRefCountHolder
>, XmlFile
, Object
>() {
46 protected CachedValue
<XmlRefCountHolder
> compute(final XmlFile file
, final Object p
) {
47 return CachedValuesManager
.getManager(file
.getProject()).createCachedValue(new CachedValueProvider
<XmlRefCountHolder
>() {
48 public Result
<XmlRefCountHolder
> compute() {
49 final XmlRefCountHolder holder
= new XmlRefCountHolder();
50 final Language language
= file
.getViewProvider().getBaseLanguage();
51 final PsiFile psiFile
= file
.getViewProvider().getPsi(language
);
52 psiFile
.accept(new IdGatheringRecursiveVisitor(holder
));
53 return new Result
<XmlRefCountHolder
>(holder
, file
);
59 private final Map
<String
, List
<Pair
<XmlAttributeValue
, Boolean
>>> myId2AttributeListMap
= new HashMap
<String
, List
<Pair
<XmlAttributeValue
, Boolean
>>>();
60 private final Set
<XmlAttributeValue
> myPossiblyDuplicateIds
= new HashSet
<XmlAttributeValue
>();
61 private final List
<XmlAttributeValue
> myIdReferences
= new ArrayList
<XmlAttributeValue
>();
62 private final Set
<String
> myAdditionallyDeclaredIds
= new HashSet
<String
>();
63 private final Set
<PsiElement
> myDoNotValidateParentsList
= new HashSet
<PsiElement
>();
65 public static XmlRefCountHolder
getInstance(final XmlFile file
) {
66 return CACHE
.get(xmlRefCountHolderKey
, file
, null).getValue();
69 private XmlRefCountHolder() {
73 public boolean isDuplicateIdAttributeValue(@NotNull final XmlAttributeValue value
) {
74 return myPossiblyDuplicateIds
.contains(value
);
77 public boolean isValidatable(@Nullable final PsiElement element
) {
78 return !myDoNotValidateParentsList
.contains(element
);
81 public boolean hasIdDeclaration(@NotNull final String idRef
) {
82 return myId2AttributeListMap
.get(idRef
) != null || myAdditionallyDeclaredIds
.contains(idRef
);
85 public boolean isIdReferenceValue(@NotNull final XmlAttributeValue value
) {
86 return myIdReferences
.contains(value
);
89 private void registerId(@NotNull final String id
, @NotNull final XmlAttributeValue attributeValue
, final boolean soft
) {
90 List
<Pair
<XmlAttributeValue
, Boolean
>> list
= myId2AttributeListMap
.get(id
);
92 list
= new ArrayList
<Pair
<XmlAttributeValue
, Boolean
>>();
93 myId2AttributeListMap
.put(id
, list
);
96 if (list
.size() == 1) {
97 if (!list
.get(0).second
.booleanValue()) {
98 myPossiblyDuplicateIds
.add(list
.get(0).first
);
99 myPossiblyDuplicateIds
.add(attributeValue
);
102 myPossiblyDuplicateIds
.add(attributeValue
);
106 list
.add(new Pair
<XmlAttributeValue
, Boolean
>(attributeValue
, soft
));
109 private void registerAdditionalId(@NotNull final String id
) {
110 myAdditionallyDeclaredIds
.add(id
);
113 private void registerIdReference(@NotNull final XmlAttributeValue value
) {
114 myIdReferences
.add(value
);
117 private void registerOuterLanguageElement(@NotNull final PsiElement element
) {
118 PsiElement parent
= element
.getParent();
120 if (parent
instanceof XmlText
) {
121 parent
= parent
.getParent();
124 myDoNotValidateParentsList
.add(parent
);
127 private static class IdGatheringRecursiveVisitor
extends XmlRecursiveElementVisitor
{
128 private final XmlRefCountHolder myHolder
;
130 private IdGatheringRecursiveVisitor(@NotNull XmlRefCountHolder holder
) {
136 public void visitElement(final PsiElement element
) {
137 if (element
instanceof OuterLanguageElement
) {
138 visitOuterLanguageElement(element
);
141 super.visitElement(element
);
144 private void visitOuterLanguageElement(@NotNull final PsiElement element
) {
145 myHolder
.registerOuterLanguageElement(element
);
149 public void visitComment(final PsiComment comment
) {
150 doVisitAnyComment(comment
);
151 super.visitComment(comment
);
155 public void visitXmlComment(final XmlComment comment
) {
156 doVisitAnyComment(comment
);
157 super.visitXmlComment(comment
);
160 private void doVisitAnyComment(final PsiComment comment
) {
161 final String id
= XmlDeclareIdInCommentAction
.getImplicitlyDeclaredId(comment
);
163 myHolder
.registerAdditionalId(id
);
169 public void visitXmlAttributeValue(final XmlAttributeValue value
) {
170 final PsiElement element
= value
.getParent();
171 if (!(element
instanceof XmlAttribute
)) return;
173 final XmlAttribute attribute
= (XmlAttribute
)element
;
175 final XmlTag tag
= attribute
.getParent();
176 if (tag
== null) return;
178 final XmlElementDescriptor descriptor
= tag
.getDescriptor();
179 if (descriptor
== null) return;
181 final XmlAttributeDescriptor attributeDescriptor
= descriptor
.getAttributeDescriptor(attribute
);
182 if (attributeDescriptor
== null) return;
184 if (attributeDescriptor
.hasIdType()) {
185 updateMap(attribute
, value
, false);
188 final PsiReference
[] references
= value
.getReferences();
189 for (PsiReference r
: references
) {
190 if (r
instanceof IdReferenceProvider
.GlobalAttributeValueSelfReference
/*&& !r.isSoft()*/) {
191 updateMap(attribute
, value
, r
.isSoft());
196 if (attributeDescriptor
.hasIdRefType() && PsiTreeUtil
.getChildOfType(value
, OuterLanguageElement
.class) == null) {
197 myHolder
.registerIdReference(value
);
200 super.visitXmlAttributeValue(value
);
203 private void updateMap(@NotNull final XmlAttribute attribute
, @NotNull final XmlAttributeValue value
, final boolean soft
) {
204 final String id
= XmlHighlightVisitor
.getUnquotedValue(value
, attribute
.getParent());
205 if (XmlUtil
.isSimpleXmlAttributeValue(id
, value
) &&
206 PsiTreeUtil
.getChildOfType(value
, OuterLanguageElement
.class) == null) {
207 myHolder
.registerId(id
, value
, soft
);