001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright (c) 2009-2018 CQSE GmbH                                        |
004|                                                                          |
005+-------------------------------------------------------------------------*/
006package eu.cqse.check.base;
007
008import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
009
010import java.util.EnumSet;
011import java.util.List;
012import java.util.Set;
013
014import eu.cqse.check.framework.core.CheckException;
015import eu.cqse.check.framework.scanner.ELanguage;
016import eu.cqse.check.framework.scanner.ETokenType;
017import eu.cqse.check.framework.scanner.IToken;
018import eu.cqse.check.framework.shallowparser.framework.ShallowEntity;
019
020/**
021 * Base class for checks that analyze comparison expressions. This handles only
022 * the very basic case of identifier compared to itself.
023 */
024public abstract class AvoidObjectComparisonWithSelfCheckBase extends EntityCheckBase {
025
026        private static final Set<ETokenType> BOOLEAN_CONNECTIVES = EnumSet.of(ETokenType.ANDAND, ETokenType.OROR);
027
028        /** {@inheritDoc} */
029        @Override
030        protected String getXPathSelectionString() {
031                return "//STATEMENT";
032        }
033
034        /** {@inheritDoc} */
035        @Override
036        protected void processEntity(ShallowEntity entity) throws CheckException {
037                List<IToken> tokens = entity.ownStartTokens();
038                for (int index : getIndicesOfComparisonExpression(tokens)) {
039                        checkOperands(tokens, index);
040                }
041        }
042
043        /** Checks whether operands of comparison are the same. */
044        private void checkOperands(List<IToken> tokens, int index) throws CheckException {
045                int secondOperandIndex = getSecondOperandIndex(index);
046                if (index <= 1 || secondOperandIndex + 1 >= tokens.size()) {
047                        return;
048                }
049
050                IToken firstOperand = tokens.get(index - 1);
051                IToken secondOperand = tokens.get(secondOperandIndex);
052
053                // we handle only single identifiers
054                if (firstOperand.getType() != IDENTIFIER || secondOperand.getType() != IDENTIFIER) {
055                        return;
056                }
057
058                // if there is something complex (anything besides the opening parenthesis and
059                // && or ||) before, we rather skip this, to avoid false positives (might be
060                // lots of complex expressions)
061                ETokenType tokenBefore = tokens.get(index - 2).getType();
062                if (tokenBefore != ETokenType.LPAREN && !BOOLEAN_CONNECTIVES.contains(tokenBefore)) {
063                        if (!(context.getLanguage() == ELanguage.OBJECTIVE_C && tokenBefore == ETokenType.LBRACK)) {
064                                return;
065                        }
066                }
067
068                // if there is something complex (anything besides the closing parenthesis and
069                // && or ||) after, we rather skip this, to avoid false positives (might be lots
070                // of complex expressions)
071                ETokenType tokenAfter = tokens.get(secondOperandIndex + 1).getType();
072                if (tokenAfter != ETokenType.RPAREN && !BOOLEAN_CONNECTIVES.contains(tokenAfter)) {
073                        if (!(context.getLanguage() == ELanguage.OBJECTIVE_C && tokenAfter == ETokenType.RBRACK)) {
074                                return;
075                        }
076                }
077
078                if (firstOperand.getText().equals(secondOperand.getText()) && !isRuleException(firstOperand)) {
079                        createFinding("`" + firstOperand.getText() + "` is compared with itself", firstOperand, secondOperand);
080                }
081        }
082
083        /**
084         * Template method that can return true, if the given operand is an exception
085         * from the rule.
086         * 
087         * @param operand
088         *            the operand to be checked for exception case.
089         * 
090         */
091        protected boolean isRuleException(IToken operand) {
092                return false;
093        }
094
095        /**
096         * Returns list of start indices of comparison expressions in entity tokens.
097         */
098        protected abstract List<Integer> getIndicesOfComparisonExpression(List<IToken> tokens);
099
100        /**
101         * Returns index of the second operand of comparison expression.
102         */
103        protected abstract int getSecondOperandIndex(int operatorIndex);
104}