package net.sourceforge.pmd.rules;
import net.sourceforge.pmd.AbstractRule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.ast.ASTLiteral;
public class SuspiciousOctalEscapeRule extends AbstractRule
{
public Object visit(ASTLiteral node, Object data)
{
RuleContext ctx = (RuleContext) data;
String image = node.getImage();
if (image != null && image.startsWith("\"")) // make sure it's a string literal
{
// 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);
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!
ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
}
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))
{
ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
}
}
}
}
else if (isDecimal(third))
{
// this is a two-digit octal escape followed by a decimal digit
// legal but very confusing
ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
}
}
}
else if (isDecimal(second))
{
// this is a one-digit octal escape followed by a decimal digit
// legal but very confusing
ctx.getReport().addRuleViolation(createRuleViolation(ctx, node.getBeginLine()));
}
}
}
offset = slash + 1;
}
}
return super.visit(node, data);
}
private boolean isOctal(char c)
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
return true;
default:
return false;
}
}
private boolean isDecimal(char c)
{
switch (c)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return true;
default:
return false;
}
}
}