This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 129239 - PropertySheets and Annotations
Summary: PropertySheets and Annotations
Status: RESOLVED INVALID
Alias: None
Product: contrib
Classification: Unclassified
Component: PlatformX (show other bugs)
Version: 6.x
Hardware: All All
: P3 blocker with 1 vote (vote)
Assignee: _ wadechandler
URL:
Keywords: API
Depends on:
Blocks:
 
Reported: 2008-03-05 09:17 UTC by maxnitribitt
Modified: 2010-05-05 13:35 UTC (History)
4 users (show)

See Also:
Issue Type: ENHANCEMENT
Exception Reporter:


Attachments
updated patch for XYZoom (40.01 KB, text/plain)
2008-03-07 09:10 UTC, maxnitribitt
Details

Note You need to log in before you can comment on or make changes to this bug.
Description maxnitribitt 2008-03-05 09:17:35 UTC
Change API to allow creating PropertySheets by the use of Annotations.

There’s a lot of boilerplate code if you want to display PropertySheets for a Bean in NetBeans. You need to create a
nodewrapper and write code to create the PropertySheet. There are some classes like the BeanNode to make things easier,
but the functionality is very basic, unless you want to write BeanInfo classes. I’ve got a lot of beans for XML binding
in my projects and I never liked the concept of BeanInfo classes, because you have to keep two classes in sync, so I
decided to use a different approach.

BeanInfo classes are carrying MetaInformation for the Beans and I guess that’s  rather a job for Annotations.  So it
would be best to create a set of Annotations to provide the same MetaInformation in a more appropriate way. 

I have implemented my own solution and it works fine; I would like to contribute this as a patch if anyone's interested. 

Proposed Patch adds these classes:
* AnnotatedBeanNode:
 // same as BeanNode with the ability to handle the annotations
* AnnotationIntrospector:
// AnnotationIntrospector creates  generic BeanInfo objects from Annotations when BeanDescriptorAnnotation is present,
calls Introspector when no annotation is present
Uses these annotations:
* BeanDescriptorAnnotation
* BeanMethodAnnotation
* EventSetDescriptorAnnotation
* FeatureDescriptorAnnotation
* MethodDescriptorAnnotation
* PropertyDescriptorAnnotation

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.
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


An example illustrating how a typical use would look like:

8<————– BaseBean.java—————–>8

 @BeanDescriptorAnnotation( simpleProperties = {"Text","ReportElement"})
public class StaticText extends BaseBean {

8<——————————————————->8

