update copyright
[fedora-idea.git] / xml / impl / src / com / intellij / xml / util / AnchorReference.java
blob5434e1f4cd2cf7788587088f86454a7793774e83
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.EmptyResolveMessageProvider;
19 import com.intellij.openapi.util.Key;
20 import com.intellij.openapi.util.TextRange;
21 import com.intellij.psi.ElementManipulators;
22 import com.intellij.psi.PsiElement;
23 import com.intellij.psi.PsiFile;
24 import com.intellij.psi.PsiReference;
25 import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
26 import com.intellij.psi.search.PsiElementProcessor;
27 import com.intellij.psi.util.CachedValue;
28 import com.intellij.psi.util.CachedValueProvider;
29 import com.intellij.psi.xml.*;
30 import com.intellij.util.ArrayUtil;
31 import com.intellij.util.IncorrectOperationException;
32 import com.intellij.util.containers.HashMap;
33 import com.intellij.xml.XmlBundle;
34 import com.intellij.xml.XmlExtension;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
39 import java.util.Map;
41 /**
42 * Created by IntelliJ IDEA.
43 * User: Maxim.Mossienko
44 * Date: Apr 15, 2005
45 * Time: 6:01:35 PM
46 * To change this template use File | Settings | File Templates.
48 class AnchorReference implements PsiReference, EmptyResolveMessageProvider {
49 private final String myAnchor;
50 private final PsiReference myFileReference;
51 private final PsiElement myElement;
52 private final int myOffset;
53 private final boolean mySoft;
54 @NonNls
55 private static final String ANCHOR_ELEMENT_NAME = "a";
56 private static final String MAP_ELEMENT_NAME = "map";
57 private static final Key<CachedValue<Map<String,XmlTag>>> ourCachedIdsKey = Key.create("cached.ids");
59 AnchorReference(final String anchor, @Nullable final FileReference psiReference, final PsiElement element, final int offset,
60 final boolean soft) {
62 myAnchor = anchor;
63 myFileReference = psiReference;
64 myElement = element;
65 myOffset = offset;
66 mySoft = soft;
69 public PsiElement getElement() {
70 return myElement;
73 public TextRange getRangeInElement() {
74 return new TextRange(myOffset,myOffset+myAnchor.length());
77 public PsiElement resolve() {
78 if (myAnchor.length() == 0) {
79 return myElement;
81 Map<String,XmlTag> map = getIdMap();
82 final XmlTag tag = map != null ? map.get(myAnchor):null;
83 if (tag != null) {
84 XmlAttribute attribute = tag.getAttribute("id", null);
85 if (attribute==null) attribute = tag.getAttribute("name",null);
87 if (attribute == null && MAP_ELEMENT_NAME.equalsIgnoreCase(tag.getName())) {
88 attribute = tag.getAttribute("usemap", null);
91 return attribute.getValueElement();
94 return null;
97 private static boolean processXmlElements(XmlTag element, PsiElementProcessor processor) {
98 if (!_processXmlElements(element,processor)) return false;
100 for(PsiElement next = element.getNextSibling(); next != null; next = next.getNextSibling()) {
101 if (next instanceof XmlTag) {
102 if (!_processXmlElements((XmlTag)next,processor)) return false;
106 return true;
109 static boolean _processXmlElements(XmlTag element, PsiElementProcessor processor) {
110 if (!processor.execute(element)) return false;
111 final XmlTag[] subTags = element.getSubTags();
113 for (int i = 0; i < subTags.length; i++) {
114 if(!_processXmlElements(subTags[i],processor)) return false;
117 return true;
120 private Map<String,XmlTag> getIdMap() {
121 final XmlFile file = getFile();
123 if (file != null) {
124 CachedValue<Map<String, XmlTag>> value = file.getUserData(ourCachedIdsKey);
125 if (value == null) {
126 value = file.getManager().getCachedValuesManager().createCachedValue(new CachedValueProvider<Map<String, XmlTag>>() {
127 public Result<Map<String, XmlTag>> compute() {
128 final Map<String,XmlTag> resultMap = new HashMap<String, XmlTag>();
129 XmlDocument document = HtmlUtil.getRealXmlDocument(file.getDocument());
130 final XmlTag rootTag = document != null ? document.getRootTag():null;
132 if (rootTag != null) {
133 processXmlElements(rootTag,
134 new PsiElementProcessor() {
135 public boolean execute(final PsiElement element) {
136 final String anchorValue = element instanceof XmlTag ? getAnchorValue((XmlTag)element):null;
138 if (anchorValue!=null) {
139 resultMap.put(anchorValue, (XmlTag)element);
141 return true;
146 return new Result<Map<String, XmlTag>>(resultMap, file);
148 }, false);
149 file.putUserData(ourCachedIdsKey, value);
152 return value.getValue();
154 return null;
157 private static String getAnchorValue(final XmlTag xmlTag) {
158 final String attributeValue = xmlTag.getAttributeValue("id");
160 if (attributeValue!=null) {
161 return attributeValue;
164 if (ANCHOR_ELEMENT_NAME.equalsIgnoreCase(xmlTag.getName())) {
165 final String attributeValue2 = xmlTag.getAttributeValue("name");
166 if (attributeValue2!=null) {
167 return attributeValue2;
171 if (MAP_ELEMENT_NAME.equalsIgnoreCase(xmlTag.getName())) {
172 final String map_anchor = xmlTag.getAttributeValue("name");
173 if (map_anchor != null) {
174 return map_anchor;
178 return null;
181 public String getCanonicalText() {
182 return myAnchor;
185 public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
186 return ElementManipulators.getManipulator(myElement).handleContentChange(
187 myElement,
188 getRangeInElement(),
189 newElementName
193 public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
194 return null;
197 public boolean isReferenceTo(PsiElement element) {
198 if (!(element instanceof XmlAttributeValue)) return false;
199 return myElement.getManager().areElementsEquivalent(element,resolve());
202 public Object[] getVariants() {
203 final Map<String, XmlTag> idMap = getIdMap();
204 if (idMap == null) return ArrayUtil.EMPTY_OBJECT_ARRAY;
206 return idMap.keySet().toArray(new Object[idMap.size()]);
209 @Nullable
210 private XmlFile getFile() {
211 if (myFileReference != null) {
212 final PsiElement psiElement = myFileReference.resolve();
213 return psiElement instanceof XmlFile ? (XmlFile)psiElement:null;
216 final PsiFile containingFile = myElement.getContainingFile();
217 if (containingFile instanceof XmlFile) {
218 return (XmlFile)containingFile;
220 else {
221 final XmlExtension extension = XmlExtension.getExtensionByElement(myElement);
222 return extension == null ? null : extension.getContainingFile(myElement);
226 public boolean isSoft() {
227 return mySoft;
230 public String getUnresolvedMessagePattern() {
231 final XmlFile xmlFile = getFile();
232 return xmlFile == null ?
233 XmlBundle.message("cannot.resolve.anchor", myAnchor) :
234 XmlBundle.message("cannot.resolve.anchor.in.file", myAnchor, xmlFile.getName());