deprecation removed
[fedora-idea.git] / xml / impl / src / com / intellij / xml / util / XmlRefCountHolder.java
blob690a4bfb3dae69cbc619ba5426dafacc92f24c07
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.
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;
36 import java.util.*;
38 /**
39 * @author spleaner
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);
55 }, false);
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);
91 if (list == null) {
92 list = new ArrayList<Pair<XmlAttributeValue, Boolean>>();
93 myId2AttributeListMap.put(id, list);
94 } else if (!soft) {
95 // mark as duplicate
96 if (list.size() == 1) {
97 if (!list.get(0).second.booleanValue()) {
98 myPossiblyDuplicateIds.add(list.get(0).first);
99 myPossiblyDuplicateIds.add(attributeValue);
101 } else {
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) {
131 super(true);
132 myHolder = holder;
135 @Override
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);
148 @Override
149 public void visitComment(final PsiComment comment) {
150 doVisitAnyComment(comment);
151 super.visitComment(comment);
154 @Override
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);
162 if (id != null) {
163 myHolder.registerAdditionalId(id);
168 @Override
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);
187 else {
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);