package com.redhat.ceylon.eclipse.code.correct;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.text.edits.ReplaceEdit;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AssignOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Block;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Condition;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Expression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.IfStatement;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SpecifierStatement;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Statement;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Term;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.ThenOp;
import com.redhat.ceylon.eclipse.util.Nodes;
class ConvertIfElseToThenElse extends CorrectionProposal {
ConvertIfElseToThenElse(int offset, TextChange change, String desc) {
super(desc, change, new Region(offset, 0));
}
static void addConvertToThenElseProposal(CompilationUnit cu, IDocument doc,
Collection<ICompletionProposal> proposals, IFile file,
Statement statement) {
TextChange change = createTextChange(cu, doc, statement, file);
if (change != null) {
String desc = change.getName()
.replace("If", "'if'")
.replace("Then", "'then'")
.replace("Else", "'else'") +
" expression";
proposals.add(new ConvertIfElseToThenElse(change.getEdit().getOffset(), change, desc));
}
}
static TextChange createTextChange(CompilationUnit cu,
IDocument doc, Statement statement, IFile file) {
if (! (statement instanceof Tree.IfStatement)) {
return null;
}
IfStatement ifStmt = (IfStatement) statement;
if (ifStmt.getElseClause() == null) {
return null;
}
Block ifBlock = ifStmt.getIfClause().getBlock();
if (ifBlock.getStatements().size() != 1) {
return null;
}
Block elseBlock = ifStmt.getElseClause().getBlock();
if (elseBlock.getStatements().size() != 1) {
return null;
}
Node ifBlockNode = ifBlock.getStatements().get(0);
Node elseBlockNode = elseBlock.getStatements().get(0);
List<Condition> conditions = ifStmt.getIfClause()
.getConditionList().getConditions();
if (conditions.size()!=1) {
return null;
}
Condition condition = conditions.get(0);
Integer replaceFrom = statement.getStartIndex();
String test = removeEnclosingParenthesis(getTerm(doc, condition));
String thenStr = null;
String elseStr = null;
String attributeIdentifier = null;
String operator = null;
String action;
if (ifBlockNode instanceof Tree.Return) {
Tree.Return ifReturn = (Tree.Return) ifBlockNode;
if (! (elseBlockNode instanceof Tree.Return)) {
return null;
}
Tree.Return elseReturn = (Tree.Return) elseBlockNode;
action = "return ";
thenStr = getOperands(doc, ifReturn.getExpression());
elseStr = getOperands(doc, elseReturn.getExpression());
} else if (ifBlockNode instanceof Tree.SpecifierStatement) {
SpecifierStatement ifSpecifierStmt = (Tree.SpecifierStatement) ifBlockNode;
attributeIdentifier = getTerm(doc, ifSpecifierStmt.getBaseMemberExpression());
operator = " = ";
action = attributeIdentifier + operator;
if (!(elseBlockNode instanceof Tree.SpecifierStatement)) {
return null;
}
String elseId = getTerm(doc, ((Tree.SpecifierStatement)elseBlockNode).getBaseMemberExpression());
if (!attributeIdentifier.equals(elseId)) {
return null;
}
thenStr = getOperands(doc, ifSpecifierStmt.getSpecifierExpression().getExpression().getTerm());
elseStr = getOperands(doc, ((Tree.SpecifierStatement) elseBlockNode).getSpecifierExpression().getExpression().getTerm());
} /*else if (ifBlockNode instanceof Tree.ExpressionStatement) {
if (!(elseBlockNode instanceof Tree.ExpressionStatement)) {
return null;
}
Term ifOperator = ((Tree.ExpressionStatement) ifBlockNode).getExpression().getTerm();
if (!(ifOperator instanceof AssignOp)) {
return null;
}
Term elseOperator = ((Tree.ExpressionStatement) elseBlockNode).getExpression().getTerm();
if (!(elseOperator instanceof AssignOp)) {
return null;
}
AssignOp ifAssign = (AssignOp) ifOperator;
AssignOp elseAssign = (AssignOp) elseOperator;
attributeIdentifier = getTerm(doc, ifAssign.getLeftTerm());
String elseId = getTerm(doc, elseAssign.getLeftTerm());
if (!attributeIdentifier.equals(elseId)) {
return null;
}
operator = " = ";
action = attributeIdentifier + operator;
thenStr = getOperands(doc, ifAssign.getRightTerm());
elseStr = getOperands(doc, elseAssign.getRightTerm());
}*/ else {
return null;
}
if (attributeIdentifier != null) {
Statement prevStatement = findPreviousStatement(cu, doc, statement);
if (prevStatement != null) {
if (prevStatement instanceof Tree.AttributeDeclaration) {
Tree.AttributeDeclaration attrDecl =
(Tree.AttributeDeclaration) prevStatement;
if (attributeIdentifier.equals(getTerm(doc, attrDecl.getIdentifier()))) {
action = removeSemiColon(getTerm(doc, attrDecl)) + operator;
replaceFrom = attrDecl.getStartIndex();
}
}
}
}
boolean abbreviateToElse = false;
if (condition instanceof Tree.ExistsCondition) {
Tree.ExistsCondition existsCond =
(Tree.ExistsCondition) condition;
Tree.Statement st = existsCond.getVariable();
if (st instanceof Tree.Variable) {
Tree.Variable variable = (Tree.Variable) st;
if (thenStr.equals(getTerm(doc, variable.getIdentifier()))) {
Expression existsExpr = variable.getSpecifierExpression().getExpression();
test = getTerm(doc, existsExpr);
abbreviateToElse = true;
}
}
}
boolean abbreviateToThen =
condition instanceof Tree.BooleanCondition &&
elseStr.equals("null");
StringBuilder replace = new StringBuilder();
replace.append(action);
if (!abbreviateToThen && !abbreviateToElse) replace.append("if (");
replace.append(test);
if (!abbreviateToThen && !abbreviateToElse) replace.append(")");
if (!abbreviateToElse) {
replace.append(" then ").append(thenStr);
}
if (!abbreviateToThen) {
replace.append(" else ").append(elseStr);
}
replace.append(";");
String desc;
if (abbreviateToThen) {
desc = "Convert to Then";
}
else if (abbreviateToElse) {
desc = "Convert to Else";
}
else {
desc = "Convert to If Then Else";
}
TextChange change = new TextFileChange(desc, file);
// TextChange change = new DocumentChange("Convert to then-else", doc);
change.setEdit(new ReplaceEdit(replaceFrom,
statement.getEndIndex() - replaceFrom,
replace.toString()));
return change;
}
private static String getOperands(IDocument doc, Term operand) {
String term = getTerm(doc, operand);
if (hasLowerPrecedenceThenElse(operand)) {
return "(" + term + ")";
}
return term;
}
private static boolean hasLowerPrecedenceThenElse(Term operand) {
Term node;
if (operand instanceof Tree.Expression) {
Tree.Expression exp = (Tree.Expression) operand;
node = exp.getTerm();
} else {
node = operand;
}
return node instanceof Tree.DefaultOp ||
node instanceof ThenOp ||
node instanceof AssignOp;
}
private static String removeSemiColon(String term) {
if (term.endsWith(";")) {
return term.substring(0, term.length() - 1);
}
return term;
}
private static Statement findPreviousStatement(CompilationUnit cu, IDocument doc,
Statement statement) {
try {
int previousLineNo = doc.getLineOfOffset(statement.getStartIndex());
while (previousLineNo > 1) {
previousLineNo--;
IRegion lineInfo = doc.getLineInformation(previousLineNo);
String prevLine = doc.get(lineInfo.getOffset(), lineInfo.getLength());
Matcher m = Pattern.compile("(\\s*)\\w+").matcher(prevLine);
if (m.find()) {
int whitespaceLen = m.group(1).length();
Node node = Nodes.findNode(cu, null, lineInfo.getOffset() + whitespaceLen,
lineInfo.getOffset() + whitespaceLen + 1);
return Nodes.findStatement(cu, node);
}
}
} catch (BadLocationException e) {
e.printStackTrace();
}
return null;
}
private static String removeEnclosingParenthesis(String s) {
if (s.charAt(0) == '(' && s.charAt(s.length() - 1) == ')') {
return s.substring(1, s.length() - 1);
}
return s;
}
private static String getTerm(IDocument doc, Node node) {
try {
return doc.get(node.getStartIndex(), node.getDistance());
} catch (BadLocationException e) {
throw new RuntimeException(e);
}
}
}