org.sonar.php.checks.utils.AbstractUnusedPrivateClassMemberCheck.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.php.checks.utils.AbstractUnusedPrivateClassMemberCheck.java

Source

/*
 * SonarQube PHP Plugin
 * Copyright (C) 2010 SonarSource and Akram Ben Aissi
 * dev@sonar.codehaus.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.php.checks.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.Token;
import org.apache.commons.lang.StringUtils;
import org.sonar.php.api.PHPKeyword;
import org.sonar.php.api.PHPPunctuator;
import org.sonar.php.parser.PHPGrammar;
import org.sonar.squidbridge.checks.SquidCheck;
import org.sonar.sslr.parser.LexerlessGrammar;

import java.util.List;
import java.util.Map;

public abstract class AbstractUnusedPrivateClassMemberCheck extends SquidCheck<LexerlessGrammar> {

    private Map<String, PrivateMember> privateMembers = Maps.newHashMap();

    private static class PrivateMember {
        final AstNode declaration;
        int usage = 0;

        private PrivateMember(AstNode declaration) {
            this.declaration = declaration;
        }
    }

    protected void addPrivateMember(String calledName, AstNode declaration) {
        privateMembers.put(calledName, new PrivateMember(declaration));
    }

    @Override
    public void init() {
        subscribeTo(PHPGrammar.CLASS_DECLARATION, PHPGrammar.MEMBER_EXPRESSION, PHPGrammar.SIMPLE_ENCAPS_VARIABLE,
                PHPGrammar.STRING_LITERAL);
    }

    @Override
    public void visitNode(AstNode astNode) {
        if (astNode.is(PHPGrammar.CLASS_DECLARATION)) {
            retrievePrivateClassMember(astNode);

        } else if (astNode.is(PHPGrammar.STRING_LITERAL)) {
            checkUsageInString(astNode);

        } else {
            checkUsage(astNode);
        }
    }

    @Override
    public void leaveNode(AstNode astNode) {
        if (astNode.is(PHPGrammar.CLASS_DECLARATION)) {
            reportUnusedPrivateField();
            privateMembers.clear();
        }
    }

    protected abstract void retrievePrivateClassMember(AstNode classDec);

    protected abstract String getIssueMessage();

    /**
     * Increase usage of a class member if its name appears in a string that does
     * not contains encapsulated variable.
     */
    protected void checkUsageInString(AstNode stringLiteral) {
        // String literal without encapsulated variable
        if (stringLiteral.getFirstChild().isNot(PHPGrammar.ENCAPS_STRING_LITERAL)) {

            for (PrivateMember member : privateMembers.values()) {
                if (stringLiteral.getTokenOriginalValue().contains(member.declaration.getTokenOriginalValue())) {
                    member.usage++;
                }
            }
        }
    }

    /**
     * Create an issue when usage of a private member have not been found
     * in a file.
     */
    protected void reportUnusedPrivateField() {
        for (PrivateMember field : privateMembers.values()) {
            if (field.usage == 0) {
                getContext().createLineViolation(this, getIssueMessage(), field.declaration,
                        field.declaration.getTokenOriginalValue());
            }
        }
    }

    /**
     * Check is the expression uses one of declared private member.
     */
    protected void checkUsage(AstNode variable) {
        for (String varName : getCalledMemberInExpression(variable)) {
            PrivateMember field = privateMembers.get(varName);

            if (field != null) {
                field.usage++;
            }
        }
    }

    /**
     * Returns "::$field" for static field and "->field" for others.
     *
     * @param identifierNode node that correspond to the name of the class member.
     */
    protected String getCalledName(AstNode identifierNode, List<AstNode> modifiers) {
        if (CheckUtils.isStaticClassMember(modifiers)) {
            return "::" + identifierNode.getTokenOriginalValue();
        } else {
            return "->" + StringUtils.remove(identifierNode.getTokenOriginalValue(), "$");
        }
    }

    protected boolean isPrivate(List<AstNode> memberModifiers) {
        for (AstNode modifier : memberModifiers) {
            if (modifier.getFirstChild().is(PHPKeyword.PRIVATE)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return a list called class member in the given expression, adding
     * empty parenthesis when its a method call.
     * <p/>
     * Example:
     * <ol>
     * <li>for "$this->myArray[0]", function returns "[->myArray]"
     * <li>for "$this->func(param)", function returns "[->func()]"
     * <li>for "static::$field", function returns "[::$field]"
     * <li>for "$this->setA()->setB()", function returns "[->setA(), setB()]"
     */
    protected List<String> getCalledMemberInExpression(AstNode var) {
        StringBuilder builder = new StringBuilder();
        List<String> list = Lists.newLinkedList();
        boolean consume = false;

        for (Token token : var.getTokens()) {
            String tokenValue = token.getOriginalValue();

            if (PHPPunctuator.ARROW.getValue().equals(tokenValue)
                    || PHPPunctuator.DOUBLECOLON.getValue().equals(tokenValue)) {
                consume = true;

            } else if ("(".equals(tokenValue)) {
                builder.append("()");
                consume = false;

            } else if ("[".equals(tokenValue)) {
                consume = false;
            }

            if (consume) {
                builder.append(tokenValue);
            } else if (builder.length() > 0) {
                list.add(builder.toString());
                builder = new StringBuilder();
            }
        }

        if (builder.length() > 0) {
            list.add(builder.toString());
        }
        return list;
    }
}