/** * If the checker class is annotated with {@link TypeQualifiers}, return an immutable set with the * same set of classes as the annotation. If the class is not so annotated, return an empty set. * * <p>Subclasses may override this method to return an immutable set of their supported type * qualifiers. * * @return the type qualifiers supported this processor, or an empty set if none * @see TypeQualifiers */ protected Set<Class<? extends Annotation>> createSupportedTypeQualifiers() { Class<?> classType = this.getClass(); TypeQualifiers typeQualifiersAnnotation = classType.getAnnotation(TypeQualifiers.class); if (typeQualifiersAnnotation == null) return Collections.emptySet(); Set<Class<? extends Annotation>> typeQualifiers = new HashSet<Class<? extends Annotation>>(); for (Class<? extends Annotation> qualifier : typeQualifiersAnnotation.value()) { typeQualifiers.add(qualifier); } return Collections.unmodifiableSet(typeQualifiers); }
/** * Invokes the constructor belonging to the class named by {@code name} having the given parameter * types on the given arguments. Returns {@code null} if the class cannot be found, or the * constructor does not exist or cannot be invoked on the given arguments. * * @param <T> the type to which the constructor belongs * @param name the name of the class to which the constructor belongs * @param paramTypes the types of the constructor's parameters * @param args the arguments on which to invoke the constructor * @return the result of the constructor invocation on {@code args}, or null if the constructor * does not exist or could not be invoked */ @SuppressWarnings("unchecked") public static <T> T invokeConstructorFor(String name, Class<?>[] paramTypes, Object[] args) { // Load the class. Class<T> cls = null; try { cls = (Class<T>) Class.forName(name); } catch (Exception e) { // no class is found, simply return null return null; } assert cls != null : "reflectively loading " + name + " failed"; // Invoke the constructor. try { Constructor<T> ctor = cls.getConstructor(paramTypes); return ctor.newInstance(args); } catch (Throwable t) { if (t instanceof InvocationTargetException) { Throwable err = t.getCause(); String msg; if (err instanceof CheckerError) { msg = err.getMessage(); } else { msg = err.toString(); } SourceChecker.errorAbort( "InvocationTargetException when invoking constructor for class " + name + "; Underlying cause: " + msg, t); } else { SourceChecker.errorAbort( "Unexpected " + t.getClass().getSimpleName() + " for " + "class " + name + " when invoking the constructor; parameter types: " + Arrays.toString(paramTypes), // + " and args: " + Arrays.toString(args), t); } return null; // dead code } }
/** * Returns the type qualifier hierarchy graph to be used by this processor. * * <p>The implementation builds the type qualifier hierarchy for the {@link * #getSupportedTypeQualifiers()} using the meta-annotations found in them. The current * implementation returns an instance of {@code GraphQualifierHierarchy}. * * <p>Subclasses may override this method to express any relationships that cannot be inferred * using meta-annotations (e.g. due to lack of meta-annotations). * * @return an annotation relation tree representing the supported qualifiers */ protected QualifierHierarchy createQualifierHierarchy() { MultiGraphQualifierHierarchy.MultiGraphFactory factory = this.createQualifierHierarchyFactory(); Elements elements = processingEnv.getElementUtils(); for (Class<? extends Annotation> typeQualifier : getSupportedTypeQualifiers()) { AnnotationMirror typeQualifierAnno = AnnotationUtils.fromClass(elements, typeQualifier); assert typeQualifierAnno != null : "Loading annotation \"" + typeQualifier + "\" failed!"; factory.addQualifier(typeQualifierAnno); // Polymorphic qualifiers can't declare their supertypes. // An error is raised if one is present. if (typeQualifier.getAnnotation(PolymorphicQualifier.class) != null) { if (typeQualifier.getAnnotation(SubtypeOf.class) != null) { // This is currently not supported. At some point we might add // polymorphic qualifiers with upper and lower bounds. errorAbort( "BaseTypeChecker: " + typeQualifier + " is polymorphic and specifies super qualifiers. " + "Remove the @checkers.quals.SubtypeOf or @checkers.quals.PolymorphicQualifier annotation from it."); } continue; } if (typeQualifier.getAnnotation(SubtypeOf.class) == null) { errorAbort( "BaseTypeChecker: " + typeQualifier + " does not specify its super qualifiers. " + "Add an @checkers.quals.SubtypeOf annotation to it."); } Class<? extends Annotation>[] superQualifiers = typeQualifier.getAnnotation(SubtypeOf.class).value(); for (Class<? extends Annotation> superQualifier : superQualifiers) { AnnotationMirror superAnno = null; superAnno = AnnotationUtils.fromClass(elements, superQualifier); factory.addSubtype(typeQualifierAnno, superAnno); } } QualifierHierarchy hierarchy = factory.build(); if (hierarchy.getTypeQualifiers().size() < 1) { errorAbort( "BaseTypeChecker: invalid qualifier hierarchy: hierarchy requires at least one annotation: " + hierarchy.getTypeQualifiers()); } return hierarchy; }
/** * Returns the appropriate visitor that type checks the compilation unit according to the type * system rules. * * <p>This implementation uses the checker naming convention to create the appropriate visitor. If * no visitor is found, it returns an instance of {@link BaseTypeVisitor}. It reflectively invokes * the constructor that accepts this checker and the compilation unit tree (in that order) as * arguments. * * <p>Subclasses have to override this method to create the appropriate visitor if they do not * follow the checker naming convention. * * @param root the compilation unit currently being visited * @return the type-checking visitor */ @Override protected SourceVisitor<?, ?> createSourceVisitor(CompilationUnitTree root) { // Try to reflectively load the visitor. Class<?> checkerClass = this.getClass(); while (checkerClass != BaseTypeChecker.class) { final String classToLoad = checkerClass.getName().replace("Checker", "Visitor").replace("Subchecker", "Visitor"); BaseTypeVisitor<?> result = invokeConstructorFor( classToLoad, new Class<?>[] {checkerClass, CompilationUnitTree.class}, new Object[] {this, root}); if (result != null) return result; checkerClass = checkerClass.getSuperclass(); } // If a visitor couldn't be loaded reflectively, return the default. return new BaseTypeVisitor<BaseTypeChecker>(this, root); }
/** * Constructs an instance of the appropriate type factory for the implemented type system. * * <p>The default implementation uses the checker naming convention to create the appropriate type * factory. If no factory is found, it returns {@link BasicAnnotatedTypeFactory}. It reflectively * invokes the constructor that accepts this checker and compilation unit tree (in that order) as * arguments. * * <p>Subclasses have to override this method to create the appropriate visitor if they do not * follow the checker naming convention. * * @param root the currently visited compilation unit * @return the appropriate type factory */ @Override public AnnotatedTypeFactory createFactory(CompilationUnitTree root) { // Try to reflectively load the type factory. Class<?> checkerClass = this.getClass(); while (checkerClass != BaseTypeChecker.class) { final String classToLoad = checkerClass .getName() .replace("Checker", "AnnotatedTypeFactory") .replace("Subchecker", "AnnotatedTypeFactory"); AnnotatedTypeFactory result = invokeConstructorFor( classToLoad, new Class<?>[] {checkerClass, CompilationUnitTree.class}, new Object[] {this, root}); if (result != null) return result; checkerClass = checkerClass.getSuperclass(); } return new BasicAnnotatedTypeFactory<BaseTypeChecker>(this, root); }