Copying JSR-303 annotations (and others) to getters?

664 views
Skip to first unread message

Andrew Phillips

unread,
Jul 14, 2010, 6:04:22 AM7/14/10
to Project Lombok
Is there any way to get Lombok to copy/move annotations other than
@Nonnull to generated getters? For example, for Hibernate Validator
(JSR 303 reference implementation), the location of the validation
annotation specifies the access type. Specifically, if the annotation
is on the *field* the framework will use reflection to access the
field directly [1].

From documentation for Lombok I gather that @Nonnull is already
recognized and copied in this way [2]. Is there any way Lombok could
be configured/extended to do this for other annotations too?

[1] http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html/validator-usingvalidator.html#validator-usingvalidator-annotate
[2] http://projectlombok.org/features/GetterSetter.html

Example:

public class Host {
@NotNull @Nonnull
@Getter @Setter private String address;
@Getter @Setter private String osFamily;
}

becomes (using delombok)

public class Host {
@NotNull
@Nonnull
private String address;
private String osFamily;

@Nonnull
public String getAddress() {
return address;
}

public void setAddress(@Nonnull final String address) {
if (address == null) throw new
java.lang.NullPointerException("address");
this.address = address;
}

public String getOsFamily() {
return osFamily;
}

public void setOsFamily(final String osFamily) {
this.osFamily = osFamily;
}
}

Andrew Phillips

unread,
Jul 14, 2010, 7:16:46 AM7/14/10
to Project Lombok
> Is there any way to get Lombok to copy/move annotations other than
> @Nonnull to generated getters? For example, for Hibernate Validator
> (JSR 303 reference implementation), the location of the validation
> annotation specifies the access type.

I should add: for JSR 303 the annotations shouldn't be *copied* but
*moved*, if desired. The spec explicitly advises [1] not to have
annotations in both places as that would cause double-validation.

ap

[1] http://people.redhat.com/~ebernard/validation/#constraintdeclarationvalidationprocess-requirements-property

Roel Spilker

unread,
Jul 14, 2010, 7:20:56 AM7/14/10
to Project Lombok
Hi Andrew,

If I recall correctly, Lombok will copy any NonNull/NotNull
annotation, regardless of the package and casing. At least, that's how
we originally did it. So there is no need for you do specify both
Nonnull and NotNull unless you have a need for both of them. We are
thinking about a way to make the handling of annotations on setters
and getters more flexible, but it does not have the highest priority.

One more thing: If I understood correctly, all work on JSR303 has been
suspended.

Roel
> [1]http://people.redhat.com/~ebernard/validation/#constraintdeclarationv...

Roel Spilker

unread,
Jul 14, 2010, 7:23:27 AM7/14/10
to Project Lombok
My last statement was incorrect, I meant 305 has been suspended, that
also specifies a NonNull annotation.

Reinier Zwitserloot

unread,
Jul 14, 2010, 9:52:42 AM7/14/10
to project...@googlegroups.com
We don't have this on the agenda right now because we don't know of a proper way to do it, short of maintaining a 'hardcoded' database of the right thing to do in the face of any given annotation. For example, @NonNull should be moved to the parameter for the setter, to the method for the getter, and should also remain on the field.

But for the validator, we should move the annotation to the getter, do nothing to the setter, and remove it from the field.

The question is: How do we know what to do? One trick that comes to mind is to look if an annotation is not legal on fields but is legal on methods and/or parameters, and thus know that the annotation should definitely be moved, but this doesn't for example work with hibernate validation; it's legal on both methods and fields, so we'd guesstimate to copy the annotation to the getter and leave it on the field, but that would be the wrong thing to do. Also, we can't actually implement this scheme today due to lack of resolution.

As long as the answer remains: Maintain a database mapping annotation names to the 'right' thing to do, it'll remain low priority, because that's not a particularly nice solution.

 --Reinier Zwitserloot



--
You received this message because you are subscribed to the Google
Groups group for http://projectlombok.org/

To post to this group, send email to project...@googlegroups.com
To unsubscribe from this group, send email to
project-lombo...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/project-lombok?hl=en

Andrew Phillips

