blob: 2a155fb0627382aea95e950ec6298d81aace10f6 [file] [log] [blame]
package annotations.el;
import java.util.LinkedHashSet;
import java.util.Set;
import annotations.Annotation;
import annotations.field.AnnotationAFT;
import annotations.field.AnnotationFieldType;
/*>>>
import org.checkerframework.checker.nullness.qual.*;
*/
/**
* A DefCollector supplies a visitor for the annotation definitions in an
* AScene. First, call the DefCollector constructor passing the AScene.
* Then, call the visit method.
* This class exists primarily for the benefit of
* {@link annotations.io.IndexFileWriter#write(AScene, Writer)}.
*/
public abstract class DefCollector {
// The set of all definitions in the Scene, which the visitor iterates
// over.
private final Set<AnnotationDef> defs;
/**
* Constructs a new {@link DefCollector}, which immediately collects all
* the definitions from annotations the given scene. Next call
* {@link #visit} to have the definitions passed back to you in topological
* order. If the scene contains two irreconcilable definitions of the
* same annotation type, a {@link DefException} is thrown.
*/
public DefCollector(AScene s)
throws DefException {
defs = new LinkedHashSet<AnnotationDef>();
collect(s);
}
// The name "collect" in the methods below means to insert or add to
// the the DefCollector. "Insert" or "add" would have been better, but
// at least the methods are private.
private AnnotationDef getDef(String name) {
for (AnnotationDef def : defs) {
if (def.name.equals(name)) {
return def;
}
}
return null;
}
private void collect(AScene s)
throws DefException {
for (AElement p : s.packages.values()) {
collect(p);
}
for (AClass c : s.classes.values()) {
collect(c);
}
}
private void addToDefs(AnnotationDef d) throws DefException {
// TODO: this mimics the condition we have in collect, but
// i don't know if we need it
if (defs.contains(d)) {
return;
}
AnnotationDef oldD = getDef(d.name);
if (oldD == null) {
defs.add(d);
} else {
AnnotationDef ud = AnnotationDef.unify(oldD, d);
if (ud == null) {
throw new DefException(d.name);
}
defs.remove(oldD);
defs.add(ud);
}
}
private void collect(AnnotationDef d) throws DefException {
if (defs.contains(d)) {
return;
}
// define the fields first
for (AnnotationFieldType aft : d.fieldTypes.values()) {
if (aft instanceof AnnotationAFT) {
collect(((AnnotationAFT) aft).annotationDef);
}
}
addToDefs(d);
// TODO: In the future we want to add the defs of meta-annotations
// as well. Enable this option by uncommenting the following line.
//
// For the time-being, the parser would fail, because of possible
// circular references (e.g. Documented and Retention). When it is
// fixed, uncomment it
//
// collect((AElement)d);
}
private void collect(AElement e)
throws DefException {
for (Annotation tla : e.tlAnnotationsHere) {
AnnotationDef tld = tla.def;
if (defs.contains(tld)) {
continue;
}
AnnotationDef d = tld;
collect(d);
addToDefs(d);
}
if (e.type != null) {
collect(e.type);
}
}
private void collect(ATypeElement e)
throws DefException {
collect((AElement) e);
for (AElement it : e.innerTypes.values()) {
collect(it);
}
}
private void collect(ADeclaration d)
throws DefException {
collect((AElement) d);
for (ATypeElement ia : d.insertAnnotations.values()) {
collect(ia);
}
for (ATypeElementWithType ic : d.insertTypecasts.values()) {
collect(ic);
}
}
private void collect(AField f)
throws DefException {
collect((ADeclaration) f);
}
private void collect(AMethod m)
throws DefException {
for (ATypeElement b : m.bounds.values()) {
collect(b);
}
collect((ADeclaration) m);
collect((ATypeElement) m.returnType);
collect(m.receiver);
for (AElement p : m.parameters.values()) {
collect(p);
}
for (AField l : m.body.locals.values()) {
collect(l);
}
for (ATypeElement tc : m.body.typecasts.values()) {
collect(tc);
}
for (ATypeElement i : m.body.instanceofs.values()) {
collect(i);
}
for (ATypeElement n : m.body.news.values()) {
collect(n);
}
}
private void collect(AClass c)
throws DefException {
collect((ADeclaration) c);
for (ATypeElement b : c.bounds.values()) {
collect(b);
}
for (ATypeElement ei : c.extendsImplements.values()) {
collect(ei);
}
for (AMethod m : c.methods.values()) {
collect(m);
}
for (AField f : c.fields.values()) {
collect(f);
}
}
/**
* Override this method to perform some sort of subclass-specific
* processing on the given {@link AnnotationDef}.
*/
protected abstract void visitAnnotationDef(AnnotationDef d);
/**
* Calls {@link #visitAnnotationDef} on the definitions collected from
* the scene that was passed to the constructor. Visiting is done in
* topological order: if the definition of <code>A</code> contains a
* subannotation of type <code>B</code>, then <code>B</code> is
* guaranteed to be visited before <code>A</code>.
*/
public final void visit() {
for (AnnotationDef d : defs) {
visitAnnotationDef(d);
}
}
}