package checkers.regex;
import checkers.regex.quals.Regex;
import java.util.regex.Pattern;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.Tree;
import checkers.basetype.BaseTypeChecker;
import checkers.types.AnnotatedTypeMirror;
import checkers.types.BasicAnnotatedTypeFactory;
import checkers.types.TreeAnnotator;
import checkers.util.TreeUtils;
/**
* Adds {@link Regex} to the type of tree, in two cases:
*
* <ol>
*
* <li value="1">a {@code String} literal that is a valid regular expression</li>
*
* <li value="2">a {@code String} concatenation tree of two valid regular
* expression values.
*
* </ol>
*
* Adds {@link Regex} to the type of each {@code String} literal that is
* a syntactically valid regular expression.
*/
public class RegexAnnotatedTypeFactory extends BasicAnnotatedTypeFactory<RegexChecker> {
public RegexAnnotatedTypeFactory(RegexChecker checker,
CompilationUnitTree root) {
super(checker, root);
}
@Override
public TreeAnnotator createTreeAnnotator(RegexChecker checker) {
return new RegexTreeAnnotator(checker);
}
private class RegexTreeAnnotator extends TreeAnnotator {
public RegexTreeAnnotator(BaseTypeChecker checker) {
super(checker, RegexAnnotatedTypeFactory.this);
}
/**
* Case 1: valid regular expression String literal
*/
@Override
public Void visitLiteral(LiteralTree tree, AnnotatedTypeMirror type) {
if (!type.isAnnotated()
&& tree.getKind() == Tree.Kind.STRING_LITERAL
&& isRegex((String)((LiteralTree)tree).getValue())) {
type.addAnnotation(Regex.class);
}
return super.visitLiteral(tree, type);
}
/**
* Case 2: concatenation of two regular expression Strings literals
*/
@Override
public Void visitBinary(BinaryTree tree, AnnotatedTypeMirror type) {
if (!type.isAnnotated()
&& TreeUtils.isStringConcatenation(tree)) {
AnnotatedTypeMirror lExpr = getAnnotatedType(tree.getLeftOperand());
AnnotatedTypeMirror rExpr = getAnnotatedType(tree.getRightOperand());
if (lExpr.hasAnnotation(Regex.class)
&& rExpr.hasAnnotation(Regex.class))
type.addAnnotation(Regex.class);
}
return super.visitBinary(tree, type);
}
}
/**
* Returns true iff {@code str} is a valid regular expression.
*/
private static boolean isRegex(String str) {
try {
Pattern.compile(str);
return true;
} catch (Exception e) {
return false;
}
}
}