/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.logging;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.jaxen.JaxenException;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.rule.optimizations.AbstractOptimizationRule;
import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
/**
* Check that log.debug, log.trace, log.error, etc... statements are guarded by
* some test expression on log.isDebugEnabled() or log.isTraceEnabled().
*
* @author Romain Pelisse - <belaran@gmail.com>
* @author Heiko Rupp - <hwr@pilhuhn.de>
* @author Tammo van Lessen - provided original XPath expression
*
*/
public class GuardLogStatementRule extends AbstractOptimizationRule implements Rule {
public static final StringMultiProperty LOG_LEVELS = new StringMultiProperty("logLevels", "LogLevels to guard",
new String[] {}, 1.0f, ',');
public static final StringMultiProperty GUARD_METHODS = new StringMultiProperty("guardsMethods",
"method use to guard the log statement", new String[] {}, 2.0f, ',');
protected Map<String, String> guardStmtByLogLevel = new HashMap<>(5);
private static final String XPATH_EXPRESSION = "//PrimaryPrefix[ends-with(Name/@Image, 'LOG_LEVEL')]"
+ "[count(../descendant::AdditiveExpression) > 0]"
+ "[count(ancestor::IfStatement/Expression/descendant::PrimaryExpression["
+ "ends-with(descendant::PrimaryPrefix/Name/@Image,'GUARD')]) = 0]";
public GuardLogStatementRule() {
definePropertyDescriptor(LOG_LEVELS);
definePropertyDescriptor(GUARD_METHODS);
}
@Override
public Object visit(ASTCompilationUnit unit, Object data) {
extractProperties();
findViolationForEachLogStatement(unit, data, XPATH_EXPRESSION);
return super.visit(unit, data);
}
protected void findViolationForEachLogStatement(ASTCompilationUnit unit, Object data, String xpathExpression) {
for (Entry<String, String> entry : guardStmtByLogLevel.entrySet()) {
List<? extends Node> nodes = findViolations(unit, entry.getKey(), entry.getValue(), xpathExpression);
for (Node node : nodes) {
super.addViolation(data, node);
}
}
}
@SuppressWarnings("unchecked")
private List<? extends Node> findViolations(ASTCompilationUnit unit, String logLevel, String guard,
String xpathExpression) {
try {
return unit
.findChildNodesWithXPath(xpathExpression.replaceAll("LOG_LEVEL_UPPERCASE", logLevel.toUpperCase())
.replaceAll("LOG_LEVEL", logLevel).replaceAll("GUARD", guard));
} catch (JaxenException e) {
e.printStackTrace();
}
return Collections.EMPTY_LIST;
}
private void setPropertiesDefaultValues(List<String> logLevels, List<String> guardMethods) {
logLevels.add("trace");
logLevels.add("debug");
logLevels.add("info");
logLevels.add("warn");
logLevels.add("error");
guardMethods.clear();
guardMethods.add("isTraceEnabled");
guardMethods.add("isDebugEnabled");
guardMethods.add("isInfoEnabled");
guardMethods.add("isWarnEnabled");
guardMethods.add("isErrorEnabled");
}
protected void extractProperties() {
if (guardStmtByLogLevel.isEmpty()) {
List<String> logLevels = new ArrayList<>(Arrays.asList(super.getProperty(LOG_LEVELS)));
List<String> guardMethods = new ArrayList<>(Arrays.asList(super.getProperty(GUARD_METHODS)));
if (guardMethods.isEmpty() && !logLevels.isEmpty()) {
throw new IllegalArgumentException("Can't specify guardMethods without specifiying logLevels.");
}
if (logLevels.isEmpty()) {
setPropertiesDefaultValues(logLevels, guardMethods);
}
buildGuardStatementMap(logLevels, guardMethods);
}
}
protected void buildGuardStatementMap(List<String> logLevels, List<String> guardMethods) {
for (String logLevel : logLevels) {
boolean found = false;
for (String guardMethod : guardMethods) {
if (!found && guardMethod.toLowerCase().contains(logLevel.toLowerCase())) {
found = true;
guardStmtByLogLevel.put("." + logLevel, guardMethod);
}
}
if (!found) {
throw new IllegalArgumentException("No guard method associated to the logLevel:" + logLevel
+ ". Should be something like 'is" + logLevel + "Enabled'.");
}
}
}
}