Annotation Processing: how to change annotation target in Kotlin code? - java

Quite often, when I use annotations from Java libraries in Kotlin code, I have to specify target in order to specify what element in the compilled code has to be annotated:
data class User(
val id: String,
#get:Email
val email: String
)
Instead of specifying #get:Email, I would love to be able to use simply #Email, especially if it occurs in many places.
Question: Is there a way to hint Kotlin compiler to use a different target in all places so that if I use #Email it will handle it as if it was #get:Email? Or is there any other way to omit specifying target? How can it be achieved? May it be done on the compilation stage via annotation processing (like Lombok does)?
I would appreciate any ideas even if they don't answer my question directly.

You can't override it. Maybe create annotation that will target #Email annotation and provide the property getter when it tries to call target.
Specifying target is dictated by the annotation target. When the target of the annotation is specified to jvm use-targets then you must use #get: to specify that the annotation targets the getter of the property.
In order to use simply #Email you must use kotlin target types for your annotation, probably PROPERTY. Note that, PROPERTY target doesn't work with Java.
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.PROPERTY)
annotation class Email

It should be possible with Kotlin Compiler plugins API. It's a complicated way, tho.
To achieve this you will need to add a ClassBuilder interceptor with a visitor handling #Email annotation and specifying a correct target.
Can't say without getting deeper if visitor will be enough or you will also need to modify source code on one of the compiler stages, check TreeVisitor.

Related

When is expression used in mapStruct?

I am getting started with MapStruct. I am unable to understand when do we use "expression" tag in MapStruct? Why do we have certain mappings where we use "target" tag and "expression" tag? Does it mean that expressions are used when you want to map two or more fields within a bean to a single property/field in the target as mentioned in the documentation "http://mapstruct.org/documentation/stable/reference/html/#expressions"
Expressions are used when you can't map a source - to a target property or when a constant does not apply. MapStruct envisioned that several language could be used to address expressions. However, only plain java is implemented (hence "java(... )" ). EL was envisioned but not yet realised.
A typical use case that I use is generating a UUID. But even there you could try the new #Context to achieve that goal.
Remember, the stuff within the brackets is put directly in the generated code. The IDE can't check its correctness, and you will only spot problems during compilation.
Expressions are IMHO a fallback means / gap filler for stuff that is not yet implemented in MapStruct.
Note: Mapping target-to-source by means of a custom method as suggested in the other answers can be done automatically. MapStruct will recognised the signature (return type, source type) and call your custom method. You can do this in the same interface (default method) or in a used mapper.
In general, MapStruct expressions are used when you simple cannot write a MapStruct mapper. They should be used as a fallback approach when the library doesn't apply to your use-case.
For example, -- as the documentation says -- when a mapping requires more than one source variable, an expression can be used to "inject" them to a mapper method.
Another use case is when the source variable you need to use -- say bar -- is not a part of the source class but a member of one of its variables (here, classVar). You would map it to the target field foo using a custom myCustomMethod method with #Mapping(target="foo", expression="java(myCustomMethod(source.classVar.bar)))".

Get all values of a generated Annotation in an Annotation Processor

