framework.src.org.checkerframework.common.reflection.ClassValVisitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of checker Show documentation
Show all versions of checker Show documentation
The Checker Framework enhances Java’s type system to
make it more powerful and useful. This lets software developers
detect and prevent errors in their Java programs.
The Checker Framework includes compiler plug-ins ("checkers")
that find bugs or verify their absence. It also permits you to
write your own compiler plug-ins.
package org.checkerframework.common.reflection;
import static org.checkerframework.common.reflection.ClassValAnnotatedTypeFactory.getClassNamesFromAnnotation;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.lang.model.element.AnnotationMirror;
import org.checkerframework.common.basetype.BaseTypeChecker;
import org.checkerframework.common.basetype.BaseTypeValidator;
import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.common.reflection.qual.ClassBound;
import org.checkerframework.common.reflection.qual.ClassVal;
import org.checkerframework.framework.source.Result;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import com.sun.source.tree.Tree;
public class ClassValVisitor extends BaseTypeVisitor {
public ClassValVisitor(BaseTypeChecker checker) {
super(checker);
}
@Override
protected ClassValAnnotatedTypeFactory createTypeFactory() {
return new ClassValAnnotatedTypeFactory(checker);
}
@Override
protected BaseTypeValidator createTypeValidator() {
return new ClassNameValidator(checker, this, atypeFactory);
}
}
class ClassNameValidator extends BaseTypeValidator {
public ClassNameValidator(BaseTypeChecker checker,
BaseTypeVisitor visitor, AnnotatedTypeFactory atypeFactory) {
super(checker, visitor, atypeFactory);
}
/**
* Reports an "illegal.classname" error if the type contains a classVal
* annotation with classNames that cannot possibly be valid class
* annotations.
*/
@Override
public boolean isValid(AnnotatedTypeMirror type, Tree tree) {
AnnotationMirror classVal = type.getAnnotation(ClassVal.class);
classVal = classVal == null ? type.getAnnotation(ClassBound.class)
: classVal;
if (classVal != null) {
List classNames = getClassNamesFromAnnotation(classVal);
for (String className : classNames) {
if (!isLegalClassName(className)) {
checker.report(
Result.failure("illegal.classname", className, type), tree);
}
}
}
return super.isValid(type, tree);
}
/**
* A string is a legal binary name if it has the following form:
* ((Java identifier)\.)*(Java identifier)([])*
* https://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.1
* @param className string to check
* @return true if className is a legal class name
*/
private boolean isLegalClassName(String className) {
String regex = "([^\\.\\[\\]](\\.[^\\.\\[\\]])*)*(\\[\\])*";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(className);
if (!m.matches()) {
return false;
}
className = m.group(1);
String[] identifiers = className.split(".");
for (String identifier : identifiers) {
if (!isJavaIdentifier(identifier)) {
return false;
}
}
return true;
}
/**
* Whether the given string is a Java Identifier. (This method returns true
* if the Identifier is a keyword, boolean literal, null literal.
*/
private boolean isJavaIdentifier(String identifier) {
char[] identifierChars = identifier.toCharArray();
if (!(identifierChars.length > 0 && (Character
.isJavaIdentifierStart(identifierChars[0])))) {
return false;
}
for (int i = 1; i < identifierChars.length; i++) {
if (!Character.isJavaIdentifierPart(identifierChars[i])) {
return false;
}
}
return true;
}
}