org.autorefactor.refactoring.ASTSemanticMatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.autorefactor.refactoring.ASTSemanticMatcher.java

Source

/*
 * AutoRefactor - Eclipse plugin to automatically refactor Java code bases.
 *
 * Copyright (C) 2018 Fabrice Tiercelin - initial API and implementation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program under LICENSE-GNUGPL.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution under LICENSE-ECLIPSE, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.autorefactor.refactoring;

import static org.autorefactor.refactoring.ASTHelper.hasType;
import static org.autorefactor.refactoring.ASTHelper.haveSameType;
import static org.autorefactor.refactoring.ASTHelper.isPassive;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.jdt.core.dom.ASTMatcher;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.ContinueStatement;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EmptyStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;

/** Matches two pieces of code on semantic (not on syntax). */
public class ASTSemanticMatcher extends ASTMatcher {
    private static final Map<PrefixExpression.Operator, Operator> PREFIX_TO_INFIX_OPERATOR = new HashMap<PrefixExpression.Operator, Operator>() {
        private static final long serialVersionUID = -8949107654517355855L;

        {
            put(PrefixExpression.Operator.INCREMENT, Operator.PLUS);
            put(PrefixExpression.Operator.DECREMENT, Operator.MINUS);
        }
    };

    private static final Map<PrefixExpression.Operator, Assignment.Operator> PREFIX_TO_ASSIGN_OPERATOR = new HashMap<PrefixExpression.Operator, Assignment.Operator>() {
        private static final long serialVersionUID = -8949107654517355856L;

        {
            put(PrefixExpression.Operator.INCREMENT, Assignment.Operator.PLUS_ASSIGN);
            put(PrefixExpression.Operator.DECREMENT, Assignment.Operator.MINUS_ASSIGN);
        }
    };

    private static final Map<PostfixExpression.Operator, Operator> POSTFIX_TO_INFIX_OPERATOR = new HashMap<PostfixExpression.Operator, Operator>() {
        private static final long serialVersionUID = -8949107654517355857L;

        {
            put(PostfixExpression.Operator.INCREMENT, Operator.PLUS);
            put(PostfixExpression.Operator.DECREMENT, Operator.MINUS);
        }
    };

    private static final Map<PostfixExpression.Operator, Assignment.Operator> POSTFIX_TO_ASSIGN_OPERATOR = new HashMap<PostfixExpression.Operator, Assignment.Operator>() {
        private static final long serialVersionUID = -8949107654517355858L;

        {
            put(PostfixExpression.Operator.INCREMENT, Assignment.Operator.PLUS_ASSIGN);
            put(PostfixExpression.Operator.DECREMENT, Assignment.Operator.MINUS_ASSIGN);
        }
    };

    private static final Map<PrefixExpression.Operator, PostfixExpression.Operator> PREFIX_TO_POSTFIX_OPERATOR = new HashMap<PrefixExpression.Operator, PostfixExpression.Operator>() {
        private static final long serialVersionUID = -8949107654517355859L;

        {
            put(PrefixExpression.Operator.INCREMENT, PostfixExpression.Operator.INCREMENT);
            put(PrefixExpression.Operator.DECREMENT, PostfixExpression.Operator.DECREMENT);
        }
    };

    private static final Map<Assignment.Operator, Operator> ASSIGN_TO_INFIX_OPERATOR = new HashMap<Assignment.Operator, Operator>() {
        private static final long serialVersionUID = -8949107654517355859L;

        {
            put(Assignment.Operator.PLUS_ASSIGN, Operator.PLUS);
            put(Assignment.Operator.MINUS_ASSIGN, Operator.MINUS);
            put(Assignment.Operator.TIMES_ASSIGN, Operator.TIMES);
            put(Assignment.Operator.DIVIDE_ASSIGN, Operator.DIVIDE);
            put(Assignment.Operator.BIT_AND_ASSIGN, Operator.AND);
            put(Assignment.Operator.BIT_OR_ASSIGN, Operator.OR);
            put(Assignment.Operator.BIT_XOR_ASSIGN, Operator.XOR);
            put(Assignment.Operator.REMAINDER_ASSIGN, Operator.REMAINDER);
            put(Assignment.Operator.LEFT_SHIFT_ASSIGN, Operator.LEFT_SHIFT);
            put(Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN, Operator.RIGHT_SHIFT_SIGNED);
            put(Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN, Operator.RIGHT_SHIFT_UNSIGNED);
        }
    };

