View Javadoc
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  }