View Javadoc

1   package net.sourceforge.pmd.rules;
2   
3   import net.sourceforge.pmd.AbstractRule;
4   import net.sourceforge.pmd.RuleContext;
5   import net.sourceforge.pmd.ast.ASTLiteral;
6   
7   public class SuspiciousOctalEscape extends AbstractRule {
8   
9       public Object visit(ASTLiteral node, Object data) {
10          RuleContext ctx = (RuleContext) data;
11          String image = node.getImage();
12          if (image != null && image.startsWith("\"")) // make sure it's a string literal
13          {
14              // trim quotes
15              String s = image.substring(1, image.length() - 1);
16  
17              // process escape sequences
18              int offset = 0;
19              for (int slash = s.indexOf('//', offset);
20                   slash != -1 && slash < s.length() - 1;
21                   slash = s.indexOf('//', offset)) {
22                  String escapeSequence = s.substring(slash + 1);
23                  char first = escapeSequence.charAt(0);
24                  if (isOctal(first)) {
25                      if (escapeSequence.length() > 1) {
26                          char second = escapeSequence.charAt(1);
27                          if (isOctal(second)) {
28                              if (escapeSequence.length() > 2) {
29                                  char third = escapeSequence.charAt(2);
30                                  if (isOctal(third)) {
31                                      // this is either a three digit octal escape or a two-digit
32                                      // octal escape followed by an octal digit. the value of
33                                      // the first digit in the sequence determines which is the
34                                      // case
35                                      if (first != '0' && first != '1' && first != '2' && first != '3') {
36                                          // VIOLATION: it's a two-digit octal escape followed by
37                                          // an octal digit -- legal but very confusing!
38                                          ctx.getReport().addRuleViolation(createRuleViolation(ctx, node));
39                                      } else {
40                                          // if there is a 4th decimal digit, it could never be part of
41                                          // the escape sequence, which is confusing
42                                          if (escapeSequence.length() > 3) {
43                                              char fourth = escapeSequence.charAt(3);
44                                              if (isDecimal(fourth)) {
45                                                  ctx.getReport().addRuleViolation(createRuleViolation(ctx, node));
46                                              }
47                                          }
48                                      }
49  
50                                  } else if (isDecimal(third)) {
51                                      // this is a two-digit octal escape followed by a decimal digit
52                                      // legal but very confusing
53                                      ctx.getReport().addRuleViolation(createRuleViolation(ctx, node));
54                                  }
55                              }
56                          } else if (isDecimal(second)) {
57                              // this is a one-digit octal escape followed by a decimal digit
58                              // legal but very confusing
59                              ctx.getReport().addRuleViolation(createRuleViolation(ctx, node));
60                          }
61                      }
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 }