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.ASTCompilationUnit;
9 import net.sourceforge.pmd.ast.ASTLiteral;
10 import net.sourceforge.pmd.ast.SimpleNode;
11
12 import java.io.BufferedReader;
13 import java.io.File;
14 import java.io.FileReader;
15 import java.io.IOException;
16 import java.io.LineNumberReader;
17 import java.text.MessageFormat;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 public class AvoidDuplicateLiteralsRule extends AbstractRule {
27
28 public static class ExceptionParser {
29
30 private static final char ESCAPE_CHAR = '//';
31 private char delimiter;
32
33 public ExceptionParser(char delimiter) {
34 this.delimiter = delimiter;
35 }
36
37 public Set parse(String in) {
38 Set result = new HashSet();
39 StringBuffer currentToken = new StringBuffer();
40 boolean inEscapeMode = false;
41 for (int i = 0; i < in.length(); i++) {
42 if (inEscapeMode) {
43 inEscapeMode = false;
44 currentToken.append(in.charAt(i));
45 continue;
46 }
47 if (in.charAt(i) == ESCAPE_CHAR) {
48 inEscapeMode = true;
49 continue;
50 }
51 if (in.charAt(i) == delimiter) {
52 result.add(currentToken.toString());
53 currentToken = new StringBuffer();
54 } else {
55 currentToken.append(in.charAt(i));
56 }
57 }
58 if (currentToken.length() > 0) {
59 result.add(currentToken.toString());
60 }
61 return result;
62 }
63 }
64
65 private static final char DEFAULT_SEPARATOR = ',';
66 private static final String EXCEPTION_LIST_PROPERTY = "exceptionlist";
67 private static final String SEPARATOR_PROPERTY = "separator";
68 private static final String EXCEPTION_FILE_NAME_PROPERTY = "exceptionfile";
69
70 private Map literals = new HashMap();
71 private Set exceptions = new HashSet();
72
73 public Object visit(ASTCompilationUnit node, Object data) {
74 literals.clear();
75
76 if (hasProperty(EXCEPTION_LIST_PROPERTY)) {
77 ExceptionParser p;
78 if (hasProperty(SEPARATOR_PROPERTY)) {
79 p = new ExceptionParser(getStringProperty(SEPARATOR_PROPERTY).charAt(0));
80 } else {
81 p = new ExceptionParser(DEFAULT_SEPARATOR);
82 }
83 exceptions = p.parse(getStringProperty(EXCEPTION_LIST_PROPERTY));
84 } else if (hasProperty(EXCEPTION_FILE_NAME_PROPERTY)) {
85 exceptions = new HashSet();
86 LineNumberReader reader = null;
87 try {
88 reader = new LineNumberReader(new BufferedReader(new FileReader(new File(getStringProperty(EXCEPTION_FILE_NAME_PROPERTY)))));
89 String line;
90 while ((line = reader.readLine()) != null) {
91 exceptions.add(line);
92 }
93 } catch (IOException ioe) {
94 ioe.printStackTrace();
95 } finally {
96 try {
97 if (reader != null)
98 reader.close();
99 } catch (IOException ioe) {
100 ioe.printStackTrace();
101 }
102 }
103 }
104
105 super.visit(node, data);
106
107 int threshold = getIntProperty("threshold");
108 for (Iterator i = literals.keySet().iterator(); i.hasNext();) {
109 String key = (String) i.next();
110 List occurrences = (List) literals.get(key);
111 if (occurrences.size() >= threshold) {
112 Object[] args = new Object[]{key, new Integer(occurrences.size()), new Integer(((SimpleNode) occurrences.get(0)).getBeginLine())};
113 String msg = MessageFormat.format(getMessage(), args);
114 RuleContext ctx = (RuleContext) data;
115 ctx.getReport().addRuleViolation(createRuleViolation(ctx, ((SimpleNode) occurrences.get(0)), msg));
116 }
117 }
118 return data;
119 }
120
121 public Object visit(ASTLiteral node, Object data) {
122
123 if (node.getImage() == null || node.getImage().indexOf('\"') == -1 || node.getImage().length() < 5) {
124 return data;
125 }
126
127
128 if (exceptions.contains(node.getImage().substring(1, node.getImage().length() - 1))) {
129 return data;
130 }
131
132 if (literals.containsKey(node.getImage())) {
133 List occurrences = (List) literals.get(node.getImage());
134 occurrences.add(node);
135 } else {
136 List occurrences = new ArrayList();
137 occurrences.add(node);
138 literals.put(node.getImage(), occurrences);
139 }
140
141 return data;
142 }
143 }
144