mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
908 lines
34 KiB
Java
908 lines
34 KiB
Java
/*
|
|
* Copyright 2000-2009 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.lang.ASTNode;
|
|
import com.intellij.openapi.diagnostic.Logger;
|
|
import com.intellij.openapi.util.TextRange;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
|
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
|
import com.intellij.psi.filters.*;
|
|
import com.intellij.psi.filters.classes.AnnotationTypeFilter;
|
|
import com.intellij.psi.filters.element.ModifierFilter;
|
|
import com.intellij.psi.impl.CheckUtil;
|
|
import com.intellij.psi.impl.DebugUtil;
|
|
import com.intellij.psi.impl.PsiImplUtil;
|
|
import com.intellij.psi.impl.PsiManagerEx;
|
|
import com.intellij.psi.impl.source.resolve.ClassResolverProcessor;
|
|
import com.intellij.psi.impl.source.resolve.ResolveCache;
|
|
import com.intellij.psi.impl.source.resolve.VariableResolverProcessor;
|
|
import com.intellij.psi.impl.source.tree.*;
|
|
import com.intellij.psi.infos.CandidateInfo;
|
|
import com.intellij.psi.scope.ElementClassFilter;
|
|
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.PsiUtil;
|
|
import com.intellij.util.IncorrectOperationException;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement implements PsiJavaCodeReferenceElement, SourceJavaCodeReference {
|
|
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl");
|
|
|
|
private volatile String myCachedQName = null;
|
|
private volatile String myCachedTextSkipWhiteSpaceAndComments;
|
|
private int myKindWhenDummy = CLASS_NAME_KIND;
|
|
|
|
public static final int CLASS_NAME_KIND = 1;
|
|
public static final int PACKAGE_NAME_KIND = 2;
|
|
public static final int CLASS_OR_PACKAGE_NAME_KIND = 3;
|
|
public static final int CLASS_FQ_NAME_KIND = 4;
|
|
public static final int CLASS_FQ_OR_PACKAGE_NAME_KIND = 5;
|
|
public static final int CLASS_IN_QUALIFIED_NEW_KIND = 6;
|
|
|
|
private final int myHC = ourHC++;
|
|
|
|
@Override
|
|
public final int hashCode() {
|
|
return myHC;
|
|
}
|
|
|
|
public PsiJavaCodeReferenceElementImpl() {
|
|
super(JavaElementType.JAVA_CODE_REFERENCE);
|
|
}
|
|
|
|
public int getTextOffset() {
|
|
final ASTNode refName = getReferenceNameNode();
|
|
return refName != null ? refName.getStartOffset() : super.getTextOffset();
|
|
}
|
|
|
|
public void setKindWhenDummy(final int kind) {
|
|
LOG.assertTrue(isDummy(getTreeParent().getElementType()));
|
|
myKindWhenDummy = kind;
|
|
}
|
|
|
|
private static boolean isDummy(final IElementType type) {
|
|
return type == TokenType.DUMMY_HOLDER || type == JavaElementType.DUMMY_ELEMENT;
|
|
}
|
|
|
|
public int getKind() {
|
|
IElementType i = getTreeParent().getElementType();
|
|
if (isDummy(i)) {
|
|
return myKindWhenDummy;
|
|
}
|
|
if (i == JavaElementType.TYPE) {
|
|
return getTreeParent().getTreeParent().getPsi() instanceof PsiTypeCodeFragment ? CLASS_OR_PACKAGE_NAME_KIND : CLASS_NAME_KIND;
|
|
}
|
|
if (i == JavaElementType.EXTENDS_LIST ||
|
|
i == JavaElementType.IMPLEMENTS_LIST ||
|
|
i == JavaElementType.EXTENDS_BOUND_LIST ||
|
|
i == JavaElementType.THROWS_LIST ||
|
|
i == JavaElementType.THIS_EXPRESSION ||
|
|
i == JavaElementType.SUPER_EXPRESSION ||
|
|
i == JavaDocElementType.DOC_METHOD_OR_FIELD_REF ||
|
|
i == JavaDocTokenType.DOC_TAG_VALUE_TOKEN ||
|
|
i == JavaElementType.REFERENCE_PARAMETER_LIST ||
|
|
i == JavaElementType.ANNOTATION) {
|
|
if (isQualified()) {
|
|
return CLASS_OR_PACKAGE_NAME_KIND;
|
|
}
|
|
return CLASS_NAME_KIND;
|
|
}
|
|
if (i == JavaElementType.NEW_EXPRESSION) {
|
|
final ASTNode qualifier = getTreeParent().findChildByRole(ChildRole.QUALIFIER);
|
|
return qualifier != null ? CLASS_IN_QUALIFIED_NEW_KIND : CLASS_NAME_KIND;
|
|
}
|
|
if (i == JavaElementType.ANONYMOUS_CLASS) {
|
|
if (getTreeParent().getChildRole(this) == ChildRole.BASE_CLASS_REFERENCE) {
|
|
LOG.assertTrue(getTreeParent().getTreeParent().getElementType() == JavaElementType.NEW_EXPRESSION);
|
|
final ASTNode qualifier = getTreeParent().getTreeParent().findChildByRole(ChildRole.QUALIFIER);
|
|
return qualifier != null ? CLASS_IN_QUALIFIED_NEW_KIND : CLASS_NAME_KIND;
|
|
}
|
|
else {
|
|
return CLASS_OR_PACKAGE_NAME_KIND; // incomplete code
|
|
}
|
|
}
|
|
if (i == JavaElementType.PACKAGE_STATEMENT) {
|
|
return PACKAGE_NAME_KIND;
|
|
}
|
|
if (i == JavaElementType.IMPORT_STATEMENT) {
|
|
final boolean isOnDemand = ((PsiImportStatement)SourceTreeToPsiMap.treeElementToPsi(getTreeParent())).isOnDemand();
|
|
return isOnDemand ? CLASS_FQ_OR_PACKAGE_NAME_KIND : CLASS_FQ_NAME_KIND;
|
|
}
|
|
if (i == JavaElementType.IMPORT_STATIC_STATEMENT) {
|
|
return CLASS_FQ_OR_PACKAGE_NAME_KIND;
|
|
}
|
|
if (i == JavaElementType.JAVA_CODE_REFERENCE) {
|
|
final int parentKind = ((PsiJavaCodeReferenceElementImpl)getTreeParent()).getKind();
|
|
switch (parentKind) {
|
|
case CLASS_NAME_KIND:
|
|
return CLASS_OR_PACKAGE_NAME_KIND;
|
|
|
|
case PACKAGE_NAME_KIND:
|
|
return PACKAGE_NAME_KIND;
|
|
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
return CLASS_OR_PACKAGE_NAME_KIND;
|
|
|
|
case CLASS_FQ_NAME_KIND:
|
|
return CLASS_FQ_OR_PACKAGE_NAME_KIND;
|
|
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
|
|
return CLASS_FQ_OR_PACKAGE_NAME_KIND;
|
|
|
|
case CLASS_IN_QUALIFIED_NEW_KIND:
|
|
return CLASS_IN_QUALIFIED_NEW_KIND; //??
|
|
|
|
default:
|
|
LOG.assertTrue(false);
|
|
return -1;
|
|
}
|
|
}
|
|
if (i == JavaElementType.CLASS || i == JavaElementType.PARAMETER_LIST || i == TokenType.ERROR_ELEMENT) {
|
|
return CLASS_OR_PACKAGE_NAME_KIND;
|
|
}
|
|
if (i == JavaElementType.IMPORT_STATIC_REFERENCE) {
|
|
return CLASS_FQ_OR_PACKAGE_NAME_KIND;
|
|
}
|
|
if (i == JavaDocElementType.DOC_TAG ||
|
|
i == JavaDocElementType.DOC_INLINE_TAG ||
|
|
i == JavaDocElementType.DOC_REFERENCE_HOLDER ||
|
|
i == JavaDocElementType.DOC_TYPE_HOLDER) {
|
|
return CLASS_OR_PACKAGE_NAME_KIND;
|
|
}
|
|
if (isCodeFragmentType(i)) {
|
|
PsiJavaCodeReferenceCodeFragment fragment = (PsiJavaCodeReferenceCodeFragment)getTreeParent().getPsi();
|
|
return fragment.isClassesAccepted() ? CLASS_FQ_OR_PACKAGE_NAME_KIND : PACKAGE_NAME_KIND;
|
|
}
|
|
CompositeElement parent = getTreeParent();
|
|
String message = "Unknown parent for java code reference: '" + parent +"'; " +
|
|
"Type: "+i+"; \n";
|
|
|
|
while (parent != null && parent.getPsi() instanceof PsiExpression) {
|
|
parent = parent.getTreeParent();
|
|
message += " Parent: '" + parent+"'; \n";
|
|
}
|
|
if (parent != null) {
|
|
message += DebugUtil.treeToString(parent, false);
|
|
}
|
|
LOG.error(message);
|
|
return CLASS_NAME_KIND;
|
|
}
|
|
|
|
private static boolean isCodeFragmentType(IElementType type) {
|
|
return type == TokenType.CODE_FRAGMENT || type instanceof ICodeFragmentElementType;
|
|
}
|
|
|
|
public void deleteChildInternal(@NotNull final ASTNode child) {
|
|
if (getChildRole(child) == ChildRole.QUALIFIER) {
|
|
final ASTNode dot = findChildByRole(ChildRole.DOT);
|
|
/*super.deleteChildInternal(child);
|
|
deleteChildInternal(dot);*/
|
|
deleteChildRange(child.getPsi(), dot.getPsi());
|
|
}
|
|
else {
|
|
super.deleteChildInternal(child);
|
|
}
|
|
}
|
|
|
|
public final ASTNode findChildByRole(final int role) {
|
|
LOG.assertTrue(ChildRole.isUnique(role));
|
|
switch (role) {
|
|
default:
|
|
return null;
|
|
|
|
case ChildRole.REFERENCE_NAME:
|
|
if (getLastChildNode().getElementType() == JavaTokenType.IDENTIFIER) {
|
|
return getLastChildNode();
|
|
}
|
|
else {
|
|
if (getLastChildNode().getElementType() == JavaElementType.REFERENCE_PARAMETER_LIST) {
|
|
ASTNode current = getLastChildNode().getTreePrev();
|
|
while (current != null && StdTokenSets.WHITE_SPACE_OR_COMMENT_BIT_SET.contains(current.getElementType())) {
|
|
current = current.getTreePrev();
|
|
}
|
|
if (current != null && current.getElementType() == JavaTokenType.IDENTIFIER) {
|
|
return current;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
case ChildRole.REFERENCE_PARAMETER_LIST:
|
|
if (getLastChildNode().getElementType() == JavaElementType.REFERENCE_PARAMETER_LIST) {
|
|
return getLastChildNode();
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
|
|
case ChildRole.QUALIFIER:
|
|
if (getFirstChildNode().getElementType() == JavaElementType.JAVA_CODE_REFERENCE) {
|
|
return getFirstChildNode();
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
|
|
case ChildRole.DOT:
|
|
return findChildByType(JavaTokenType.DOT);
|
|
}
|
|
}
|
|
|
|
public final int getChildRole(final ASTNode child) {
|
|
LOG.assertTrue(child.getTreeParent() == this);
|
|
final IElementType i = child.getElementType();
|
|
if (i == JavaElementType.REFERENCE_PARAMETER_LIST) {
|
|
return ChildRole.REFERENCE_PARAMETER_LIST;
|
|
}
|
|
else 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;
|
|
}
|
|
}
|
|
|
|
@NotNull
|
|
public String getCanonicalText() {
|
|
switch (getKind()) {
|
|
case CLASS_NAME_KIND:
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
case CLASS_IN_QUALIFIED_NEW_KIND:
|
|
final PsiElement target = resolve();
|
|
if (target instanceof PsiClass) {
|
|
final PsiClass aClass = (PsiClass)target;
|
|
String name = aClass.getQualifiedName();
|
|
if (name == null) {
|
|
name = aClass.getName(); //?
|
|
}
|
|
final PsiType[] types = getTypeParameters();
|
|
if (types.length == 0) return name;
|
|
|
|
final StringBuilder buf = new StringBuilder();
|
|
buf.append(name);
|
|
buf.append('<');
|
|
for (int i = 0; i < types.length; i++) {
|
|
if (i > 0) buf.append(',');
|
|
buf.append(types[i].getCanonicalText());
|
|
}
|
|
buf.append('>');
|
|
|
|
return buf.toString();
|
|
}
|
|
else if (target instanceof PsiPackage) {
|
|
return ((PsiPackage)target).getQualifiedName();
|
|
}
|
|
else {
|
|
LOG.assertTrue(target == null);
|
|
return getTextSkipWhiteSpaceAndComments();
|
|
}
|
|
case PACKAGE_NAME_KIND:
|
|
case CLASS_FQ_NAME_KIND:
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
|
|
return getTextSkipWhiteSpaceAndComments();
|
|
|
|
default:
|
|
LOG.assertTrue(false);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public PsiReference getReference() {
|
|
return this;
|
|
}
|
|
|
|
public final PsiElement resolve() {
|
|
return advancedResolve(false).getElement();
|
|
}
|
|
|
|
private static final class OurGenericsResolver implements ResolveCache.PolyVariantResolver<PsiJavaReference> {
|
|
private static final OurGenericsResolver INSTANCE = new OurGenericsResolver();
|
|
|
|
public static JavaResolveResult[] _resolve(final PsiJavaReference ref, final boolean incompleteCode) {
|
|
final PsiJavaCodeReferenceElementImpl referenceElement = (PsiJavaCodeReferenceElementImpl)ref;
|
|
final int kind = referenceElement.getKind();
|
|
JavaResolveResult[] result = referenceElement.resolve(kind);
|
|
if (incompleteCode && result.length == 0 && kind != CLASS_FQ_NAME_KIND && kind != CLASS_FQ_OR_PACKAGE_NAME_KIND) {
|
|
final VariableResolverProcessor processor = new VariableResolverProcessor(referenceElement);
|
|
PsiScopesUtil.resolveAndWalk(processor, referenceElement, null, incompleteCode);
|
|
result = processor.getResult();
|
|
if (result.length > 0) {
|
|
return result;
|
|
}
|
|
if (kind == CLASS_NAME_KIND) {
|
|
return referenceElement.resolve(PACKAGE_NAME_KIND);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public JavaResolveResult[] resolve(final PsiJavaReference ref, final boolean incompleteCode) {
|
|
final JavaResolveResult[] result = _resolve(ref, incompleteCode);
|
|
if (result.length > 0 && result[0].getElement() instanceof PsiClass) {
|
|
final PsiType[] parameters = ((PsiJavaCodeReferenceElement)ref).getTypeParameters();
|
|
final JavaResolveResult[] newResult = new JavaResolveResult[result.length];
|
|
for (int i = 0; i < result.length; i++) {
|
|
final CandidateInfo resolveResult = (CandidateInfo)result[i];
|
|
final PsiClass aClass = (PsiClass)resolveResult.getElement();
|
|
assert aClass != null;
|
|
newResult[i] = !aClass.hasTypeParameters() ? resolveResult : new CandidateInfo(resolveResult, resolveResult.getSubstitutor().putAll(aClass, parameters));
|
|
}
|
|
return newResult;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
@NotNull
|
|
public JavaResolveResult advancedResolve(final boolean incompleteCode) {
|
|
final JavaResolveResult[] results = multiResolve(incompleteCode);
|
|
if (results.length == 1) return results[0];
|
|
return JavaResolveResult.EMPTY;
|
|
}
|
|
|
|
@NotNull
|
|
public JavaResolveResult[] multiResolve(final boolean incompleteCode) {
|
|
final PsiManagerEx manager = getManager();
|
|
if (manager == null) {
|
|
LOG.error("getManager() == null!");
|
|
return JavaResolveResult.EMPTY_ARRAY;
|
|
}
|
|
|
|
final ResolveCache resolveCache = manager.getResolveCache();
|
|
final ResolveResult[] results = resolveCache.resolveWithCaching(this, OurGenericsResolver.INSTANCE, true, incompleteCode);
|
|
return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results;
|
|
}
|
|
|
|
private PsiSubstitutor updateSubstitutor(PsiSubstitutor subst, final PsiClass psiClass) {
|
|
final PsiType[] parameters = getTypeParameters();
|
|
if (psiClass != null) {
|
|
subst = subst.putAll(psiClass, parameters);
|
|
}
|
|
return subst;
|
|
}
|
|
|
|
private JavaResolveResult[] resolve(final int kind) {
|
|
switch (kind) {
|
|
case CLASS_FQ_NAME_KIND:
|
|
// TODO: support type parameters in FQ names
|
|
final String textSkipWhiteSpaceAndComments = getTextSkipWhiteSpaceAndComments();
|
|
if (textSkipWhiteSpaceAndComments == null || textSkipWhiteSpaceAndComments.length() == 0) return JavaResolveResult.EMPTY_ARRAY;
|
|
final PsiClass aClass =
|
|
JavaPsiFacade.getInstance(getProject()).findClass(textSkipWhiteSpaceAndComments, getResolveScope());
|
|
if (aClass == null) return JavaResolveResult.EMPTY_ARRAY;
|
|
return new JavaResolveResult[]{new CandidateInfo(aClass, updateSubstitutor(PsiSubstitutor.EMPTY, aClass), this, false)};
|
|
|
|
case CLASS_IN_QUALIFIED_NEW_KIND: {
|
|
PsiElement parent = getParent();
|
|
if (parent instanceof JavaDummyHolder) {
|
|
parent = parent.getContext();
|
|
}
|
|
|
|
if (parent instanceof PsiAnonymousClass) {
|
|
parent = parent.getParent();
|
|
}
|
|
final PsiExpression qualifier;
|
|
if (parent instanceof PsiNewExpression) {
|
|
qualifier = ((PsiNewExpression)parent).getQualifier();
|
|
LOG.assertTrue(qualifier != null);
|
|
}
|
|
else if (parent instanceof PsiJavaCodeReferenceElement) {
|
|
return JavaResolveResult.EMPTY_ARRAY;
|
|
}
|
|
else {
|
|
LOG.error("Invalid java reference!");
|
|
return JavaResolveResult.EMPTY_ARRAY;
|
|
}
|
|
|
|
final PsiType qualifierType = qualifier.getType();
|
|
if (qualifierType == null) return JavaResolveResult.EMPTY_ARRAY;
|
|
if (!(qualifierType instanceof PsiClassType)) return JavaResolveResult.EMPTY_ARRAY;
|
|
final JavaResolveResult result = PsiUtil.resolveGenericsClassInType(qualifierType);
|
|
if (result.getElement() == null) return JavaResolveResult.EMPTY_ARRAY;
|
|
final PsiElement classNameElement = getReferenceNameElement();
|
|
if (!(classNameElement instanceof PsiIdentifier)) return JavaResolveResult.EMPTY_ARRAY;
|
|
final String className = classNameElement.getText();
|
|
|
|
final ClassResolverProcessor processor = new ClassResolverProcessor(className, this);
|
|
result.getElement().processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, result.getSubstitutor()), this, this);
|
|
return processor.getResult();
|
|
}
|
|
case CLASS_NAME_KIND: {
|
|
final PsiElement classNameElement = getReferenceNameElement();
|
|
if (!(classNameElement instanceof PsiIdentifier)) return JavaResolveResult.EMPTY_ARRAY;
|
|
final String className = classNameElement.getText();
|
|
|
|
final ClassResolverProcessor processor = new ClassResolverProcessor(className, this);
|
|
PsiScopesUtil.resolveAndWalk(processor, this, null);
|
|
|
|
return processor.getResult();
|
|
}
|
|
|
|
case PACKAGE_NAME_KIND:
|
|
final String packageName = getTextSkipWhiteSpaceAndComments();
|
|
final PsiManager manager = getManager();
|
|
final PsiPackage aPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage(packageName);
|
|
if (aPackage == null || !aPackage.isValid()) {
|
|
return JavaPsiFacade.getInstance(manager.getProject()).isPartOfPackagePrefix(packageName)
|
|
? CandidateInfo.RESOLVE_RESULT_FOR_PACKAGE_PREFIX_PACKAGE
|
|
: JavaResolveResult.EMPTY_ARRAY;
|
|
}
|
|
return new JavaResolveResult[]{new CandidateInfo(aPackage, PsiSubstitutor.EMPTY)};
|
|
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND: {
|
|
final JavaResolveResult[] result = resolve(CLASS_FQ_NAME_KIND);
|
|
if (result.length == 0) {
|
|
return resolve(PACKAGE_NAME_KIND);
|
|
}
|
|
return result;
|
|
}
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
final JavaResolveResult[] classResolveResult = resolve(CLASS_NAME_KIND);
|
|
// [dsl]todo[ik]: review this change I guess ResolveInfo should be merged if both
|
|
// class and package resolve failed.
|
|
if (classResolveResult.length == 0) {
|
|
final JavaResolveResult[] packageResolveResult = resolve(PACKAGE_NAME_KIND);
|
|
if (packageResolveResult.length > 0) return packageResolveResult;
|
|
}
|
|
return classResolveResult;
|
|
default:
|
|
LOG.assertTrue(false);
|
|
}
|
|
return JavaResolveResult.EMPTY_ARRAY;
|
|
}
|
|
|
|
public final PsiElement handleElementRename(final String newElementName) throws IncorrectOperationException {
|
|
final PsiElement oldIdentifier = getReferenceNameElement();
|
|
if (oldIdentifier == null) {
|
|
throw new IncorrectOperationException();
|
|
}
|
|
final PsiElement identifier = JavaPsiFacade.getInstance(getProject()).getElementFactory().createIdentifier(newElementName);
|
|
oldIdentifier.replace(identifier);
|
|
return this;
|
|
}
|
|
|
|
|
|
public PsiElement bindToElement(@NotNull final PsiElement element) throws IncorrectOperationException {
|
|
CheckUtil.checkWritable(this);
|
|
if (isReferenceTo(element)) return this;
|
|
|
|
switch (getKind()) {
|
|
case CLASS_NAME_KIND:
|
|
case CLASS_FQ_NAME_KIND:
|
|
if (!(element instanceof PsiClass)) {
|
|
throw cannotBindError(element);
|
|
}
|
|
return bindToClass((PsiClass)element);
|
|
|
|
case PACKAGE_NAME_KIND:
|
|
if (!(element instanceof PsiPackage)) {
|
|
throw cannotBindError(element);
|
|
}
|
|
return bindToPackage((PsiPackage)element);
|
|
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
|
|
if (element instanceof PsiClass) {
|
|
return bindToClass((PsiClass)element);
|
|
}
|
|
else if (element instanceof PsiPackage) {
|
|
return bindToPackage((PsiPackage)element);
|
|
}
|
|
else {
|
|
throw cannotBindError(element);
|
|
}
|
|
case CLASS_IN_QUALIFIED_NEW_KIND:
|
|
if (element instanceof PsiClass) {
|
|
final PsiClass aClass = (PsiClass)element;
|
|
final String name = aClass.getName();
|
|
if (name == null) {
|
|
throw new IncorrectOperationException(aClass.toString());
|
|
}
|
|
final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(getProject()).getParserFacade();
|
|
final PsiJavaCodeReferenceElement ref = parserFacade.createReferenceFromText(name, getParent());
|
|
getTreeParent().replaceChildInternal(this, (TreeElement)ref.getNode());
|
|
return ref;
|
|
}
|
|
else {
|
|
throw cannotBindError(element);
|
|
}
|
|
|
|
default:
|
|
LOG.assertTrue(false);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static IncorrectOperationException cannotBindError(final PsiElement element) {
|
|
return new IncorrectOperationException("Cannot bind to "+element);
|
|
}
|
|
|
|
private PsiElement bindToClass(final PsiClass aClass) throws IncorrectOperationException {
|
|
String qName = aClass.getQualifiedName();
|
|
final JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
|
|
if (qName == null) {
|
|
qName = aClass.getName();
|
|
final PsiClass psiClass = facade.getResolveHelper().resolveReferencedClass(qName, this);
|
|
if (!getManager().areElementsEquivalent(psiClass, aClass)) {
|
|
throw cannotBindError(aClass);
|
|
}
|
|
}
|
|
else {
|
|
if (facade.findClass(qName, getResolveScope()) == null) {
|
|
return this;
|
|
}
|
|
}
|
|
|
|
final boolean preserveQualification = CodeStyleSettingsManager.getSettings(getProject()).USE_FQ_CLASS_NAMES && isFullyQualified();
|
|
final PsiManager manager = aClass.getManager();
|
|
final PsiReferenceParameterList parameterList = getParameterList();
|
|
String text = (parameterList != null ? qName + parameterList.getText() : qName);
|
|
PsiJavaCodeReferenceElement ref = facade.getParserFacade().createReferenceFromText(text, getParent());
|
|
getTreeParent().replaceChildInternal(this, (TreeElement)ref.getNode());
|
|
if (!preserveQualification /*&& (TreeUtil.findParent(ref, ElementType.DOC_COMMENT) == null)*/) {
|
|
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(aClass.getProject());
|
|
ref = (PsiJavaCodeReferenceElement)codeStyleManager.shortenClassReferences(ref, JavaCodeStyleManager.UNCOMPLETE_CODE);
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
private boolean isFullyQualified() {
|
|
switch (getKind()) {
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
if (resolve() instanceof PsiPackage) return true;
|
|
//noinspection fallthrough
|
|
case CLASS_NAME_KIND:
|
|
break;
|
|
|
|
case PACKAGE_NAME_KIND:
|
|
case CLASS_FQ_NAME_KIND:
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
|
|
return true;
|
|
|
|
default:
|
|
LOG.assertTrue(false);
|
|
return true;
|
|
}
|
|
|
|
final ASTNode qualifier = findChildByRole(ChildRole.QUALIFIER);
|
|
if (qualifier == null) return false;
|
|
|
|
LOG.assertTrue(qualifier.getElementType() == JavaElementType.JAVA_CODE_REFERENCE);
|
|
final PsiElement refElement = ((PsiJavaCodeReferenceElement)SourceTreeToPsiMap.treeElementToPsi(qualifier)).resolve();
|
|
if (refElement instanceof PsiPackage) return true;
|
|
|
|
return ((PsiJavaCodeReferenceElementImpl)SourceTreeToPsiMap.treeElementToPsi(qualifier)).isFullyQualified();
|
|
}
|
|
|
|
private PsiElement bindToPackage(final PsiPackage aPackage) throws IncorrectOperationException {
|
|
final String qName = aPackage.getQualifiedName();
|
|
if (qName.length() == 0) {
|
|
throw new IncorrectOperationException("Cannot bind to default package: "+aPackage);
|
|
}
|
|
final PsiJavaParserFacade parserFacade = JavaPsiFacade.getInstance(getProject()).getParserFacade();
|
|
final PsiJavaCodeReferenceElement ref = parserFacade.createReferenceFromText(qName, getParent());
|
|
getTreeParent().replaceChildInternal(this, (TreeElement)ref.getNode());
|
|
return ref;
|
|
}
|
|
|
|
public boolean isReferenceTo(final PsiElement element) {
|
|
switch (getKind()) {
|
|
case CLASS_NAME_KIND:
|
|
case CLASS_IN_QUALIFIED_NEW_KIND:
|
|
if (!(element instanceof PsiClass)) return false;
|
|
break;
|
|
|
|
case CLASS_FQ_NAME_KIND: {
|
|
if (!(element instanceof PsiClass)) return false;
|
|
final String qName = ((PsiClass)element).getQualifiedName();
|
|
return qName != null && qName.equals(getCanonicalText());
|
|
}
|
|
|
|
case PACKAGE_NAME_KIND: {
|
|
if (!(element instanceof PsiPackage)) return false;
|
|
final String qName = ((PsiPackage)element).getQualifiedName();
|
|
return qName.equals(getCanonicalText());
|
|
}
|
|
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
// if (lastChild.type != IDENTIFIER) return false;
|
|
if (element instanceof PsiPackage) {
|
|
final String qName = ((PsiPackage)element).getQualifiedName();
|
|
return qName.equals(getCanonicalText());
|
|
}
|
|
else if (element instanceof PsiClass) {
|
|
final PsiIdentifier nameIdentifier = ((PsiClass)element).getNameIdentifier();
|
|
if (nameIdentifier == null) return false;
|
|
PsiElement nameElement = getReferenceNameElement();
|
|
return nameElement != null && nameElement.textMatches(nameIdentifier) &&
|
|
element.getManager().areElementsEquivalent(resolve(), element);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
|
|
if (element instanceof PsiClass) {
|
|
final String qName = ((PsiClass)element).getQualifiedName();
|
|
return qName != null && qName.equals(getCanonicalText());
|
|
}
|
|
else if (element instanceof PsiPackage) {
|
|
final String qName = ((PsiPackage)element).getQualifiedName();
|
|
return qName.equals(getCanonicalText());
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
default:
|
|
LOG.assertTrue(false);
|
|
return true;
|
|
}
|
|
|
|
final ASTNode referenceNameElement = getReferenceNameNode();
|
|
if (referenceNameElement == null || referenceNameElement.getElementType() != JavaTokenType.IDENTIFIER) return false;
|
|
final String name = ((PsiClass)element).getName();
|
|
return name != null && referenceNameElement.getText().equals(name) && element.getManager().areElementsEquivalent(resolve(), element);
|
|
}
|
|
|
|
private String getTextSkipWhiteSpaceAndComments() {
|
|
String whiteSpaceAndComments = myCachedTextSkipWhiteSpaceAndComments;
|
|
if (whiteSpaceAndComments == null) {
|
|
myCachedTextSkipWhiteSpaceAndComments = whiteSpaceAndComments = SourceUtil.getTextSkipWhiteSpaceAndComments(this);
|
|
}
|
|
return whiteSpaceAndComments;
|
|
}
|
|
|
|
public String getClassNameText() {
|
|
String cachedQName = myCachedQName;
|
|
if (cachedQName == null) {
|
|
myCachedQName = cachedQName = PsiNameHelper.getQualifiedClassName(getTextSkipWhiteSpaceAndComments(), false);
|
|
}
|
|
return cachedQName;
|
|
}
|
|
|
|
public void fullyQualify(final PsiClass targetClass) {
|
|
final int kind = getKind();
|
|
if (kind != CLASS_NAME_KIND && kind != CLASS_OR_PACKAGE_NAME_KIND && kind != CLASS_IN_QUALIFIED_NEW_KIND) {
|
|
LOG.error("Wrong kind " + kind);
|
|
return;
|
|
}
|
|
JavaSourceUtil.fullyQualifyReference(this, targetClass);
|
|
}
|
|
|
|
public boolean isQualified() {
|
|
return getChildRole(getFirstChildNode()) != ChildRole.REFERENCE_NAME;
|
|
}
|
|
|
|
public PsiElement getQualifier() {
|
|
return SourceTreeToPsiMap.treeElementToPsi(findChildByRole(ChildRole.QUALIFIER));
|
|
}
|
|
|
|
public void clearCaches() {
|
|
super.clearCaches();
|
|
myCachedQName = null;
|
|
myCachedTextSkipWhiteSpaceAndComments = null;
|
|
}
|
|
|
|
@NotNull
|
|
public Object[] getVariants() {
|
|
final ElementFilter filter;
|
|
switch (getKind()) {
|
|
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
filter = new OrFilter();
|
|
((OrFilter)filter).addFilter(ElementClassFilter.CLASS);
|
|
((OrFilter)filter).addFilter(ElementClassFilter.PACKAGE_FILTER);
|
|
break;
|
|
case CLASS_NAME_KIND:
|
|
filter = ElementClassFilter.CLASS;
|
|
break;
|
|
case PACKAGE_NAME_KIND:
|
|
filter = ElementClassFilter.PACKAGE_FILTER;
|
|
break;
|
|
case CLASS_FQ_NAME_KIND:
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
|
|
filter = new OrFilter();
|
|
((OrFilter)filter).addFilter(ElementClassFilter.PACKAGE_FILTER);
|
|
if (isQualified()) {
|
|
((OrFilter)filter).addFilter(ElementClassFilter.CLASS);
|
|
}
|
|
break;
|
|
case CLASS_IN_QUALIFIED_NEW_KIND:
|
|
filter = ElementClassFilter.CLASS;
|
|
break;
|
|
default:
|
|
throw new RuntimeException("Unknown reference type");
|
|
}
|
|
|
|
return PsiImplUtil.getReferenceVariantsByFilter(this, filter);
|
|
}
|
|
|
|
public boolean isSoft() {
|
|
return false;
|
|
}
|
|
|
|
public void processVariants(final PsiScopeProcessor processor) {
|
|
final OrFilter filter = new OrFilter();
|
|
PsiElement superParent = getParent();
|
|
boolean smartCompletion = true;
|
|
if (isQualified()) {
|
|
smartCompletion = false;
|
|
}
|
|
else {
|
|
while (superParent != null) {
|
|
if (superParent instanceof PsiCodeBlock || superParent instanceof PsiLocalVariable) {
|
|
smartCompletion = false;
|
|
break;
|
|
}
|
|
superParent = superParent.getParent();
|
|
}
|
|
}
|
|
if (!smartCompletion && !isCodeFragmentType(getTreeParent().getElementType()) && !(getParent() instanceof PsiAnnotation)) {
|
|
filter.addFilter(new AndFilter(ElementClassFilter.METHOD, new NotFilter(new ConstructorFilter())));
|
|
filter.addFilter(ElementClassFilter.VARIABLE);
|
|
}
|
|
switch (getKind()) {
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
addClassFilter(filter);
|
|
filter.addFilter(ElementClassFilter.PACKAGE_FILTER);
|
|
break;
|
|
case CLASS_NAME_KIND:
|
|
addClassFilter(filter);
|
|
break;
|
|
case PACKAGE_NAME_KIND:
|
|
filter.addFilter(ElementClassFilter.PACKAGE_FILTER);
|
|
break;
|
|
case CLASS_FQ_NAME_KIND:
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
|
|
filter.addFilter(ElementClassFilter.PACKAGE_FILTER);
|
|
if (isQualified()) {
|
|
filter.addFilter(ElementClassFilter.CLASS);
|
|
}
|
|
break;
|
|
case CLASS_IN_QUALIFIED_NEW_KIND:
|
|
final PsiElement parent = getParent();
|
|
if (parent instanceof PsiNewExpression) {
|
|
final PsiNewExpression newExpr = (PsiNewExpression)parent;
|
|
final PsiType type = newExpr.getQualifier().getType();
|
|
final PsiClass aClass = PsiUtil.resolveClassInType(type);
|
|
if (aClass != null) {
|
|
aClass.processDeclarations(new FilterScopeProcessor(new AndFilter(ElementClassFilter.CLASS, new ModifierFilter(PsiModifier.STATIC, false)),
|
|
processor), ResolveState.initial(), null, this);
|
|
}
|
|
// else{
|
|
// throw new RuntimeException("Qualified new is not allowed for primitives");
|
|
// }
|
|
}
|
|
// else{
|
|
// throw new RuntimeException("Reference type is qualified new, but parent expression is: " + getParent());
|
|
// }
|
|
|
|
return;
|
|
default:
|
|
throw new RuntimeException("Unknown reference type");
|
|
}
|
|
final FilterScopeProcessor proc = new FilterScopeProcessor(filter, processor);
|
|
PsiScopesUtil.resolveAndWalk(proc, this, null, true);
|
|
}
|
|
|
|
private void addClassFilter(final OrFilter filter) {
|
|
if (getParent() instanceof PsiAnnotation) {
|
|
filter.addFilter(new AnnotationTypeFilter());
|
|
}
|
|
else {
|
|
filter.addFilter(ElementClassFilter.CLASS);
|
|
}
|
|
}
|
|
|
|
public PsiElement getReferenceNameElement() {
|
|
return SourceTreeToPsiMap.treeElementToPsi(getReferenceNameNode());
|
|
}
|
|
|
|
@Nullable
|
|
private ASTNode getReferenceNameNode() {
|
|
return findChildByRole(ChildRole.REFERENCE_NAME);
|
|
}
|
|
|
|
|
|
public PsiReferenceParameterList getParameterList() {
|
|
return (PsiReferenceParameterList)findChildByRoleAsPsiElement(ChildRole.REFERENCE_PARAMETER_LIST);
|
|
}
|
|
|
|
public String getQualifiedName() {
|
|
switch (getKind()) {
|
|
case CLASS_NAME_KIND:
|
|
case CLASS_OR_PACKAGE_NAME_KIND:
|
|
case CLASS_IN_QUALIFIED_NEW_KIND:
|
|
final PsiElement target = resolve();
|
|
if (target instanceof PsiClass) {
|
|
final PsiClass aClass = (PsiClass)target;
|
|
String name = aClass.getQualifiedName();
|
|
if (name == null) {
|
|
name = aClass.getName(); //?
|
|
}
|
|
return name;
|
|
}
|
|
else if (target instanceof PsiPackage) {
|
|
return ((PsiPackage)target).getQualifiedName();
|
|
}
|
|
else {
|
|
LOG.assertTrue(target == null);
|
|
return getClassNameText();
|
|
}
|
|
|
|
case PACKAGE_NAME_KIND:
|
|
case CLASS_FQ_NAME_KIND:
|
|
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
|
|
return getTextSkipWhiteSpaceAndComments(); // there cannot be any <...>
|
|
|
|
default:
|
|
LOG.assertTrue(false);
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
public String getReferenceName() {
|
|
final ASTNode childByRole = getReferenceNameNode();
|
|
if (childByRole == null) return null;
|
|
return childByRole.getText();
|
|
}
|
|
|
|
public final TextRange getRangeInElement() {
|
|
final TreeElement nameChild = (TreeElement)getReferenceNameNode();
|
|
if (nameChild == null) return new TextRange(0, getTextLength());
|
|
final int startOffset = nameChild.getStartOffsetInParent();
|
|
return new TextRange(startOffset, startOffset + nameChild.getTextLength());
|
|
}
|
|
|
|
@NotNull
|
|
public PsiType[] getTypeParameters() {
|
|
final PsiReferenceParameterList parameterList = getParameterList();
|
|
if (parameterList == null) return PsiType.EMPTY_ARRAY;
|
|
return parameterList.getTypeArguments();
|
|
|
|
}
|
|
|
|
public final PsiElement getElement() {
|
|
return this;
|
|
}
|
|
|
|
public final void accept(@NotNull final PsiElementVisitor visitor) {
|
|
if (visitor instanceof JavaElementVisitor) {
|
|
((JavaElementVisitor)visitor).visitReferenceElement(this);
|
|
}
|
|
else {
|
|
visitor.visitElement(this);
|
|
}
|
|
}
|
|
|
|
public final String toString() {
|
|
return "PsiJavaCodeReferenceElement:" + getText();
|
|
}
|
|
}
|