unread,
Jul 14, 2010, 10:05:26 AM7/14/10
to Project Lombok
> As long as the answer remains: Maintain a database mapping annotation names
> to the 'right' thing to do, it'll remain low priority, because that's not a
> particularly nice solution.

Completely agree with that. I haven't looked at the source code (still
neted to download the sources JAR from the Maven repo), but I imagine
you must have some place to store the "special case" logic for
@Nonnull. So if that could be factored into a "descriptor", and if
there were a way to add additional descriptors via an SPI, other
annotations or even just on the classpath, that could be one way.

An uglier solution that came to mind would be something like:

public class Host {
@NotNull @Nonnull
@Getter(annotationsToTake = { NotNull.class }) @Setter
private String address;
...

where you could somehow indicate a) which classes of annotations to
process and b) whether to copy or move them. Of course there's lots of
syntactic variations here such as

public class Host {
@NotNull @Nonnull
@GetterAnnotations(annotations = { NotNull.class },
preserveOnField = false)
@Getter @Setter private String address;

Fijne dag verder ;-)

ap

Reinier Zwitserloot

unread,
Jul 14, 2010, 10:20:38 AM7/14/10
to project...@googlegroups.com
Hmm, maybe a combination with a db of 'do the right thing', powered by SPI of course (NonNull and friends is currently hardcoded. In retrospect we didn't realize this was such a can of worms, but as we've released a widely used version of lombok that supported it we can't very well drop it now), together with the option to override this behaviour using parameters on the annotation, might be the best we could possibly do. That, and, once resolution is built into lombok, the option to use lombok meta annotations (annotations meant to go on @interface declarations) to explain the right thing to do, which lombok will automatically pick up and apply. The db is then a backup for those classes that are commonly used but refuse to release a version that works with the lombok meta annotations.

 --Reinier Zwitserloot




--

Andrew Phillips

unread,
Jul 15, 2010, 3:40:33 AM7/15/10
to Project Lombok
Any pointers to where in the code I should best start looking? Just
being lazy here... ;-)

ap

Reinier Zwitserloot

unread,
Jul 15, 2010, 2:00:32 PM7/15/10
to project...@googlegroups.com
line 122 of eclipse's HandleGetter
line 160 of eclipse's HandleSetter
line 141 of javac's HandleGetter
line 140 of javac's HandleSetter
line 181 and 245 of eclipse's HandleData
line 131 and 189 of javac's HandleData

as you can see, the copy-some-annotations system was never really designed for scaling.


 --Reinier Zwitserloot




ap

Andrew Phillips

unread,
Jul 15, 2010, 6:04:52 PM7/15/10
to Project Lombok
> line 122 of eclipse's HandleGetter
> line 160 of eclipse's HandleSetter
> line 141 of javac's HandleGetter
> line 140 of javac's HandleSetter
> line 181 and 245 of eclipse's HandleData
> line 131 and 189 of javac's HandleData
>
> as you can see, the copy-some-annotations system was never really designed
> for scaling.

*grin*. Thanks!

ap

Maaartin-1

unread,
Nov 24, 2010, 6:59:58 PM11/24/10
to project...@googlegroups.com
I've got an idea to this old problem.

On 10-07-14 16:20, Reinier Zwitserloot wrote:
> Hmm, maybe a combination with a db of 'do the right thing', powered by
> SPI of course (NonNull and friends is currently hardcoded. In retrospect
> we didn't realize this was such a can of worms, but as we've released a
> widely used version of lombok that supported it we can't very well drop
> it now), together with the option to override this behaviour using
> parameters on the annotation, might be the best we could possibly do.

IMHO, a separate annotation specifying the behavior would be better. You
could place it on the whole class and specify that each @Foo should be
copied to the getter and to the argument of setter and kept on the
field. You could override it on a field, but I don't think you'd need it
often.

It's more general than the now deprecated
AnyAnnotation[] onMethod() default {};
since you can define all the needed annotations on fields and let lombok
move there where they belong to. This way the annotations may be
parametrized and you may specify their target locations easily and exactly.

A non-trivial example may look like

