View Javadoc
1 /*** 2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html 3 */ 4 package net.sourceforge.pmd.rules; 5 6 import net.sourceforge.pmd.AbstractRule; 7 import net.sourceforge.pmd.RuleContext; 8 import net.sourceforge.pmd.ast.ASTArgumentList; 9 import net.sourceforge.pmd.ast.ASTCompilationUnit; 10 import net.sourceforge.pmd.ast.ASTLiteral; 11 import net.sourceforge.pmd.ast.Node; 12 import net.sourceforge.pmd.ast.SimpleNode; 13 import net.sourceforge.pmd.ast.ASTVariableInitializer; 14 15 import java.io.BufferedReader; 16 import java.io.File; 17 import java.io.FileReader; 18 import java.io.IOException; 19 import java.io.LineNumberReader; 20 import java.text.MessageFormat; 21 import java.util.ArrayList; 22 import java.util.HashMap; 23 import java.util.HashSet; 24 import java.util.Iterator; 25 import java.util.List; 26 import java.util.Map; 27 import java.util.Set; 28 29 public class AvoidDuplicateLiteralsRule extends AbstractRule { 30 31 public static class ExceptionParser { 32 33 private static final char ESCAPE_CHAR = '//'; 34 private char delimiter; 35 36 public ExceptionParser(char delimiter) { 37 this.delimiter = delimiter; 38 } 39 40 public Set parse(String in) { 41 Set result = new HashSet(); 42 43 StringBuffer currentToken = new StringBuffer(); 44 boolean inEscapeMode = false; 45 46 for (int i=0; i<in.length(); i++) { 47 48 if (inEscapeMode) { 49 inEscapeMode = false; 50 currentToken.append(in.charAt(i)); 51 continue; 52 } 53 54 if (!inEscapeMode && in.charAt(i) == ESCAPE_CHAR) { 55 inEscapeMode = true; 56 continue; 57 } 58 59 if (in.charAt(i) == delimiter) { 60 result.add(currentToken.toString()); 61 currentToken = new StringBuffer(); 62 } else { 63 currentToken.append(in.charAt(i)); 64 } 65 } 66 67 if (currentToken.length()>0) { 68 result.add(currentToken.toString()); 69 currentToken = new StringBuffer(); 70 } 71 72 return result; 73 } 74 } 75 76 private static final char DEFAULT_SEPARATOR = ','; 77 private static final String EXCEPTION_LIST_PROPERTY = "exceptionlist"; 78 private static final String SEPARATOR_PROPERTY = "separator"; 79 private static final String EXCEPTION_FILE_NAME_PROPERTY = "exceptionfile"; 80 81 private Map literals = new HashMap(); 82 private Set exceptions = new HashSet(); 83 84 public Object visit(ASTCompilationUnit node, Object data) { 85 literals.clear(); 86 87 if (hasProperty(EXCEPTION_LIST_PROPERTY)) { 88 ExceptionParser p; 89 if (hasProperty(SEPARATOR_PROPERTY)) { 90 p = new ExceptionParser(getStringProperty(SEPARATOR_PROPERTY).charAt(0)); 91 } else { 92 p = new ExceptionParser(DEFAULT_SEPARATOR); 93 } 94 exceptions = p.parse(getStringProperty(EXCEPTION_LIST_PROPERTY)); 95 } else if (hasProperty(EXCEPTION_FILE_NAME_PROPERTY)) { 96 exceptions = new HashSet(); 97 try { 98 LineNumberReader reader = new LineNumberReader(new BufferedReader(new FileReader(new File(getStringProperty(EXCEPTION_FILE_NAME_PROPERTY))))); 99 String line = null; 100 while ((line = reader.readLine()) != null) { 101 exceptions.add(line); 102 } 103 reader.close(); 104 } catch (IOException ioe) { 105 ioe.printStackTrace(); 106 } 107 } 108 109 super.visit(node, data); 110 111 int threshold = getIntProperty("threshold"); 112 for (Iterator i = literals.keySet().iterator(); i.hasNext();) { 113 String key = (String) i.next(); 114 List occurrences = (List) literals.get(key); 115 if (occurrences.size() >= threshold) { 116 Object[] args = new Object[]{new Integer(occurrences.size()), new Integer(((SimpleNode) occurrences.get(0)).getBeginLine())}; 117 String msg = MessageFormat.format(getMessage(), args); 118 RuleContext ctx = (RuleContext) data; 119 ctx.getReport().addRuleViolation(createRuleViolation(ctx, ((SimpleNode) occurrences.get(0)).getBeginLine(), msg)); 120 } 121 } 122 return data; 123 } 124 125 public Object visit(ASTLiteral node, Object data) { 126 if (!hasAtLeastSevenParents(node) || (!seventhParentIsAnArgList(node) && !seventhParentIsAVariableInitializer(node))) { 127 return data; 128 } 129 130 // just catching strings of 3 chars or more for now - no numbers 131 if (node.getImage() == null || node.getImage().indexOf('\"') == -1 || node.getImage().length() < 3) { 132 return data; 133 } 134 135 // skip any exceptions 136 if (exceptions.contains(node.getImage().substring(1, node.getImage().length()-1))) { 137 return data; 138 } 139 140 if (literals.containsKey(node.getImage())) { 141 List occurrences = (List) literals.get(node.getImage()); 142 occurrences.add(node); 143 } else { 144 List occurrences = new ArrayList(); 145 occurrences.add(node); 146 literals.put(node.getImage(), occurrences); 147 } 148 149 return data; 150 } 151 152 private boolean seventhParentIsAVariableInitializer(ASTLiteral node) { 153 return node.jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent() instanceof ASTVariableInitializer; 154 } 155 156 private boolean seventhParentIsAnArgList(ASTLiteral node) { 157 return node.jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent().jjtGetParent() instanceof ASTArgumentList; 158 } 159 160 private boolean hasAtLeastSevenParents(Node node) { 161 Node currentNode = node; 162 for (int i = 0; i < 7; i++) { 163 if (currentNode instanceof ASTCompilationUnit) { 164 return false; 165 } 166 currentNode = currentNode.jjtGetParent(); 167 } 168 return true; 169 } 170 } 171

This page was automatically generated by Maven