package com.redhat.ceylon.eclipse.code.refactor;
import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedDeclaration;
import static com.redhat.ceylon.eclipse.util.Nodes.getTokenLength;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.antlr.runtime.CommonToken;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.ui.IEditorPart;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.FunctionOrValue;
import com.redhat.ceylon.model.typechecker.model.Referenceable;
import com.redhat.ceylon.model.typechecker.model.Unit;
import com.redhat.ceylon.model.typechecker.model.Value;
public class InvertBooleanRefactoring extends AbstractRefactoring {
private Value value = null;
public InvertBooleanRefactoring(IEditorPart editor) {
super(editor);
Referenceable declaration =
getReferencedDeclaration(node);
if (declaration instanceof Value) {
value = (Value) declaration;
}
}
public Value getValue() {
return value;
}
@Override
public boolean getEnabled() {
return value != null
&& value.getTypeDeclaration() instanceof Class
&& value.getTypeDeclaration()
.equals(value.getUnit().getBooleanDeclaration());
}
@Override
public String getName() {
return "Invert Boolean";
}
@Override
public boolean isAffectingOtherFiles() {
if (value==null) {
return false;
}
if (value.isToplevel() ||
value.isShared()) {
return true;
}
if (value.isParameter()) {
FunctionOrValue fov =
(FunctionOrValue)
value;
Declaration container =
fov.getInitializerParameter()
.getDeclaration();
if (container.isToplevel() ||
container.isShared()) {
return true;
}
}
return false;
}
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
return new RefactoringStatus();
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
throws CoreException, OperationCanceledException {
return new RefactoringStatus();
}
@Override
protected void refactorInFile(TextChange tc,
CompositeChange cc, Tree.CompilationUnit root,
List<CommonToken> tokens) {
tc.setEdit(new MultiTextEdit());
InternalVisitor visitor = new InternalVisitor(value);
visitor.visit(root);
for (Tree.AttributeDeclaration attributeDeclaration :
visitor.attributeDeclarations) {
Tree.SpecifierOrInitializerExpression sie =
attributeDeclaration.getSpecifierOrInitializerExpression();
invertSpecifierExpression(sie, tc);
}
for (Tree.SpecifierStatement specifierStatement :
visitor.specifierStatements) {
Tree.SpecifierExpression se =
specifierStatement.getSpecifierExpression();
invertSpecifierExpression(se, tc);
}
for (Tree.AssignOp assignOp : visitor.assignOps) {
invertTerm(assignOp.getRightTerm(), tc);
}
for (Node node : visitor.getNodes()) {
if (visitor.notOpMap.containsKey(node)) {
Tree.NotOp notOp = visitor.notOpMap.get(node);
invertTerm(notOp, tc);
} else {
invertTerm((Tree.Term) node, tc);
}
}
if (tc.getEdit().hasChildren()) {
cc.add(tc);
}
}
private void invertSpecifierExpression(
Tree.SpecifierOrInitializerExpression specifierExpression,
TextChange textChange) {
if (specifierExpression != null) {
Tree.Expression e =
specifierExpression.getExpression();
if (e != null && e.getTerm() != null) {
invertTerm(e.getTerm(), textChange);
}
}
}
public static void invertTerm(Tree.Term term, TextChange change) {
CommonToken token = (CommonToken) term.getMainToken();
if (term instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) term;
if (bme.getDeclaration() instanceof Value) {
Value v = (Value) bme.getDeclaration();
Unit unit = term.getUnit();
Value trueDeclaration =
unit.getTrueValueDeclaration();
Value falseDeclaration =
unit.getFalseValueDeclaration();
if (v.equals(trueDeclaration)) {
change.addEdit(new ReplaceEdit(bme.getStartIndex(),
bme.getDistance(),
falseDeclaration.getName(unit)));
}
else if (v.equals(falseDeclaration)) {
change.addEdit(new ReplaceEdit(bme.getStartIndex(),
bme.getDistance(),
trueDeclaration.getName(unit)));
}
else {
change.addEdit(new InsertEdit(term.getStartIndex(), "!"));
}
}
}
else if (term instanceof Tree.NotOp) {
change.addEdit(new DeleteEdit(token.getStartIndex(),
getTokenLength(token)));
}
else if (term instanceof Tree.EqualOp) {
change.addEdit(new ReplaceEdit(token.getStartIndex(),
getTokenLength(token), "!="));
}
else if (term instanceof Tree.NotEqualOp) {
change.addEdit(new ReplaceEdit(token.getStartIndex(),
getTokenLength(token), "=="));
}
else if (term instanceof Tree.LargerOp) {
change.addEdit(new ReplaceEdit(token.getStartIndex(),
getTokenLength(token), "<="));
}
else if (term instanceof Tree.LargeAsOp) {
change.addEdit(new ReplaceEdit(token.getStartIndex(),
getTokenLength(token), "<"));
}
else if (term instanceof Tree.SmallerOp) {
change.addEdit(new ReplaceEdit(token.getStartIndex(),
getTokenLength(token), ">="));
}
else if (term instanceof Tree.SmallAsOp) {
change.addEdit(new ReplaceEdit(token.getStartIndex(),
getTokenLength(token), ">"));
}
else if (term instanceof Tree.AndOp) {
Tree.AndOp andOp = (Tree.AndOp) term;
change.addEdit(new ReplaceEdit(token.getStartIndex(),
getTokenLength(token), "||"));
invertTerm(andOp.getLeftTerm(), change);
invertTerm(andOp.getRightTerm(), change);
}
else if (term instanceof Tree.OrOp) {
Tree.OrOp orOp = (Tree.OrOp) term;
change.addEdit(new ReplaceEdit(token.getStartIndex(),
getTokenLength(token), "&&"));
//if either operand is an && then add parens
Tree.Term lt = orOp.getLeftTerm();
Tree.Term rt = orOp.getRightTerm();
if (lt instanceof Tree.AndOp) {
change.addEdit(new InsertEdit(lt.getStartIndex(), "("));
}
invertTerm(lt, change);
if (lt instanceof Tree.AndOp) {
change.addEdit(new InsertEdit(lt.getEndIndex(), ")"));
}
if (rt instanceof Tree.AndOp) {
change.addEdit(new InsertEdit(rt.getStartIndex(), "("));
}
invertTerm(rt, change);
if (rt instanceof Tree.AndOp) {
change.addEdit(new InsertEdit(rt.getEndIndex(), ")"));
}
}
else if (term instanceof Tree.Expression && term.getToken()==null) {
invertTerm(((Tree.Expression) term).getTerm(), change);
}
else if (term instanceof Tree.ThenOp
|| term instanceof Tree.DefaultOp
|| term instanceof Tree.LetExpression
|| term instanceof Tree.SwitchExpression
|| term instanceof Tree.IfExpression
|| term instanceof Tree.AssignmentOp) {
change.addEdit(new InsertEdit(term.getStartIndex(), "!("));
change.addEdit(new InsertEdit(term.getEndIndex(), ")"));
}
else {
change.addEdit(new InsertEdit(term.getStartIndex(), "!"));
}
}
private static class InternalVisitor extends FindReferencesVisitor {
Set<Tree.AttributeDeclaration> attributeDeclarations =
new HashSet<Tree.AttributeDeclaration>();
Set<Tree.SpecifierStatement> specifierStatements =
new HashSet<Tree.SpecifierStatement>();
Set<Tree.AssignOp> assignOps =
new HashSet<Tree.AssignOp>();
Map<Node, Tree.NotOp> notOpMap =
new HashMap<Node, Tree.NotOp>();
public InternalVisitor(Declaration declaration) {
super(declaration);
}
@Override
public void visit(Tree.AttributeDeclaration that) {
if (getDeclaration().equals(that.getDeclarationModel())) {
attributeDeclarations.add(that);
return;
}
super.visit(that);
}
@Override
public void visit(Tree.AssignOp that) {
if (that.getLeftTerm()
instanceof Tree.MemberOrTypeExpression) {
Tree.MemberOrTypeExpression mote =
(Tree.MemberOrTypeExpression)
that.getLeftTerm();
if (getDeclaration().equals(mote.getDeclaration())) {
assignOps.add(that);
return;
}
}
super.visit(that);
}
@Override
public void visit(Tree.SpecifierStatement that) {
if (that.getBaseMemberExpression()
instanceof Tree.MemberOrTypeExpression) {
Tree.MemberOrTypeExpression mote =
(Tree.MemberOrTypeExpression)
that.getBaseMemberExpression();
if (getDeclaration().equals(mote.getDeclaration())) {
specifierStatements.add(that);
return;
}
}
super.visit(that);
}
@Override
public void visit(Tree.NotOp that) {
super.visit(that);
if (getNodes().contains(that.getTerm())) {
notOpMap.put(that.getTerm(), that);
}
}
}
}