[JSR308] Suggestion (and example) for annotations that take any annotation or for annotation inheritance

Ciera Jaspan ciera at cmu.edu
Fri Oct 3 02:15:46 EDT 2008


Hi everyone,

I, and several others, have a need for either annotations that can take any
annotation or (preferably) for annotation inheritance. While annotation
inheritance is preferred, I understand there may be good reasons this was
left out of JSR-175 initially. If annotation inheritance is not possible,
having annotations which can take any annotation would at least help. I've
provided examples that motivate each of these suggestions below, from a
static analysis tool we are building. Please let me know if you have any
questions about the examples given, or need further examples for motivation.


1) Annotations that can take ANY annotation as a property (as opposed to a
specific annotation type)

Example:
We have some annotations which signify the relationships between different
objects at runtime. For example, the user-defined annotation:

@Child({"this", "ret"})
Object getItem(int ndx);

signifies that the object returned from getItem() is a child of 'this'.

We later use these annotations to define pre and post conditions for other
methods. This is done with the annotation @Constraint. Shown below is a
constraint which is enabled only when the three relationships in the trigger
array are true, and the post condition is a single relationship set to
false, as shown.

@Constraint(
   trigger={"Child(ddl, this)", "Type(ddl, DropDownListCtrl.class)",
"Equals(select, true)"},
   post={"!Selected(ddl)"}
)

This is somewhat awkward though. We have annotations in some places in the
code (such as the annotation on getItem), but in the constraints, we use
them as a string rather than as an annotation. What we really want to is
continue to treat these as annotations. For example, we could turn our
@Constraint annotation above into:

@Constraint(
   trigger={@Child({"ddl", "this"}), at Type({"ddl", DropDownListCtrl.class}),
@Equals({"select", "true"})},
   post={@Selected({"ddl", REMOVE})}
)

This provides several benefits. Of course, in this case, there is
consistency for the user. More importantly, with more structure, this is
more checkable using existing Java tooling. With the string representation,
a tool must parse the strings itself and report any errors. The annotations,
however, provide a structure that is already checked by the Java parser.

To do this, we would need, at minimum, for annotations to take any
annotation as a parameter (as opposed to one of a particular type). This
would then allow the above situation, where we have an array of annotations
of different types.

Alternately, annotation inheritance would also have sufficed in this
example, if Child, Type, and Equals all had a common base annotation (see
below).


2) Annotation inheritance
Annotation inheritance was explicitly left off in JSR-175, for the reason of
promoting composition over inheritance. However, this means that a) the
above scenario is not possible and b) the syntax is unweildy.

As implied earlier, most of our annotations, such as Child, have a common
base class. These user-defined annotations must have the following
properties:

String[] value;
Effect effect; //Effect is an enum
String test;

Currently, we signal that an annotation has these three properties by using
the meta-annotation @Relation. Anything using this meta-annotation is
assumed to have these properties. Therefore, Child looks like this:

@Relation
public @interface Child {
   public String[] value();
   public Effect effect() default Effect.ADD;
   public String test() default "";
}

As shown before, we use it like this:
@Child({"this", "ret"})
Object getItem(int ndx);

Since Child is a user-defined property, we are depending on the programmer
(not the developer of this tool) to define this properly.

If we were supposed to use composition, then this would imply that Relation
should have had these properties, and the definition of Child then looks
like:

public @interface Child {
   public Relation value();
}

Of course, that means that to use Child, we have to do:
@Child(@Relation({"this", "ret"}))
Object getItem(int ndx);

which is rather confusing.

Instead, we'd rather show the inheritance here, which was really what we
were doing anyway:

public @interface Relation {
   public String[] value();
   public Effect effect() default Effect.ADD;
   public String test() default "";
}

public @interface Child extends Relation {
}

and we would use it like this:

@Child({"this", "ret"})
Object getItem(int ndx);

This keeps the syntax simple, and it makes it checkable. Notice also that if
we had inheritance for annotations, problem 1 is no longer an issue since
logically, we are just wanting inheritance in the first place.

Thanks,
Ciera

-- 
Ciera N.C. Jaspan
Institute for Software Research
Carnegie Mellon University
www.cs.cmu.edu/~cchristo <http://www.cs.cmu.edu/%7Ecchristo>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: https://lists.csail.mit.edu/pipermail/jsr308/attachments/20081003/acccf545/attachment.htm 


More information about the JSR308 mailing list