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 }