View Javadoc

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                  } 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          switch (c) {
73              case '0':
74              case '1':
75              case '2':
76              case '3':
77              case '4':
78              case '5':
79              case '6':
80              case '7':
81                  return true;
82              default:
83                  return false;
84          }
85      }
86  
87      private boolean isDecimal(char c) {
88          switch (c) {
89              case '0':
90              case '1':
91              case '2':
92              case '3':
93              case '4':
94              case '5':
95              case '6':
96              case '7':
97              case '8':
98              case '9':
99                  return true;
100             default:
101                 return false;
102         }
103     }
104 }