In my last blog entry I wrote about how the BeanInfo part in Java could be replaced by Annotations. My first tests were successful, and it seems that this approach would work nicely. Now for the details:
First there is a FeatureDescriptorAnnotation that holds the elements shared by all Descriptors. Annotations don’t support inheritance, so this is used as a MetaAnnotation (@Target({ ElementType.ANNOTATION_TYPE})) for the other Descriptors. I created default values for the elements, so the elements do not need to be set in an annotation explicitely. If, for example, the AnnotationIntrospector finds DEFAULT_NAME as name() it will use reflection to set the default name of the Descriptor. I found out that you can’t check elements for identity at runtime, so this:
featureDescriptorAnnotation.name() == FeatureDescriptorAnnotation.DEFAULT_NAME
will always be false. It’s a bit dangerous to use equals here, but I don’t know a better aproach yet.
8<——————————————>8
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE})
public @interface FeatureDescriptorAnnotation {
public static String DEFAULT_NAME = "Default_Name";
public static String DEFAULT_DISPLAY_NAME = "Default_Display_ Name";
public static String DEFAULT_DESCRIPTION = "Default_Description";
String displayName() default DEFAULT_DISPLAY_NAME;
String name() default DEFAULT_NAME;
String shortDescription() default DEFAULT_DESCRIPTION;
boolean expert() default false;
boolean hidden() default false;
boolean preferred() default false;
}
8<——————————————>8
Next I needed a BeanDescriptor-equivalent, that can be used to mark up Beans for the AnnotationIntrospector. The PropertyDescriptor is split up into two parts. SimpleProperties refers to properties that only use default methods, names, etc. This will cover most cases, so the code to annotate a bean is very reduced. For Properties with more advanced settings there is the customizedProperties Array.
8<——————————————>8
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface BeanDescriptorAnnotation {
public static String DEFAULT_CUSTOMIZER = "Default_Customizer";
FeatureDescriptorAnnotation featureDescriptorAnnotation() default @FeatureDescriptorAnnotation;
String customizerClassName() default DEFAULT_CUSTOMIZER;
PropertyDescriptorAnnotation [] customizedProperties() default {};
String [] simpleProperties() default {};
}
8<——————————————>8
Next there is the PropertyDescriptorAnnotation ( and the MethodDescriptorAnnotation, ParameterDescriptorAnnotation,EventSetDescriptorAnnotation but I won’t show those now ). It can be used either in the customizedProperties Array, or as a marker for Fields, which would make the code look cleaner:
8<——————————————>8
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface PropertyDescriptorAnnotation {
public static String DEFAULT_PROPERTY_EDITOR = "Default_Property_Editor";
public static String DEFAULT_GETTER = "Default_Getter";
public static String DEFAULT_SETTER = "Default_Setter";
FeatureDescriptorAnnotation featureDescriptorAnnotation() default @FeatureDescriptorAnnotation;
String propertyEditorClassName() default DEFAULT_PROPERTY_EDITOR;
BeanMethodAnnotation readMethod() default @BeanMethodAnnotation(name=DEFAULT_GETTER);
BeanMethodAnnotation writeMethod() default @BeanMethodAnnotation(name=DEFAULT_SETTER) ;
boolean bound() default false;
boolean constrained() default false;
}
8<——————————————>8
At last we need an Introspector, that can retrofit those into a generic BeanInfo Here is for example how the simpleProperties are converted to PropertyDescriptors:
8<——————————————>8
String [] simpleProperties = beanAnnotation.simpleProperties();
if (simpleProperties!=null){
for (int i = 0; i < simpleProperties.length; i++) {
try {
PropertyDescriptor propertyDescriptor=new PropertyDescriptor(simpleProperties[i],beanClass);
propertyDescriptors.add(propertyDescriptor );
} catch (IntrospectionException ex) {
ex.printStackTrace();
}
}
}
8<——————————————>8
I created an AnnotatedBeanNode that uses the AnnotationIntrospector for generating PropertySheets and my annotated beans work fine with it, although there is still a lot to implement (EventSets, Methods …).