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
.psi
.impl
.source
.resolve
.reference
.impl
.providers
;
18 import com
.intellij
.codeInsight
.daemon
.EmptyResolveMessageProvider
;
19 import com
.intellij
.codeInspection
.LocalQuickFix
;
20 import com
.intellij
.codeInspection
.LocalQuickFixProvider
;
21 import com
.intellij
.lang
.html
.HTMLLanguage
;
22 import com
.intellij
.lang
.xhtml
.XHTMLLanguage
;
23 import com
.intellij
.openapi
.util
.TextRange
;
24 import com
.intellij
.psi
.*;
25 import com
.intellij
.psi
.templateLanguages
.TemplateLanguageFileViewProvider
;
26 import com
.intellij
.psi
.filters
.ElementFilter
;
27 import com
.intellij
.psi
.impl
.source
.xml
.XmlEntityRefImpl
;
28 import com
.intellij
.psi
.meta
.PsiMetaData
;
29 import com
.intellij
.psi
.util
.PsiTreeUtil
;
30 import com
.intellij
.psi
.util
.PsiUtilBase
;
31 import com
.intellij
.psi
.xml
.*;
32 import com
.intellij
.util
.ArrayUtil
;
33 import com
.intellij
.util
.IncorrectOperationException
;
34 import com
.intellij
.util
.ProcessingContext
;
35 import com
.intellij
.xml
.XmlBundle
;
36 import com
.intellij
.xml
.XmlElementDescriptor
;
37 import com
.intellij
.xml
.XmlNSDescriptor
;
38 import com
.intellij
.xml
.impl
.dtd
.XmlNSDescriptorImpl
;
39 import com
.intellij
.xml
.util
.CheckDtdReferencesInspection
;
40 import com
.intellij
.xml
.util
.XmlUtil
;
41 import org
.jetbrains
.annotations
.NonNls
;
42 import org
.jetbrains
.annotations
.NotNull
;
43 import org
.jetbrains
.annotations
.Nullable
;
45 import java
.util
.ArrayList
;
46 import java
.util
.List
;
49 * Created by IntelliJ IDEA.
50 * User: Maxim.Mossienko
53 * To change this template use File | Settings | File Templates.
55 public class DtdReferencesProvider
extends PsiReferenceProvider
{
56 static class ElementReference
implements PsiReference
, LocalQuickFixProvider
, EmptyResolveMessageProvider
{
57 private final XmlElement myElement
;
58 private XmlElement myNameElement
;
59 private final TextRange myRange
;
60 @NonNls private static final String ELEMENT_DECLARATION_NAME
= "ELEMENT";
62 public ElementReference(final XmlElement element
, final XmlElement nameElement
) {
64 myNameElement
= nameElement
;
66 final int textOffset
= element
.getTextRange().getStartOffset();
67 final int nameTextOffset
= nameElement
.getTextOffset();
69 myRange
= new TextRange(
70 nameTextOffset
- textOffset
,
71 nameTextOffset
+ nameElement
.getTextLength() - textOffset
76 public PsiElement
getElement() {
80 public TextRange
getRangeInElement() {
85 public PsiElement
resolve() {
86 XmlNSDescriptor rootTagNSDescriptor
= getNsDescriptor();
88 if (rootTagNSDescriptor
instanceof XmlNSDescriptorImpl
) {
89 final XmlElementDescriptor elementDescriptor
= ((XmlNSDescriptorImpl
)rootTagNSDescriptor
).getElementDescriptor(getCanonicalText());
91 if (elementDescriptor
!= null) return elementDescriptor
.getDeclaration();
96 private XmlNSDescriptor
getNsDescriptor() {
97 final XmlElement parentThatProvidesMetaData
= PsiTreeUtil
.getParentOfType(
98 PsiUtilBase
.getOriginalElement(myElement
,(Class
<XmlElement
>)myElement
.getClass()),
103 if (parentThatProvidesMetaData
instanceof XmlDocument
) {
104 final XmlDocument document
= (XmlDocument
)parentThatProvidesMetaData
;
105 XmlNSDescriptor rootTagNSDescriptor
= document
.getRootTagNSDescriptor();
106 if (rootTagNSDescriptor
== null) rootTagNSDescriptor
= (XmlNSDescriptor
)document
.getMetaData();
107 return rootTagNSDescriptor
;
108 } else if (parentThatProvidesMetaData
instanceof XmlMarkupDecl
) {
109 final XmlMarkupDecl markupDecl
= (XmlMarkupDecl
)parentThatProvidesMetaData
;
110 final PsiMetaData psiMetaData
= markupDecl
.getMetaData();
112 if (psiMetaData
instanceof XmlNSDescriptor
) {
113 return (XmlNSDescriptor
)psiMetaData
;
120 public String
getCanonicalText() {
121 final XmlElement nameElement
= myNameElement
;
122 return nameElement
!= null ? nameElement
.getText() : "";
125 public PsiElement
handleElementRename(String newElementName
) throws IncorrectOperationException
{
126 myNameElement
= ElementManipulators
.getManipulator(myNameElement
).handleContentChange(
128 new TextRange(0,myNameElement
.getTextLength()),
135 public PsiElement
bindToElement(@NotNull PsiElement element
) throws IncorrectOperationException
{
139 public boolean isReferenceTo(PsiElement element
) {
140 return myElement
.getManager().areElementsEquivalent(element
, resolve());
143 public Object
[] getVariants() {
144 final XmlNSDescriptor rootTagNSDescriptor
= getNsDescriptor();
145 return rootTagNSDescriptor
!= null ?
146 rootTagNSDescriptor
.getRootElementsDescriptors(((XmlFile
)getRealFile()).getDocument()):
147 ArrayUtil
.EMPTY_OBJECT_ARRAY
;
150 private PsiFile
getRealFile() {
151 PsiFile psiFile
= myElement
.getContainingFile();
152 if (psiFile
!= null) psiFile
= psiFile
.getOriginalFile();
156 public boolean isSoft() {
160 public LocalQuickFix
[] getQuickFixes() {
161 if (!canHaveAdequateFix(getElement())) return LocalQuickFix
.EMPTY_ARRAY
;
163 return new LocalQuickFix
[] {
164 new CheckDtdReferencesInspection
.AddDtdDeclarationFix(
165 "xml.dtd.create.dtd.element.intention.name",
166 ELEMENT_DECLARATION_NAME
,
172 public String
getUnresolvedMessagePattern() {
173 return XmlBundle
.message("xml.dtd.unresolved.element.reference", getCanonicalText());
177 static class EntityReference
implements PsiReference
,LocalQuickFixProvider
, EmptyResolveMessageProvider
{
178 private final PsiElement myElement
;
179 private final TextRange myRange
;
180 @NonNls private static final String ENTITY_DECLARATION_NAME
= "ENTITY";
182 EntityReference(PsiElement element
) {
184 if (element
instanceof XmlEntityRef
) {
185 final PsiElement child
= element
.getLastChild();
186 final int startOffsetInParent
= child
.getStartOffsetInParent();
187 myRange
= new TextRange(startOffsetInParent
+ 1, startOffsetInParent
+ child
.getTextLength() - 1);
189 myRange
= new TextRange(1,myElement
.getTextLength()-1);
193 public PsiElement
getElement() {
197 public TextRange
getRangeInElement() {
202 public PsiElement
resolve() {
203 XmlEntityDecl xmlEntityDecl
= XmlEntityRefImpl
.resolveEntity(
204 (XmlElement
)myElement
,
205 (myElement
instanceof XmlEntityRef ? myElement
.getLastChild():myElement
).getText(),
206 myElement
.getContainingFile()
209 if (xmlEntityDecl
!= null && !xmlEntityDecl
.isPhysical()) {
210 PsiNamedElement element
= XmlUtil
.findRealNamedElement(xmlEntityDecl
);
211 if (element
!= null) xmlEntityDecl
= (XmlEntityDecl
)element
;
213 return xmlEntityDecl
;
216 public String
getCanonicalText() {
217 return myRange
.substring(myElement
.getText());
220 public PsiElement
handleElementRename(String newElementName
) throws IncorrectOperationException
{
221 final PsiElement elementAt
= myElement
.findElementAt(myRange
.getStartOffset());
222 return ElementManipulators
.getManipulator(elementAt
).handleContentChange(elementAt
, getRangeInElement(), newElementName
);
225 public PsiElement
bindToElement(@NotNull PsiElement element
) throws IncorrectOperationException
{
229 public boolean isReferenceTo(PsiElement element
) {
230 return myElement
.getManager().areElementsEquivalent(resolve(), element
);
233 public Object
[] getVariants() {
234 return ArrayUtil
.EMPTY_OBJECT_ARRAY
;
237 public boolean isSoft() {
241 public LocalQuickFix
[] getQuickFixes() {
242 if (!canHaveAdequateFix(getElement())) return LocalQuickFix
.EMPTY_ARRAY
;
244 return new LocalQuickFix
[] {
245 new CheckDtdReferencesInspection
.AddDtdDeclarationFix(
246 "xml.dtd.create.entity.intention.name",
247 myElement
.getText().charAt(myRange
.getStartOffset() - 1) == '%' ?
248 ENTITY_DECLARATION_NAME
+ " %":
249 ENTITY_DECLARATION_NAME
,
255 public String
getUnresolvedMessagePattern() {
256 return XmlBundle
.message("xml.dtd.unresolved.entity.reference", getCanonicalText());
260 private static boolean canHaveAdequateFix(PsiElement element
) {
261 final PsiFile containingFile
= element
.getContainingFile();
263 if (containingFile
.getLanguage() == HTMLLanguage
.INSTANCE
||
264 containingFile
.getLanguage() == XHTMLLanguage
.INSTANCE
||
265 containingFile
.getViewProvider() instanceof TemplateLanguageFileViewProvider
273 public PsiReference
[] getReferencesByElement(@NotNull PsiElement element
, @NotNull final ProcessingContext context
) {
274 XmlElement nameElement
= null;
276 if (element
instanceof XmlDoctype
) {
277 nameElement
= ((XmlDoctype
)element
).getNameElement();
278 } else if (element
instanceof XmlElementDecl
) {
279 nameElement
= ((XmlElementDecl
)element
).getNameElement();
280 } else if (element
instanceof XmlAttlistDecl
) {
281 nameElement
= ((XmlAttlistDecl
)element
).getNameElement();
282 } else if (element
instanceof XmlElementContentSpec
) {
283 final PsiElement
[] children
= element
.getChildren();
284 final List
<PsiReference
> psiRefs
= new ArrayList
<PsiReference
>(children
.length
);
286 for (final PsiElement child
: children
) {
287 if (child
instanceof XmlToken
&& ((XmlToken
)child
).getTokenType() == XmlTokenType
.XML_NAME
) {
288 psiRefs
.add(new ElementReference((XmlElement
)element
, (XmlElement
)child
));
292 return psiRefs
.toArray(new PsiReference
[psiRefs
.size()]);
295 if (nameElement
!= null) {
296 return new PsiReference
[] { new ElementReference((XmlElement
)element
, nameElement
) };
299 if (element
instanceof XmlEntityRef
||
300 (element
instanceof XmlToken
&& ((XmlToken
)element
).getTokenType() == XmlTokenType
.XML_CHAR_ENTITY_REF
)) {
301 return new PsiReference
[] { new EntityReference(element
) };
304 return PsiReference
.EMPTY_ARRAY
;
307 public ElementFilter
getSystemReferenceFilter() {
308 return new ElementFilter() {
309 public boolean isAcceptable(Object element
, PsiElement context
) {
310 final PsiElement parent
= context
.getParent();
312 if((parent
instanceof XmlEntityDecl
&&
313 !((XmlEntityDecl
)parent
).isInternalReference()
316 PsiElement prevSibling
= context
.getPrevSibling();
317 if (prevSibling
instanceof PsiWhiteSpace
) {
318 prevSibling
= prevSibling
.getPrevSibling();
321 if (prevSibling
instanceof XmlToken
&&
322 ((XmlToken
)prevSibling
).getTokenType() == XmlTokenType
.XML_DOCTYPE_SYSTEM
||
323 prevSibling
instanceof XmlAttributeValue
332 public boolean isClassAcceptable(Class hintClass
) {