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 SuspiciousOctalEscapeRule extends AbstractRule
8 {
9 public Object visit(ASTLiteral node, Object data)
10 {
11 RuleContext ctx = (RuleContext) data;
12 String image = node.getImage();
13 if (image != null && image.startsWith("\"")) // make sure it's a string literal
14 {
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 {
24 String escapeSequence = s.substring(slash+1);
25 char first = escapeSequence.charAt(0);
26 if (isOctal(first))
27 {
28 if (escapeSequence.length() > 1)
29 {
30 char second = escapeSequence.charAt(1);
31 if (isOctal(second))
32 {
33 if (escapeSequence.length() > 2)
34 {
35 char third = escapeSequence.charAt(2);
36 if (isOctal(third))
37 {
38 // this is either a three digit octal escape or a two-digit
39 // octal escape followed by an octal digit. the value of
40 // the first digit in the sequence determines which is the
41 // case
42 if (first != '0' && first != '1' && first != '2' && first != '3')
43 {
44 // VIOLATION: it's a two-digit octal escape followed by
45 // an octal digit -- legal but very confusing!
46 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
47 }
48 else
49 {
50 // if there is a 4th decimal digit, it could never be part of
51 // the escape sequence, which is confusing
52 if (escapeSequence.length() > 3)
53 {
54 char fourth = escapeSequence.charAt(3);
55 if (isDecimal(fourth))
56 {
57 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
58 }
59 }
60 }
61
62 }
63 else if (isDecimal(third))
64 {
65 // this is a two-digit octal escape followed by a decimal digit
66 // legal but very confusing
67 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
68 }
69 }
70 }
71 else if (isDecimal(second))
72 {
73 // this is a one-digit octal escape followed by a decimal digit
74 // legal but very confusing
75 ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
76 }
77 }
78 }
79
80 offset = slash + 1;
81 }
82 }
83
84 return super.visit(node, data);
85 }
86
87 private boolean isOctal(char c)
88 {
89 switch (c)
90 {
91 case '0':
92 case '1':
93 case '2':
94 case '3':
95 case '4':
96 case '5':
97 case '6':
98 case '7':
99 return true;
100 default:
101 return false;
102 }
103 }
104
105 private boolean isDecimal(char c)
106 {
107 switch (c)
108 {
109 case '0':
110 case '1':
111 case '2':
112 case '3':
113 case '4':
114 case '5':
115 case '6':
116 case '7':
117 case '8':
118 case '9':
119 return true;
120 default:
121 return false;
122 }
123 }
124 }