1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.lang.java.rule.controversial;
5
6 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
7 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
8
9 public class SuspiciousOctalEscapeRule extends AbstractJavaRule {
10
11 @Override
12 public Object visit(ASTLiteral node, Object data) {
13 if (node.isStringLiteral()) {
14 String image = node.getImage();
15 // trim quotes
16 String s = image.substring(1, image.length() - 1);
17
18 // process escape sequences
19 int offset = 0;
20 for (int slash = s.indexOf('\\', offset);
21 slash != -1 && slash < s.length() - 1;
22 slash = s.indexOf('\\', offset)) {
23 String escapeSequence = s.substring(slash + 1);
24 char first = escapeSequence.charAt(0);
25 if (isOctal(first)) {
26 if (escapeSequence.length() > 1) {
27 char second = escapeSequence.charAt(1);
28 if (isOctal(second)) {
29 if (escapeSequence.length() > 2) {
30 char third = escapeSequence.charAt(2);
31 if (isOctal(third)) {
32 // this is either a three digit octal escape or a two-digit
33 // octal escape followed by an octal digit. the value of
34 // the first digit in the sequence determines which is the
35 // case
36 if (first != '0' && first != '1' && first != '2' && first != '3') {
37 // VIOLATION: it's a two-digit octal escape followed by
38 // an octal digit -- legal but very confusing!
39 addViolation(data, node);
40 } else {
41 // if there is a 4th decimal digit, it could never be part of
42 // the escape sequence, which is confusing
43 if (escapeSequence.length() > 3) {
44 char fourth = escapeSequence.charAt(3);
45 if (isDecimal(fourth)) {
46 addViolation(data, node);
47 }
48 }
49 }
50
51 } else if (isDecimal(third)) {
52 // this is a two-digit octal escape followed by a decimal digit
53 // legal but very confusing
54 addViolation(data, node);
55 }
56 }
57 } else if (isDecimal(second)) {
58 // this is a one-digit octal escape followed by a decimal digit
59 // legal but very confusing
60 addViolation(data, node);
61 }
62 }
63 } else if (first == '\\') {
64 slash++;
65 }
66
67 offset = slash + 1;
68 }
69 }
70
71 return super.visit(node, data);
72 }
73
74 private boolean isOctal(char c) {
75 return c >= '0' && c <= '7';
76 }
77
78 private boolean isDecimal(char c) {
79 return c >= '0' && c <= '9';
80 }
81 }