PROPOSAL: Lightweight Properties

David Goodenough david.goodenough at linkchoose.co.uk
Thu Mar 5 07:27:51 PST 2009


Rienier,

Yes I know that Fields have Generics information available, but they are
not used in the declaration of a Field object, and so the getter and 
setter to the value of the field are NOT typed, and thus can not be 
checked by the compiler/IDE.

I do not have the knowledge to add this to the compiler, and the only
documentation I have found for adding things to the compiler (as I 
have already mentioned) did not help - it only talked about adding 
operators to the compiler.  The compiler is not exactly well documented
or if it is, Google has been unable to furnish the right documents.

The reason that it is needed is very simple.  One of the big strengths
of Java is that compilers and IDEs can do a really good job of detecting
errors, and in the case of IDEs making suggestions as to what can come
next.  Using String field names in places like Bean Binding frameworks
and API approaches to SQL and friends (like the JPA Criteria API) drives
a coach and horses through that strength.  For that reason, and the fact
that this is a VERY simple suggestion, this proposal should be given 
serious consideration.

Now if there people out there with the knowledge to help me get started
in understanding what needs to be changed in Javac (I tried Remi Forax
as he had already worked in this area, but he was too busy to help when
I asked) I am more than happy to try to produce a working javac.  But 
simply jumping into the source from scratch is unlikely to produce a good
solution.

So perhaps rather than spending our time throwing out reasons why this
can not be done, lets try to work together to find a way in which is can
be done.

David

