/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.lang.psi.impl.expressions;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import gw.lang.parser.IExpression;
import gw.lang.parser.IParsedElement;
import gw.lang.reflect.IBlockType;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeVariableType;
import gw.lang.reflect.gs.ICompilableType;
import gw.plugin.ij.codeInsight.GosuTargetElementEvaluator;
import gw.plugin.ij.lang.parser.GosuCompositeElement;
import gw.plugin.ij.lang.parser.GosuElementTypes;
import gw.plugin.ij.lang.psi.api.IGosuResolveResult;
import gw.plugin.ij.lang.psi.api.expressions.IGosuReferenceExpression;
import gw.plugin.ij.lang.psi.impl.GosuElementVisitor;
import gw.plugin.ij.lang.psi.impl.GosuPsiElementImpl;
import gw.plugin.ij.lang.psi.impl.GosuResolveResultImpl;
import gw.plugin.ij.lang.psi.impl.resolvers.PsiTypeResolver;
import gw.plugin.ij.lang.psi.util.GosuPsiParseUtil;
import gw.plugin.ij.util.ExecutionUtil;
import gw.plugin.ij.util.SafeCallable;
import gw.plugin.ij.util.JavaPsiFacadeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class GosuReferenceExpressionImpl<T extends IExpression> extends GosuPsiElementImpl<T> implements IGosuReferenceExpression {
public GosuReferenceExpressionImpl(GosuCompositeElement node) {
super(node);
}
public PsiReference getReference() {
return this;
}
public String getReferenceName() {
final PsiElement element = getReferenceNameElement();
return element != null ? element.getText() : null;
}
@Nullable
abstract public PsiElement getReferenceNameElement();
@NotNull
public PsiElement getElement() {
return this;
}
public TextRange getRangeInElement() {
final PsiElement refNameElement = getReferenceNameElement();
if (refNameElement != null) {
final int offsetInParent = refNameElement.getStartOffsetInParent();
return new TextRange(offsetInParent, offsetInParent + refNameElement.getTextLength());
}
return new TextRange(0, getTextLength());
}
@Override
public PsiType getType() {
return ExecutionUtil.execute(new SafeCallable<PsiType>(this) {
public PsiType execute() throws Exception {
IType type = getTypeReferenced();
if (type instanceof ITypeVariableType) {
PsiElement typeVariable = null;
if (type.getEnclosingType() instanceof IFunctionType) {
for (PsiElement parent = getParent(); typeVariable == null && parent != null; parent = parent.getParent()) {
if (parent instanceof PsiTypeParameterListOwner) {
typeVariable = PsiTypeResolver.findTypeVariable((ITypeVariableType) type, (PsiTypeParameterListOwner) parent);
}
}
} else {
PsiElement enclosingType = PsiTypeResolver.resolveType(type.getEnclosingType(), GosuReferenceExpressionImpl.this);
if( enclosingType != null ) {
typeVariable = PsiTypeResolver.findTypeVariable((ITypeVariableType) type, (PsiTypeParameterListOwner) enclosingType);
}
}
return typeVariable == null ? null : JavaPsiFacadeUtil.getElementFactory(getProject()).createType((PsiClass) typeVariable);
} else {
if (type instanceof ICompilableType && ((ICompilableType) type).isAnonymous()) {
IType superType = type.getSupertype();
if (superType == null) {
superType = type.getInterfaces()[0];
}
type = superType;
}
PsiType psiType = null;
if (type instanceof IBlockType && !getNode().getText().contains(":")) {
// handle case where the block type has a default void return type: block(...) vs block(...) : void
psiType = createType(type, getNode().getPsi());
} else {
psiType = createType(type, getNameIdentifierImpl());
}
return psiType;
}
}
});
}
@Nullable
protected IType getTypeReferenced() {
IParsedElement parsedElement = getParsedElementImpl();
if (parsedElement instanceof IExpression) {
return ((IExpression) parsedElement).getType();
} else {
return null;
}
}
public boolean isAssigned() {
IElementType parent = getParent().getNode().getElementType();
return parent == GosuElementTypes.ELEM_TYPE_AssignmentStatement ||
parent == GosuElementTypes.ELEM_TYPE_MemberAssignmentStatement ||
(getParent().getParent() != null && getParent().getParent().getNode().getElementType() == GosuElementTypes.ELEM_TYPE_ObjectInitializerExpression);
}
protected PsiElement handleElementRenameInner(String newElementName) throws IncorrectOperationException {
PsiElement nameElement = getReferenceNameElement();
if (nameElement != null) {
ASTNode node = nameElement.getNode();
ASTNode newNameNode = GosuPsiParseUtil.createReferenceNameFromText(this, newElementName).getNode();
CodeEditUtil.setOldIndentation((TreeElement) newNameNode, 0); // this is to avoid a stupid exception
node.getTreeParent().replaceChild(node, newNameNode);
}
return this;
}
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
return handleElementRenameInner(newElementName);
}
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
// throw new UnsupportedOperationException("Men at work");
return this;
}
@NotNull
public String getCanonicalText() {
return getRangeInElement().substring(getElement().getText());
}
public boolean isReferenceTo(final PsiElement element) {
ProgressManager.checkCanceled();
return ExecutionUtil.execute(new SafeCallable<Boolean>(this) {
public Boolean execute() throws Exception {
PsiElement resolveResult = resolve();
PsiElement referenceTo = GosuTargetElementEvaluator.correctSearchTargets(resolveResult);
if (getManager().areElementsEquivalent(element, referenceTo)) {
return true;
}
if (referenceTo instanceof PsiNameIdentifierOwner) {
final PsiElement identifier = ((PsiNameIdentifierOwner) referenceTo).getNameIdentifier();
if (identifier == null) {
return false;
}
if (getManager().areElementsEquivalent(element, identifier)) {
return true;
}
PsiElement child = identifier.getFirstChild();
while (child != null) {
if (child.getNode().getElementType() == GosuElementTypes.TT_IDENTIFIER &&
getManager().areElementsEquivalent(element, child)) {
return true;
}
child = child.getNextSibling();
}
}
return false;
}
});
}
@NotNull
public Object[] getVariants() {
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
public boolean isSoft() {
return false;
}
@NotNull
public IGosuResolveResult[] multiResolve(boolean incomplete) {
final PsiElement element = resolve();
return element != null ? new IGosuResolveResult[]{new GosuResolveResultImpl(element, !incomplete, null)} : IGosuResolveResult.EMPTY_ARRAY;
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if( visitor instanceof GosuElementVisitor) {
((GosuElementVisitor)visitor).visitReferenceExpression(this);
}
else {
visitor.visitElement( this );
}
}
}