001: package net.sourceforge.pmd.rules;
002:
003: import net.sourceforge.pmd.AbstractRule;
004: import net.sourceforge.pmd.ast.ASTLiteral;
005:
006: public class SuspiciousOctalEscape extends AbstractRule {
007:
008: public Object visit(ASTLiteral node, Object data) {
009: String image = node.getImage();
010: if (image != null && image.startsWith("\"")) // make sure it's a string literal
011: {
012: // trim quotes
013: String s = image.substring(1, image.length() - 1);
014:
015: // process escape sequences
016: int offset = 0;
017: for (int slash = s.indexOf('\\', offset); slash != -1
018: && slash < s.length() - 1; slash = s.indexOf('\\',
019: offset)) {
020: String escapeSequence = s.substring(slash + 1);
021: char first = escapeSequence.charAt(0);
022: if (isOctal(first)) {
023: if (escapeSequence.length() > 1) {
024: char second = escapeSequence.charAt(1);
025: if (isOctal(second)) {
026: if (escapeSequence.length() > 2) {
027: char third = escapeSequence.charAt(2);
028: if (isOctal(third)) {
029: // this is either a three digit octal escape or a two-digit
030: // octal escape followed by an octal digit. the value of
031: // the first digit in the sequence determines which is the
032: // case
033: if (first != '0' && first != '1'
034: && first != '2'
035: && first != '3') {
036: // VIOLATION: it's a two-digit octal escape followed by
037: // an octal digit -- legal but very confusing!
038: addViolation(data, node);
039: } else {
040: // if there is a 4th decimal digit, it could never be part of
041: // the escape sequence, which is confusing
042: if (escapeSequence.length() > 3) {
043: char fourth = escapeSequence
044: .charAt(3);
045: if (isDecimal(fourth)) {
046: addViolation(data, node);
047: }
048: }
049: }
050:
051: } else if (isDecimal(third)) {
052: // this is a two-digit octal escape followed by a decimal digit
053: // legal but very confusing
054: addViolation(data, node);
055: }
056: }
057: } else if (isDecimal(second)) {
058: // this is a one-digit octal escape followed by a decimal digit
059: // legal but very confusing
060: addViolation(data, node);
061: }
062: }
063: }
064:
065: offset = slash + 1;
066: }
067: }
068:
069: return super .visit(node, data);
070: }
071:
072: private boolean isOctal(char c) {
073: switch (c) {
074: case '0':
075: case '1':
076: case '2':
077: case '3':
078: case '4':
079: case '5':
080: case '6':
081: case '7':
082: return true;
083: default:
084: return false;
085: }
086: }
087:
088: private boolean isDecimal(char c) {
089: switch (c) {
090: case '0':
091: case '1':
092: case '2':
093: case '3':
094: case '4':
095: case '5':
096: case '6':
097: case '7':
098: case '8':
099: case '9':
100: return true;
101: default:
102: return false;
103: }
104: }
105: }
|