I would like to generate tables from annotated objects. Right now I have the following schema in mind. I would like to annotate the object as follows:
#UI.App(
name = "locations",
columns = {
#UI.Presenter.PropertyColumn("title"),
#UI.Presenter.PropertyColumn("enabled"),
#UI.Presenter.StatusColumn,
#UI.Presenter.LastModifiedColumn
}
)
public class Location {
private String title;
private Boolean enabled;
}
For that I intended to use the following annotations
public interface UI {
#Retention(RetentionPolicy.RUNTIME) #Target({ElementType.TYPE})
public #interface App {
public String name();
public Presenter.Column[] columns() default {};
}
public interface Presenter {
#Retention(RetentionPolicy.RUNTIME) #Target({ElementType.TYPE})
public #interface Column {}
#Retention(RetentionPolicy.RUNTIME) #Target({ElementType.TYPE})
public #interface PropertyColumn {
public String value();
public boolean editable() default false;
}
#Retention(RetentionPolicy.RUNTIME) #Target({ElementType.TYPE})
public #interface StatusColumn {}
#Retention(RetentionPolicy.RUNTIME) #Target({ElementType.TYPE})
public #interface LastModifiedColumn {}
}
}
With annotation inheritance I would just let PropertyColumn, StatusColumn, and LastModifiedColumn to extend the Column interface. But there's no interface inheritance.
The main goal here is to have the overview annotation as concise as possible. What is the best way to achieve my goal?
This might be a case where annotations simply aren't flexible enough to represent complex structures. Although not as clean looking, I would consider using a single column annotation and creating enum constants for each column type like this:
#Retention(RetentionPolicy.RUNTIME) #Target({ElementType.TYPE})
public #interface Column {
ColumnType value();
String property() default "";
boolean editable() default false;
}
#UI.App(
name = "locations",
columns = {
#UI.Presenter.Column(value=ColumnType.PROPERTY, property="title"),
#UI.Presenter.Column(value=ColumnType.PROPERTY, property="enabled"),
#UI.Presenter.Column(ColumnType.STATUS),
#UI.Presenter.Column(ColumnType.LAST_MODIFIED)
}
)
Caveats are you need additional checking and documentation to prevent property and editable to be used with any column type. This approach won't work if you plan to add more column types with additional values, as it probably gets too complex then.
Related
I am trying to understand why I would use #interface. I see many tutorials explaining what all those annotations mean but no where could I find in simple terms how (or why) I can use them.
As a made up example
#Target({ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface MyAnnotation {
String getAString() default "blah";
}
Suppose I use this annotation on a class.
#MyAnnotation
public class TestClass {
public String test(){
return this.getAString();
}
}
Can I call getAString() without using reflection?
If not, what can be
a possible use of it?
Here is an example on how you can use annotation fields:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface MyAnnotation {
public String name();
public String value();
}
for(Annotation annotation : annotations){
if(annotation instanceof MyAnnotation){
MyAnnotation myAnnotation = (MyAnnotation) annotation;
System.out.println("name: " + myAnnotation.name());
System.out.println("value: " + myAnnotation.value());
}
}
The example is taken from here:
http://tutorials.jenkov.com/java-reflection/annotations.html
Also see this:
#interface default declaration usage in Java
I'm trying to initialise an array of annotations but can't figure out the syntax.
public #interface Tag {
String key();
String value();
}
#Target({ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface BaseAnnotation {
String[] names();
Tag[] tags();
}
As you can see, BaseAnnotation has an array of Tag annotation and I want to use it to annotate another annotation like this:
#BaseAnnotation(names={"abcs", "bnm"},
tags = {key="aaa",value="bbb"}) //I can't figure out the syntax for this one
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface ToBeUsedAnnotation {
}
The problem is that I've tried a few ways to set the values for tags but can't find any useful documentation on how to do it.
Thanks.
I have a Jersey Rest API like this:
#POST
#Path("/doorder")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces("text/plain")
public String doOrder(#BeanParam final #Valid OrderBean order) {
// Some implementation here
}
All my inputs are store in this bean:
#AddressAtLeastOne
public final class OrderBean {
#FormDataParam("address")
private String address;
#FormDataParam("city")
private String city;
#FormDataParam("postcode")
private String postcode;
// Other member variables
// Getters and setters
}
I added an annotation to validate the address (#AddressAtLeastOne). The address is valid if at least one of the 3 fields has a value.
Here's the annotation definition:
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
#Retention(RUNTIME)
#Constraint(validatedBy = AddressAtLeastOneValidator.class)
#Documented
public #interface AddressAtLeastOne {
String message() default "Address requires at least one field";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And here's the validator:
public class AddressAtLeastOneValidator implements ConstraintValidator<AddressAllOrNone, OrderBean> {
#Override
public boolean isValid(OrderBean demoBean, ConstraintValidatorContext constraintValidatorContext) {
// Check for at least one value
if((demoBean.getAddress() != null && !demoBean.getAddress().equals("") ||
(demoBean.getCity() != null && !demoBean.getCity().equals("")) ||
(demoBean.getPostcode() != null && !demoBean.getPostcode().equals("")))) {
return true;
}
return false;
}
}
Everything is fine! But now I want to rename the annotation #AddressAtLeastOne to #AtLeastOne and make it generic, so that I can apply it to any class. I need a mechanism where I can specify which member variables are part of the group I want to validate with #AtLeastOne. How can I do that?
One approach of doing this is to use Reflection -
Create a custom annotation suppose #GroupNotNullField and apply this annotation to all fields in bean class in which at least one field should have value. By this way, you can skip some fields in which validation is not required.
In the validator class, get all the fields of the bean class using Reflection
Check all the fields which are annotated with #GroupNotNullField annotation
Get the value of all such fields and check that at least one has value.
Return true or false depending on validation check.
I have an enum :
public enum Vehicle {
CAR,
BUS,
BIKE,
}
I intend to use these enum values as annotations : #Vehicle.CAR, #Vehicle.BUS, #Vehicle.BIKE. Does java allow me to define them as annotations ?
No You can not do this. But if you want to use enum in annotation you can do like this
class Person {
#Presentable({
#Restriction(type = RestrictionType.LENGTH, value = 5),
#Restriction(type = RestrictionType.FRACTION_DIGIT, value = 2)
})
public String name;
}
enum RestrictionType {
NONE, LENGTH, FRACTION_DIGIT;
}
#Retention(RetentionPolicy.RUNTIME)
#interface Restriction {
//The below fixes the compile error by changing type from String to RestrictionType
RestrictionType type() default RestrictionType.NONE;
int value() default 0;
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
#interface Presentable {
Restriction[] value();
}
You can't use enum as annotations. But you can add the enum as an element of the annotation.
The enum
public enum Priority {
LOW,
MEDIUM,
HIGH
}
The annotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface TestAnnotation {
Priority priority() default Priority.MEDIUM;
}
The annotation usage
#TestAnnotation(priority = Priority.HIGH)
public void method() {
//Do something
}
I have a GSON annotation ("SerializedName") which I want to translate from my custom annotation. I mean, if I have "Serial" annotation with "SerialType" element (which tell me what the serialization type I want for the field), after setting the GSON type in the "SerialType" - how can I generate the GSON annotation for the specific field?
Example code:
#Target(ElementType.FIELD)
public #interface Serial
{
SerialType type();
String value();
}
public class Example
{
#Serial(type = SerialType.GSON, value = "test")
public int field;
}
will generated to:
public class Example
{
#SerializedName("test")
public int field;
}
Try looking at annotation processors. You can find more info in the docs
Here is a good post describing how to use them.