View Javadoc

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