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
;
18 import com
.intellij
.codeInsight
.daemon
.JavaErrorMessages
;
19 import com
.intellij
.lang
.ASTNode
;
20 import com
.intellij
.openapi
.diagnostic
.Logger
;
21 import com
.intellij
.openapi
.util
.Key
;
22 import com
.intellij
.openapi
.util
.TextRange
;
23 import com
.intellij
.psi
.*;
24 import com
.intellij
.psi
.filters
.ClassFilter
;
25 import com
.intellij
.psi
.impl
.source
.parsing
.Parsing
;
26 import com
.intellij
.psi
.impl
.source
.resolve
.ResolveCache
;
27 import com
.intellij
.psi
.impl
.source
.tree
.*;
28 import com
.intellij
.psi
.scope
.BaseScopeProcessor
;
29 import com
.intellij
.psi
.scope
.NameHint
;
30 import com
.intellij
.psi
.scope
.PsiScopeProcessor
;
31 import com
.intellij
.psi
.scope
.processor
.FilterScopeProcessor
;
32 import com
.intellij
.psi
.scope
.util
.PsiScopesUtil
;
33 import com
.intellij
.psi
.tree
.ChildRoleBase
;
34 import com
.intellij
.psi
.tree
.IElementType
;
35 import com
.intellij
.psi
.util
.PsiTreeUtil
;
36 import com
.intellij
.util
.ArrayUtil
;
37 import com
.intellij
.util
.IncorrectOperationException
;
38 import org
.jetbrains
.annotations
.NotNull
;
40 import java
.util
.ArrayList
;
41 import java
.util
.List
;
46 public class PsiImportStaticReferenceElementImpl
extends CompositePsiElement
implements PsiImportStaticReferenceElement
{
47 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.impl.source.PsiImportStaticReferenceElementImpl");
48 private volatile String myCanonicalText
;
50 public PsiImportStaticReferenceElementImpl() {
51 super(JavaElementType
.IMPORT_STATIC_REFERENCE
);
54 public int getTextOffset() {
55 ASTNode refName
= findChildByRole(ChildRole
.REFERENCE_NAME
);
57 return refName
.getStartOffset();
60 return super.getTextOffset();
64 public void clearCaches() {
66 myCanonicalText
= null;
69 public final ASTNode
findChildByRole(int role
) {
70 LOG
.assertTrue(ChildRole
.isUnique(role
));
75 case ChildRole
.REFERENCE_NAME
:
76 if (getLastChildNode().getElementType() == JavaTokenType
.IDENTIFIER
){
77 return getLastChildNode();
83 case ChildRole
.QUALIFIER
:
84 if (getFirstChildNode().getElementType() == JavaElementType
.JAVA_CODE_REFERENCE
){
85 return getFirstChildNode();
92 return findChildByType(JavaTokenType
.DOT
);
96 public final int getChildRole(ASTNode child
) {
97 LOG
.assertTrue(child
.getTreeParent() == this);
98 IElementType i
= child
.getElementType();
99 if (i
== JavaElementType
.JAVA_CODE_REFERENCE
) {
100 return ChildRole
.QUALIFIER
;
102 else if (i
== JavaTokenType
.DOT
) {
103 return ChildRole
.DOT
;
105 else if (i
== JavaTokenType
.IDENTIFIER
) {
106 return ChildRole
.REFERENCE_NAME
;
109 return ChildRoleBase
.NONE
;
114 public PsiElement
getReferenceNameElement() {
115 return findChildByRoleAsPsiElement(ChildRole
.REFERENCE_NAME
);
118 public PsiReferenceParameterList
getParameterList() {
123 public PsiType
[] getTypeParameters() {
124 return PsiType
.EMPTY_ARRAY
;
127 public PsiElement
getQualifier() {
128 return findChildByRoleAsPsiElement(ChildRole
.QUALIFIER
);
131 public PsiJavaCodeReferenceElement
getClassReference() {
132 return (PsiJavaCodeReferenceElement
)findChildByRoleAsPsiElement(ChildRole
.QUALIFIER
);
135 public PsiImportStaticStatement
bindToTargetClass(PsiClass aClass
) throws IncorrectOperationException
{
136 final String qualifiedName
= aClass
.getQualifiedName();
137 if (qualifiedName
== null) throw new IncorrectOperationException();
138 final CompositeElement newRef
= Parsing
.parseJavaCodeReferenceText(getManager(), qualifiedName
, SharedImplUtil
.findCharTableByTree(this));
139 if (getQualifier() != null) {
140 replaceChildInternal(findChildByRole(ChildRole
.QUALIFIER
), newRef
);
141 return (PsiImportStaticStatement
)getParent();
144 final LeafElement dot
= Factory
.createSingleLeafElement(JavaTokenType
.DOT
, ".", 0, 1, SharedImplUtil
.findCharTableByTree(newRef
), getManager());
145 newRef
.rawInsertAfterMe(dot
);
146 final CompositeElement errorElement
=
147 Factory
.createErrorElement(JavaErrorMessages
.message("import.statement.identifier.or.asterisk.expected."));
148 dot
.rawInsertAfterMe(errorElement
);
149 final CompositeElement parentComposite
= (CompositeElement
)SourceTreeToPsiMap
.psiElementToTree(getParent());
150 parentComposite
.addInternal(newRef
, errorElement
, this, Boolean
.TRUE
);
151 parentComposite
.deleteChildInternal(this);
152 return (PsiImportStaticStatement
)SourceTreeToPsiMap
.treeElementToPsi(parentComposite
);
156 public boolean isQualified() {
157 return findChildByRole(ChildRole
.QUALIFIER
) != null;
160 public String
getQualifiedName() {
161 return getCanonicalText();
164 public boolean isSoft() {
168 public String
getReferenceName() {
169 final ASTNode childByRole
= findChildByRole(ChildRole
.REFERENCE_NAME
);
170 if (childByRole
== null) return "";
171 return childByRole
.getText();
174 public PsiElement
getElement() {
178 public TextRange
getRangeInElement() {
179 TreeElement nameChild
= (TreeElement
)findChildByRole(ChildRole
.REFERENCE_NAME
);
180 if (nameChild
== null) return new TextRange(0, getTextLength());
181 final int startOffset
= nameChild
.getStartOffsetInParent();
182 return new TextRange(startOffset
, startOffset
+ nameChild
.getTextLength());
185 public String
getCanonicalText() {
186 String canonicalText
= myCanonicalText
;
187 if (canonicalText
== null) {
188 myCanonicalText
= canonicalText
= calcCanonicalText();
190 return canonicalText
;
193 private String
calcCanonicalText() {
194 final PsiJavaCodeReferenceElement referenceElement
= (PsiJavaCodeReferenceElement
)getQualifier();
195 if (referenceElement
== null) {
196 return getReferenceName();
199 return referenceElement
.getCanonicalText() + "." + getReferenceName();
203 public String
toString() {
204 return "PsiImportStaticReferenceElement:" + getText();
208 public JavaResolveResult
advancedResolve(boolean incompleteCode
) {
209 final JavaResolveResult
[] results
= multiResolve(incompleteCode
);
210 if (results
.length
== 1) return results
[0];
211 return JavaResolveResult
.EMPTY
;
215 public JavaResolveResult
[] multiResolve(boolean incompleteCode
) {
216 final ResolveCache resolveCache
= getManager().getResolveCache();
217 return (JavaResolveResult
[])resolveCache
.resolveWithCaching(this, OurGenericsResolver
.INSTANCE
, true, incompleteCode
);
220 private class OurResolveResult
implements JavaResolveResult
{
221 final PsiMember myTarget
;
222 Boolean myAccessible
= null;
225 public OurResolveResult(PsiMember target
) {
229 public PsiMember
getElement() {
233 public PsiSubstitutor
getSubstitutor() {
234 return PsiSubstitutor
.EMPTY
;
237 public boolean isValidResult() {
238 return isAccessible();
241 public boolean isAccessible() {
242 if (myAccessible
== null) {
243 myAccessible
= JavaPsiFacade
.getInstance(getProject()).getResolveHelper().isAccessible(myTarget
, PsiImportStaticReferenceElementImpl
.this, null);
245 return myAccessible
.booleanValue();
248 public boolean isStaticsScopeCorrect() {
252 public PsiElement
getCurrentFileResolveScope() {
256 public boolean isPackagePrefixPackageReference() {
262 private static final class OurGenericsResolver
implements ResolveCache
.PolyVariantResolver
<PsiImportStaticReferenceElementImpl
> {
263 private static final OurGenericsResolver INSTANCE
= new OurGenericsResolver();
264 public JavaResolveResult
[] resolve(PsiImportStaticReferenceElementImpl referenceElement
, boolean incompleteCode
) {
265 final PsiElement qualifier
= referenceElement
.getQualifier();
266 if (!(qualifier
instanceof PsiJavaCodeReferenceElement
)) return JavaResolveResult
.EMPTY_ARRAY
;
267 final PsiElement target
= ((PsiJavaCodeReferenceElement
)qualifier
).resolve();
268 if (!(target
instanceof PsiClass
)) return JavaResolveResult
.EMPTY_ARRAY
;
269 final ArrayList
<JavaResolveResult
> results
= new ArrayList
<JavaResolveResult
>();
271 .processDeclarations(referenceElement
.new MyScopeProcessor(results
), ResolveState
.initial(), referenceElement
, referenceElement
);
272 if (results
.size() <= 1) {
273 return results
.toArray(new JavaResolveResult
[results
.size()]);
275 for(int i
= results
.size() - 1; i
>= 0; i
--) {
276 final JavaResolveResult resolveResult
= results
.get(i
);
277 if (!resolveResult
.isValidResult()) {
281 return results
.toArray(new JavaResolveResult
[results
.size()]);
286 private class MyScopeProcessor
extends BaseScopeProcessor
implements NameHint
{
287 private final List
<JavaResolveResult
> myResults
;
289 public MyScopeProcessor(List
<JavaResolveResult
> results
) {
293 public boolean execute(PsiElement element
, ResolveState state
) {
294 if (element
instanceof PsiMember
295 && ((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
)) {
296 myResults
.add(new OurResolveResult((PsiMember
)element
));
301 public String
getName(ResolveState state
) {
302 return getReferenceName();
306 public <T
> T
getHint(Key
<T
> hintKey
) {
307 if (hintKey
== NameHint
.KEY
) {
311 return super.getHint(hintKey
);
315 public PsiReference
getReference() {
319 public PsiElement
resolve() {
320 return advancedResolve(false).getElement();
323 public boolean isReferenceTo(PsiElement element
) {
324 final String name
= getReferenceName();
325 return name
!= null &&
326 element
instanceof PsiNamedElement
&&
327 name
.equals(((PsiNamedElement
)element
).getName()) && element
.getManager().areElementsEquivalent(resolve(), element
);
330 public PsiElement
handleElementRename(String newElementName
) throws IncorrectOperationException
{
331 PsiElement oldIdentifier
= findChildByRoleAsPsiElement(ChildRole
.REFERENCE_NAME
);
332 if (oldIdentifier
== null){
333 throw new IncorrectOperationException();
335 PsiIdentifier identifier
= JavaPsiFacade
.getInstance(getProject()).getElementFactory().createIdentifier(newElementName
);
336 oldIdentifier
.replace(identifier
);
340 public PsiElement
bindToElement(@NotNull PsiElement element
) throws IncorrectOperationException
{
341 if (!(element
instanceof PsiMember
) ||
342 !(element
instanceof PsiNamedElement
) ||
343 ((PsiNamedElement
)element
).getName() == null) {
344 throw new IncorrectOperationException();
346 if (!((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
)) {
347 if (element
instanceof PsiClass
&& ((PsiClass
)element
).getContainingClass() == null) {
348 // "move inner to upper level" of a statically imported inner class => replace with regular import
349 return replaceWithRegularImport((PsiClass
) element
);
351 throw new IncorrectOperationException();
354 PsiClass containingClass
= ((PsiMember
)element
).getContainingClass();
355 if (containingClass
== null) throw new IncorrectOperationException();
356 PsiElement qualifier
= getQualifier();
357 if (qualifier
== null) {
358 throw new IncorrectOperationException();
360 ((PsiReference
)qualifier
).bindToElement(containingClass
);
362 PsiElement oldIdentifier
= findChildByRoleAsPsiElement(ChildRole
.REFERENCE_NAME
);
363 if (oldIdentifier
== null){
364 throw new IncorrectOperationException();
367 PsiIdentifier identifier
= JavaPsiFacade
.getInstance(getProject()).getElementFactory().createIdentifier(((PsiNamedElement
)element
).getName());
368 oldIdentifier
.replace(identifier
);
372 private PsiElement
replaceWithRegularImport(final PsiClass psiClass
) throws IncorrectOperationException
{
373 PsiImportStaticStatement baseStatement
= PsiTreeUtil
.getParentOfType(getElement(), PsiImportStaticStatement
.class);
374 PsiImportStatement statement
= JavaPsiFacade
.getInstance(getProject()).getElementFactory().createImportStatement(psiClass
);
375 statement
= (PsiImportStatement
) baseStatement
.replace(statement
);
376 final PsiJavaCodeReferenceElement reference
= statement
.getImportReference();
377 assert reference
!= null;
381 public void processVariants(PsiScopeProcessor processor
) {
382 FilterScopeProcessor proc
= new FilterScopeProcessor(new ClassFilter(PsiModifierListOwner
.class), processor
);
383 PsiScopesUtil
.resolveAndWalk(proc
, this, null, true);
386 public Object
[] getVariants() {
388 return ArrayUtil
.EMPTY_OBJECT_ARRAY
;
391 public void accept(@NotNull PsiElementVisitor visitor
) {
392 if (visitor
instanceof JavaElementVisitor
) {
393 ((JavaElementVisitor
)visitor
).visitImportStaticReferenceElement(this);
396 visitor
.visitElement(this);