@FieldReannotation({
// move all Inject and Named annotations to setters
@PlaceAnnotations(ofType={Inject.class, Named.class}, on="S"),
// keep all NonNull annotations on fields
// and copy them to getters and setters' arguments
@PlaceAnnotations(ofType=NonNull.class, on="FGA"),
})
@Getter @Setter
public class FieldReannotationExample1 {
@Inject @Named("NameOfTheBeast") @NonNull
private String name;
}

I was thinking about using an enum for the destination, but it's not
necessary, all errors will show up immediately and the Javadoc on "on"
explains how it works, see
http://dl.dropbox.com/u/4971686/101123/reanno/FieldReannotation.java
http://dl.dropbox.com/u/4971686/101123/reanno/PlaceAnnotations.java
A more complex example
http://dl.dropbox.com/u/4971686/101123/reanno/FieldReannotationExample2.java

IMHO, @FieldReannotation should be @Inherited, since this minimizes the
boilerplate a bit. Actually, a global configuration would be ideal, I
think it could be done without any magic and without run-time dependency
on lombok, however, it needs to access other files in project, is this
already possible?

> That, and, once resolution is built into lombok, the option to use
> lombok meta annotations (annotations meant to go on @interface
> declarations) to explain the right thing to do, which lombok will
> automatically pick up and apply. The db is then a backup for those
> classes that are commonly used but refuse to release a version that
> works with the lombok meta annotations.

Neither the db not those meta annotations can help always as there are
annotations (like JPA @Id) to be placed either on fields or on
accessors, and the choice is up to the user.

There are annotations which must not be copied to accessors since
they're illegal there, but current version of Lombok does it, for
example when placing
com.google.inject.internal.Nullable
on a field with @Getter I get "The annotation @Nullable is disallowed
for this location". IMHO, this is a bug, lombok should never copy an
annotation to a place where it's not allowed (this is possibly hard to
find out) and always remove it from a field when it's not allowed there.

In my proposal, even annotation with @Target(ElementType.METHOD) would
get placed on fields and moved to the right place by lombok. I suppose,
it's doable and maybe even easy?

Reinier Zwitserloot

unread,
Nov 25, 2010, 1:31:54 AM11/25/10
to Project Lombok
onMethod is NOT deprecated. AnyAnnotation is 'deprecated' because its
a dummy. you're not actually supposed to make any @AnyAnnotations. If
you do by accident, the @Deprecated ensures it'll look funny in your
IDE.

@Inherited won't do squat, @Getter and @Setter run before resolution
and will continue to do so for the forseeable future (Technically
@Getter/@Setter at least doesn't run afoul of phases, but at least for
now supporting resolution-based transformers is much more difficult.
We need resolution to learn about @FieldReannotation annotations on
parent classes).

I also flat out don't see the upside. Its way more code, and far more
obscure. If we do use it, we'd definitely use enums instead of cryptic
strings. Sure, we can generate compile-time errors, but Random Joe
reading that stuff will just go: What the heck is that?!?.

I might be mistaken (and please call me on it if I am), but if Random
Joe sees this:

@Getter(onMethod=@Deprecated) private String foo;

I have hopes that he might just figure out what that's supposed to
mean without looking at @Getter's javadoc or knowing about Project
Lombok.

NB: We copy inject.internal.Nullable because we copy everything that's
called 'Nullable'. No resolution, so we certainly don't know that
"com.google.inject.internal.Nullable" isn't actually legal on methods
(we can of course hard-code that), and we'd have to use our own not-
quite-perfect (as in, can fail on star imports) type resolver to
figure out that a "@Nullable" is in fact referring to
com.google.inject.internal.Nullable, and then decide not to copy it.
If com.google.inject.internal.Nullable is not an academic case, please
file an issue report for it so we can set that up.

So doable? Possibly. Easy: No. Resolution is hard, unfortunately. val
and @Delegate are already very hard to get right (i.e. totally broken
on netbeans right now, as an example), and those are far more
interesting than moving annotations around.
> A more complex examplehttp://dl.dropbox.com/u/4971686/101123/reanno/FieldReannotationExampl...
Reply all
Reply to author
Forward
0 new messages