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