001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.rules.strings;
004:
005: import net.sourceforge.pmd.AbstractRule;
006: import net.sourceforge.pmd.ast.ASTCompilationUnit;
007: import net.sourceforge.pmd.ast.ASTLiteral;
008:
009: import java.io.BufferedReader;
010: import java.io.File;
011: import java.io.FileReader;
012: import java.io.IOException;
013: import java.io.LineNumberReader;
014: import java.util.ArrayList;
015: import java.util.HashMap;
016: import java.util.HashSet;
017: import java.util.List;
018: import java.util.Map;
019: import java.util.Set;
020:
021: public class AvoidDuplicateLiteralsRule extends AbstractRule {
022:
023: public static class ExceptionParser {
024:
025: private static final char ESCAPE_CHAR = '\\';
026: private char delimiter;
027:
028: public ExceptionParser(char delimiter) {
029: this .delimiter = delimiter;
030: }
031:
032: public Set<String> parse(String in) {
033: Set<String> result = new HashSet<String>();
034: StringBuffer currentToken = new StringBuffer();
035: boolean inEscapeMode = false;
036: for (int i = 0; i < in.length(); i++) {
037: if (inEscapeMode) {
038: inEscapeMode = false;
039: currentToken.append(in.charAt(i));
040: continue;
041: }
042: if (in.charAt(i) == ESCAPE_CHAR) {
043: inEscapeMode = true;
044: continue;
045: }
046: if (in.charAt(i) == delimiter) {
047: result.add(currentToken.toString());
048: currentToken = new StringBuffer();
049: } else {
050: currentToken.append(in.charAt(i));
051: }
052: }
053: if (currentToken.length() > 0) {
054: result.add(currentToken.toString());
055: }
056: return result;
057: }
058: }
059:
060: private static final char DEFAULT_SEPARATOR = ',';
061: private static final String EXCEPTION_LIST_PROPERTY = "exceptionlist";
062: private static final String SEPARATOR_PROPERTY = "separator";
063: private static final String EXCEPTION_FILE_NAME_PROPERTY = "exceptionfile";
064:
065: private Map<String, List<ASTLiteral>> literals = new HashMap<String, List<ASTLiteral>>();
066: private Set<String> exceptions = new HashSet<String>();
067:
068: public Object visit(ASTCompilationUnit node, Object data) {
069: literals.clear();
070:
071: if (hasProperty(EXCEPTION_LIST_PROPERTY)) {
072: ExceptionParser p;
073: if (hasProperty(SEPARATOR_PROPERTY)) {
074: p = new ExceptionParser(getStringProperty(
075: SEPARATOR_PROPERTY).charAt(0));
076: } else {
077: p = new ExceptionParser(DEFAULT_SEPARATOR);
078: }
079: exceptions = p
080: .parse(getStringProperty(EXCEPTION_LIST_PROPERTY));
081: } else if (hasProperty(EXCEPTION_FILE_NAME_PROPERTY)) {
082: exceptions = new HashSet<String>();
083: LineNumberReader reader = null;
084: try {
085: reader = new LineNumberReader(
086: new BufferedReader(
087: new FileReader(
088: new File(
089: getStringProperty(EXCEPTION_FILE_NAME_PROPERTY)))));
090: String line;
091: while ((line = reader.readLine()) != null) {
092: exceptions.add(line);
093: }
094: } catch (IOException ioe) {
095: ioe.printStackTrace();
096: } finally {
097: try {
098: if (reader != null)
099: reader.close();
100: } catch (IOException ioe) {
101: ioe.printStackTrace();
102: }
103: }
104: }
105:
106: super .visit(node, data);
107:
108: int threshold = getIntProperty("threshold");
109: for (String key : literals.keySet()) {
110: List<ASTLiteral> occurrences = literals.get(key);
111: if (occurrences.size() >= threshold) {
112: Object[] args = new Object[] {
113: key,
114: Integer.valueOf(occurrences.size()),
115: Integer.valueOf(occurrences.get(0)
116: .getBeginLine()) };
117: addViolation(data, occurrences.get(0), args);
118: }
119: }
120: return data;
121: }
122:
123: public Object visit(ASTLiteral node, Object data) {
124: // just catching strings of 5 chars or more (including the enclosing quotes) for now - no numbers
125: if (node.getImage() == null
126: || node.getImage().indexOf('\"') == -1
127: || node.getImage().length() < 5) {
128: return data;
129: }
130:
131: // skip any exceptions
132: if (exceptions.contains(node.getImage().substring(1,
133: node.getImage().length() - 1))) {
134: return data;
135: }
136:
137: if (literals.containsKey(node.getImage())) {
138: List<ASTLiteral> occurrences = literals
139: .get(node.getImage());
140: occurrences.add(node);
141: } else {
142: List<ASTLiteral> occurrences = new ArrayList<ASTLiteral>();
143: occurrences.add(node);
144: literals.put(node.getImage(), occurrences);
145: }
146:
147: return data;
148: }
149: }
|