/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.controversial;
import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
public class SuspiciousOctalEscapeRule extends AbstractJavaRule {
@Override
public Object visit(ASTLiteral node, Object data) {
if (node.isStringLiteral()) {
String image = node.getImage();
// trim quotes
String s = image.substring(1, image.length() - 1);
// process escape sequences
int offset = 0;
for (int slash = s.indexOf('\\', offset); slash != -1
&& slash < s.length() - 1; slash = s.indexOf('\\', offset)) {
String escapeSequence = s.substring(slash + 1);
char first = escapeSequence.charAt(0);
offset = slash + 1; // next offset - after slash
if (isOctal(first)) {
if (escapeSequence.length() > 1) {
char second = escapeSequence.charAt(1);
if (isOctal(second)) {
if (escapeSequence.length() > 2) {
char third = escapeSequence.charAt(2);
if (isOctal(third)) {
// this is either a three digit octal escape
// or a two-digit
// octal escape followed by an octal digit.
// the value of
// the first digit in the sequence
// determines which is the
// case
if (first != '0' && first != '1' && first != '2' && first != '3') {
// VIOLATION: it's a two-digit octal
// escape followed by
// an octal digit -- legal but very
// confusing!
addViolation(data, node);
} else {
// if there is a 4th decimal digit, it
// could never be part of
// the escape sequence, which is
// confusing
if (escapeSequence.length() > 3) {
char fourth = escapeSequence.charAt(3);
if (isDecimal(fourth)) {
addViolation(data, node);
}
}
}
} else if (isDecimal(third)) {
// this is a two-digit octal escape followed
// by a decimal digit
// legal but very confusing
addViolation(data, node);
}
}
} else if (isDecimal(second)) {
// this is a one-digit octal escape followed by a
// decimal digit
// legal but very confusing
addViolation(data, node);
}
}
} else if (first == '\\') {
offset++;
}
}
}
return super.visit(node, data);
}
private boolean isOctal(char c) {
return c >= '0' && c <= '7';
}
private boolean isDecimal(char c) {
return c >= '0' && c <= '9';
}
}