I have a VariableElement field that is annotated with a generated Annotation (which is why I can't use field.getAnnotation(annotationClass)). I need to get all parameters passed to this annotation.
Note that by "a generated Annotation" I mean that literally the Annotation class itself (not the annotated one) has been generated by an Annotation Processor. The field/class that is being annotated is in the handwritten source code.
It didn't look like it'd be that hard, so far I've come up with this:
for (AnnotationMirror annotation : field.getAnnotationMirrors()) {
Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValueMap = annotation.getElementValues();
messager.printMessage(Diagnostic.Kind.WARNING, annotation.toString() + ":" + annotationValueMap.toString());
}
I thought this would do it, but the output for the field is the following:
#MyAnnotation:{}
So, the processor does recognize that the field is annotated, but I'm unable to access the passed parameters. Even though the field is definetely annotated and does pass parameters with the annotation (it has to, since the annotation defines required parameters and no defaults):
#MyAnnotation(max = 387, min = 66876, ...)
private Integer myField;
Here's the generated annotation code:
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.FIELD)
public #interface MyAnnotation {
int max();
boolean allowAuto();
int min();
}
I've clean-compiled the project multiple times, the processor never sees the values. What am I overlooking here? The processor can obviously see the annotation itself, yet the parameters passed to it are hidden.
Recall that annotation processors run as part of the compiler, in steps called "rounds". This process runs iteratively until there is no new code to compile, and then processors get one last chance to run (not necessary for this answer, but helpful for more context). Each round only the newly created types are directly given to the processor to examine.
What seems to be happening here is that during a round you are emitting a new annotation type, which should allow the processor to observe certain features about some code submitted to be compiled. However, any types created during a given round are not yet compiled until the next round begins.
For this question, we run into a conflict here - some Java sources are compiled which use an annotation that doesn't exist yet. The processor first creates the annotation, and then tries to read the newly-created annotation out of those partly-compiled sources. Unfortunately, until the annotation has been compiled, we can't actually read the annotation. Instead, we need to wait until the subsequent round (once the annotation itself has compiled), then go back to that class which has finished being compiled and examine it.
This can be implemented yourself without too much trouble, but the easiest way is often to rely on the google/auto project (specifically the auto-common library, see https://github.com/google/auto/tree/master/common), and extend their BasicAnnotationProcessor class. One of the nice features it supports is to automatically examine types and check if there are any compilation issues - if so, they are deferred until a later round so you can handle them without any type resolution issues.
Use getAnnotation(MyAnnotation.class) available from VariableElement
in your example code you can do this to get the min and max parameters
MyAnnotation myAnnotation= field.getAnnotation(MyAnnotation.class);
int max = myAnnotation.max();
int min = myAnnotation.min();
this will work unless the annotation members returns class/class[] value, in which you will get an exception if you try to get the value using this method.
more about how to get class literal values can be found on this answer
How to read a Class[] values from a nested annotation in an annotation processor
Or using annotation mirrors
for (AnnotationMirror annotation : field.getAnnotationMirrors()) {
Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValueMap = annotation.getElementValues();
annotationValueMap.forEach((element, annotationValue) -> {
messager.printMessage(Diagnostic.Kind.WARNING, element.getSimpleName().toString() + ":" + annotationValue.getValue());
});
}
In case you have more than one annotation on the field then you can iterate over the annotation mirrors and use the check types.isSameType(annotationMirror.getAnnotationType(), elements.getTypeElement(MyAnnotation.class.getName()).asType()) to find the annotation you are interested in
Yes, you will not be able to instantiate a Class object for a type which is not available in your annotation processor's classloader, and may not even have been compiled into a class file yet at all. A similar problem exists for retrieving enum constants.
There are a few wrinkles to dealing with this sort of thing:
Any annotation value that is declared as an array might come to you at compile time either as a single value, or a list of values - so any code needs a path to handle both the list and non-list case - like this
What you get may be a generic type, and if you are generating Java code or similar that wants to insert a reference to Foo.class you need to get the erasure of that type, so you don't generate Foo<Bar>.class into your generated sources.
One of the places your annotation processor is going to get run is in an IDE, on broken code still being typed, so it is important to fail gracefully in the case that code elements that, you would think, can't possibly be missing or broken or unlikely values, are. In an IDE, your annotation processor may also be kept alive for a long time and reused, so it's important not to pile up objects modeling stuff that has already been generated and emitted.
FWIW, I wrote a library to solve this and related problems, which can be found on Maven central at the coordinates com.mastfrog:annotations-tools:2.8.3.4 (check for newer versions). The usage pattern is simple:
Instantiate an instance of AnnotationUtils in an override of the init() method of your annotation processor and store it in a field
Use it to, for example, resolve a Class<?>[] into a list of string class names that you can work with inside javac, and similar
It makes it pretty straightforward to write annotation processors that do not directly depending on the classes they processes at all - which means the annotation processors (and their dependency graphs!) be completely independent of what they process, and can depend on whatever libraries they like without forcing those dependencies into the dependency graph of any project that uses them - the most common pattern is someone writes some annotations and then puts the annotation processor in the same project, or even package, and so anything the annotation processor uses becomes a dependency of every consumer of the annotations, even though those dependencies will probably never be used at runtime at all. That, it seems to me, is an antipattern worth avoiding.

Can you limit annotation target to be subclasses of a certain class?

Can you limit that a target of an annotation must be of a certain class?
I want to create a new validation Constraint to limit file types that are uploaded. The constraint annotation must only go on a MultipartFile property, not on String or anything like that. How do I limit this?
Not at compile-time; the only restrictions available for annotation placement are by element type (method, class, etc.).
Yes, this is possible (and was possible when the question was asked).
As a general rule, when working with annotations you need to use an annotation processor. You can write an annotation processor that issues errors whenever an annotation is written in a disallowed location.
If your question is whether this is possible with plain javac and no annotation processor, then the answer is "no".

Jackson #JsonIgnore changed semantics?

We just upgraded some maven dependencies, that triggered an update of Jackson from 1.7.x to 1.9.x
We use to annotate #JsonIgnore on setter methods, on methods not supposed to be set from client side. for example: the owner of an object (with should be set from the authentication principal), etc.
It seems to us that the semanthincs of this annotation have changed, is that possible? now the field is been jsonignored in all case, and not only when being set.
is that the case? is there an alternative way in 1.9 to implement ignore SET only?
thanks!
r.
Ok, I think I found and explicit mention to this issue in the javadoc. (jackson documentation is not easy to go through).
In addition, starting with Jackson 1.9, if this is the only annotation associated with a property, it will also cause cause the whole property to be ignored: that is, if setter has this annotation and getter has no annotations, getter is also effectively ignored. It is still possible for different accessors to use different annotations; so if only "getter" is to be ignored, other accessors (setter or field) would need explicit annotation to prevent ignoral (usually JsonProperty).

Java Annotation: On property or on method? What the difference

Hibernate Search, Hibernate, Struts2... I see more examples... In same examples I see the annotation on the field.. Other on the get/set method.. There are differences? Or is casual..
I hope that is not a stupid question!
Saluti!
Luigi
The difference depends on the annotation and how it is used. For example, in Spring you can use the #Controller annotation only on a class. This tells Spring that the class is a controller.
As far as methods are concerned, #RequestMapping is an annotation that goes on a method. For properties, you can have validation annotations like #NotNull (in Hibernate validator).
Annotations are definitely not casual; they carry meaning and can affect the way the code behaves.
From the Java documentation regarding annotations:
Annotations provide data about a
program that is not part of the
program itself. They have no direct
effect on the operation of the code
they annotate.
Annotations have a number of uses,
among them:
Information for the compiler — Annotations can be used by the
compiler to detect errors or suppress
warnings.
Compiler-time and deployment-time processing — Software tools can
process annotation information to
generate code, XML files, and so
forth.
Runtime processing — Some annotations are available to be
examined at runtime.
Annotations can be applied to a
program's declarations of classes,
fields, methods, and other program
elements.
You can specify what an annotation can annotate by specifying the the elements (using a #Target annotation) when you define your own annotation.
This really depends on the code that interprets the annotations. It can of course make a difference, but the annotations you are talking about are probably meant to annotate a "property", which is something that technically does not exist in Java.
Java has fields and methods, but these are used to simulate properties under the "Java Bean" conventions, i.e. you have a public setX() and a getX() method that often (but not always) write and read a private field x. They're tied together via a naming condition, not a language mechanism.
Because of that, most frameworks that use annotations for such properties (e.g. for persistence mapping or dependency injection) are flexible and allow you to annotate either the field or the get or set method.

Categories