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 }