package com.redhat.ceylon.eclipse.code.correct;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C;
import java.util.Collection;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
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.Block;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.BooleanCondition;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.ComparisonOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Condition;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.EqualOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.EqualityOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Expression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.IfClause;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.IfExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.IfStatement;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.LargeAsOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.LargerOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.NotOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SmallAsOp;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SmallerOp;
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.Visitor;
class InvertIfElseProposal extends CorrectionProposal {
private InvertIfElseProposal(int offset, String desc,
TextChange change) {
super(desc, change, new Region(offset, 0));
}
static void addInvertIfElseProposal(IDocument doc,
Collection<ICompletionProposal> proposals,
IFile file, Statement statement, Node node ,
Tree.CompilationUnit cu) {
addInvertIfElseExpressionProposal(doc, proposals,
file, node, cu);
addInvertIfElseStatementProposal(doc, proposals,
file, statement, cu);
}
static void addInvertIfElseExpressionProposal(IDocument doc,
Collection<ICompletionProposal> proposals,
IFile file, final Node node,
Tree.CompilationUnit cu) {
try {
IfExpression ifExpr;
class FindIf extends Visitor {
IfExpression result;
@Override
public void visit(IfExpression that) {
super.visit(that);
if (that.getIfClause()!=null &&
that.getElseClause()!=null &&
that.getStartIndex()<=node.getStartIndex() &&
that.getEndIndex()>=node.getEndIndex()) {
result = that;
}
}
}
FindIf fi = new FindIf();
fi.visit(cu);
if (fi.result==null) {
return;
}
else {
ifExpr = fi.result;
}
IfClause ifClause = ifExpr.getIfClause();
Expression ifBlock = ifClause.getExpression();
Expression elseBlock =
ifExpr.getElseClause()
.getExpression();
List<Condition> conditions =
ifClause.getConditionList()
.getConditions();
if (conditions.size()!=1) {
return;
}
Condition ifCondition = conditions.get(0);
String test = null;
String term = getTerm(doc, ifCondition);
if (term.equals("(true)")) {
test = "false";
} else if (term.equals("(false)")) {
test = "true";
} else if (ifCondition instanceof BooleanCondition) {
BooleanCondition boolCond =
(BooleanCondition) ifCondition;
Term bt = boolCond.getExpression().getTerm();
if (bt instanceof NotOp) {
NotOp no = (NotOp) bt;
String t = getTerm(doc, no.getTerm());
test = removeEnclosingParenthesis(t);
} else if (bt instanceof EqualityOp) {
EqualityOp eo = (EqualityOp) bt;
test = getInvertedEqualityTest(doc, eo);
} else if (bt instanceof ComparisonOp) {
ComparisonOp co = (ComparisonOp)bt;
test = getInvertedComparisonTest(doc, co);
} else if (! (bt instanceof Tree.OperatorExpression)
|| bt instanceof Tree.UnaryOperatorExpression) {
term = removeEnclosingParenthesis(term);
}
} else {
term = removeEnclosingParenthesis(term);
}
if (test == null) {
if (term.startsWith("!")) {
test = term.substring(1);
}
else {
test = "!" + term;
}
}
String elseIndent = utilJ2C().indents().getIndent(elseBlock, doc);
String thenIndent = utilJ2C().indents().getIndent(ifBlock, doc);
// String indent = getDefaultIndent();
String delim = utilJ2C().indents().getDefaultLineDelimiter(doc);
String elseStr = getTerm(doc, elseBlock);
// elseStr = addEnclosingBraces(elseStr,
// baseIndent, indent, delim);
test = removeEnclosingParenthesis(test);
StringBuilder replace = new StringBuilder();
replace.append("if (").append(test).append(")");
if (isElseOnOwnLine(doc, ifCondition, ifBlock)) {
replace.append(delim).append(thenIndent);
} else {
replace.append(" ");
}
replace.append("then ").append(elseStr);
if (isElseOnOwnLine(doc, ifBlock, elseBlock)) {
replace.append(delim).append(elseIndent);
} else {
replace.append(" ");
}
replace.append("else ")
.append(getTerm(doc, ifBlock));
TextChange change =
new TextFileChange("Invert If Then Else",
file);
change.setEdit(new ReplaceEdit(
ifExpr.getStartIndex(),
ifExpr.getDistance(),
replace.toString()));
proposals.add(new InvertIfElseProposal(
ifExpr.getStartIndex(),
"Invert 'if' 'then' 'else' expression",
change));
} catch (Exception e) {
e.printStackTrace();
}
}
static void addInvertIfElseStatementProposal(IDocument doc,
Collection<ICompletionProposal> proposals,
IFile file, final Statement statement,
Tree.CompilationUnit cu) {
try {
IfStatement ifStmt;
if (statement instanceof IfStatement) {
ifStmt = (IfStatement) statement;
}
else {
class FindIf extends Visitor {
IfStatement result;
@Override
public void visit(IfStatement that) {
super.visit(that);
if (that.getIfClause()!=null &&
that.getElseClause()!=null &&
that.getStartIndex()<=statement.getStartIndex() &&
that.getEndIndex()>=statement.getEndIndex()) {
result = that;
}
}
}
FindIf fi = new FindIf();
fi.visit(cu);
if (fi.result==null) {
return;
}
else {
ifStmt = fi.result;
}
}
IfClause ifClause = ifStmt.getIfClause();
Block ifBlock = ifClause.getBlock();
Block elseBlock =
ifStmt.getElseClause()
.getBlock();
List<Condition> conditions =
ifClause.getConditionList()
.getConditions();
if (conditions.size()!=1) {
return;
}
Condition ifCondition = conditions.get(0);
String test = null;
String term = getTerm(doc, ifCondition);
if (term.equals("(true)")) {
test = "false";
} else if (term.equals("(false)")) {
test = "true";
} else if (ifCondition instanceof BooleanCondition) {
BooleanCondition boolCond =
(BooleanCondition) ifCondition;
Term bt = boolCond.getExpression().getTerm();
if (bt instanceof NotOp) {
NotOp no = (NotOp) bt;
String t = getTerm(doc, no.getTerm());
test = removeEnclosingParenthesis(t);
} else if (bt instanceof EqualityOp) {
EqualityOp eo = (EqualityOp) bt;
test = getInvertedEqualityTest(doc, eo);
} else if (bt instanceof ComparisonOp) {
ComparisonOp co = (ComparisonOp)bt;
test = getInvertedComparisonTest(doc, co);
} else if (! (bt instanceof Tree.OperatorExpression)
|| bt instanceof Tree.UnaryOperatorExpression) {
term = removeEnclosingParenthesis(term);
}
} else {
term = removeEnclosingParenthesis(term);
}
if (test == null) {
if (term.startsWith("!")) {
test = term.substring(1);
}
else {
test = "!" + term;
}
}
String baseIndent = utilJ2C().indents().getIndent(ifStmt, doc);
String indent = utilJ2C().indents().getDefaultIndent();
String delim = utilJ2C().indents().getDefaultLineDelimiter(doc);
String elseStr = getTerm(doc, elseBlock);
elseStr =
addEnclosingBraces(elseStr,
baseIndent, indent, delim);
test = removeEnclosingParenthesis(test);
StringBuilder replace = new StringBuilder();
replace.append("if (").append(test).append(") ")
.append(elseStr);
if (isElseOnOwnLine(doc, ifBlock, elseBlock)) {
replace.append(delim).append(baseIndent);
} else {
replace.append(" ");
}
replace.append("else ")
.append(getTerm(doc, ifBlock));
TextChange change =
new TextFileChange("Invert If Else",
file);
change.setEdit(new ReplaceEdit(
ifStmt.getStartIndex(),
ifStmt.getDistance(),
replace.toString()));
proposals.add(new InvertIfElseProposal(
ifStmt.getStartIndex(),
"Invert 'if' 'else' statement",
change));
} catch (Exception e) {
e.printStackTrace();
}
}
private static String getInvertedEqualityTest(IDocument doc,
EqualityOp equalityOp)
throws BadLocationException {
String op =
equalityOp instanceof EqualOp ?
" != " : " == ";
return getTerm(doc,
equalityOp.getLeftTerm()) + op +
getTerm(doc, equalityOp.getRightTerm());
}
private static String getInvertedComparisonTest(IDocument doc,
ComparisonOp compOp)
throws BadLocationException {
String op;
if (compOp instanceof LargerOp) {
op = " <= ";
} else if (compOp instanceof LargeAsOp) {
op = " < ";
} else if (compOp instanceof SmallerOp) {
op = " >= ";
} else if (compOp instanceof SmallAsOp) {
op = " > ";
} else {
throw new RuntimeException("Unknown Comparision op " +
compOp);
}
return getTerm(doc,
compOp.getLeftTerm()) + op +
getTerm(doc, compOp.getRightTerm());
}
private static boolean isElseOnOwnLine(IDocument doc,
Node ifBlock, Node elseBlock)
throws BadLocationException {
return doc.getLineOfOffset(ifBlock.getStopIndex()) !=
doc.getLineOfOffset(elseBlock.getStartIndex());
}
private static String addEnclosingBraces(String s,
String baseIndent, String indent, String delim) {
if (s.charAt(0) != '{') {
return "{" + delim + baseIndent +
indent + indent(s, indent, delim) +
delim + baseIndent + "}";
}
return s;
}
private static String indent(String s, String indentation,
String delim) {
return s.replaceAll(delim+"(\\s*)",
delim+"$1" + indentation);
}
private static String removeEnclosingParenthesis(String s) {
if (s.charAt(0) == '(') {
int endIndex = 0;
int startIndex = 0;
//Make sure we are not in this case ((a) == (b))
while ((endIndex = s.indexOf(')', endIndex + 1)) > 0) {
if (endIndex == s.length() -1 ) {
return s.substring(1, s.length() - 1);
}
if ((startIndex = s.indexOf('(', startIndex + 1)) > endIndex) {
return s;
}
}
}
return s;
}
private static String getTerm(IDocument doc, Node node)
throws BadLocationException {
return doc.get(node.getStartIndex(), node.getDistance());
}
}