package com.redhat.ceylon.eclipse.code.correct; import static com.redhat.ceylon.eclipse.code.correct.CorrectionUtil.collectUninitializedMembers; import static com.redhat.ceylon.eclipse.util.EditorUtil.getDocument; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C; import static com.redhat.ceylon.eclipse.util.Nodes.findDeclarationWithBody; import java.util.Collection; import java.util.Collections; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.viewers.StyledString; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.text.edits.InsertEdit; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.eclipse.util.Highlights; import com.redhat.ceylon.model.typechecker.model.Reference; import com.redhat.ceylon.model.typechecker.model.Type; import com.redhat.ceylon.model.typechecker.model.TypedDeclaration; import com.redhat.ceylon.model.typechecker.model.Unit; public class AddConstructorProposal extends CorrectionProposal { public AddConstructorProposal(String name, Change change, Region selection) { super(name, change, selection); } @Deprecated public static void addConstructorProposal(IFile file, Collection<ICompletionProposal> proposals, Node node, Tree.CompilationUnit rootNode) { if (node instanceof Tree.TypedDeclaration) { node = findDeclarationWithBody(rootNode, node); } if (node instanceof Tree.ClassDefinition) { TextFileChange change = new TextFileChange("Add Default Constructor", file); Tree.ClassDefinition cd = (Tree.ClassDefinition) node; if (cd.getParameterList()!=null) return; Tree.ClassBody body = cd.getClassBody(); if (body!=null && cd.getIdentifier()!=null) { IDocument doc = getDocument(change); List<TypedDeclaration> uninitialized = collectUninitializedMembers(body); Tree.Statement les = findLastExecutable(body); String defaultIndent = utilJ2C().indents().getDefaultIndent(); String delim = utilJ2C().indents().getDefaultLineDelimiter(doc); String indent; indent = les==null ? utilJ2C().indents().getIndent(cd, doc) + defaultIndent : utilJ2C().indents().getIndent(les, doc); Unit unit = node.getUnit(); StringBuilder params = new StringBuilder(); StringBuilder initializers = new StringBuilder(); if (!uninitialized.isEmpty()) { initializers.append(delim); } for (TypedDeclaration dec: uninitialized) { if (params.length()!=0) { params.append(", "); } Reference pr = dec.appliedReference(null, Collections.<Type>emptyList()); String type = pr.getFullType().asString(unit); String name = dec.getName(); params.append(type) .append(" ") .append(name); initializers.append(indent) .append(defaultIndent) .append("this.") .append(name) .append(" = ") .append(name) .append(";") .append(delim); } if (!uninitialized.isEmpty()) { initializers.append(indent); } String text = delim + indent + "shared new (" + params + ") {" + initializers + "}"; int start; if (les==null) { start = body.getStartIndex()+1; if (body.getEndIndex()-1==start) { text += delim + utilJ2C().indents().getIndent(cd, doc); } } else { start = les.getEndIndex(); } InsertEdit edit = new InsertEdit(start, text); change.setEdit(edit); int loc = start + text.indexOf('(') + 1; String name = cd.getDeclarationModel().getName(); proposals.add(new AddConstructorProposal( "Add constructor 'new (" + params + ")' of '" + name + "'", change, new Region(loc, 0))); } } } @Deprecated private static Tree.Statement findLastExecutable(Tree.ClassBody body) { Tree.Statement les = null; if (body!=null) { List<Tree.Statement> statements = body.getStatements(); for (Tree.Statement st: statements) { if (isExecutableStatement(st) || st instanceof Tree.Constructor) { les = st; } } } return les; } @Deprecated private static boolean isExecutableStatement(Tree.Statement s) { Unit unit = s.getUnit(); if (s instanceof Tree.SpecifierStatement) { //shortcut refinement statements with => aren't really "executable" Tree.SpecifierStatement ss = (Tree.SpecifierStatement) s; return !(ss.getSpecifierExpression() instanceof Tree.LazySpecifierExpression && !ss.getRefinement()); } else if (s instanceof Tree.ExecutableStatement) { return true; } else { if (s instanceof Tree.AttributeDeclaration) { Tree.AttributeDeclaration ad = (Tree.AttributeDeclaration) s; Tree.SpecifierOrInitializerExpression sie = ad.getSpecifierOrInitializerExpression(); return !(sie instanceof Tree.LazySpecifierExpression) && !ad.getDeclarationModel().isFormal(); } else if (s instanceof Tree.MethodDeclaration) { Tree.MethodDeclaration ad = (Tree.MethodDeclaration) s; Tree.SpecifierExpression sie = ad.getSpecifierExpression(); return !(sie instanceof Tree.LazySpecifierExpression) && !ad.getDeclarationModel().isFormal(); } else if (s instanceof Tree.ObjectDefinition) { Tree.ObjectDefinition o = (Tree.ObjectDefinition) s; if (o.getExtendedType()!=null) { Type et = o.getExtendedType().getType().getTypeModel(); if (et!=null && !et.getDeclaration().equals(unit.getObjectDeclaration()) && !et.getDeclaration().equals(unit.getBasicDeclaration())) { return true; } } Tree.ClassBody ocb = o.getClassBody(); if (ocb!=null) { List<Tree.Statement> statements = ocb.getStatements(); for (int i=statements.size()-1; i>=0; i--) { Tree.Statement st = statements.get(i); if (isExecutableStatement(st) || st instanceof Tree.Constructor) { return true; } } return false; } return false; } else { return false; } } } @Override public StyledString getStyledDisplayString() { String hint = CorrectionUtil.shortcut( "com.redhat.ceylon.eclipse.ui.action.addConstructor"); return Highlights.styleProposal(getDisplayString(), false) .append(hint, StyledString.QUALIFIER_STYLER); } }