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, 'LOG_LEVEL')]"
45              + "[count(../descendant::AdditiveExpression) > 0]"
46              + "[count(ancestor::IfStatement/Expression/descendant::PrimaryExpression["
47                  + "ends-with(descendant::PrimaryPrefix/Name/@Image,'GUARD')]) = 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, xpathExpression);
58  		return super.visit(unit, data);
59  	}
60  
61  	protected void findViolationForEachLogStatement(ASTCompilationUnit unit, Object data, String xpathExpression) {
62  		for (Entry<String, String> entry : guardStmtByLogLevel.entrySet()) {
63  			List<Node> nodes = findViolations(unit, entry.getKey(),
64  					entry.getValue(), xpathExpression);
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 logLevel,
73  			String guard, String xpathExpression) {
74  		try {
75  			return unit.findChildNodesWithXPath(xpathExpression
76  			        .replaceAll("LOG_LEVEL_UPPERCASE", logLevel.toUpperCase())
77  			        .replaceAll("LOG_LEVEL", logLevel)
78  			        .replaceAll("GUARD", guard));
79  		} catch (JaxenException e) {
80  			e.printStackTrace();
81  		}
82  		return Collections.EMPTY_LIST;
83  	}
84  
85  	private void setPropertiesDefaultValues(List<String> logLevels,
86  			List<String> guardMethods) {
87  		logLevels.add("trace");
88  		logLevels.add("debug");
89  		logLevels.add("info");
90  		logLevels.add("warn");
91  		logLevels.add("error");
92  
93  		guardMethods.clear();
94  		guardMethods.add("isTraceEnabled");
95  		guardMethods.add("isDebugEnabled");
96  		guardMethods.add("isInfoEnabled");
97  		guardMethods.add("isWarnEnabled");
98  		guardMethods.add("isErrorEnabled");
99  	}
100 
101 	protected void extractProperties() {
102 		if (guardStmtByLogLevel.isEmpty()) {
103 
104 			List<String> logLevels = new ArrayList<String>(Arrays.asList(super
105 					.getProperty(LOG_LEVELS)));
106 			List<String> guardMethods = new ArrayList<String>(
107 					Arrays.asList(super.getProperty(GUARD_METHODS)));
108 
109 			if (guardMethods.isEmpty() && !logLevels.isEmpty()) {
110 				throw new IllegalArgumentException(
111 						"Can't specify guardMethods without specifiying logLevels.");
112 			}
113 
114 			if (logLevels.isEmpty())
115 				setPropertiesDefaultValues(logLevels, guardMethods);
116 
117 			buildGuardStatementMap(logLevels, guardMethods);
118 		}
119 	}
120 
121 	protected void buildGuardStatementMap(List<String> logLevels,
122 			List<String> guardMethods) {
123 		for (String logLevel : logLevels) {
124 			boolean found = false;
125 			for (String guardMethod : guardMethods) {
126 				if (!found
127 						&& guardMethod.toLowerCase().contains(
128 								logLevel.toLowerCase())) {
129 					found = true;
130 					guardStmtByLogLevel.put("." + logLevel, guardMethod);
131 				}
132 			}
133 
134 			if (!found)
135 				throw new IllegalArgumentException(
136 						"No guard method associated to the logLevel:"
137 								+ logLevel + ". Should be something like 'is"
138 								+ logLevel + "Enabled'.");
139 		}
140 	}
141 }