package com.redhat.ceylon.eclipse.code.correct;
import static com.redhat.ceylon.eclipse.code.correct.CorrectionUtil.defaultValue;
import static com.redhat.ceylon.eclipse.code.correct.ImportProposals.importProposals;
import static com.redhat.ceylon.eclipse.ui.CeylonResources.LOCAL_ATTRIBUTE;
import static com.redhat.ceylon.eclipse.ui.CeylonResources.LOCAL_METHOD;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isTypeUnknown;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import org.eclipse.swt.graphics.Image;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AssignmentOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.MemberOrTypeExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SpecifierStatement;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.UnaryOperatorExpression;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.Unit;
class ValueFunctionDefinitionGenerator extends DefinitionGenerator {
private final String brokenName;
private final MemberOrTypeExpression node;
private final CompilationUnit rootNode;
private final String desc;
private final Image image;
private final Type returnType;
private final LinkedHashMap<String, Type> parameters;
private final Boolean isVariable;
@Override
String getBrokenName() {
return brokenName;
}
@Override
Type getReturnType() {
return returnType;
}
@Override
LinkedHashMap<String, Type> getParameters() {
return parameters;
}
@Override
String getDescription() {
return desc;
}
@Override
Image getImage() {
return image;
}
@Override
Tree.CompilationUnit getRootNode() {
return rootNode;
}
@Override
Node getNode() {
return node;
}
private ValueFunctionDefinitionGenerator(
String brokenName,
Tree.MemberOrTypeExpression node,
Tree.CompilationUnit rootNode,
String desc,
Image image,
Type returnType,
LinkedHashMap<String, Type> paramTypes,
Boolean isVariable) {
this.brokenName = brokenName;
this.node = node;
this.rootNode = rootNode;
this.desc = desc;
this.image = image;
this.returnType = returnType;
this.parameters = paramTypes;
this.isVariable = isVariable;
}
String generateShared(String indent, String delim) {
return "shared " +
generateInternal(indent, delim, false);
}
String generate(String indent, String delim) {
return generateInternal(indent, delim, false);
}
String generateSharedFormal(String indent, String delim) {
return "shared formal "+
generateInternal(indent, delim, true);
}
boolean isFormalSupported(){
return true;
}
private String generateInternal(
String indent, String delim, boolean isFormal) {
StringBuffer def = new StringBuffer();
boolean isVoid = returnType==null;
Unit unit = node.getUnit();
if (parameters!=null) {
List<TypeParameter> typeParams =
new ArrayList<TypeParameter>();
StringBuilder typeParamDef =
new StringBuilder();
StringBuilder typeParamConstDef =
new StringBuilder();
appendTypeParams(typeParams,
typeParamDef, typeParamConstDef,
returnType);
appendTypeParams(typeParams,
typeParamDef, typeParamConstDef,
parameters.values());
if (typeParamDef.length() > 0) {
typeParamDef.insert(0, "<");
typeParamDef.setLength(typeParamDef.length() - 1);
typeParamDef.append(">");
}
if (isVoid) {
def.append("void");
}
else {
if (isTypeUnknown(returnType)) {
def.append("function");
}
else {
def.append(returnType.asSourceCodeString(unit));
}
}
def.append(" ")
.append(brokenName).append(typeParamDef);
appendParameters(parameters, def,
unit.getAnythingDeclaration());
def.append(typeParamConstDef);
if(isFormal){
def.append(";");
}
else if (isVoid) {
def.append(" {}");
}
else {
//removed because it's ugly for parameters:
//delim + indent + defIndent + defIndent +
def.append(" => ")
.append(defaultValue(unit, returnType))
.append(";");
}
}
else {
if(isVariable){
def.append("variable ");
}
if (isVoid) {
def.append("Anything");
}
else {
if (isTypeUnknown(returnType)) {
def.append("value");
}
else {
def.append(returnType.asSourceCodeString(unit));
}
}
def.append(" ")
.append(brokenName);
if(!isFormal){
def.append(" = ")
.append(defaultValue(unit, returnType));
}
def.append(";");
}
return def.toString();
}
Set<Declaration> getImports() {
Set<Declaration> imports =
new HashSet<Declaration>();
importProposals().importType(imports, returnType, rootNode);
if (parameters!=null) {
importProposals().importTypes(imports, parameters.values(), rootNode);
}
return imports;
}
static ValueFunctionDefinitionGenerator create(
String brokenName,
Tree.MemberOrTypeExpression node,
Tree.CompilationUnit rootNode) {
boolean isUpperCase =
Character.isUpperCase(
brokenName.codePointAt(0));
if (isUpperCase) return null;
FindValueFunctionVisitor fav =
new FindValueFunctionVisitor(node);
rootNode.visit(fav);
Type et = fav.expectedType;
final boolean isVoid = et==null;
Type returnType = isVoid ? null :
node.getUnit().denotableType(et);
StringBuilder params = new StringBuilder();
LinkedHashMap<String, Type> paramTypes =
getParameters(fav);
if (paramTypes!=null) {
String desc =
"function '" + brokenName + params + "'";
return new ValueFunctionDefinitionGenerator(
brokenName, node, rootNode, desc,
LOCAL_METHOD, returnType, paramTypes,
null);
}
else {
String desc =
"value '" + brokenName + "'";
return new ValueFunctionDefinitionGenerator(
brokenName, node, rootNode, desc,
LOCAL_ATTRIBUTE, returnType, null,
fav.isVariable);
}
}
private static class FindValueFunctionVisitor
extends FindArgumentsVisitor{
boolean isVariable = false;
FindValueFunctionVisitor(MemberOrTypeExpression smte) {
super(smte);
}
@Override
public void visit(AssignmentOp that) {
Tree.AssignmentOp ao = (Tree.AssignmentOp) that;
isVariable = ao.getLeftTerm() == smte;
super.visit(that);
}
@Override
public void visit(UnaryOperatorExpression that) {
Tree.UnaryOperatorExpression uoe =
(Tree.UnaryOperatorExpression) that;
isVariable = uoe.getTerm() == smte;
super.visit(that);
}
@Override
public void visit(SpecifierStatement that) {
Tree.SpecifierStatement ss =
(Tree.SpecifierStatement) that;
isVariable = ss.getBaseMemberExpression() == smte;
super.visit(that);
}
}
}