/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.lang.plsql.rule.codesize; import java.util.logging.Level; import java.util.logging.Logger; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.plsql.ast.ASTCaseStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTCaseWhenClause; import net.sourceforge.pmd.lang.plsql.ast.ASTContinueStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTElseClause; import net.sourceforge.pmd.lang.plsql.ast.ASTElsifClause; import net.sourceforge.pmd.lang.plsql.ast.ASTExceptionHandler; import net.sourceforge.pmd.lang.plsql.ast.ASTExitStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTExpression; import net.sourceforge.pmd.lang.plsql.ast.ASTForStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTGotoStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTIfStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTLabelledStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTLoopStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTRaiseStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTReturnStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTStatement; import net.sourceforge.pmd.lang.plsql.ast.ASTWhileStatement; import net.sourceforge.pmd.lang.plsql.ast.PLSQLNode; import net.sourceforge.pmd.lang.plsql.rule.AbstractStatisticalPLSQLRule; import net.sourceforge.pmd.stat.DataPoint; import net.sourceforge.pmd.util.NumericConstants; /** * Abstract superclass for NCSS counting methods. Analogous to and cribbed from * the Java version of the rule. */ public abstract class AbstractNcssCountRule extends AbstractStatisticalPLSQLRule { private static final Logger LOGGER = Logger.getLogger(AbstractNcssCountRule.class.getName()); private Class<?> nodeClass; /** * Count the nodes of the given type using NCSS rules. * * @param nodeClass * class of node to count */ protected AbstractNcssCountRule(Class<?> nodeClass) { this.nodeClass = nodeClass; if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Counting for " + nodeClass.getCanonicalName()); } } @Override public Object visit(PLSQLNode node, Object data) { int numNodes = 0; for (int i = 0; i < node.jjtGetNumChildren(); i++) { PLSQLNode n = (PLSQLNode) node.jjtGetChild(i); Integer treeSize = (Integer) n.jjtAccept(this, data); numNodes += treeSize.intValue(); } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("Checking candidate " + node.getClass().getCanonicalName() + " against target class " + nodeClass.getCanonicalName() + " with " + numNodes + " nodes"); } if (this.nodeClass.isInstance(node)) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Matched candidate " + node.getClass().getCanonicalName() + " against target class " + nodeClass.getCanonicalName()); } // Add 1 to account for base node numNodes++; DataPoint point = new DataPoint(); point.setNode(node); point.setScore(1.0 * numNodes); point.setMessage(getMessage()); addDataPoint(point); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Running score is " + point.getScore()); } } return Integer.valueOf(numNodes); } /** * Count the number of children of the given PLSQL node. Adds one to count * the node itself. * * @param node * PLSQL node having children counted * @param data * node data * @return count of the number of children of the node, plus one */ protected Integer countNodeChildren(Node node, Object data) { Integer nodeCount = null; int lineCount = 0; for (int i = 0; i < node.jjtGetNumChildren(); i++) { nodeCount = (Integer) ((PLSQLNode) node.jjtGetChild(i)).jjtAccept(this, data); lineCount += nodeCount.intValue(); } return ++lineCount; } @Override public Object visit(ASTForStatement node, Object data) { return countNodeChildren(node, data); } @Override public Object visit(ASTLoopStatement node, Object data) { return countNodeChildren(node, data); } @Override public Object visit(ASTIfStatement node, Object data) { Integer lineCount = countNodeChildren(node, data); return lineCount; } @Override public Object visit(ASTElsifClause node, Object data) { Integer lineCount = countNodeChildren(node, data); return lineCount; } @Override public Object visit(ASTElseClause node, Object data) { Integer lineCount = countNodeChildren(node, data); return lineCount; } @Override public Object visit(ASTWhileStatement node, Object data) { return countNodeChildren(node, data); } @Override public Object visit(ASTExitStatement node, Object data) { return NumericConstants.ONE; } @Override public Object visit(ASTExceptionHandler node, Object data) { return countNodeChildren(node, data); } @Override public Object visit(ASTContinueStatement node, Object data) { return NumericConstants.ONE; } @Override public Object visit(ASTGotoStatement node, Object data) { return NumericConstants.ONE; } @Override public Object visit(ASTReturnStatement node, Object data) { return countNodeChildren(node, data); } @Override public Object visit(ASTCaseStatement node, Object data) { return countNodeChildren(node, data); } @Override public Object visit(ASTRaiseStatement node, Object data) { return NumericConstants.ONE; } @Override public Object visit(ASTExpression node, Object data) { // "For" update expressions do not count as separate lines of code if (node.jjtGetParent() instanceof ASTStatement) { return NumericConstants.ZERO; } return NumericConstants.ONE; } @Override public Object visit(ASTLabelledStatement node, Object data) { return countNodeChildren(node, data); } @Override public Object visit(ASTCaseWhenClause node, Object data) { return countNodeChildren(node, data); } @Override public Object[] getViolationParameters(DataPoint point) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Point score is " + point.getScore()); } return new String[] { String.valueOf((int) point.getScore()) }; } }