| /* |
| * Copyright 2000-2012 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.psi.impl.source; |
| |
| import com.intellij.codeInsight.daemon.JavaErrorMessages; |
| import com.intellij.lang.ASTNode; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.*; |
| import com.intellij.psi.filters.ClassFilter; |
| import com.intellij.psi.impl.source.resolve.ResolveCache; |
| import com.intellij.psi.impl.source.resolve.StaticImportResolveProcessor; |
| import com.intellij.psi.impl.source.tree.*; |
| import com.intellij.psi.scope.PsiScopeProcessor; |
| import com.intellij.psi.scope.processor.FilterScopeProcessor; |
| import com.intellij.psi.scope.util.PsiScopesUtil; |
| import com.intellij.psi.tree.ChildRoleBase; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import org.jetbrains.annotations.NotNull; |
| |
| /** |
| * @author dsl |
| */ |
| public class PsiImportStaticReferenceElementImpl extends CompositePsiElement implements PsiImportStaticReferenceElement { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiImportStaticReferenceElementImpl"); |
| private volatile String myCanonicalText; |
| |
| public PsiImportStaticReferenceElementImpl() { |
| super(JavaElementType.IMPORT_STATIC_REFERENCE); |
| } |
| |
| @Override |
| public int getTextOffset() { |
| ASTNode refName = findChildByRole(ChildRole.REFERENCE_NAME); |
| if (refName != null){ |
| return refName.getStartOffset(); |
| } |
| else{ |
| return super.getTextOffset(); |
| } |
| } |
| |
| @Override |
| public void clearCaches() { |
| super.clearCaches(); |
| myCanonicalText = null; |
| } |
| |
| @Override |
| public final ASTNode findChildByRole(int role) { |
| LOG.assertTrue(ChildRole.isUnique(role)); |
| switch (role) { |
| default: |
| return null; |
| |
| case ChildRole.REFERENCE_NAME: |
| return findChildByType(JavaTokenType.IDENTIFIER); |
| |
| case ChildRole.QUALIFIER: |
| final TreeElement node = getFirstChildNode(); |
| return node.getElementType() == JavaElementType.JAVA_CODE_REFERENCE ? node : null; |
| |
| case ChildRole.DOT: |
| return findChildByType(JavaTokenType.DOT); |
| } |
| } |
| |
| @Override |
| public final int getChildRole(ASTNode child) { |
| LOG.assertTrue(child.getTreeParent() == this); |
| IElementType i = child.getElementType(); |
| if (i == JavaElementType.JAVA_CODE_REFERENCE) { |
| return ChildRole.QUALIFIER; |
| } |
| else if (i == JavaTokenType.DOT) { |
| return ChildRole.DOT; |
| } |
| else if (i == JavaTokenType.IDENTIFIER) { |
| return ChildRole.REFERENCE_NAME; |
| } |
| else { |
| return ChildRoleBase.NONE; |
| } |
| } |
| |
| |
| @Override |
| public PsiElement getReferenceNameElement() { |
| return findChildByRoleAsPsiElement(ChildRole.REFERENCE_NAME); |
| } |
| |
| @Override |
| public PsiReferenceParameterList getParameterList() { |
| return null; |
| } |
| |
| @Override |
| @NotNull |
| public PsiType[] getTypeParameters() { |
| return PsiType.EMPTY_ARRAY; |
| } |
| |
| @Override |
| public PsiElement getQualifier() { |
| return findChildByRoleAsPsiElement(ChildRole.QUALIFIER); |
| } |
| |
| @Override |
| public PsiJavaCodeReferenceElement getClassReference() { |
| return (PsiJavaCodeReferenceElement)findChildByRoleAsPsiElement(ChildRole.QUALIFIER); |
| } |
| |
| @Override |
| public PsiImportStaticStatement bindToTargetClass(final PsiClass aClass) throws IncorrectOperationException { |
| final String qualifiedName = aClass.getQualifiedName(); |
| if (qualifiedName == null) throw new IncorrectOperationException(); |
| final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(getProject()).getParserFacade(); |
| final CompositeElement newRef = (CompositeElement)parserFacade.createReferenceFromText(qualifiedName, null).getNode(); |
| if (getQualifier() != null) { |
| replaceChildInternal(findChildByRole(ChildRole.QUALIFIER), newRef); |
| return (PsiImportStaticStatement)getParent(); |
| } |
| else { |
| final LeafElement dot = Factory.createSingleLeafElement(JavaTokenType.DOT, ".", 0, 1, SharedImplUtil.findCharTableByTree(newRef), getManager()); |
| newRef.rawInsertAfterMe(dot); |
| final CompositeElement errorElement = Factory.createErrorElement(JavaErrorMessages.message("import.statement.identifier.or.asterisk.expected.")); |
| dot.rawInsertAfterMe(errorElement); |
| final CompositeElement parentComposite = (CompositeElement)SourceTreeToPsiMap.psiElementToTree(getParent()); |
| parentComposite.addInternal(newRef, errorElement, this, Boolean.TRUE); |
| parentComposite.deleteChildInternal(this); |
| return (PsiImportStaticStatement)SourceTreeToPsiMap.treeElementToPsi(parentComposite); |
| } |
| } |
| |
| @Override |
| public boolean isQualified() { |
| return findChildByRole(ChildRole.QUALIFIER) != null; |
| } |
| |
| @Override |
| public String getQualifiedName() { |
| return getCanonicalText(); |
| } |
| |
| @Override |
| public boolean isSoft() { |
| return false; |
| } |
| |
| @Override |
| public String getReferenceName() { |
| final ASTNode childByRole = findChildByRole(ChildRole.REFERENCE_NAME); |
| if (childByRole == null) return ""; |
| return childByRole.getText(); |
| } |
| |
| @Override |
| public PsiElement getElement() { |
| return this; |
| } |
| |
| @Override |
| public TextRange getRangeInElement() { |
| TreeElement nameChild = (TreeElement)findChildByRole(ChildRole.REFERENCE_NAME); |
| if (nameChild == null) return new TextRange(0, getTextLength()); |
| final int startOffset = nameChild.getStartOffsetInParent(); |
| return new TextRange(startOffset, startOffset + nameChild.getTextLength()); |
| } |
| |
| @Override |
| @NotNull |
| public String getCanonicalText() { |
| String canonicalText = myCanonicalText; |
| if (canonicalText == null) { |
| myCanonicalText = canonicalText = calcCanonicalText(); |
| } |
| return canonicalText; |
| } |
| |
| private String calcCanonicalText() { |
| final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)getQualifier(); |
| if (referenceElement == null) { |
| return getReferenceName(); |
| } |
| else { |
| return referenceElement.getCanonicalText() + "." + getReferenceName(); |
| } |
| } |
| |
| public String toString() { |
| return "PsiImportStaticReferenceElement:" + getText(); |
| } |
| |
| @Override |
| @NotNull |
| public JavaResolveResult advancedResolve(boolean incompleteCode) { |
| final JavaResolveResult[] results = multiResolve(incompleteCode); |
| if (results.length == 1) return results[0]; |
| return JavaResolveResult.EMPTY; |
| } |
| |
| @Override |
| @NotNull |
| public JavaResolveResult[] multiResolve(boolean incompleteCode) { |
| PsiFile file = getContainingFile(); |
| final ResolveCache resolveCache = ResolveCache.getInstance(file.getProject()); |
| final ResolveResult[] results = resolveCache.resolveWithCaching(this, OurGenericsResolver.INSTANCE, true, incompleteCode,file); |
| return results instanceof JavaResolveResult[] ? (JavaResolveResult[])results : JavaResolveResult.EMPTY_ARRAY; |
| } |
| |
| private static final class OurGenericsResolver implements ResolveCache.PolyVariantResolver<PsiImportStaticReferenceElementImpl> { |
| private static final OurGenericsResolver INSTANCE = new OurGenericsResolver(); |
| |
| @NotNull |
| @Override |
| public JavaResolveResult[] resolve(@NotNull final PsiImportStaticReferenceElementImpl referenceElement, final boolean incompleteCode) { |
| final PsiElement qualifier = referenceElement.getQualifier(); |
| if (!(qualifier instanceof PsiJavaCodeReferenceElement)) return JavaResolveResult.EMPTY_ARRAY; |
| final PsiElement target = ((PsiJavaCodeReferenceElement)qualifier).resolve(); |
| if (!(target instanceof PsiClass)) return JavaResolveResult.EMPTY_ARRAY; |
| final StaticImportResolveProcessor processor = new StaticImportResolveProcessor(referenceElement); |
| target.processDeclarations(processor, ResolveState.initial(), referenceElement, referenceElement); |
| return processor.getResults(); |
| } |
| } |
| |
| @Override |
| public PsiReference getReference() { |
| return this; |
| } |
| |
| @Override |
| public PsiElement resolve() { |
| return advancedResolve(false).getElement(); |
| } |
| |
| @Override |
| public boolean isReferenceTo(PsiElement element) { |
| final String name = getReferenceName(); |
| if (name == null || !(element instanceof PsiNamedElement) || !name.equals(((PsiNamedElement)element).getName())) { |
| return false; |
| } |
| |
| for (JavaResolveResult result : multiResolve(false)) { |
| if (getManager().areElementsEquivalent(result.getElement(), element)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { |
| PsiElement oldIdentifier = findChildByRoleAsPsiElement(ChildRole.REFERENCE_NAME); |
| if (oldIdentifier == null) { |
| throw new IncorrectOperationException(); |
| } |
| PsiIdentifier identifier = JavaPsiFacade.getInstance(getProject()).getElementFactory().createIdentifier(newElementName); |
| oldIdentifier.replace(identifier); |
| return this; |
| } |
| |
| @Override |
| public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { |
| if (!(element instanceof PsiMember) || |
| !(element instanceof PsiNamedElement) || |
| ((PsiNamedElement)element).getName() == null) { |
| throw new IncorrectOperationException(); |
| } |
| if (!((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) { |
| if (element instanceof PsiClass && ((PsiClass)element).getContainingClass() == null) { |
| // "move inner to upper level" of a statically imported inner class => replace with regular import |
| return replaceWithRegularImport((PsiClass) element); |
| } |
| throw new IncorrectOperationException(); |
| } |
| |
| PsiClass containingClass = ((PsiMember)element).getContainingClass(); |
| if (containingClass == null) throw new IncorrectOperationException(); |
| PsiElement qualifier = getQualifier(); |
| if (qualifier == null) { |
| throw new IncorrectOperationException(); |
| } |
| ((PsiReference)qualifier).bindToElement(containingClass); |
| |
| PsiElement oldIdentifier = findChildByRoleAsPsiElement(ChildRole.REFERENCE_NAME); |
| if (oldIdentifier == null){ |
| throw new IncorrectOperationException(); |
| } |
| |
| PsiIdentifier identifier = JavaPsiFacade.getInstance(getProject()).getElementFactory().createIdentifier(((PsiNamedElement)element).getName()); |
| oldIdentifier.replace(identifier); |
| return this; |
| } |
| |
| private PsiElement replaceWithRegularImport(final PsiClass psiClass) throws IncorrectOperationException { |
| PsiImportStaticStatement baseStatement = PsiTreeUtil.getParentOfType(getElement(), PsiImportStaticStatement.class); |
| PsiImportStatement statement = JavaPsiFacade.getInstance(getProject()).getElementFactory().createImportStatement(psiClass); |
| statement = (PsiImportStatement) baseStatement.replace(statement); |
| final PsiJavaCodeReferenceElement reference = statement.getImportReference(); |
| assert reference != null; |
| return reference; |
| } |
| |
| @Override |
| public void processVariants(@NotNull PsiScopeProcessor processor) { |
| FilterScopeProcessor proc = new FilterScopeProcessor(new ClassFilter(PsiModifierListOwner.class), processor); |
| PsiScopesUtil.resolveAndWalk(proc, this, null, true); |
| } |
| |
| @Override |
| @NotNull |
| public Object[] getVariants() { |
| // IMPLEMENT[dsl] |
| return ArrayUtil.EMPTY_OBJECT_ARRAY; |
| } |
| |
| @Override |
| public void accept(@NotNull PsiElementVisitor visitor) { |
| if (visitor instanceof JavaElementVisitor) { |
| ((JavaElementVisitor)visitor).visitImportStaticReferenceElement(this); |
| } |
| else { |
| visitor.visitElement(this); |
| } |
| } |
| } |