On Thursday 05 March 2009, you wrote:
> I think you'll need to spend some more time getting familiar with the
> JLS.
>
> For example, Field objects *DO* have generics information. That's what
> Field.getGenericType() is for.
>
> I -really- doubt you'll get anywhere with your proposal unless you do
> both of the following:
>
>   - impress everybody with an exceedingly complete proposal along with
> a prototype implementation of javac. Which is going to take a lot more
> working knowledge of the JLS and how javac parses source files than
> you currently have.
>
>   - either a grassroots campaign of many java programmers that
> vindicates your insinuation that properties are so important, they
> need to be added to java7, even with this bandaid solution, or
> alternatively, convince a number of property evangelising people in
> the java community to openly speak in favour of this proposal.
>
> I imagine that's going to cost you a week or 5 at the very least of
> your time, including getting more familiar with the JLS. March is
> going to be over by then.
>
>   --Reinier Zwitserloot
>
> On Mar 5, 2009, at 11:31, David Goodenough wrote:
> > Thinking further about this, there is a problem with using Field
> > objects.
> >
> > Field objects predate Generics, and so the getter/setter that it
> > provides
> > is not one that the compiler/IDE can check.  This is real problem and
> > undermines one of the important drivers for this proposal.
> >
> > Now one could rewrite Field to require Generics, but that would break
> > so much code that is is not something I would want to contemplate.
> > In addition applying that to Methods would be well neigh impossible.
> >
> > There is a further problem, which is that as I read it the FCM
> > proposal
> > is only for a single Field object, where my Property object actually
> > holds a Field array, so that it can handle Foo#bar#ali.
> >
> > All in all, I think that introducing a new class (my Property class)
> > solves
> > both the problems, not breaking any existing code and building
> > something
> > that is type safe and checkable and that FCM could subsequently build
> > upon.
> >
> > David
> >
> > On Wednesday 04 March 2009, David Goodenough wrote:
> >> On Wednesday 04 March 2009, Roel Spilker wrote:
> >>> In your proposal, foo#bar would be translated by the compiler
> >>> into: new
> >>> Property<Foo,Bar>(foo,"bar");
> >>>
> >>> But I think you should split your proposal into two parts. The
> >>> first part
> >>> is using the Foo#bar syntax to be a field literal for the field
> >>> "bar" in
> >>> the type "Foo". This is similar to the FCM proposal
> >>> (http://docs.google.com/Doc?id=ddhp95vd_6hg3qhc). So it would be a
> >>> java.lang.reflect.Field literal.
> >>>
> >>> The second part would be a proposal to build properties using field
> >>> literals.
> >>>
> >>> I give the field literal proposal a chance to be included.
> >>>
> >>> Roel
> >>
> >> I would not be unhappy with that, if were it possible.  But I think
> >> that
> >> due to the way that Field objects are represented in the class file
> >> (in an
> >> odd binary format) and then constructed at run time, that this
> >> would not be
> >> possible.   It is important (for things like accessing Annotations)
> >> that we
> >> get the REAL Field object, and not a partial copy or lookalike.
> >>
> >> If someone can come up with a way of doing that I would be delighted.
> >>
> >> David
> >>
> >>> -----Oorspronkelijk bericht-----
> >>> Van: david.goodenough at linkchoose.co.uk
> >>> [mailto:coin-dev-bounces at openjdk.java.net] Namens David Goodenough
> >>> Verzonden: woensdag 4 maart 2009 10:50
> >>> Aan: coin-dev at openjdk.java.net
> >>> Onderwerp: Re: PROPOSAL: Lightweight Properties
> >>>
> >>> I am not sure that we would loose anything.  The important thing
> >>> is to
> >>> have an IDE/compiler checkable way of generating Property objects,
> >>> and
> >>> having two ways of generating them is not really a problem.  My
> >>> Property
> >>> object would happily detect either an explicitly generated getter/
> >>> setter
> >>> or one generated for you by a property keyword.  The property
> >>> keyword
> >>> would resolve other things (like the automatic generation of
> >>> PropertyChangeSupport, and the controlling of visibility) but it
> >>> is still
> >>> building on the same basic concept.
> >>>
> >>> If you don't want to use # then the work needed for the compiler
> >>> gets
> >>> more complicated (I think).  One could use a sort of compiler
> >>> function,
> >>> so one would say property(foo.bar) and it would modify the
> >>> function of .
> >>> inside the function.
> >>>
> >>> I really want to find a way to get something into Java 7,
> >>> otherwise we
> >>> are looking to Java 8 (which is at least 3 years away if current
> >>> timescales persist), by which time I suspect that everyone will have
> >>> gotten bored and gone to find pastures new.
> >>>
> >>> David
> >>>
> >>> On Tuesday 03 March 2009, Reinier Zwitserloot wrote:
> >>>> But it seems that we _would_ lose a thing or two with this
> >>>> proposal.
> >>>> For example, if a complete properties proposal supports full
> >>>> backwards
> >>>> compatibility and generates getters and setters, then we have a
> >>>> bunch
> >>>> of code around that uses the foo#bar notation, which is by then
> >>>> already outdated, and would in fact get in the way of using foo#bar
> >>>> notation as a shorthand for calling the appropriate getter/
> >>>> setter. I
> >>>> don't know if that is a desired syntax sugar, but the point is:
> >>>> Your
> >>>> proposal would effectively make it impossible to add that later,
> >>>> as it
> >>>> would already be shorthand for creating a Property object.
> >>>>
> >>>> The # and the backtick are the only easily typed symbols that
> >>>> have no
> >>>> meaning whatsoever in java right now. Using one up for an
> >>>> simplified
> >>>> properties proposal is another serious disdvantage. I don't think
> >>>> your
> >>>> proposal makes property support light enough to warrant its
> >>>> inclusion
> >>>> in project coin. That's not to say I have something against
> >>>> properties; on the contrary, I love the idea and I'm very impressed
> >>>> with the way properties work in JavaFX. All the more reason to
> >>>> get it
> >>>> right instead of using a bandage.
> >>>>
> >>>>  --Reinier Zwitserloot
> >>>>
> >>>> On Mar 3, 2009, at 23:00, David Goodenough wrote:
> >>>>> Well that depends on what you mean by a complete proposal.
> >>>>>
> >>>>> There are two parts to the use of Properties.  There is the
> >>>>> framework side, inside BeansBinding or JPA and then there is the
> >>>>> consumer side, the application code.
> >>>>>
> >>>>> My proposal is very simple on the consumer side (the only bit
> >>>>> needed
> >>>>> is the # notation).  You can use clasical getters and setters (a
> >>>>> nuisance but everyone understands them), or the simple getter/
> >>>>> setter
> >>>>> mechanism that my Property provides.  So for the consumer my
> >>>>> proposal is real simple.  All you need do is add (either explicity
> >>>>> or by byte code
> >>>>> enhancement)
> >>>>> a PropertyChangeSupport object and the relevant methods and you
> >>>>> are
> >>>>> home and dry.
> >>>>>
> >>>>> For the Frameworks, well you only write them once.  Even so
> >>>>> something nice and simple appeals and my proposal is simple.
> >>>>>
> >>>>> It may not be Beans as we know it, but they never were integrated
> >>>>> properly into Java.  But its really not that dissimilar just
> >>>>> slightly different.
> >>>>>
> >>>>> So other than a little sytactic sugar (not having to code the
> >>>>> PropertyChangeSupport object) we have not really lost anything  of
> >>>>> the "full" solution.  Having a Bound annotation which would add
> >>>>> this
> >>>>> under the covers with a byte code enhancer would not be difficult.
> >>>>> The implicit getters and setters come with the package.  So the
> >>>>> question to be asked whether a fuller solution is really needed.
> >>>>>
> >>>>> David
> >>>>>
> >>>>> On Tuesday 03 March 2009, Reinier Zwitserloot wrote:
> >>>>>> The language change required for your proposal is indeed lighter,
> >>>>>> but to understand it, it is seems a lot more complicated.
> >>>>>>
> >>>>>> Also, it would be infeasible to introduce a 'lightweight'
> >>>>>> (according to anyone's definition) proposal now that is lacking
> >>>>>> in
> >>>>>> certain aspects, and then fix it later, unless that fix is
> >>>>>> syntactically very similar and backwards- and migration
> >>>>>> compatible.
> >>>>>> That's the major beef I have with this proposal: It effectively
> >>>>>> shuts the door on any other property proposal. In my view, a
> >>>>>> proposal solves the problem properly, or shouldn't be added at
> >>>>>> all;
> >>>>>> no quick hacky fixes that aren't part of a planned evolution path
> >>>>>> to a complete solution.
> >>>>>>
> >>>>>> If you can highlight how a complete properties proposal will
> >>>>>> seamlessly work with your syntax, I'm more inclined to like it.
> >>>>>>
> >>>>>> --Reinier Zwitserloot
> >>>>>>
> >>>>>> On Mar 3, 2009, at 22:04, David Goodenough wrote:
> >>>>>>> Yes I do.  What you propose is much more invasive.  Look at it
> >>>>>>> again and maybe you will see the Light(ness).
> >>>>>>>
> >>>>>>> David
> >>>>>>>
> >>>>>>> On Tuesday 03 March 2009, Reinier Zwitserloot wrote:
> >>>>>>>> You call that lightweight?
> >>>>>>>>
> >>>>>>>> How about following the beans spec more to the letter and just
> >>>>>>>> generate the addPropertyChangeListener,
> >>>>>>>> removePropertyChangeListener, setX(), and get/isX() method in
> >>>>>>>> response to seeing a keyword or annotation on a given field.
> >>>>>>>> You'll have to work out the details, but that sounds far, far
> >>>>>>>> simpler to understand.
> >>>>>>>>
> >>>>>>>> You'll need to flesh this out, but it would look something
> >>>>>>>> like:
> >>>>>>>>
> >>>>>>>> public class Foo {
> >>>>>>>>  private property int x;
> >>>>>>>> }
> >>>>>>>>
> >>>>>>>> Which would generate the addPropertyChangeListener,
> >>>>>>>> removePropertyChangeListener, setX, getX methods, all public,
> >>>>>>>> along with the required infrastructure to make it tick. If you
> >>>>>>>> don't like the generation, for example because you want the
> >>>>>>>> setter to be package private, you just add the setter in the
> >>>>>>>> source file; the keyword will only generate the missing
> >>>>>>>> stuff. It
> >>>>>>>> doesn't cover every use case, but there's always the
> >>>>>>>> alternative
> >>>>>>>> of doing whatever people do now with beans. Something you
> >>>>>>>> didn't
> >>>>>>>> mention in your proposal, by the way.
> >>>>>>>>
> >>>>>>>> I think there's also a fully fleshed out property proposal
> >>>>>>>> (including a 'property' keyword) out there somewhere.
> >>>>>>>>
> >>>>>>>> Possibly make a way to opt out of generating the property
> >>>>>>>> change
> >>>>>>>> listener support, and just the getters/setters.
> >>>>>>>> --Reinier Zwitserloot
> >>>>>>>>
> >>>>>>>> On Mar 3, 2009, at 15:59, David Goodenough wrote:
> >>>>>>>>> Below is my proposal for Lightweight Properties.  I know that
> >>>>>>>>> the syntax change is an abbomination to some people, but I
> >>>>>>>>> have
> >>>>>>>>> tried to reduce this to its absolute minimum, while still
> >>>>>>>>> getting a significant benefit.
> >>>>>>>>>
> >>>>>>>>> PROJECT COIN SMALL LANGUAGE CHANGE PROPOSAL FORM v1.0
> >>>>>>>>>
> >>>>>>>>> AUTHOR(S):
> >>>>>>>>>
> >>>>>>>>> David Goodenough, long time Java user. I can be reached at
> >>>>>>>>> david.goodenough at linkchoose.co.uk.
> >>>>>>>>>
> >>>>>>>>> OVERVIEW
> >>>>>>>>>
> >>>>>>>>> FEATURE SUMMARY:
> >>>>>>>>>
> >>>>>>>>> Lightweight Property support
> >>>>>>>>>
> >>>>>>>>> MAJOR ADVANTAGE:
> >>>>>>>>>
> >>>>>>>>> Both BeansBinding (whether JSR-295 or others such an JFace or
> >>>>>>>>> the JGoodies
> >>>>>>>>> binding) and the JPA Criteria API currently require field
> >>>>>>>>> names
> >>>>>>>>> (as
> >>>>>>>>> Strings)
> >>>>>>>>> as arguments, which an IDE/compiler can not check. With this
> >>>>>>>>> proposal the strings would be abandoned, and the IDE/compiler
> >>>>>>>>> will be able to check the correctness of the code.
> >>>>>>>>>
> >>>>>>>>> MAJOR BENEFIT:
> >>>>>>>>>
> >>>>>>>>> Manual checking no longer required. This proposal introduces a
> >>>>>>>>> simple well defined IDE/compiler checkable solution.
> >>>>>>>>>
> >>>>>>>>> MAJOR DISADVANTAGE:
> >>>>>>>>>
> >>>>>>>>> It is a language change, and this seems to upset some people.
> >>>>>>>>>
> >>>>>>>>> ALTERNATIVES:
> >>>>>>>>>
> >>>>>>>>> None really, apart from using another language or continuing
> >>>>>>>>> to
> >>>>>>>>> use String names. The existing solutions all require String
> >>>>>>>>> names which are uncheckable.
> >>>>>>>>>
> >>>>>>>>> EXAMPLES
> >>>>>>>>>
> >>>>>>>>> Lets assume we have a POJO called foo, of type Foo with a
> >>>>>>>>> field
> >>>>>>>>> bar of type Bar, which itself has a field of type Jim called
> >>>>>>>>> jim.
> >>>>>>>>>
> >>>>>>>>> There are two forms of lightweight properties:-
> >>>>>>>>>
> >>>>>>>>> 1) foo#bar would be translated by the compiler into:-
> >>>>>>>>>
> >>>>>>>>> 	new Property<Foo,Bar>(foo,"bar");
> >>>>>>>>>
> >>>>>>>>> while foo#bar#jim would be translated into:-
> >>>>>>>>>
> >>>>>>>>> 	new Property<Foo,Jim>(foo,"bar","jim");
> >>>>>>>>>
> >>>>>>>>> 2) Foo#bar would be translated into:-
> >>>>>>>>>
> >>>>>>>>> 	new Property<Foo,Bar>(Foo.class,"bar");
> >>>>>>>>>
> >>>>>>>>> while Foo#bar#jim would be translated into:-
> >>>>>>>>>
> >>>>>>>>> 	new Property<Foo,Jim>(Foo.class,"bar","jim");
> >>>>>>>>>
> >>>>>>>>> These two forms create (1) a bound Property, or (2) an unbound
> >>>>>>>>> one.
> >>>>>>>>> Bound
> >>>>>>>>> Properties are explicitly bound to a particular instance of a
> >>>>>>>>> class (in this case foo), while unbound Properties are
> >>>>>>>>> templates
> >>>>>>>>> which can be applied to any instance of class Foo. Actually
> >>>>>>>>> bound properties can also be used as unbound properties, but
> >>>>>>>>> that is a harmless and useful side effect not a primary
> >>>>>>>>> intent.
> >>>>>>>>>
> >>>>>>>>> The Property class would need to be added (it is appended
> >>>>>>>>> below), and should be added either to the java.beans package
> >>>>>>>>> or
> >>>>>>>>> to the java.lang.reflect package (with which is probably has
> >>>>>>>>> more in common).
> >>>>>>>>>
> >>>>>>>>> Syntactically a "#" can be placed wherever a "." can be placed
> >>>>>>>>> (except inside a number), and the same checks need to be made
> >>>>>>>>> (that each field to the right of a # is a field in the left
> >>>>>>>>> hand
> >>>>>>>>> side) as would be made for a ".".
> >>>>>>>>> The only
> >>>>>>>>> difference is in field visibility - For the "#" any field is
> >>>>>>>>> visible, which follows the model currently available in the
> >>>>>>>>> Field class with getDeclaredFields(). It also follows the
> >>>>>>>>> model
> >>>>>>>>> that while a field might be private and therefore not directly
> >>>>>>>>> accessible from outside, getters and setters can provide
> >>>>>>>>> access.
> >>>>>>>>>
> >>>>>>>>> The Property object provides type safe access to the field in
> >>>>>>>>> the form of getters and setters. These come in pairs, one for
> >>>>>>>>> bound and the other for unbound access. So for bound access no
> >>>>>>>>> object is required to fetch the value, for an unbound object
> >>>>>>>>> the
> >>>>>>>>> parent object needs to be specified.
> >>>>>>>>> So if
> >>>>>>>>> we
> >>>>>>>>> have:-
> >>>>>>>>>
> >>>>>>>>> 	Property<Foo,Bar>prop = foo#bar;
> >>>>>>>>>
> >>>>>>>>> we can later say:-
> >>>>>>>>>
> >>>>>>>>> 	Bar b = prop.get();
> >>>>>>>>>
> >>>>>>>>> or for an unbound one from a second Foo object foo2:-
> >>>>>>>>>
> >>>>>>>>> 	Bar b = prop.get(foo2);
> >>>>>>>>>
> >>>>>>>>> The getters and setters in the Property object will defer to
> >>>>>>>>> explicitly coded getters and setters if present, otherwise
> >>>>>>>>> they
> >>>>>>>>> will use the Field getter and setter.
> >>>>>>>>>
> >>>>>>>>> If a setter is not explicitly coded, the implicit setter will
> >>>>>>>>> look for a PropertyChangeSupport object in the parent object
> >>>>>>>>> of
> >>>>>>>>> the rightmost field and fire a PropertyChangeEvent to that
> >>>>>>>>> object.
> >>>>>>>>>
> >>>>>>>>> There are also two Annotations provided by the Property class,
> >>>>>>>>> ReadOnly and WriteOnly. These stop implicit getters and
> >>>>>>>>> setters
> >>>>>>>>> from trying to read/write the property.
> >>>>>>>>>
> >>>>>>>>> Talking of Annotations, this notation can also be used to
> >>>>>>>>> get at
> >>>>>>>>> the Annotations for a field. So to test for the presence of an
> >>>>>>>>> Annotation Ann on Foo.bar we would use:-
> >>>>>>>>>
> >>>>>>>>> 	if(Foo#bar.getFields()[0].isAnnotationPresent(Ann.class)) ...
> >>>>>>>>>
> >>>>>>>>> SIMPLE EXAMPLE:
> >>>>>>>>>
> >>>>>>>>> To take an example from BeansBinding (taken from Shannon
> >>>>>>>>> Hickey's
> >>>>>>>>> blog):-
> >>>>>>>>>
> >>>>>>>>> 	// create a BeanProperty representing a bean's firstName
> >>>>>>>>> 	Property firstP = BeanProperty.create("firstName");
> >>>>>>>>> 	// Bind Duke's first name to the text property of a Swing
> >>>>>>>>> JTextField
> >>>>>>>>> 	BeanProperty textP = BeanProperty.create("text");
> >>>>>>>>> 	Binding binding = Bindings.createAutoBinding(READ_WRITE,
> >>>>>>>>> duke,
> >>>>>>>>> 					firstP, textfield, textP);
> >>>>>>>>> 	binding.bind();
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> would instead be written:-
> >>>>>>>>>
> >>>>>>>>> 	Binding binding = Bindings.createAutoBinding(READ_WRITE,
> >>>>>>>>> 					duke#firstName, textfield#text);
> >>>>>>>>> 	binding.bind();
> >>>>>>>>>
> >>>>>>>>> which of course can be checked by the IDE/compiler, and will
> >>>>>>>>> not
> >>>>>>>>> wait until run time (not even instantiation time) to show up
> >>>>>>>>> the
> >>>>>>>>> error.
> >>>>>>>>>
> >>>>>>>>> ADVANCED EXAMPLE:
> >>>>>>>>>
> >>>>>>>>> For a JComboBox (or JList or JTable or JTree) there is a
> >>>>>>>>> need to
> >>>>>>>>> map a list of objects to the value strings (or column
> >>>>>>>>> contents).
> >>>>>>>>> For this we need to have an unbound Property which can be
> >>>>>>>>> applied to each element of the list.
> >>>>>>>>>
> >>>>>>>>> 	Duke duke;
> >>>>>>>>> 	List<Duke>dukes;
> >>>>>>>>> 	BoundComboBox combo = new
> >>>>>>>>> BoundComboBox(dukes,Duke#fullname,this#duke);
> >>>>>>>>>
> >>>>>>>>> and now the combo box will be populated from the list dukes,
> >>>>>>>>> and
> >>>>>>>>> the display values in the list will be taken from the fullname
> >>>>>>>>> field of each Duke element, and the initial value will be set
> >>>>>>>>> from the local class field duke and any changes to the combo
> >>>>>>>>> box
> >>>>>>>>> selected element will be reflected back to the duke field.
> >>>>>>>>>
> >>>>>>>>> DETAILS
> >>>>>>>>>
> >>>>>>>>> SPECIFICATION:
> >>>>>>>>>
> >>>>>>>>> This proposal adds a new syntactic element, "#", which can be
> >>>>>>>>> used in the same way that "." can be used to qualify fields
> >>>>>>>>> within a Java object.
> >>>>>>>>>
> >>>>>>>>> COMPILATION:
> >>>>>>>>>
> >>>>>>>>> This proposal requires no change to the class files, and is
> >>>>>>>>> implemented by a simple generation of the required instance
> >>>>>>>>> using the relevant Property constructor. Obviously the
> >>>>>>>>> compiler
> >>>>>>>>> would have to make sure that the use that the property object
> >>>>>>>>> was being put to (in the examples above the left hand side of
> >>>>>>>>> the assignment) had the correct Generic attributes.
> >>>>>>>>>
> >>>>>>>>> TESTING:
> >>>>>>>>>
> >>>>>>>>> How can the feature be tested?
> >>>>>>>>>
> >>>>>>>>> LIBRARY SUPPORT:
> >>>>>>>>>
> >>>>>>>>> The new Property class is required (see below).
> >>>>>>>>>
> >>>>>>>>> REFLECTIVE APIS:
> >>>>>>>>>
> >>>>>>>>> No changes are required to the reflective APIs although it
> >>>>>>>>> makes
> >>>>>>>>> extensive use of those APIs.
> >>>>>>>>>
> >>>>>>>>> OTHER CHANGES:
> >>>>>>>>>
> >>>>>>>>> No other changes are requires.
> >>>>>>>>>
> >>>>>>>>> MIGRATION:
> >>>>>>>>>
> >>>>>>>>> Fortunately there is no code that is formally part of J2SE 6
> >>>>>>>>> which uses such Properties. There are however two proposals
> >>>>>>>>> which will need it (BeansBinding and JPA Criteria API), but
> >>>>>>>>> neither of these seem to be destined to be part of J2SE 7
> >>>>>>>>> (BeansBinding seems to have died the death and the Criteria
> >>>>>>>>> API
> >>>>>>>>> would be part of the next J2EE which will follow J2SE 7), so
> >>>>>>>>> this will provide a base for them to use and no existing code
> >>>>>>>>> need to be updated.
> >>>>>>>>>
> >>>>>>>>> There are other extant Beans-Binding libraries, which could be
> >>>>>>>>> modified to use this proposal, but as none of the existing
> >>>>>>>>> features have been changed there is no need to change them
> >>>>>>>>> (other than for type safety and compiler/ IDE checkability).
> >>>>>>>>>
> >>>>>>>>> COMPATIBILITY
> >>>>>>>>>
> >>>>>>>>> BREAKING CHANGES:
> >>>>>>>>>
> >>>>>>>>> None.  This change should not make any existing correct code
> >>>>>>>>> fail to compile or run or change the way in which it
> >>>>>>>>> compiles/runs.
> >>>>>>>>>
> >>>>>>>>> EXISTING PROGRAMS:
> >>>>>>>>>
> >>>>>>>>> No change required to any existing programs
> >>>>>>>>>
> >>>>>>>>> REFERENCES
> >>>>>>>>>
> >>>>>>>>> EXISTING BUGS:
> >>>>>>>>>
> >>>>>>>>> None
> >>>>>>>>>
> >>>>>>>>> URL FOR PROTOTYPE (optional):
> >>>>>>>>>
> >>>>>>>>> I do not have the knowledge to make changes to the compiler,
> >>>>>>>>> and
> >>>>>>>>> the only documentation making such changes concentrated on
> >>>>>>>>> adding operators not changes at this level. So there is no
> >>>>>>>>> prototype of the compiler part, but the Property class
> >>>>>>>>> follows:-
> >>>>>>>>>
> >>>>>>>>> package java.lang.reflect;
> >>>>>>>>>
> >>>>>>>>> import java.beans.BeanInfo;
> >>>>>>>>> import java.beans.Introspector;
> >>>>>>>>> import java.beans.PropertyChangeSupport; import
> >>>>>>>>> java.beans.PropertyDescriptor; import java.lang.reflect.Field;
> >>>>>>>>> import java.lang.reflect.Method;
> >>>>>>>>>
> >>>>>>>>> /**
> >>>>>>>>> * Property class
> >>>>>>>>> * This is the support class for use with the # notation to
> >>>>>>>>> provide lightweight
> >>>>>>>>> * Property support for Java.
> >>>>>>>>> *
> >>>>>>>>> * @copyright Copyright(C) 2009 David Goodenough Linkchoose Ltd
> >>>>>>>>> * @licence LPGL V2 : details of which can be found at http://
> >>>>>>>>> fsf.org.
> >>>>>>>>> * @author david.goodenough at linkchoose.co.uk
> >>>>>>>>> *
> >>>>>>>>> * @param <C> The Parent class for this field
> >>>>>>>>> * @param <F> The Type of this field */ public class
> >>>>>>>>> Property<C,F> {  private C parent; private Class<?>
> >>>>>>>>> parentClass;
> >>>>>>>>> private Field[] fields; private PropertyDescriptor[] pd =
> >>>>>>>>> null;
> >>>>>>>>> /**
> >>>>>>>>> * Constructor used to create Property objects.  The Parent
> >>>>>>>>> object may be
> >>>>>>>>> * null, but should normally be specified as it can be
> >>>>>>>>> overridden anyway.
> >>>>>>>>> * @param parent C object that contains the field
> >>>>>>>>> * @param field Field describing this field  */ public
> >>>>>>>>> Property(C parent, String ... fieldNames) {
> >>>>>>>>>    this.parent = parent;
> >>>>>>>>>     this(parent.getClass(), fieldNames);
> >>>>>>>>>     }
> >>>>>>>>> /**
> >>>>>>>>>  * Constructor for unbound Properties, but also used
> >>>>>>>>> internally
> >>>>>>>>> after setting
> >>>>>>>>>  * the parent object by the bound Property objects.
> >>>>>>>>>  * @param parentClass Class of the parent object
> >>>>>>>>>  * @param fieldNames String[] of field names
> >>>>>>>>>  */
> >>>>>>>>> public Property(Class<?>parentClass, String .. fieldNames) {
> >>>>>>>>>     this.parentClass = parentClass;
> >>>>>>>>>    fields = new Field[fieldNames.length];
> >>>>>>>>>    pd = new PropertyDescriptor[fieldNames.length];
> >>>>>>>>> outer:  for(int index = 0; index < fields.length; index++) {
> >>>>>>>>>        Field[]dclFields = parentClass.getDeclaredFields();
> >>>>>>>>>    	    for(Field field:dclFields) {
> >>>>>>>>>    		if(field.getName().equals(fieldNames[index])) {
> >>>>>>>>>    			fields[index] = field;
> >>>>>>>>>    			field.setAccessible(true);
> >>>>>>>>>    	    	try {
> >>>>>>>>>    	    		BeanInfo beanInfo =
> >>>>>>>>> Introspector.getBeanInfo(parent.getClass());
> >>>>>>>>>    	    		PropertyDescriptor[]props =
> >>>>>>>>> beanInfo.getPropertyDescriptors();
> >>>>>>>>>    	    		for(PropertyDescriptor prop : props) {
> >>>>>>>>>    	    			if(prop.getName().equals(field.getName())) {
> >>>>>>>>>    	    				pd[index] = prop;
> >>>>>>>>>    	    				break;
> >>>>>>>>>    	    				}
> >>>>>>>>>    	    			}
> >>>>>>>>>    	    		} catch(Exception e) { /* assume can not find
> >>>>>>>>> getter/
> >>>>>>>>> setter */ }
> >>>>>>>>>    			parentClass = field.getType();
> >>>>>>>>>    			continue outer;
> >>>>>>>>>    			}
> >>>>>>>>>    		}
> >>>>>>>>> 	throw new IllegalArgumentException("Field " +
> >>>>>>>>> fieldNames[index]
> >>>>>>>>> + " not found in class " +
> >>>>>>>>> parentClass.getCanonicalName());
> >>>>>>>>>    	}
> >>>>>>>>>    }
> >>>>>>>>> /**
> >>>>>>>>> * Getter from the field in the parent specified when this
> >>>>>>>>> Property was created.
> >>>>>>>>> * @see Property.get(C otherParent)
> >>>>>>>>> * @return F the value of this field  */ public F get() {
> >>>>>>>>>    return get(parent);
> >>>>>>>>>    }
> >>>>>>>>> /**
> >>>>>>>>> * Getter with explicit parent.
> >>>>>>>>> * This code will check see if this field is WriteOnly, and
> >>>>>>>>> complain if it is.
> >>>>>>>>> * It will then see if the use has provided am explicit getter,
> >>>>>>>>> and call that
> >>>>>>>>> * if present, otherwise it will just fetch the value through
> >>>>>>>>> the Field provided
> >>>>>>>>> * method.
> >>>>>>>>> * @param otherParent C parent object
> >>>>>>>>> * @return F value of the field
> >>>>>>>>> */
> >>>>>>>>> @SuppressWarnings("unchecked") // This should actually not be
> >>>>>>>>> needed,
> >>>>>>>>>                               // but the Field.get method is
> >>>>>>>>> not typed public F get(C otherParent) {
> >>>>>>>>> 	Object result = otherParent;
> >>>>>>>>> 	try {
> >>>>>>>>> 	    for(int index = 0; index < fields.length; index++) {
> >>>>>>>>>
> >>>>>>>>> if
> >>>>>>>>> (fields[index].getType().isAnnotationPresent(WriteOnly.class))
> >>>>>>>>>                throw new IllegalAccessException(
> >>>>>>>>>                    "Can not get from a WriteOnly field - " +
> >>>>>>>>> fields[index].getName());
> >>>>>>>>> 		Method getter = pd[index] == null ? null :
> >>>>>>>>> pd[index].getReadMethod();
> >>>>>>>>> 		if(getter == null) result = fields[index].get(result);
> >>>>>>>>> 		    else result = getter.invoke(result);
> >>>>>>>>> 		}
> >>>>>>>>> 	    } catch(Exception e) {
> >>>>>>>>> 		throw new RuntimeException("Should not occur exception", e);
> >>>>>>>>> 		}
> >>>>>>>>> 	return (F)result;
> >>>>>>>>>    }
> >>>>>>>>> /**
> >>>>>>>>> * Setter to set the value of the field in the parent object
> >>>>>>>>> declared with the
> >>>>>>>>> * Property object
> >>>>>>>>> * @param newValue F new value of this field  */ public void
> >>>>>>>>> set(F newValue) {
> >>>>>>>>>    set(parent,newValue);
> >>>>>>>>>    }
> >>>>>>>>> /**
> >>>>>>>>> * Setter to set the value of the field to an explicit parent
> >>>>>>>>> object.
> >>>>>>>>> * If there is a ReadOnly annotation, then we object.  If there
> >>>>>>>>> is an explicit
> >>>>>>>>> * setter then we use that, otherwise we set the field using
> >>>>>>>>> the
> >>>>>>>>> Field provided
> >>>>>>>>> * set method and if there is a PropertyChangeSupport field,
> >>>>>>>>> fire a property
> >>>>>>>>> * change event to it.
> >>>>>>>>> * We walk our way down the field chain, until we have the last
> >>>>>>>>> object and its
> >>>>>>>>> * field, and then we do the set.
> >>>>>>>>> * @param parent C explicit parent object
> >>>>>>>>> * @param newValue F new value for field in parent  */ public
> >>>>>>>>> void set(C parent,F newValue) {
> >>>>>>>>> 	try {
> >>>>>>>>> 	    Object last = parent;
> >>>>>>>>> 	    int index;
> >>>>>>>>> 	    for(index = 0; index < fields.length - 1; index++) {
> >>>>>>>>>
> >>>>>>>>> if
> >>>>>>>>> (fields[index].getType().isAnnotationPresent(WriteOnly.class))
> >>>>>>>>>                throw new IllegalAccessException(
> >>>>>>>>>                    "Can not get from a WriteOnly field - " +
> >>>>>>>>>                    fields[index].getName());
> >>>>>>>>>            Method getter = pd[index] == null ? null :
> >>>>>>>>> pd[index].getReadMethod();
> >>>>>>>>> 	        if(getter == null) last = fields[index].get(last);
> >>>>>>>>> 	        else last = getter.invoke(last);
> >>>>>>>>>            }
> >>>>>>>>>
> >>>>>>>>> if
> >>>>>>>>> (fields[index].getType().isAnnotationPresent(ReadOnly.class))
> >>>>>>>>>            throw new IllegalAccessException(
> >>>>>>>>>                "Can not get from a WriteOnly field - " +
> >>>>>>>>> fields[index].getName());
> >>>>>>>>> 		Method setter = pd[index] == null ? null :
> >>>>>>>>> pd[index].getWriteMethod();
> >>>>>>>>> 		if(setter == null) {
> >>>>>>>>> 	            PropertyChangeSupport pcs =
> >>>>>>>>> findPcs(last.getClass()); fields[index].set(last,newValue);
> >>>>>>>>> 	        if(pcs != null)
> >>>>>>>>> pcs.firePropertyChange(fields[index].getName(),
> >>>>>>>>>                                                    newValue,
> >>>>>>>>>
> >>>>>>>>> fields[index].get(last));
> >>>>>>>>> 		} else setter.invoke(last,newValue);
> >>>>>>>>> 	    } catch(Exception e) {
> >>>>>>>>>            throw new RuntimeException("Should not occur
> >>>>>>>>> exception", e);
> >>>>>>>>>            }
> >>>>>>>>>    }
> >>>>>>>>> /**
> >>>>>>>>> * This is used so that the caller can view the Field name
> >>>>>>>>> * @return String field name
> >>>>>>>>> */
> >>>>>>>>> public String[] getFieldName() {
> >>>>>>>>> 	String[]names = new String[fields.length];
> >>>>>>>>> 	for(int index = 0; index < fields.length; index++) {
> >>>>>>>>>        names[index] = fields[index].getName();
> >>>>>>>>>        }
> >>>>>>>>> 	return names;
> >>>>>>>>> 	}
> >>>>>>>>> /**
> >>>>>>>>>  * This method is used to fetch the Field array, which is
> >>>>>>>>> useful if you need to
> >>>>>>>>>  * access the Annotations of a field.
> >>>>>>>>>  * @return Field[] the array of Fields describing this
> >>>>>>>>> Property.
> >>>>>>>>>  */
> >>>>>>>>> public Field[] getFields() {
> >>>>>>>>>     return fields;
> >>>>>>>>>     }
> >>>>>>>>> /**
> >>>>>>>>> * This private method looks for a PropertyChangeSupport object
> >>>>>>>>> in the class and
> >>>>>>>>> * if one is found it will return it.  It looks right the way
> >>>>>>>>> up
> >>>>>>>>> the class tree
> >>>>>>>>> * by recurring up the superClasses.
> >>>>>>>>> * @param parent Class to check for PropertyChangeSupport
> >>>>>>>>> fields
> >>>>>>>>> * @return PropertyChangeSupport first found object, or null if
> >>>>>>>>> not found  */ private PropertyChangeSupport findPcs(Class<?>
> >>>>>>>>> parent) {
> >>>>>>>>> 	Field fields[] = parent.getDeclaredFields();
> >>>>>>>>> 	for(Field field:fields) {
> >>>>>>>>> 	    field.setAccessible(true);
> >>>>>>>>> 	    try {
> >>>>>>>>> 	        if(field.getType() == PropertyChangeSupport.class)
> >>>>>>>>> 	            return (PropertyChangeSupport)field.get(parent);
> >>>>>>>>>            } catch(Exception e) { }
> >>>>>>>>> 	    }
> >>>>>>>>> 	// If we did not find it then try the superclass
> >>>>>>>>> 	Class<?>superClass = parent.getSuperclass();
> >>>>>>>>> 	if(superClass == null) return null;
> >>>>>>>>> 	return findPcs(parent.getClass().getSuperclass());
> >>>>>>>>>    }
> >>>>>>>>> /**
> >>>>>>>>> * This annotation is used to mark a field as WriteOnly, i.e.
> >>>>>>>>> it
> >>>>>>>>> can not be read.
> >>>>>>>>> * This stops the automatic getter operation.
> >>>>>>>>> */
> >>>>>>>>> public @interface WriteOnly {
> >>>>>>>>> 	}
> >>>>>>>>> /**
> >>>>>>>>> * This annotation is used to mark a field as ReadOnly, i.e. it
> >>>>>>>>> can not be written.
> >>>>>>>>> * This stops the automatic setter operation.
> >>>>>>>>> */
> >>>>>>>>> public @interface ReadOnly {
> >>>>>>>>> 	}
> >>>>>>>>> }





More information about the coin-dev mailing list