/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.intentions;
import com.intellij.psi.PsiType;
import gw.plugin.ij.lang.psi.impl.expressions.GosuTypeLiteralImpl;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiRecursiveElementVisitor;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.PsiTreeUtil;
import gw.lang.parser.IGosuParser;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.expressions.ITypeLiteralExpression;
import gw.lang.reflect.IErrorType;
import gw.lang.reflect.IMetaType;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuProgram;
import gw.plugin.ij.lang.psi.IGosuPsiElement;
import gw.plugin.ij.lang.psi.api.expressions.IGosuReferenceExpression;
import gw.plugin.ij.lang.psi.api.statements.IGosuUsesStatementList;
import gw.plugin.ij.util.ClassLord;
import static gw.plugin.ij.util.ClassLord.purgeClassName;
public class ImportClassHelper {
private final String fqn;
private final String simpleName;
private final IGosuPsiElement context;
public ResolveTypeResult resolveResult;
public FQNAnalyzeResult fqnAnalyzeResult;
public enum ResolveTypeResult {
RESOLVED, UNRESOLVED, CONFLICT
}
public ImportClassHelper(String fqn, IGosuPsiElement context) {
this.fqn = purgeClassName(fqn);
this.simpleName = ClassUtil.extractClassName(fqn);
this.context = context;
}
public ImportClassHelper(IType targetType, IGosuPsiElement context) {
this(targetType.getName(), context);
}
public boolean isResolved() {
return ResolveTypeResult.RESOLVED.equals(resolveResult);
}
public boolean useFQN() {
return resolveResult == ResolveTypeResult.CONFLICT || fqnIsPreferred();
}
public boolean fqnIsPreferred() {
return fqnAnalyzeResult.fqnHits > fqnAnalyzeResult.simpleHits;
}
public boolean needImport() {
return !useFQN() && resolveResult == ResolveTypeResult.UNRESOLVED;
}
public void resolveType() {
resolveResult = resolveTypeInParser();
}
private ResolveTypeResult resolveTypeInParser() {
IGosuClass gosuClass = getGosuClass();
if (gosuClass != null) {
IGosuParser parser = gosuClass.getParser();
if( parser != null ) {
TypeSystem.pushModule(gosuClass.getTypeLoader().getModule());
try {
IType type = parser.resolveTypeLiteral(simpleName).getType().getType();
if (type instanceof IErrorType) {
return ResolveTypeResult.UNRESOLVED;
}
if(fqn.equals(purgeClassName(type.getName()))) {
return ResolveTypeResult.RESOLVED;
} else {
return ResolveTypeResult.CONFLICT;
}
}
finally {
TypeSystem.popModule( gosuClass.getTypeLoader().getModule() );
}
}
}
return ResolveTypeResult.UNRESOLVED;
}
public void analizeFQNUsing() {
class FQNAnalyzer extends PsiRecursiveElementVisitor {
FQNAnalyzeResult result = new FQNAnalyzeResult();
public void visitElement(PsiElement element) {
if (element instanceof IGosuUsesStatementList) {
return;
}
if (element instanceof GosuTypeLiteralImpl) {
PsiType type = ((GosuTypeLiteralImpl) element).getType();
if (fqn.equals(purgeClassName(type.getCanonicalText()))) {
if (fqn.equals(purgeClassName(element.getText()))) {
++result.fqnHits;
} else {
++result.simpleHits;
}
}
return;
}
if (element instanceof IGosuPsiElement) {
IParsedElement parsedElement = ((IGosuPsiElement) element).getParsedElement();
if (parsedElement instanceof ITypeLiteralExpression) {
IType type = getType((ITypeLiteralExpression) parsedElement);
if (type != null && fqn.equals(purgeClassName(type.getName()))) {
PsiElement memberAccess = PsiTreeUtil.findChildOfType(element, IGosuReferenceExpression.class);
if (memberAccess != null) {
++result.fqnHits;
} else {
++result.simpleHits;
}
return;
}
}
}
super.visitElement(element);
}
}
FQNAnalyzer fqnAnalyzer = new FQNAnalyzer();
getFile().accept(fqnAnalyzer);
this.fqnAnalyzeResult = fqnAnalyzer.result;
}
private IType getType(ITypeLiteralExpression literalExpression) {
IMetaType metaType = literalExpression.getType();
if (metaType == null) {
return null;
}
IType type = metaType.getType();
if (type == null) {
return null;
}
return type;
}
class FQNAnalyzeResult {
int fqnHits;
int simpleHits;
}
public IGosuClass getGosuClass() {
IParsedElement pe = context.getParsedElement();
if (pe == null) {
return null;
}
IGosuClass gosuClass = pe.getGosuClass();
if (gosuClass != null) {
return gosuClass;
}
IGosuProgram gosuProgram = pe.getGosuProgram();
if (gosuProgram == null) {
return gosuProgram;
}
return null;
}
public void stickImport() {
ClassLord.stickImport(fqn, getFile());
}
private PsiFile getFile() {
return context.getContainingFile();
}
}