    private static final Map<Operator, Operator> INFIX_TO_MIRROR_OPERATOR = new HashMap<Operator, Operator>() {
        private static final long serialVersionUID = -8949107654517355857L;

        {
            put(Operator.EQUALS, Operator.EQUALS);
            put(Operator.NOT_EQUALS, Operator.NOT_EQUALS);
            put(Operator.CONDITIONAL_AND, Operator.CONDITIONAL_AND);
            put(Operator.CONDITIONAL_OR, Operator.CONDITIONAL_OR);
            put(Operator.AND, Operator.AND);
            put(Operator.OR, Operator.OR);
            put(Operator.XOR, Operator.XOR);
            put(Operator.GREATER, Operator.LESS);
            put(Operator.LESS, Operator.GREATER);
            put(Operator.LESS_EQUALS, Operator.GREATER_EQUALS);
            put(Operator.GREATER_EQUALS, Operator.LESS_EQUALS);
        }
    };

    @Override
    public boolean match(final InfixExpression node, final Object other) {
        if (!node.hasExtendedOperands() && other instanceof InfixExpression) {
            InfixExpression ie = (InfixExpression) other;

            if (!ie.hasExtendedOperands() && isPassive(node.getLeftOperand()) && isPassive(node.getRightOperand())
                    && safeSubtreeMatch(node.getLeftOperand(), ie.getRightOperand())
                    && safeSubtreeMatch(node.getRightOperand(), ie.getLeftOperand())) {
                if (node.getOperator().equals(INFIX_TO_MIRROR_OPERATOR.get(ie.getOperator()))) {
                    return true;
                } else if (Arrays.asList(InfixExpression.Operator.PLUS, InfixExpression.Operator.TIMES)
                        .contains(ie.getOperator())
                        && node.getOperator().equals(ie.getOperator())
                        && hasType(node.getLeftOperand(), "short", "int", "long", "float", "double",
                                "java.lang.Short", "java.lang.Integer", "java.lang.Long", "java.lang.Float",
                                "java.lang.Double")
                        && haveSameType(node.getLeftOperand(), node.getRightOperand())) {
                    return true;
                }
            }
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final PrefixExpression node, final Object other) {
        if (node.getParent() instanceof Statement) {
            if (other instanceof Assignment) {
                return match0(node, (Assignment) other);
            } else if (other instanceof PostfixExpression) {
                return match0(node, (PostfixExpression) other);
            }
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final PostfixExpression node, final Object other) {
        if (node.getParent() instanceof Statement) {
            if (other instanceof Assignment) {
                return match0(node, (Assignment) other);
            } else if (other instanceof PrefixExpression) {
                return match0((PrefixExpression) other, node);
            }
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final Assignment node, final Object other) {
        if (other instanceof PrefixExpression && ((PrefixExpression) other).getParent() instanceof Statement) {
            return match0((PrefixExpression) other, node);
        } else if (other instanceof PostfixExpression
                && ((PostfixExpression) other).getParent() instanceof Statement) {
            return match0((PostfixExpression) other, node);
        } else if (other instanceof Assignment) {
            return matchAssignmentWithAndWithoutEqual(node, (Assignment) other)
                    || matchAssignmentWithAndWithoutEqual((Assignment) other, node) || super.match(node, other);
        }

        return super.match(node, other);
    }

    private boolean matchAssignmentWithAndWithoutEqual(final Assignment node, final Assignment assignment) {
        if (Assignment.Operator.ASSIGN.equals(node.getOperator())
                && node.getRightHandSide() instanceof InfixExpression) {
            InfixExpression infixExpr = (InfixExpression) node.getRightHandSide();

            if (!infixExpr.hasExtendedOperands()
                    && Arrays.asList(Assignment.Operator.PLUS_ASSIGN, Assignment.Operator.MINUS_ASSIGN,
                            Assignment.Operator.TIMES_ASSIGN, Assignment.Operator.DIVIDE_ASSIGN,
                            Assignment.Operator.BIT_AND_ASSIGN, Assignment.Operator.BIT_OR_ASSIGN,
                            Assignment.Operator.BIT_XOR_ASSIGN, Assignment.Operator.REMAINDER_ASSIGN,
                            Assignment.Operator.LEFT_SHIFT_ASSIGN, Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN,
                            Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN).contains(assignment.getOperator())
                    && ASSIGN_TO_INFIX_OPERATOR.get(assignment.getOperator()).equals(infixExpr.getOperator())) {
                return safeSubtreeMatch(node.getLeftHandSide(), assignment.getLeftHandSide())
                        && safeSubtreeMatch(infixExpr.getLeftOperand(), assignment.getLeftHandSide())
                        && safeSubtreeMatch(infixExpr.getRightOperand(), assignment.getRightHandSide());
            }
        }

        return false;
    }

    private boolean match0(final PrefixExpression prefixExpr, final PostfixExpression postfixExpr) {
        return postfixExpr.getOperator().equals(PREFIX_TO_POSTFIX_OPERATOR.get(prefixExpr.getOperator()))
                && safeSubtreeMatch(prefixExpr.getOperand(), postfixExpr.getOperand());
    }

    private boolean match0(final PrefixExpression prefixExpr, final Assignment assignment) {
        return match0(assignment, prefixExpr.getOperand(), PREFIX_TO_INFIX_OPERATOR.get(prefixExpr.getOperator()),
                PREFIX_TO_ASSIGN_OPERATOR.get(prefixExpr.getOperator()));
    }

    private boolean match0(final PostfixExpression postfixExpr, final Assignment assignment) {
        return match0(assignment, postfixExpr.getOperand(),
                POSTFIX_TO_INFIX_OPERATOR.get(postfixExpr.getOperator()),
                POSTFIX_TO_ASSIGN_OPERATOR.get(postfixExpr.getOperator()));
    }

    private boolean match0(final Assignment assignment, final Expression prefixOrPostfixOperand,
            final Operator infixAssociatedOperator, final Assignment.Operator assignmentAssociatedOperator) {
        if (Assignment.Operator.ASSIGN.equals(assignment.getOperator())
                && assignment.getRightHandSide() instanceof InfixExpression) {
            InfixExpression infixExpr = (InfixExpression) assignment.getRightHandSide();
            if (!infixExpr.hasExtendedOperands() && infixAssociatedOperator.equals(infixExpr.getOperator())) {
                if (isOneLiteral(infixExpr.getRightOperand())) {
                    return safeSubtreeMatch(prefixOrPostfixOperand, assignment.getLeftHandSide())
                            && safeSubtreeMatch(prefixOrPostfixOperand, infixExpr.getLeftOperand());
                } else if (Operator.PLUS.equals(infixExpr.getOperator())
                        && isOneLiteral(infixExpr.getLeftOperand())) {
                    return safeSubtreeMatch(prefixOrPostfixOperand, assignment.getLeftHandSide())
                            && safeSubtreeMatch(prefixOrPostfixOperand, infixExpr.getRightOperand());
                }
            }
        } else if (Arrays.asList(Assignment.Operator.PLUS_ASSIGN, Assignment.Operator.MINUS_ASSIGN).contains(
                assignment.getOperator()) && assignmentAssociatedOperator.equals(assignment.getOperator())) {
            Object assignmentExpr = assignment.resolveConstantExpressionValue();

            if (assignmentExpr instanceof Number && ((Number) assignmentExpr).longValue() == 1) {
                return safeSubtreeMatch(prefixOrPostfixOperand, assignment.getLeftHandSide());
            }
        }

        return false;
    }

    private boolean isOneLiteral(Expression operand) {
        Object rightExpr = operand.resolveConstantExpressionValue();

        return rightExpr instanceof Number && ((Number) rightExpr).longValue() == 1;
    }

    @Override
    public boolean match(final Block node, final Object other) {
        if (other instanceof AssertStatement || other instanceof BreakStatement
                || other instanceof ConstructorInvocation || other instanceof ContinueStatement
                || other instanceof DoStatement || other instanceof EmptyStatement
                || other instanceof EnhancedForStatement || other instanceof ExpressionStatement
                || other instanceof ForStatement || other instanceof IfStatement
                || other instanceof LabeledStatement || other instanceof ReturnStatement
                || other instanceof SuperConstructorInvocation || other instanceof SwitchStatement
                || other instanceof SynchronizedStatement || other instanceof ThrowStatement
                || other instanceof TryStatement || other instanceof TypeDeclarationStatement
                || other instanceof VariableDeclarationStatement || other instanceof WhileStatement) {
            return match0(node, (Statement) other);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final AssertStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final BreakStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final ConstructorInvocation node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final ContinueStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final DoStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final EmptyStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final EnhancedForStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final ExpressionStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final ForStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final IfStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final LabeledStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final ReturnStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final SuperConstructorInvocation node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final SwitchStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final SynchronizedStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final ThrowStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final TryStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final TypeDeclarationStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final VariableDeclarationStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    @Override
    public boolean match(final WhileStatement node, final Object other) {
        if (other instanceof Block) {
            return match0((Block) other, (Statement) node);
        }

        return super.match(node, other);
    }

    private boolean match0(final Block node, final Statement other) {
        if ((node.getParent() instanceof IfStatement || node.getParent() instanceof ForStatement
                || node.getParent() instanceof EnhancedForStatement || node.getParent() instanceof WhileStatement
                || node.getParent() instanceof DoStatement) && node.statements().size() == 1) {
            return safeSubtreeMatch(node.statements().get(0), other) || super.match(node, other);
        }

        return super.match(node, other);
    }
}