/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.intentions;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateClassFromNewFix;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils;
import com.intellij.codeInsight.template.Template;
import com.intellij.codeInsight.template.TemplateBuilderImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiExpressionList;
import com.intellij.psi.PsiExpressionStatement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiNewExpression;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.PsiType;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import gw.lang.parser.expressions.IMethodCallExpression;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IType;
import gw.plugin.ij.lang.psi.impl.GosuBaseElementImpl;
import gw.plugin.ij.lang.psi.impl.expressions.GosuExpressionListImpl;
import gw.plugin.ij.lang.psi.impl.expressions.GosuMethodCallExpressionImpl;
import gw.plugin.ij.lang.psi.impl.expressions.GosuNewExpressionImpl;
import gw.plugin.ij.lang.psi.impl.expressions.GosuTypeLiteralImpl;
import gw.plugin.ij.lang.psi.impl.statements.typedef.members.GosuMethodBaseImpl;
import gw.plugin.ij.lang.psi.util.GosuPsiParseUtil;
import gw.plugin.ij.util.GosuModuleUtil;
import gw.plugin.ij.util.JavaPsiFacadeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author mike
*/
public class CreateGosuClassFromNewFix extends CreateClassFromNewFix implements ITestableCreateClassFix {
public CreateGosuClassFromNewFix(PsiNewExpression newExpression) {
super(newExpression);
}
protected void invokeImpl(PsiClass targetClass) {
assert ApplicationManager.getApplication().isWriteAccessAllowed();
final PsiNewExpression newExpression = getNewExpression();
final PsiJavaCodeReferenceElement referenceElement = getReferenceElement(newExpression);
final Runnable runnable = new Runnable() {
public void run() {
final GosuTypeLiteralImpl typeLiteral = (GosuTypeLiteralImpl) referenceElement.getReferenceNameElement().getParent();
final PsiClass psiClass = GosuCreateFromUsageUtils.createClass(typeLiteral, GosuCreateClassKind.GOSU_CLASS, getSuperClass(typeLiteral));
new WriteCommandAction(newExpression.getProject()) {
@Override
protected void run(Result result) throws Throwable {
typeLiteral.bindToElement(psiClass);
setupClassFromNewExpression(psiClass, newExpression);
}
}.execute();
}
};
if (ApplicationManager.getApplication().isUnitTestMode()) {
runnable.run();
} else {
ApplicationManager.getApplication().invokeLater(runnable);
}
}
public void invokeForTest(@NotNull String packageName) {
assert ApplicationManager.getApplication().isWriteAccessAllowed();
final PsiNewExpression newExpression = getNewExpression();
final PsiPackage pkg = JavaPsiFacadeUtil.findPackage(newExpression.getProject(), packageName);
final PsiDirectory dir = pkg.getDirectories()[0];
final PsiJavaCodeReferenceElement referenceElement = getReferenceElement(newExpression);
final GosuTypeLiteralImpl typeLiteral = (GosuTypeLiteralImpl) referenceElement.getReferenceNameElement().getParent();
final PsiClass psiClass = GosuCreateFromUsageUtils.createClass(GosuCreateClassKind.GOSU_CLASS, dir, referenceElement.getReferenceName(), referenceElement.getManager(), referenceElement, referenceElement.getContainingFile(), getSuperClass(typeLiteral));
new WriteCommandAction(newExpression.getProject()) {
@Override
protected void run(Result result) throws Throwable {
typeLiteral.bindToElement(psiClass);
setupClassFromNewExpression(psiClass, newExpression);
}
}.execute();
}
protected void setupClassFromNewExpression(final PsiClass psiClass, @NotNull final PsiNewExpression newExpression) {
assert ApplicationManager.getApplication().isWriteAccessAllowed();
// final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(newExpression.getProject()).getElementFactory();
PsiClass aClass = psiClass;
if (aClass == null) return;
final PsiJavaCodeReferenceElement classReference = newExpression.getClassReference();
if (classReference != null) {
classReference.bindToElement(aClass);
}
// setupInheritance(newExpression, aClass);
PsiExpressionList argList = newExpression.getArgumentList();
Project project = aClass.getProject();
if (argList != null && argList.getExpressions().length > 0) {
GosuMethodBaseImpl constructor = (GosuMethodBaseImpl) GosuPsiParseUtil.parseDeclaration(
"construct() {\n}", aClass.getManager(), GosuModuleUtil.findModuleForPsiElement(aClass));
CodeEditUtil.setOldIndentation(constructor.getNode(), 0); // this is to avoid a stupid exception
constructor = (GosuMethodBaseImpl) aClass.add(constructor);
TemplateBuilderImpl templateBuilder = new TemplateBuilderImpl(aClass);
GosuCreateFromUsageUtils.setupMethodParameters(constructor, templateBuilder, argList, getTargetSubstitutor(newExpression));
// setupSuperCall(aClass, constructor, templateBuilder);
// getReferenceElement(newExpression).bindToElement(aClass);
aClass = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(aClass);
Template template = templateBuilder.buildTemplate();
template.setToReformat(true);
PsiDocumentManager.getInstance(project).commitAllDocuments();
Editor editor = positionCursor(project, aClass.getContainingFile(), aClass);
TextRange textRange = aClass.getTextRange();
editor.getDocument().deleteString(textRange.getStartOffset(), textRange.getEndOffset());
startTemplate(editor, template, project);
} else {
positionCursor(project, aClass.getContainingFile(), aClass);
}
}
@Nullable
public static PsiMethod setupSuperCall(@NotNull PsiClass targetClass, @NotNull PsiMethod constructor, @NotNull TemplateBuilderImpl templateBuilder)
throws IncorrectOperationException {
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(targetClass.getProject()).getElementFactory();
PsiMethod supConstructor = null;
PsiClass superClass = targetClass.getSuperClass();
if (superClass != null && !"java.lang.Object".equals(superClass.getQualifiedName()) &&
!"java.lang.Enum".equals(superClass.getQualifiedName())) {
PsiMethod[] constructors = superClass.getConstructors();
boolean hasDefaultConstructor = false;
for (PsiMethod superConstructor : constructors) {
if (superConstructor.getParameterList().getParametersCount() == 0) {
hasDefaultConstructor = true;
supConstructor = null;
break;
} else {
supConstructor = superConstructor;
}
}
if (!hasDefaultConstructor) {
PsiExpressionStatement statement =
(PsiExpressionStatement) elementFactory.createStatementFromText("super();", constructor);
statement = (PsiExpressionStatement) constructor.getBody().add(statement);
PsiMethodCallExpression call = (PsiMethodCallExpression) statement.getExpression();
PsiExpressionList argumentList = call.getArgumentList();
templateBuilder.setEndVariableAfter(argumentList.getFirstChild());
}
}
templateBuilder.setEndVariableAfter(constructor.getBody().getLBrace());
return supConstructor;
}
@Nullable
private static PsiFile getTargetFile(PsiElement element) {
PsiJavaCodeReferenceElement referenceElement = getReferenceElement((PsiNewExpression) element);
PsiElement q = referenceElement.getQualifier();
if (q instanceof PsiJavaCodeReferenceElement) {
PsiJavaCodeReferenceElement qualifier = (PsiJavaCodeReferenceElement) q;
PsiElement psiElement = qualifier.resolve();
if (psiElement instanceof PsiClass) {
PsiClass psiClass = (PsiClass) psiElement;
return psiClass.getContainingFile();
}
}
return null;
}
protected PsiElement getElement() {
final PsiNewExpression expression = getNewExpression();
if (expression == null || !expression.getManager().isInProject(expression)) return null;
PsiJavaCodeReferenceElement referenceElement = getReferenceElement(expression);
if (referenceElement == null) return null;
if (referenceElement.getReferenceNameElement() instanceof PsiIdentifier) return expression;
return null;
}
protected boolean isAllowOuterTargetClass() {
return false;
}
protected boolean isValidElement(PsiElement element) {
PsiJavaCodeReferenceElement ref = PsiTreeUtil.getChildOfType(element, PsiJavaCodeReferenceElement.class);
return ref != null && ref.resolve() != null;
}
protected boolean isAvailableImpl(int offset) {
PsiElement nameElement = getNameElement(getNewExpression());
PsiFile targetFile = getTargetFile(getNewExpression());
if (targetFile != null && !targetFile.getManager().isInProject(targetFile)) {
return false;
}
if (CreateFromUsageUtils.shouldShowTag(offset, nameElement, getNewExpression())) {
String varName = nameElement.getText();
setText(getText(varName));
return true;
}
return false;
}
@NotNull
protected String getText(final String varName) {
return "Create Gosu class '" + varName + "'";
// return QuickFixBundle.message("create.class.from.new.text", varName);
}
@Nullable
protected static PsiJavaCodeReferenceElement getReferenceElement(@NotNull PsiNewExpression expression) {
return expression.getClassOrAnonymousClassReference();
}
@Nullable
private static PsiElement getNameElement(@NotNull PsiNewExpression targetElement) {
PsiJavaCodeReferenceElement referenceElement = getReferenceElement(targetElement);
if (referenceElement == null) return null;
return referenceElement.getReferenceNameElement();
}
@NotNull
public String getFamilyName() {
return QuickFixBundle.message("create.class.from.new.family");
}
@Nullable
public static PsiType getSuperClass(@NotNull final GosuTypeLiteralImpl element) {
PsiElement parent1 = element.getParent();
PsiElement parent2 = parent1.getParent();
PsiElement parent3 = parent2.getParent();
if (parent1 instanceof GosuNewExpressionImpl &&
parent2 instanceof GosuExpressionListImpl &&
parent3 instanceof GosuMethodCallExpressionImpl) {
int i = findChild(parent1, parent2);
IMethodCallExpression parsedElement = ((GosuMethodCallExpressionImpl) parent3).getParsedElement();
IFunctionType functionType = parsedElement.getFunctionType();
if (functionType == null) {
return null;
}
IType paramType = functionType.getParameterTypes()[i];
return GosuBaseElementImpl.createType(paramType, element);
}
return null;
}
private static int findChild(PsiElement child, @NotNull PsiElement parent) {
PsiElement[] children = parent.getChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] == child) {
return i;
}
}
return -1;
}
}