View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.java.rule.logging;
5   
6   import java.util.ArrayList;
7   import java.util.Arrays;
8   import java.util.Collections;
9   import java.util.HashMap;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Map.Entry;
13  
14  import net.sourceforge.pmd.Rule;
15  import net.sourceforge.pmd.lang.ast.Node;
16  import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
17  import net.sourceforge.pmd.lang.java.rule.optimizations.AbstractOptimizationRule;
18  import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
19  
20  import org.jaxen.JaxenException;
21  
22  /**
23   * Check that log.debug, log.trace, log.error, etc... statements are guarded by
24   * some test expression on log.isDebugEnabled() or log.isTraceEnabled().
25   * 
26   * @author Romain Pelisse - <belaran@gmail.com>
27   * @author Heiko Rupp - <hwr@pilhuhn.de>
28   * @author Tammo van Lessen - provided original XPath expression
29   * 
30   */
31  public class GuardLogStatementRule extends AbstractOptimizationRule implements
32  		Rule {
33  
34  	public static final StringMultiProperty LOG_LEVELS = new StringMultiProperty(
35  			"logLevels", "LogLevels to guard", new String[] {}, 1.0f, ',');
36  
37  	public static final StringMultiProperty GUARD_METHODS = new StringMultiProperty(
38  			"guardsMethods", "method use to guard the log statement",
39  			new String[] {}, 2.0f, ',');
40  
41  	protected Map<String, String> guardStmtByLogLevel = new HashMap<String, String>(
42  			5);
43  
44  	private static final String xpathExpression = "//PrimaryPrefix[ends-with(Name/@Image, 'KEY') and "
45  			+ "count("
46  			+ "ancestor::IfStatement/Expression/descendant::PrimaryExpression["
47  			+ "ends-with(descendant::PrimaryPrefix/Name/@Image,'VALUE')]) = 0]";
48  
49  	public GuardLogStatementRule() {
50  		definePropertyDescriptor(LOG_LEVELS);
51  		definePropertyDescriptor(GUARD_METHODS);
52  	}
53  
54  	@Override
55  	public Object visit(ASTCompilationUnit unit, Object data) {
56  		extractProperties();
57  		findViolationForEachLogStatement(unit, data);
58  		return super.visit(unit, data);
59  	}
60  
61  	private void findViolationForEachLogStatement(ASTCompilationUnit unit, Object data) {
62  		for (Entry<String, String> entry : guardStmtByLogLevel.entrySet()) {
63  			List<Node> nodes = findViolations(unit, entry.getKey(),
64  					entry.getValue());
65  			for (Node node : nodes) {
66  				super.addViolation(data, node);
67  			}
68  		}		
69  	}
70  	
71  	@SuppressWarnings("unchecked")
72  	private List<Node> findViolations(ASTCompilationUnit unit, String key,
73  			String value) {
74  		try {
75  			return unit.findChildNodesWithXPath(xpathExpression.replaceFirst(
76  					"KEY", key).replaceFirst("VALUE", value));
77  		} catch (JaxenException e) {
78  			e.printStackTrace();
79  		}
80  		return Collections.EMPTY_LIST;
81  	}
82  
83  	private void setPropertiesDefaultValues(List<String> logLevels,
84  			List<String> guardMethods) {
85  		logLevels.add("trace");
86  		logLevels.add("debug");
87  		logLevels.add("info");
88  		logLevels.add("warn");
89  		logLevels.add("error");
90  
91  		guardMethods.clear();
92  		guardMethods.add("isTraceEnabled");
93  		guardMethods.add("isDebugEnabled");
94  		guardMethods.add("isInfoEnabled");
95  		guardMethods.add("isWarnEnabled");
96  		guardMethods.add("isErrorEnabled");
97  	}
98  
99  	protected void extractProperties() {
100 		if (guardStmtByLogLevel.isEmpty()) {
101 
102 			List<String> logLevels = new ArrayList<String>(Arrays.asList(super
103 					.getProperty(LOG_LEVELS)));
104 			List<String> guardMethods = new ArrayList<String>(
105 					Arrays.asList(super.getProperty(GUARD_METHODS)));
106 
107 			if (guardMethods.isEmpty() && !logLevels.isEmpty()) {
108 				throw new IllegalArgumentException(
109 						"Can't specify guardMethods without specifiying logLevels.");
110 			}
111 
112 			if (logLevels.isEmpty())
113 				setPropertiesDefaultValues(logLevels, guardMethods);
114 
115 			buildGuardStatementMap(logLevels, guardMethods);
116 		}
117 	}
118 
119 	protected void buildGuardStatementMap(List<String> logLevels,
120 			List<String> guardMethods) {
121 		for (String logLevel : logLevels) {
122 			boolean found = false;
123 			for (String guardMethod : guardMethods) {
124 				if (!found
125 						&& guardMethod.toLowerCase().contains(
126 								logLevel.toLowerCase())) {
127 					found = true;
128 					guardStmtByLogLevel.put("." + logLevel, guardMethod);
129 				}
130 			}
131 
132 			if (!found)
133 				throw new IllegalArgumentException(
134 						"No guard method associated to the logLevel:"
135 								+ logLevel + ". Should be something like 'is"
136 								+ logLevel + "Enabled'.");
137 		}
138 	}
139 }