blob: c1893da7162ece32af1ff535b30161bb284e74e1 [file] [log] [blame]
/*
* Copyright 2000-2014 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.javadoc.JavaDocUtil;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleSettingsFacade;
import com.intellij.psi.filters.*;
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.JavaResolveUtil;
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.javadoc.PsiDocComment;
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.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
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 myCachedNormalizedText;
private volatile 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;
@SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") private final int myHC = ourHC++;
public PsiJavaCodeReferenceElementImpl() {
super(JavaElementType.JAVA_CODE_REFERENCE);
}
@Override
public final int hashCode() {
return myHC;
}
@Override
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(@NotNull PsiFile containingFile) {
PsiUtilCore.ensureValid(containingFile);
CompositeElement treeParent = getTreeParent();
IElementType i = treeParent.getElementType();
if (isDummy(i)) {
return myKindWhenDummy;
}
if (i == JavaElementType.TYPE) {
return treeParent.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 == JavaDocElementType.DOC_TAG_VALUE_ELEMENT ||
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 = treeParent.findChildByRole(ChildRole.QUALIFIER);
return qualifier != null ? CLASS_IN_QUALIFIED_NEW_KIND : CLASS_NAME_KIND;
}
if (i == JavaElementType.ANONYMOUS_CLASS) {
if (treeParent.getChildRole(this) == ChildRole.BASE_CLASS_REFERENCE) {
LOG.assertTrue(treeParent.getTreeParent().getElementType() == JavaElementType.NEW_EXPRESSION);
final ASTNode qualifier = treeParent.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) {
boolean isOnDemand = SourceTreeToPsiMap.<PsiImportStatement>treeToPsiNotNull(treeParent).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) {
int parentKind = ((PsiJavaCodeReferenceElementImpl)treeParent).getKind(containingFile);
if (parentKind == CLASS_NAME_KIND) {
return CLASS_OR_PACKAGE_NAME_KIND;
}
if (parentKind == CLASS_FQ_NAME_KIND) {
return CLASS_FQ_OR_PACKAGE_NAME_KIND;
}
return parentKind;
}
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) {
PsiDocComment docComment = PsiTreeUtil.getParentOfType(this, PsiDocComment.class);
if (JavaDocUtil.isInsidePackageInfo(docComment)) {
return CLASS_FQ_OR_PACKAGE_NAME_KIND;
}
return CLASS_OR_PACKAGE_NAME_KIND;
}
if (isCodeFragmentType(i)) {
PsiJavaCodeReferenceCodeFragment fragment = (PsiJavaCodeReferenceCodeFragment)treeParent.getPsi();
return fragment.isClassesAccepted() ? CLASS_FQ_OR_PACKAGE_NAME_KIND : PACKAGE_NAME_KIND;
}
diagnoseUnknownParent();
return CLASS_NAME_KIND;
}
private void diagnoseUnknownParent() {
CompositeElement parent = getTreeParent();
IElementType i = parent.getElementType();
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);
}
private static boolean isCodeFragmentType(IElementType type) {
return type == TokenType.CODE_FRAGMENT || type instanceof ICodeFragmentElementType;
}
@Override
public void deleteChildInternal(@NotNull ASTNode child) {
if (getChildRole(child) == ChildRole.QUALIFIER) {
ASTNode dot = findChildByType(JavaTokenType.DOT, child);
assert dot != null : this;
deleteChildRange(child.getPsi(), dot.getPsi());
List<PsiAnnotation> annotations = PsiTreeUtil.getChildrenOfTypeAsList(this, PsiAnnotation.class);
setAnnotations(annotations);
return;
}
super.deleteChildInternal(child);
}
@Override
public final ASTNode findChildByRole(final int role) {
LOG.assertTrue(ChildRole.isUnique(role));
switch (role) {
case ChildRole.REFERENCE_NAME:
return TreeUtil.findChildBackward(this, JavaTokenType.IDENTIFIER);
case ChildRole.REFERENCE_PARAMETER_LIST: {
TreeElement lastChild = getLastChildNode();
return lastChild.getElementType() == JavaElementType.REFERENCE_PARAMETER_LIST ? lastChild : null;
}
case ChildRole.QUALIFIER:
return findChildByType(JavaElementType.JAVA_CODE_REFERENCE);
case ChildRole.DOT:
return findChildByType(JavaTokenType.DOT);
}
return null;
}
@Override
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;
}
if (i == JavaElementType.JAVA_CODE_REFERENCE) {
return ChildRole.QUALIFIER;
}
if (i == JavaTokenType.DOT) {
return ChildRole.DOT;
}
if (i == JavaTokenType.IDENTIFIER) {
return ChildRole.REFERENCE_NAME;
}
return ChildRoleBase.NONE;
}
@Override
@NotNull
public String getCanonicalText() {
return getCanonicalText(false, null, getContainingFile());
}
@NotNull
public String getCanonicalText(boolean annotated, @Nullable PsiAnnotation[] annotations, @NotNull PsiFile containingFile) {
switch (getKind(containingFile)) {
case CLASS_NAME_KIND:
case CLASS_OR_PACKAGE_NAME_KIND:
case CLASS_IN_QUALIFIED_NEW_KIND:
JavaResolveResult[] results = multiResolve(false, containingFile, containingFile.getProject());
final PsiElement target = results.length == 1 ? results[0].getElement() : null;
if (target instanceof PsiClass) {
PsiClass aClass = (PsiClass)target;
StringBuilder buffer = new StringBuilder();
PsiElement qualifier = getQualifier();
String prefix = null;
if (qualifier instanceof PsiJavaCodeReferenceElementImpl) {
prefix = ((PsiJavaCodeReferenceElementImpl)qualifier).getCanonicalText(annotated, null, containingFile);
}
else {
String fqn = aClass.getQualifiedName();
if (fqn != null) {
prefix = StringUtil.getPackageName(fqn);
}
}
if (!StringUtil.isEmpty(prefix)) {
buffer.append(prefix);
buffer.append('.');
}
if (annotated) {
List<PsiAnnotation> list = annotations != null ? Arrays.asList(annotations) : getAnnotations();
PsiNameHelper.appendAnnotations(buffer, list, true);
}
buffer.append(aClass.getName());
PsiNameHelper.appendTypeArgs(buffer, getTypeParameters(), true, annotated);
return buffer.toString();
}
else if (target instanceof PsiPackage) {
return ((PsiPackage)target).getQualifiedName();
}
else {
LOG.assertTrue(target == null, target);
return getNormalizedText();
}
case PACKAGE_NAME_KIND:
case CLASS_FQ_NAME_KIND:
case CLASS_FQ_OR_PACKAGE_NAME_KIND:
return getNormalizedText();
default:
LOG.assertTrue(false);
return null;
}
}
@Override
public PsiReference getReference() {
return this;
}
@Override
public final PsiElement resolve() {
return advancedResolve(false).getElement();
}
private static final class OurGenericsResolver implements ResolveCache.PolyVariantContextResolver<PsiJavaReference> {
private static final OurGenericsResolver INSTANCE = new OurGenericsResolver();
@NotNull
@Override
public ResolveResult[] resolve(@NotNull PsiJavaReference ref, @NotNull PsiFile containingFile, boolean incompleteCode) {
PsiJavaCodeReferenceElementImpl referenceElement = (PsiJavaCodeReferenceElementImpl)ref;
int kind = referenceElement.getKind(containingFile);
JavaResolveResult[] result = referenceElement.resolve(kind, containingFile);
if (incompleteCode && result.length == 0 && kind != CLASS_FQ_NAME_KIND && kind != CLASS_FQ_OR_PACKAGE_NAME_KIND) {
VariableResolverProcessor processor = new VariableResolverProcessor(referenceElement, containingFile);
PsiScopesUtil.resolveAndWalk(processor, referenceElement, null, true);
result = processor.getResult();
if (result.length == 0 && kind == CLASS_NAME_KIND) {
result = referenceElement.resolve(PACKAGE_NAME_KIND, containingFile);
}
}
JavaResolveUtil.substituteResults(referenceElement, result);
return result;
}
}
@Override
@NotNull
public JavaResolveResult advancedResolve(final boolean incompleteCode) {
final JavaResolveResult[] results = multiResolve(incompleteCode);
return results.length == 1 ? results[0] : JavaResolveResult.EMPTY;
}
@Override
@NotNull
public JavaResolveResult[] multiResolve(final boolean incompleteCode) {
FileElement fileElement = SharedImplUtil.findFileElement(this);
if (fileElement == null) {
LOG.error("fileElement == null!");
return JavaResolveResult.EMPTY_ARRAY;
}
final PsiManagerEx manager = fileElement.getManager();
if (manager == null) {
LOG.error("getManager() == null!");
return JavaResolveResult.EMPTY_ARRAY;
}
PsiFile containingFile = SharedImplUtil.getContainingFile(fileElement);
boolean valid = containingFile != null && containingFile.isValid();
if (!valid) {
PsiUtilCore.ensureValid(this);
LOG.error("invalid!");
return JavaResolveResult.EMPTY_ARRAY;
}
Project project = manager.getProject();
return multiResolve(incompleteCode, containingFile, project);
}
@NotNull
private JavaResolveResult[] multiResolve(boolean incompleteCode, @NotNull PsiFile containingFile, @NotNull Project project) {
final ResolveCache resolveCache = ResolveCache.getInstance(project);
final ResolveResult[] results = resolveCache.resolveWithCaching(this, OurGenericsResolver.INSTANCE, true, incompleteCode, containingFile);
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;
}
@NotNull
private JavaResolveResult[] resolve(final int kind, @NotNull PsiFile containingFile) {
switch (kind) {
case CLASS_FQ_NAME_KIND: {
// TODO: support type parameters in FQ names
String text = getNormalizedText();
if (StringUtil.isEmptyOrSpaces(text)) return JavaResolveResult.EMPTY_ARRAY;
PsiClass aClass = JavaPsiFacade.getInstance(containingFile.getProject()).findClass(text, getResolveScope());
if (aClass == null) return JavaResolveResult.EMPTY_ARRAY;
if (!isQualified() && text.equals(aClass.getQualifiedName())) {
if (containingFile instanceof PsiJavaFile && !((PsiJavaFile)containingFile).getPackageName().isEmpty()) {
// classes in default (unnamed) package cannot be referenced from other packages
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);
final PsiElement resultElement = result.getElement();
if (resultElement == 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, containingFile);
resultElement.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, containingFile);
PsiScopesUtil.resolveAndWalk(processor, this, null);
return processor.getResult();
}
case PACKAGE_NAME_KIND: {
String packageName = getNormalizedText();
Project project = getManager().getProject();
PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(packageName);
if (aPackage == null || !aPackage.isValid()) {
return JavaPsiFacade.getInstance(project).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:
case CLASS_OR_PACKAGE_NAME_KIND: {
int classKind = kind == CLASS_OR_PACKAGE_NAME_KIND ? CLASS_NAME_KIND : CLASS_FQ_NAME_KIND;
JavaResolveResult[] result = resolve(classKind, containingFile);
if (result.length == 1 && !result[0].isAccessible()) {
JavaResolveResult[] packageResult = resolve(PACKAGE_NAME_KIND, containingFile);
if (packageResult.length != 0) {
result = packageResult;
}
}
else if (result.length == 0) {
result = resolve(PACKAGE_NAME_KIND, containingFile);
}
return result;
}
}
LOG.error(this);
return JavaResolveResult.EMPTY_ARRAY;
}
@Override
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;
}
@Override
public PsiElement bindToElement(@NotNull final PsiElement element) throws IncorrectOperationException {
PsiFile containingFile = getContainingFile();
CheckUtil.checkWritable(containingFile);
if (isReferenceTo(element)) return this;
switch (getKind(containingFile)) {
case CLASS_NAME_KIND:
case CLASS_FQ_NAME_KIND:
if (!(element instanceof PsiClass)) {
throw cannotBindError(element);
}
return bindToClass((PsiClass)element, containingFile);
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, containingFile);
}
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(containingFile.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(PsiElement element) {
return new IncorrectOperationException("Cannot bind to " + element);
}
private PsiElement bindToClass(@NotNull PsiClass aClass, @NotNull PsiFile containingFile) throws IncorrectOperationException {
String qName = aClass.getQualifiedName();
Project project = containingFile.getProject();
boolean preserveQualification = JavaCodeStyleSettingsFacade.getInstance(project).useFQClassNames() && isFullyQualified(containingFile);
JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
if (qName == null) {
qName = aClass.getName();
PsiClass psiClass = facade.getResolveHelper().resolveReferencedClass(qName, this);
if (!getManager().areElementsEquivalent(psiClass, aClass)) {
throw cannotBindError(aClass);
}
}
else if (facade.findClass(qName, getResolveScope()) == null && !preserveQualification) {
return this;
}
List<PsiAnnotation> annotations = getAnnotations();
String text = qName;
PsiReferenceParameterList parameterList = getParameterList();
if (parameterList != null) {
text += parameterList.getText();
}
PsiJavaCodeReferenceElement ref;
try {
ref = facade.getParserFacade().createReferenceFromText(text, getParent());
}
catch (IncorrectOperationException e) {
throw new IncorrectOperationException(e.getMessage() + " [qname=" + qName + " class=" + aClass + ";" + aClass.getClass().getName() + "]");
}
((PsiJavaCodeReferenceElementImpl)ref).setAnnotations(annotations);
getTreeParent().replaceChildInternal(this, (TreeElement)ref.getNode());
if (!preserveQualification) {
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
ref = (PsiJavaCodeReferenceElement)codeStyleManager.shortenClassReferences(ref, JavaCodeStyleManager.UNCOMPLETE_CODE);
}
return ref;
}
private List<PsiAnnotation> getAnnotations() {
List<PsiAnnotation> annotations = PsiTreeUtil.getChildrenOfTypeAsList(this, PsiAnnotation.class);
if (!isQualified()) {
PsiModifierList modifierList = PsiImplUtil.findNeighbourModifierList(this);
if (modifierList != null) {
List<PsiAnnotation> typeAnnotations = PsiImplUtil.getTypeUseAnnotations(modifierList);
if (typeAnnotations != null && !typeAnnotations.isEmpty()) {
annotations.addAll(typeAnnotations);
}
}
}
return annotations;
}
private void setAnnotations(List<PsiAnnotation> annotations) {
if (annotations.isEmpty()) return;
PsiElement newParent = this;
PsiElement anchor = SourceTreeToPsiMap.treeElementToPsi(findChildByType(JavaTokenType.DOT));
if (anchor == null) {
PsiModifierList modifierList = PsiImplUtil.findNeighbourModifierList(this);
if (modifierList != null) {
newParent = modifierList;
}
}
for (PsiAnnotation annotation : annotations) {
if (annotation.getParent() != newParent) {
if (anchor != null) {
newParent.addAfter(annotation, anchor);
}
else {
newParent.add(annotation);
}
annotation.delete();
}
}
}
private boolean isFullyQualified(@NotNull PsiFile containingFile) {
switch (getKind(containingFile)) {
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 = SourceTreeToPsiMap.<PsiJavaCodeReferenceElement>treeToPsiNotNull(qualifier).resolve();
if (refElement instanceof PsiPackage) return true;
return SourceTreeToPsiMap.<PsiJavaCodeReferenceElementImpl>treeToPsiNotNull(qualifier).isFullyQualified(containingFile);
}
private PsiElement bindToPackage(@NotNull PsiPackage aPackage) throws IncorrectOperationException {
final String qName = aPackage.getQualifiedName();
if (qName.isEmpty()) {
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;
}
@Override
public boolean isReferenceTo(final PsiElement element) {
PsiFile containingFile = getContainingFile();
return isReferenceTo(element, containingFile);
}
private boolean isReferenceTo(PsiElement element, @NotNull PsiFile containingFile) {
switch (getKind(containingFile)) {
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(false, null, containingFile));
}
case PACKAGE_NAME_KIND: {
if (!(element instanceof PsiPackage)) return false;
final String qName = ((PsiPackage)element).getQualifiedName();
return qName.equals(getCanonicalText(false, null, containingFile));
}
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(false, null, containingFile));
}
if (element instanceof PsiClass) {
final PsiIdentifier nameIdentifier = ((PsiClass)element).getNameIdentifier();
if (nameIdentifier == null) return false;
PsiElement nameElement = getReferenceNameElement();
return nameElement != null && nameElement.textMatches(nameIdentifier) &&
containingFile.getManager().areElementsEquivalent(resolve(), element);
}
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(false, null, containingFile));
}
if (element instanceof PsiPackage) {
final String qName = ((PsiPackage)element).getQualifiedName();
return qName.equals(getCanonicalText(false, null, containingFile));
}
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) && containingFile.getManager().areElementsEquivalent(resolve(), element);
}
private String getNormalizedText() {
String whiteSpaceAndComments = myCachedNormalizedText;
if (whiteSpaceAndComments == null) {
myCachedNormalizedText = whiteSpaceAndComments = JavaSourceUtil.getReferenceText(this);
}
return whiteSpaceAndComments;
}
@Override
public String getClassNameText() {
String cachedQName = myCachedQName;
if (cachedQName == null) {
myCachedQName = cachedQName = PsiNameHelper.getQualifiedClassName(getNormalizedText(), false);
}
return cachedQName;
}
@Override
public void fullyQualify(@NotNull final PsiClass targetClass) {
final int kind = getKind(getContainingFile());
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);
}
@Override
public boolean isQualified() {
return getQualifier() != null;
}
@Override
public PsiElement getQualifier() {
return SourceTreeToPsiMap.treeElementToPsi(findChildByRole(ChildRole.QUALIFIER));
}
@Override
public void clearCaches() {
super.clearCaches();
myCachedQName = null;
myCachedNormalizedText = null;
}
@Override
@NotNull
public Object[] getVariants() {
final ElementFilter filter;
switch (getKind(getContainingFile())) {
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);
}
@Override
public boolean isSoft() {
return false;
}
@Override
public void processVariants(@NotNull final PsiScopeProcessor processor) {
final OrFilter filter = new OrFilter();
if (isInCode() && !(getParent() instanceof PsiImportStatement) && !(getParent() instanceof PsiReferenceList)) {
filter.addFilter(new AndFilter(ElementClassFilter.METHOD, new NotFilter(new ConstructorFilter())));
filter.addFilter(ElementClassFilter.VARIABLE);
}
switch (getKind(getContainingFile())) {
case CLASS_OR_PACKAGE_NAME_KIND:
filter.addFilter(ElementClassFilter.CLASS);
filter.addFilter(ElementClassFilter.PACKAGE_FILTER);
break;
case CLASS_NAME_KIND:
filter.addFilter(ElementClassFilter.CLASS);
if (isQualified()) {
filter.addFilter(ElementClassFilter.PACKAGE_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) {
PsiExpression qualifier = ((PsiNewExpression)parent).getQualifier();
assert qualifier != null : parent;
PsiType type = qualifier.getType();
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);
}
}
return;
default:
throw new RuntimeException("Unknown reference type");
}
final FilterScopeProcessor proc = new FilterScopeProcessor(filter, processor);
PsiScopesUtil.resolveAndWalk(proc, this, null, true);
}
private boolean isInCode() {
if (isCodeFragmentType(getTreeParent().getElementType()) || getParent() instanceof PsiAnnotation) {
return false;
}
PsiElement superParent = getParent();
while (superParent != null) {
if (superParent instanceof PsiCodeBlock || superParent instanceof PsiLocalVariable) {
return true;
}
if (superParent instanceof PsiClass || superParent instanceof PsiCatchSection) {
return false;
}
superParent = superParent.getParent();
}
return false;
}
@Override
public PsiElement getReferenceNameElement() {
return SourceTreeToPsiMap.treeElementToPsi(getReferenceNameNode());
}
@Nullable
private ASTNode getReferenceNameNode() {
return findChildByRole(ChildRole.REFERENCE_NAME);
}
@Override
public PsiReferenceParameterList getParameterList() {
return (PsiReferenceParameterList)findChildByRoleAsPsiElement(ChildRole.REFERENCE_PARAMETER_LIST);
}
@Override
public String getQualifiedName() {
switch (getKind(getContainingFile())) {
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 getNormalizedText(); // there cannot be any <...>
default:
LOG.assertTrue(false);
return null;
}
}
@Override
public String getReferenceName() {
final ASTNode childByRole = getReferenceNameNode();
if (childByRole == null) return null;
return childByRole.getText();
}
@Override
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());
}
@Override
@NotNull
public PsiType[] getTypeParameters() {
final PsiReferenceParameterList parameterList = getParameterList();
if (parameterList == null) return PsiType.EMPTY_ARRAY;
return parameterList.getTypeArguments();
}
@Override
public final PsiElement getElement() {
return this;
}
@Override
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();
}
}