Clover coverage report - PMD - 3.9 Coverage timestamp: Tue Dec 19 2006 09:38:44 EST FRAMES NO FRAMES |
file stats: | LOC: | 140 |  | Methods: | 4
| NCLOC: | 117 |  | Classes: | 2
Source file | Conditionals | Statements | Methods | TOTAL
AvoidDuplicateLiteralsRule.java | 66.7% | 69.5% | 100% | 69.9% |

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