SuspiciousOctalEscapeRule.java
01 package net.sourceforge.pmd.lang.java.rule.controversial;
02 
03 import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
04 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
05 
06 public class SuspiciousOctalEscapeRule extends AbstractJavaRule {
07 
08     @Override
09     public Object visit(ASTLiteral node, Object data) {
10         if (node.isStringLiteral()) {
11             String image = node.getImage();
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 != -&& 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                 else if (first == '\\') {
61                     slash++;
62                 }
63 
64                 offset = slash + 1;
65             }
66         }
67 
68         return super.visit(node, data);
69     }
70 
71     private boolean isOctal(char c) {
72         return c >= '0' && c <= '7';
73     }
74 
75     private boolean isDecimal(char c) {
76         return c >= '0' && c <= '9';
77     }
78 }