1 package net.sourceforge.pmd.rules;
2
3 import net.sourceforge.pmd.AbstractRule;
4 import net.sourceforge.pmd.ast.ASTLiteral;
5
6 public class SuspiciousOctalEscape extends AbstractRule {
7
8 public Object visit(ASTLiteral node, Object data) {
9 String image = node.getImage();
10 if (image != null && image.startsWith("\"")) // make sure it's a string literal
11 {
12 // trim quotes
13 String s = image.substring(1, image.length() - 1);
14
15 // process escape sequences
16 int offset = 0;
17 for (int slash = s.indexOf('//', offset);
18 slash != -1 && slash < s.length() - 1;
19 slash = s.indexOf('//', offset)) {
20 String escapeSequence = s.substring(slash + 1);
21 char first = escapeSequence.charAt(0);
22 if (isOctal(first)) {
23 if (escapeSequence.length() > 1) {
24 char second = escapeSequence.charAt(1);
25 if (isOctal(second)) {
26 if (escapeSequence.length() > 2) {
27 char third = escapeSequence.charAt(2);
28 if (isOctal(third)) {
29 // this is either a three digit octal escape or a two-digit
30 // octal escape followed by an octal digit. the value of
31 // the first digit in the sequence determines which is the
32 // case
33 if (first != '0' && first != '1' && first != '2' && first != '3') {
34 // VIOLATION: it's a two-digit octal escape followed by
35 // an octal digit -- legal but very confusing!
36 addViolation(data, node);
37 } else {
38 // if there is a 4th decimal digit, it could never be part of
39 // the escape sequence, which is confusing
40 if (escapeSequence.length() > 3) {
41 char fourth = escapeSequence.charAt(3);
42 if (isDecimal(fourth)) {
43 addViolation(data, node);
44 }
45 }
46 }
47
48 } else if (isDecimal(third)) {
49 // this is a two-digit octal escape followed by a decimal digit
50 // legal but very confusing
51 addViolation(data, node);
52 }
53 }
54 } else if (isDecimal(second)) {
55 // this is a one-digit octal escape followed by a decimal digit
56 // legal but very confusing
57 addViolation(data, node);
58 }
59 }
60 }
61
62 offset = slash + 1;
63 }
64 }
65
66 return super.visit(node, data);
67 }
68
69 private boolean isOctal(char c) {
70 switch (c) {
71 case '0':
72 case '1':
73 case '2':
74 case '3':
75 case '4':
76 case '5':
77 case '6':
78 case '7':
79 return true;
80 default:
81 return false;
82 }
83 }
84
85 private boolean isDecimal(char c) {
86 switch (c) {
87 case '0':
88 case '1':
89 case '2':
90 case '3':
91 case '4':
92 case '5':
93 case '6':
94 case '7':
95 case '8':
96 case '9':
97 return true;
98 default:
99 return false;
100 }
101 }
102 }