package com.redhat.ceylon.eclipse.code.correct;
import static com.redhat.ceylon.eclipse.code.complete.CodeCompletions.getRefinementTextFor;
import static com.redhat.ceylon.eclipse.code.complete.CompletionUtil.overloads;
import static com.redhat.ceylon.eclipse.code.complete.RefinementCompletionProposal.getRefinedProducedReference;
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_CLASS;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.intersectionType;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isTypeUnknown;
import java.util.ArrayList;
import java.util.Collection;
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.CompilationUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.MemberOrTypeExpression;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.DeclarationWithProximity;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.Reference;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.Unit;
class ObjectClassDefinitionGenerator extends DefinitionGenerator {
private final String brokenName;
private final boolean isUpperCase;
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;
@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 ObjectClassDefinitionGenerator(
String brokenName,
Tree.MemberOrTypeExpression node,
Tree.CompilationUnit rootNode,
String desc,
Image image,
Type returnType,
LinkedHashMap<String, Type> paramTypes) {
this.brokenName = brokenName;
this.isUpperCase =
Character.isUpperCase(
brokenName.codePointAt(0));
this.node = node;
this.rootNode = rootNode;
this.desc = desc;
this.image = image;
this.returnType = returnType;
this.parameters = paramTypes;
}
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 isClassGenerator();
}
private String generateInternal(String indent,
String delim, boolean isFormal) {
StringBuffer def = new StringBuffer();
boolean isVoid = returnType==null;
if (isClassGenerator()) {
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(">");
}
String defIndent = utilJ2C().indents().getDefaultIndent();
String supertype = isVoid ? null :
supertypeDeclaration(returnType);
def.append("class ")
.append(brokenName)
.append(typeParamDef);
appendParameters(parameters, def,
getDefaultedSupertype());
if (supertype!=null) {
def.append(delim)
.append(indent)
.append(defIndent)
.append(defIndent)
.append(supertype);
}
def.append(typeParamConstDef);
def.append(" {").append(delim);
if (!isVoid) {
appendMembers(indent, delim, def, defIndent);
}
def.append(indent).append("}");
}
else if (isObjectGenerator()) {
String defIndent = utilJ2C().indents().getDefaultIndent();
String supertype = isVoid ? null :
supertypeDeclaration(returnType);
def.append("object ").append(brokenName);
if (supertype!=null) {
def.append(delim)
.append(indent)
.append(defIndent)
.append(defIndent)
.append(supertype);
}
def.append(" {").append(delim);
if (!isVoid) {
appendMembers(indent, delim, def, defIndent);
}
def.append(indent).append("}");
}
else {
return null;
}
return def.toString();
}
private boolean isClassGenerator(){
return isUpperCase && parameters!=null;
}
private boolean isObjectGenerator(){
return !isUpperCase && parameters==null;
}
Set<Declaration> getImports() {
Set<Declaration> imports = new HashSet<Declaration>();
importProposals().importType(imports, returnType, rootNode);
if (parameters!=null) {
importProposals().importTypes(imports, parameters.values(), rootNode);
}
if (returnType!=null) {
importMembers(imports);
}
return imports;
}
private void importMembers(Set<Declaration> imports) {
//TODO: this is a major copy/paste from appendMembers() below
TypeDeclaration td = getDefaultedSupertype();
Set<String> ambiguousNames = new HashSet<String>();
Unit unit = rootNode.getUnit();
Collection<DeclarationWithProximity> members =
td.getMatchingMemberDeclarations(unit, null,
"", 0)
.values();
for (DeclarationWithProximity dwp: members) {
Declaration dec = dwp.getDeclaration();
for (Declaration d: overloads(dec)) {
if (d.isFormal() /*&& td.isInheritedFromSupertype(d)*/) {
importProposals().importSignatureTypes(d, rootNode, imports);
ambiguousNames.add(d.getName());
}
}
}
for (TypeDeclaration superType:
td.getSupertypeDeclarations()) {
for (Declaration m: superType.getMembers()) {
if (m.isShared()) {
Declaration r =
td.getMember(m.getName(),
null, false);
if (r==null ||
!r.refines(m) &&
// !r.getContainer().equals(ut) &&
!ambiguousNames.add(m.getName())) {
importProposals().importSignatureTypes(m, rootNode, imports);
}
}
}
}
}
private void appendMembers(
String indent, String delim,
StringBuffer def, String defIndent) {
TypeDeclaration td = getDefaultedSupertype();
Set<String> ambiguousNames = new HashSet<String>();
Unit unit = rootNode.getUnit();
Collection<DeclarationWithProximity> members =
td.getMatchingMemberDeclarations(unit, null,
"", 0)
.values();
for (DeclarationWithProximity dwp: members) {
Declaration dec = dwp.getDeclaration();
if (ambiguousNames.add(dec.getName())) {
for (Declaration d: overloads(dec)) {
if (d.isFormal() /*&& td.isInheritedFromSupertype(d)*/) {
appendRefinementText(indent, delim,
def, defIndent, d);
}
}
}
}
for (TypeDeclaration superType:
td.getSupertypeDeclarations()) {
for (Declaration m: superType.getMembers()) {
if (m.isShared()) {
Declaration r =
td.getMember(m.getName(),
null, false);
if ((r==null ||
!r.refines(m)) &&
// !r.getContainer().equals(ut)) &&
ambiguousNames.add(m.getName())) {
appendRefinementText(indent, delim,
def, defIndent, m);
}
}
}
}
}
private TypeDeclaration getDefaultedSupertype() {
if (isNotBasic(returnType)) {
return returnType.getDeclaration();
}
else {
Unit unit = rootNode.getUnit();
return intersectionType(returnType,
unit.getBasicType(), unit)
.getDeclaration();
}
}
private void appendRefinementText(
String indent, String delim,
StringBuffer def, String defIndent,
Declaration d) {
Reference pr =
getRefinedProducedReference(returnType, d);
Unit unit = node.getUnit();
String text =
getRefinementTextFor(d, pr, unit, false,
null, "", false);
if (parameters!=null &&
parameters.containsKey(d.getName())) {
text = text.substring(0, text.indexOf(" =>")) + ";";
}
def.append(indent)
.append(defIndent)
.append(text)
.append(delim);
}
static ObjectClassDefinitionGenerator create(
String brokenName,
Tree.MemberOrTypeExpression node,
Tree.CompilationUnit rootNode) {
boolean isUpperCase =
Character.isUpperCase(
brokenName.charAt(0));
FindArgumentsVisitor fav =
new FindArgumentsVisitor(node);
rootNode.visit(fav);
Unit unit = node.getUnit();
Type returnType =
unit.denotableType(fav.expectedType);
StringBuilder params = new StringBuilder();
LinkedHashMap<String,Type> paramTypes = getParameters(fav);
if (returnType!=null) {
if (unit.isOptionalType(returnType)) {
returnType = returnType.eliminateNull();
}
if (returnType.isObject() ||
returnType.isAnything()) {
returnType = null;
}
}
if (!isValidSupertype(returnType)) {
return null;
}
if (paramTypes!=null && isUpperCase) {
String supertype =
supertypeDeclaration(returnType);
if (supertype==null) {
supertype = "";
}
String desc =
"class '" +
brokenName + params + supertype + "'";
return new ObjectClassDefinitionGenerator(
brokenName, node, rootNode, desc,
LOCAL_CLASS, returnType, paramTypes);
}
else if (paramTypes==null && !isUpperCase) {
String desc = "object '" + brokenName + "'";
return new ObjectClassDefinitionGenerator(
brokenName, node, rootNode, desc,
LOCAL_ATTRIBUTE, returnType, null);
}
else {
return null;
}
}
private static String supertypeDeclaration(Type returnType) {
if (isTypeUnknown(returnType)) {
return null;
}
else {
if (returnType.isClass()) {
return " extends " +
returnType.asString() + "()"; //TODO: supertype arguments!
}
else if (returnType.isInterface()) {
return " satisfies " +
returnType.asString();
}
else if (returnType.isIntersection()) {
String extendsClause = "";
StringBuilder satisfiesClause =
new StringBuilder();
for (Type st: returnType.getSatisfiedTypes()) {
if (st.isClass()) {
extendsClause =
" extends " + st.asString() + "()"; //TODO: supertype arguments!
}
else if (st.isInterface()) {
if (satisfiesClause.length()==0) {
satisfiesClause.append(" satisfies ");
}
else {
satisfiesClause.append(" & ");
}
satisfiesClause.append(st.asString());
}
}
return extendsClause + satisfiesClause;
}
else {
return null;
}
}
}
private static boolean isValidSupertype(Type returnType) {
if (isTypeUnknown(returnType)) {
return true;
}
else {
if (returnType.getCaseTypes()!=null) {
return false;
}
TypeDeclaration rtd =
returnType.getDeclaration();
if (returnType.isClass()) {
return !rtd.isFinal();
}
else if (returnType.isInterface()) {
Interface cd =
rtd.getUnit()
.getCallableDeclaration();
return !rtd.equals(cd);
}
else if (returnType.isIntersection()) {
for (Type st: returnType.getSatisfiedTypes()) {
if (!isValidSupertype(st)) {
return false;
}
}
return true;
}
else {
return false;
}
}
}
private static boolean isNotBasic(Type returnType) {
if (isTypeUnknown(returnType)) {
return false;
}
else {
TypeDeclaration rtd =
returnType.getDeclaration();
Class bd =
rtd.getUnit()
.getBasicDeclaration();
if (returnType.isClass()) {
return rtd.inherits(bd);
}
else if (returnType.isInterface()) {
return false;
}
else if (returnType.isIntersection()) {
for (Type st: returnType.getSatisfiedTypes()) {
if (st.isClass()) {
return rtd.inherits(bd);
}
}
return false;
}
else {
return false;
}
}
}
}