Open 573 opened 9 years ago
This doesn't fit well into the current AutoValue Extensions API, but we could either add something to that API (for example, the ability to add an arbitrary string before the declaration of any given field), or we could have an annotation that specifies such a string. Then, in the original example, you would just specify that the string is to be "@javax.xml.bind.annotation.XmlTransient"
, and in the Objectify example you should be able to do something similar.
Both sound reasonable to me. As a String value, I'm guessing the full namespace would need to be specified without an import?
Yes. Alternatively, or as well, we could have a way to specify annotations explicitly. Then we could handle imports correctly. There's already code in AutoAnnotation for rendering annotation instances into code, though that would only be usable if you're able to make an instance of the annotation at compile time. (For annotations that exist before you start, such as the examples here, there should be no problem doing that.)
Personally that sounds more ideal to me vs hoping devs don't fat finger the full path. My $0.02.
Hi. Quick bump on this, @eamonnmcmanus. Any thoughts on whether this might make its way into the roadmap?
No immediate plans, though since I work on App Engine for my day job, making AutoValue work better with Objectify would definitely make sense. Thanks for the ping.
AutoValue now copies annotations onto fields as well as methods, if @CopyAnnotations
tells it to and if the annotations can go on fields. Does this address the problem at hand?
I believe the current behaviour of @CopyAnnotations
does not help with the Objectify case, because some of the annotations in question apply only to fields. That means we can't apply them to the abstract property methods in the @AutoValue
class, which is what we would need to do for @CopyAnnotations
to copy them into the fields.
To take an Objectify example, let's say we have a value class that defines a field like this:
@Id @Index @Expose private String name;
We'd like to convert it into an @AutoValue
class that defines a property like this:
@Something public abstract String getName();
and we'd like the @Something
annotation to cause the name
field in the generated subclass to be declared as shown. @CopyAnnotations
does copy annotations from the abstract property method to the generated field, when that is valid. But if an annotation that you want on the generated field isn't valid on a method then you can't use this. That's the case for the @Id
and @Index
fields here.
There's a couple of ways we could address problems like these: (1) a mechanism for explicitly describing annotations that should be applied, or (2) a mechanism for inserting arbitrary text before the field declaration.
(1) With an explicit mechanism for annotations we might have this:
@FieldAnnotations({
@FieldAnnotation(Id.class), @FieldAnnotation(Index.class), @FieldAnnotation(Expose.class)
})
public abstract String getName();
which would cause this to be generated:
import com.google.gson.annotations.Expose;
import com.googlecode.objectify.v4.annotation.Id;
import com.googlecode.objectify.v4.annotation.Index;
...
final class AutoValue_Foo extends Foo {
@Id @Index @Expose private final String name;
...
The annotations here happen not to have parameters, but if we did support parameters that would have to be through some mechanism allowing us to write something like
@FieldAnnotation(value = Id.class, parameters = {
@Parameter(name = "string", value = "\"foo\""), @Parameter(name = "integer", value = "23")
})
or maybe
@FieldAnnotation(value = Id.class, parameters = "(string = \"foo\", integer = 23)")
to generate:
@Id(string = "foo", integer = 23)
This is a lot of new API for a relatively obscure case.
(2) With a mechanism for arbitrary text we might have this:
@ExtraText("@com.googlecode.objectify.v4.annotation.Id @com.googlecode.objectify.v4.annotation.Index @com.google.gson.annotations.Expose")
public abstract String getName();
which would cause this to be generated:
@com.googlecode.objectify.v4.annotation.Id @com.googlecode.objectify.v4.annotation.Index @com.google.gson.annotations.Expose private final String name;
A small additional advantage of this approach is that it could be used to declare the generated field as transient
. (Also volatile
, but I'm not sure what that would be for.)
This is much simpler, although the need to spell out the fully-qualified names of the annotations is sad.
I should also note that neither of these mechanisms gives you a way to say that you don't want the generated field to be final. I suspect that the Objectify use case might require that too.
Another idea, which we've been exploring in conjunction with AutoFactory, is to allow @CopyAnnotations
to specify somewhere to copy annotations from. Currently it copies class annotations to generated subclasses and method annotations to generated overriding methods, and it will copy those method annotations to generated fields too if they are applicable. But imagine you could say something like this:
@CopyAnnotations(fromClass = AnnotationTemplates.class, fromFields = {"id", "index", "expose"})
public abstract String getName();
and
// This only exists as a source from which to copy annotations.
public class AnnotationTemplates {
@Id private Object id;
@Index private Object index;
@Expose private Object expose;
private AnnotationTemplates() {}
}
That would mean that field annotations would be copied from the fields in AnnotationTemplates
with the given names. In this case those are dummy fields that are only there as a place to park annotations we want copied. In that case, the copied annotations would be copied the same as copied annotations elsewhere. That means that they will be imported in the usual way in the generated code. And it also means that you can write arbitrary complicated annotations, which will be checked by the compiler, and have them copied into the generated code.
Let's say I have a class ExcelDto:
Now I wanted the generator to build an
AutoValue_ExcelDto
class, but with the fieldtables
annotated like:...
...
Is that possible, how to achieve this?