/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.intentions;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import gw.internal.gosu.parser.Expression;
import gw.internal.gosu.parser.expressions.BeanMethodCallExpression;
import gw.internal.gosu.parser.expressions.MethodCallExpression;
import gw.lang.parser.IExpression;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IParsedElement;
import gw.lang.reflect.*;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IExecutionEnvironment;
import gw.lang.reflect.module.IModule;
import gw.plugin.ij.util.GosuModuleUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class HandleVarArgFix extends LocalQuickFixAndIntentionActionOnPsiElement {
private final IParsedElement parsedElement;
public HandleVarArgFix(@Nullable PsiElement element, IParsedElement pe) {
super(element);
this.parsedElement = pe;
}
@Override
public void invoke(@NotNull Project project,
@NotNull PsiFile file,
@Nullable("is null when called from inspection") Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
if(editor != null && !CodeInsightUtilBase.prepareFileForWrite(startElement.getContainingFile()) ) {
return;
}
IParseTree location = null;
String functionName = null;
Expression[] args = null;
int argPosition = -1;
IFunctionType functionType = null;
IGosuClass gosuClass = null;
VirtualFile vfile = file.getVirtualFile();
if (parsedElement instanceof MethodCallExpression) {
MethodCallExpression methCallEx = (MethodCallExpression) parsedElement;
argPosition = methCallEx.getArgPosition();
args = methCallEx.getArgs();
location = methCallEx.getParent().getLocation();
functionName = methCallEx.getFunctionSymbol().getDisplayName();
functionType = methCallEx.getFunctionType();
gosuClass = methCallEx.getGosuClass();
} else if (parsedElement instanceof BeanMethodCallExpression) {
BeanMethodCallExpression methCallEx = (BeanMethodCallExpression) parsedElement;
argPosition = methCallEx.getArgPosition();
args = methCallEx.getArgs();
location = methCallEx.getLocation();
functionName = methCallEx.getMemberName();
functionType = methCallEx.getFunctionType();
gosuClass = methCallEx.getGosuClass();
}
if (functionName != null && vfile != null) {
IModule module = GosuModuleUtil.findModuleForFile(vfile, project);
if (module == null) {
return;
}
IExecutionEnvironment executionEnvironment = module.getExecutionEnvironment();
TypeSystem.pushModule(module);
try {
IMethodInfo methodInfo = functionType.getMethodInfo();
ITypeInfo typeInfo = methodInfo.getOwnersType().getTypeInfo();
MethodList methods = ((IRelativeTypeInfo) typeInfo).getMethods(gosuClass);
for (IMethodInfo m : methods) {
if (m.getDisplayName().equals(functionName)) {
if (handleVarArg(editor, location, m.getParameters(), args, argPosition)) {
break;
}
}
}
} finally {
TypeSystem.popModule(module);
}
}
}
private boolean handleVarArg(Editor editor, IParseTree location, IParameterInfo[] parameterInfos, Expression[] args, int argPosition) {
boolean handled = false;
int i = findStartingVarArg(parameterInfos, args);
int end = location.getLength() + location.getOffset();
if (i != -1) {
String lpar = "{";
String rpar = "}";
if (i == 0) {
i = argPosition;
}
else if (i == args.length) {
lpar = ", {";
i = end - 1;
} else {
i = args[i].getLocation().getOffset();
}
if (editor != null) {
Document document = editor.getDocument();
document.insertString(i, lpar);
document.insertString(end + lpar.length() - 1, rpar);
handled = true;
}
}
return handled;
}
private int findStartingVarArg(IParameterInfo[] parameterInfos, IExpression[] args) {
int last = parameterInfos.length - 1;
if (!parameterInfos[last].getFeatureType().isArray()) {
return -1;
}
if(args == null) {
return 0;
}
int i = 0;
while (i < last && i < args.length) {
if (!parameterInfos[i].getFeatureType().isAssignableFrom(args[i].getType())) {
return -1;
}
i++;
}
int start = i;
IType varArgType = parameterInfos[last].getFeatureType().getComponentType();
while (i < args.length) {
IType type = args[i].getType();
type = type.isPrimitive() && varArgType.equals(JavaTypes.OBJECT()) ? JavaTypes.OBJECT() : type;
if (!varArgType.isAssignableFrom(type)) {
return -1;
}
i++;
}
return start;
}
@NotNull
@Override
public String getText() {
return "Add array literal";
}
@NotNull
@Override
public String getFamilyName() {
return getText();
}
}