 nested elements

8<———From ReportElement.java—————–>8

@BeanDescriptorAnnotation(
featureDescriptorAnnotation=@FeatureDescriptorAnnotation( displayName= "Generic"),simpleProperties =
{"X","Y","Width","Height"})
public class ReportElement extends org.netbeans.modules.schema2beans.BaseBean

8<——————————————————->8

Results can be seen here: http://eppleton.com/blog/?p=103

I can upload a Mercurial patch if it's considered worth testing. Sorry for the length of this report. I've created the
patch for nodes API, but utilities might be considered since the BeanInfo creation has been moved there.
Comment 1 Jesse Glick 2008-03-05 16:13:50 UTC
displayName() and shortDescription() are not good as is since these need to be localizable in general.

String customizerClassName() should be Class<? extends Customizer> customizerClass().

Property names should follow Beans conventions, e.g. "reportElement", not "ReportElement".

I doubt we really need to implement the full range of options in the Beans spec. Nearly everyone would only use simple
properties with default getter/setter names and the occasional customizer. For use in NetBeans there is no need for bean
context, method descriptors, etc. I'm sure you could find uses for all these things in e.g.
http://commons.apache.org/beanutils/ but we would not need them.

Anyway I am dubious that there is much value here for module development. Assuming you have the bean written, your
proposal would let me add a few annotations with fixed values, followed by creation of a BeanNode (which we generally
discourage the use of). But it is already pretty easy to make a Node with whatever properties you want:

AbstractNode n = ...;
n.getPropertySheet().add(new PropertySupport.ReadWrite<String>("foo", String.class, NbBundle..., NbBundle...) {
  public String getValue() {...}
  public void setValue(String v) {...}
});

which is much more flexible as you can choose to include certain properties or not at runtime, add a Reset to Default
method, make the short description vary according to runtime criteria, create different Sheet's, etc.

Generally annotations are useful for

1. Marking something about the nature of code that should be checked by static analyzers, e.g. @NonNull as a workaround
for a deficient type system in the language.

2. Express a cross-cutting concern that can be used by a container, e.g. transaction attributes in EJBs.

3. Indicating declarative registration of objects that could be extracted using an annotation processor to avoid class
loading or implement inversion of control.

But they should not be used for routine procedures which can be done in a straightforward manner using plain Java code.
Comment 2 maxnitribitt 2008-03-06 07:54:05 UTC
Jesse, thanks for the fast feedback!

first off, I didn't know that the use of BeanNode is discouraged, and I didn't yet find information about that. But I
assume there are not many usecases where you would like to use BeanNode anyway. I agree on that. Actually this is the
reason why I wrote this enhancement.
 
You can declare the metadata very efficiently, and most of the time one line of code is enough to expose the properties
as you wish. This is extremely helpful when you have a large cllection of domain obejects e.g. xml binding beans. This
is often the case in RPC development, especially when you are porting an application. It simplifies plugging large
collections of beans into the NetBeans platform. Maybe it won't be as useful for developing the IDE itself, but it
definitely simplified RCP development for me.

I disagree with you regarding the use of annotations. The declarative nature of annotations is perfect for this kind of
job. I would even say that this use of annotations is exactly what annotations are meant for. See JSR 175 and the sun
java language guide:

"There has been a growing trend towards annotating fields, methods, and classes as having particular attributes that
indicate they should be processed in special ways by development tools, deployment tools, or run-time libraries. We call
such annotations metadata. For example, the JavaBeansTM architecture introduced various stylistic naming patterns (such
as getFoo/setFoo method names) that could be used to indicate that particular methods were used for accessing
properties, for registering event handlers, and so forth. [...] In light of this situation, it seems appropriate to add
to the Java programming language a means of associating arbitrary attribute information with particular
classes/interfaces/methods/fields." [ http://jcp.org/en/jsr/detail?id=175 ]

"Other APIs require “side files” to be maintained in parallel with programs. For example JavaBeans requires a BeanInfo
class to be maintained in parallel with a bean, and Enterprise JavaBeans (EJB) requires a deployment descriptor. It
would be more convenient and less error-prone if the information in these side files were maintained as annotations in
the program itself." [ http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html ]

The standard method offers more flexibility and internationalization, agreed. But the intention of the proposal is not
to replace the standard mechanism, but to make it easy to add Propertysheets to large collections of pojos. 
Comment 3 Jesse Glick 2008-03-07 01:34:59 UTC
Likely inappropriate for NB IDE. CCing Tim as it might be a useful addition to platformx.
Comment 4 maxnitribitt 2008-03-07 09:10:47 UTC
Created attachment 57955 [details]
updated patch for XYZoom
Comment 5 maxnitribitt 2008-03-07 09:12:27 UTC
Sorry, I attached this patch to the wrong issue!
Comment 6 _ tboudreau 2008-03-11 04:07:55 UTC
Generally I agree there is a lot of boilerplate code everyone with a hierarchy of beans they want to wrap in nodes has to write - particularly when it comes 
to implementing properties;  it would also be nice to reduce the number of classes one needs to write - all those PropertySupport.ReadWrite subclasses 
certainly take up memory (there is PropertySupport.Reflection, but it's somewhat broken - it doesn't actually use BeanInfos).

The localization issue could probably be handled fairly easily by providing a bundle key instead of an actual display name, and some convention for where it 
should be looked for.

Another issue worth addressing in a work like this is making it easy to annotate a getter that returns a collection that should be turned into child nodes;  in 
an ideal world, creating a hierarchy of nodes ought to be as simple as a few annotations on a domain object that you pass to some sort of node factory.

If you'd be interested in contributing it to the http://platformx.netbeans.org incubator, we'd be happy to host it there - which means there would be 
platform builds that include these modules, and anyone who wants it can get it and build against it.  It's basically a project for contributed extensions to 
NetBeans that are, which not necessary to the IDE, are useful for building RCP apps.  The project is still getting off the ground.  Interested?
Comment 7 maxnitribitt 2008-03-11 09:07:15 UTC
Tim, thanks for your feedback, especially the hints on how to support internationalization. Although I think in many
cases property editor internationalization is odd ( think of property editors in Matisse :-) ), I see that it might be
useful in other cases and I'll try to address it . From what I read on the platformx page and wiki contributing it there
would be a good solution, and I'm willing to do that. Is there already a repository? 
Comment 8 t_h 2008-10-13 15:31:00 UTC
Ok, please incubate in platformx workspace. Come back when ready for API review. Thanks.
Comment 9 maxnitribitt 2010-04-22 08:55:19 UTC
It's bin a long time, since I reported this, but the project is available at Kenai now for anyone interested:

http://kenai.com/projects/beaninfo-annotations

Currently doesn't compile due to #184694
Comment 10 Jesse Glick 2010-04-22 17:24:11 UTC
Should not keep this open on netbeans.org if it is hosted elsewhere; use the issue tracker on the Kenai project for any problems or suggestions.
Comment 11 tk_fhd_aui 2010-05-05 11:51:57 UTC
When I understand him right, he will import his project into the netbeans version control once it compiles, which depended on 184694 and is solved now. So why is it resolved invalid? Don't want to offend, just understand.

Regards
     Torsten
Comment 12 maxnitribitt 2010-05-05 13:35:23 UTC
(In reply to comment #11)
> When I understand him right, he will import his project into the netbeans
> version control once it compiles, which depended on 184694 and is solved now.
> So why is it resolved invalid? Don't want to offend, just understand.
> 
> Regards
>      Torsten

Hi Torsten, 

I'm currently hosting this project somewhere else, so I agree with Jesse that closing as invalid here (for NetBeans) is the right solution. If you like to check it out it compiles in 6.9 beta thanks to Jesse's tips. 

thanks for your interest

--Toni