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.comments;
5   
6   import java.util.ArrayList;
7   import java.util.Collections;
8   import java.util.HashSet;
9   import java.util.List;
10  import java.util.Set;
11  
12  import net.sourceforge.pmd.PropertyDescriptor;
13  import net.sourceforge.pmd.PropertySource;
14  import net.sourceforge.pmd.Rule;
15  import net.sourceforge.pmd.RuleContext;
16  import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
17  import net.sourceforge.pmd.lang.java.ast.Comment;
18  import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
19  import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
20  import net.sourceforge.pmd.util.CollectionUtil;
21  import net.sourceforge.pmd.util.StringUtil;
22  
23  /**
24   * A rule that checks for illegal words in the comment text.
25   *
26   * TODO implement regex option
27   *
28   * @author Brian Remedios
29   */
30  public class CommentContentRule extends AbstractCommentRule {
31  
32  	private boolean caseSensitive;
33  	private boolean wordsAreRegex;
34  	private String[] originalBadWords;
35  	private String[] currentBadWords;
36  
37  	private static final String[] badWords = new String[] { "idiot", "jerk" };	// FIXME need some better defaults (or none?)
38  
39  	public static final BooleanProperty WORDS_ARE_REGEX_DESCRIPTOR = new BooleanProperty("wordsAreRegex",
40      		"Use regular expressions", false, 1.0f);
41  
42  	// ignored when property above == True
43  	public static final BooleanProperty CASE_SENSITIVE_DESCRIPTOR = new BooleanProperty("caseSensitive",
44      		"Case sensitive", false, 2.0f);
45  
46      public static final StringMultiProperty DISSALLOWED_TERMS_DESCRIPTOR = new StringMultiProperty("disallowedTerms",
47      		"Illegal terms or phrases", badWords, 3.0f, '|');
48  
49      private static final Set<PropertyDescriptor<?>> NonRegexProperties;
50      static {
51      	NonRegexProperties = new HashSet<PropertyDescriptor<?>>(1);
52      	NonRegexProperties.add(CASE_SENSITIVE_DESCRIPTOR);
53      }
54  
55  	public CommentContentRule() {
56  		definePropertyDescriptor(WORDS_ARE_REGEX_DESCRIPTOR);
57  		definePropertyDescriptor(CASE_SENSITIVE_DESCRIPTOR);
58  		definePropertyDescriptor(DISSALLOWED_TERMS_DESCRIPTOR);
59  	}
60  
61  	 /**
62  	  * Capture values and perform all the case-conversions once per run
63  	  */
64  	 @Override
65  	public void start(RuleContext ctx) {
66  		 wordsAreRegex = getProperty(WORDS_ARE_REGEX_DESCRIPTOR);
67  		 originalBadWords = getProperty(DISSALLOWED_TERMS_DESCRIPTOR);
68  		 caseSensitive = getProperty(CASE_SENSITIVE_DESCRIPTOR);
69  		 if (caseSensitive) {
70  			 currentBadWords = originalBadWords;
71  		 	} else {
72  		 		currentBadWords = new String[originalBadWords.length];
73  		 		 for (int i=0; i<currentBadWords.length; i++) {
74  					 currentBadWords[i] = originalBadWords[i].toUpperCase();
75  				 	}
76  		 	}
77  	 }
78  
79  	 @Override
80  	 public Set<PropertyDescriptor<?>> ignoredProperties() {
81  		 return getProperty(WORDS_ARE_REGEX_DESCRIPTOR) ?
82  				NonRegexProperties :
83  				Collections.EMPTY_SET;
84  	 }
85  
86  	 /**
87  	  * @see Rule#end(RuleContext)
88  	  */
89  	 @Override
90  	public void end(RuleContext ctx) {
91  		 // Override as needed
92  	 }
93  
94  	private List<String> illegalTermsIn(Comment comment) {
95  
96  		if (currentBadWords.length == 0) return Collections.emptyList();
97  
98  		String commentText = filteredCommentIn(comment);
99  		if (StringUtil.isEmpty(commentText)) return Collections.emptyList();
100 
101 		if (!caseSensitive) commentText = commentText.toUpperCase();
102 
103 		List<String> foundWords = new ArrayList<String>();
104 
105 		for (int i=0; i<currentBadWords.length; i++) {
106 			if (commentText.indexOf(currentBadWords[i]) >= 0) {
107 				foundWords.add(originalBadWords[i]);
108 			}
109 		}
110 
111 		return foundWords;
112 	}
113 
114 	private String errorMsgFor(List<String> badWords) {
115 	    StringBuilder msg = new StringBuilder(this.getMessage()).append(": ");
116 	    if (badWords.size() == 1 ) {
117 		msg.append("Invalid term: '").append(badWords.get(0)).append('\'');
118 	    } else {
119 		msg.append("Invalid terms: '");
120 		msg.append(badWords.get(0));
121 		for (int i=1; i<badWords.size(); i++) {
122 		    msg.append("', '").append(badWords.get(i));
123 		}
124 		msg.append('\'');
125 	    }
126 	    return msg.toString();
127 	}
128 
129 	@Override
130     public Object visit(ASTCompilationUnit cUnit, Object data) {
131 
132 		// NPE patch: Eclipse plugin doesn't call start() at onset?
133 		if (currentBadWords == null) start(null);
134 
135 		for (Comment comment : cUnit.getComments()) {
136 			List<String> badWords = illegalTermsIn(comment);
137 			if (badWords.isEmpty()) continue;
138 
139 			addViolationWithMessage(data, cUnit, errorMsgFor(badWords), comment.getBeginLine(), comment.getEndLine());
140 		}
141 
142         return super.visit(cUnit, data);
143     }
144 
145 	public boolean hasDissallowedTerms() {
146 		String[] terms = getProperty(DISSALLOWED_TERMS_DESCRIPTOR);
147 		return CollectionUtil.isNotEmpty(terms);
148 	}
149 
150 	/**
151 	 * @see PropertySource#dysfunctionReason()
152 	 */
153 	@Override
154 	public String dysfunctionReason() {
155 
156 		return hasDissallowedTerms() ?
157 				null :
158 				"No disallowed terms specified";
159 	}
160 }