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
;
42 * Created by IntelliJ IDEA.
43 * User: Maxim.Mossienko
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
;
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
,
63 myFileReference
= psiReference
;
69 public PsiElement
getElement() {
73 public TextRange
getRangeInElement() {
74 return new TextRange(myOffset
,myOffset
+myAnchor
.length());
77 public PsiElement
resolve() {
78 if (myAnchor
.length() == 0) {
81 Map
<String
,XmlTag
> map
= getIdMap();
82 final XmlTag tag
= map
!= null ? map
.get(myAnchor
):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();
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;
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;
120 private Map
<String
,XmlTag
> getIdMap() {
121 final XmlFile file
= getFile();
124 CachedValue
<Map
<String
, XmlTag
>> value
= file
.getUserData(ourCachedIdsKey
);
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
);
146 return new Result
<Map
<String
, XmlTag
>>(resultMap
, file
);
149 file
.putUserData(ourCachedIdsKey
, value
);
152 return value
.getValue();
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) {
181 public String
getCanonicalText() {
185 public PsiElement
handleElementRename(String newElementName
) throws IncorrectOperationException
{
186 return ElementManipulators
.getManipulator(myElement
).handleContentChange(
193 public PsiElement
bindToElement(@NotNull PsiElement element
) throws IncorrectOperationException
{
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()]);
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
;
221 final XmlExtension extension
= XmlExtension
.getExtensionByElement(myElement
);
222 return extension
== null ?
null : extension
.getContainingFile(myElement
);
226 public boolean isSoft() {
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());