Bug 123470 - AST: new type IResolvedAnnotation
Summary: AST: new type IResolvedAnnotation
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.2 M5   Edit
Assignee: Jerome Lanneluc CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-01-11 12:29 EST by Martin Aeschlimann CLA
Modified: 2006-02-14 12:33 EST (History)
6 users (show)

See Also:


Attachments
Patch for jdt.core projects (63.34 KB, patch)
2006-02-09 10:10 EST, Jerome Lanneluc CLA
no flags Details | Diff
Patch for jdt.apt projects (17.88 KB, patch)
2006-02-09 10:11 EST, Jerome Lanneluc CLA
no flags Details | Diff
Improved patch for jdt.core projects (67.59 KB, patch)
2006-02-09 13:32 EST, Jerome Lanneluc CLA
no flags Details | Diff
Improved patch for jdt.apt projects (18.63 KB, patch)
2006-02-09 13:33 EST, Jerome Lanneluc CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Aeschlimann CLA 2006-01-11 12:29:42 EST
20060110

A new types IResolvedAnnotation and IResolvedMemberValuePair has been added to the AST. It basically represents a new kind of binding that group the bindings of an annotation and the agruments of an annotation.
IMO this might be handy, but doesn't really fit in the existing style of the AST.
The existing bindings are enough tor represent the information required.

e.g. annotation.resolveTypeBinding() gives you directly access to the binding of the annotation.

To simplify the access to the bindings of the attributes, why not add a method
  annotation.resolveAnnotationArguments() : IMethodBinding[]
and for the values a API
  annotation.resolveAnnotationValues() : Object[]
(I'm not sure if the annotation values really help much here, they are only in vary rare cases configured)

resulting in no new types.
Comment 1 Martin Aeschlimann CLA 2006-01-11 12:55:22 EST
Oops, I didn't realize what's the main reason for this new type:
To give access to the annotations when having a IBinding
(IBinding.getAnnotations()).

It's strange that this is not a IBinding, but I guess adding a new binding might be highly breaking.
I also think that IResolvedMemberValuePair could be folded in IResolvedAnnotation.
All information about the value name can be accessed through the methods of the annotations ITypeBinding (IResolvedAnnotation.getAnnotationType().getDeclaredMethods())
Comment 2 Dirk Baeumer CLA 2006-01-12 11:22:39 EST
A couple of additional comments:

- all types in the DOM that provide resolved information currently have 
  binding it their names. IMO even if this class can't be made a binding we
  should put binding into its name.

- the pair Annotation definition and annotation reference has IMO similarities
  with generic and parameterized types. For generic/parameterized types 
  all information is available through ITypeBinding. What was the underlying
  reason for coming up with a new class IResolvedAnnotation and not "merging"
  it into the current type binding.

- if we want to have this class separately from bindings then we should think
  about creating an interface above binding which is then implemented by
  IResolvedAnnotation as well. Otherwise clients can't deal with all resolved
  items in a DOM polymorphical anymore.

Thoughts ?
Comment 3 Markus Keller CLA 2006-01-19 06:23:53 EST
More comments:

(1) The identity and equality relationships between two IResolvedAnnotations should be documented (same for IResolvedMemberValuePair).

(2) IMethodBinding's new query
    IResolvedAnnotation[] getParameterAnnotations(int paramIndex)
does not fit the style of the rest of the API. It answers a question about an IVariableBinding, and it's strange that this is offered on IMethodBinding.

If the query is really necessary on IMethodBinding, it should rather be
    IResolvedAnnotation[][] getParameterAnnotations().

A cleaner approach would be to offer
    IVariableBinding[] getParameters()
on IMethodBinding, from where clients can easily ask IVariableBinding#getAnnotations() for each parameter.
Comment 4 Olivier Thomann CLA 2006-01-30 12:45:07 EST
(In reply to comment #3)
> A cleaner approach would be to offer
>     IVariableBinding[] getParameters()
> on IMethodBinding, from where clients can easily ask
> IVariableBinding#getAnnotations() for each parameter.
The problem with this solution is that there is no such thing as getParameters() on IMethodBinding that would return IVariableBinding[].
The reason for that is that the method binding doesn't know anything about the parameters' names.
IVariableBinding would need to provide getName(), ...
A method binding simply knows about the parameter types.
Comment 5 Olivier Thomann CLA 2006-01-30 17:02:44 EST
(In reply to comment #2)
> - all types in the DOM that provide resolved information currently have 
>   binding it their names. IMO even if this class can't be made a binding we
>   should put binding into its name.
I never liked this idea of having a binding for a name. The name itself means nothing. The binding only has a meaning according to the context. So it is a good thing to retrieve the binding using the name's parent.

> - the pair Annotation definition and annotation reference has IMO similarities
>   with generic and parameterized types. For generic/parameterized types 
>   all information is available through ITypeBinding. What was the underlying
>   reason for coming up with a new class IResolvedAnnotation and not "merging"
>   it into the current type binding.
Are you suggesting to move all the methods from IResolvedAnnotation to ITypeBinding?

> - if we want to have this class separately from bindings then we should think
>   about creating an interface above binding which is then implemented by
>   IResolvedAnnotation as well. Otherwise clients can't deal with all resolved
>   items in a DOM polymorphical anymore.
Would not it be an API breakage to do so?

Tim, any comment regarding the second question?
Comment 6 Tim Hanson CLA 2006-01-30 18:29:16 EST
>> - the pair Annotation definition and annotation reference has IMO similarities
>>   with generic and parameterized types. For generic/parameterized types 
>>   all information is available through ITypeBinding. What was the underlying
>>   reason for coming up with a new class IResolvedAnnotation and not "merging"
>>   it into the current type binding.
>Are you suggesting to move all the methods from IResolvedAnnotation to
>ITypeBinding?

Something other than the type binding is needed because the values are needed. It is also neccessary to couple the values to the the member. This seems like the logical representation to me. 
Comment 7 Dirk Baeumer CLA 2006-01-31 04:26:32 EST
In reply to comment #5:

1.) May be a misunderstanding: my suggestion is to name the class something like
    IAnnotationReferenceBinding instead of IResolvedAnnotation to make clear it 
    is an type from the binding world.

2.) Yes, one of my first thought was "Why isn't this a ITypeBinding". It could
    have a method isAnnotationReference. If true then the analogous methods 
    currently available on IResolvedAnnotation have a meaningful value. I 
    compared this with generic/parameterized types since (although both are 
    declaration) the second is also an "instantiation" of the first.

3.) I don't think so. Added a new superclass is IMO not an API breakage. It is
    binary compatible but not source comaptible.
Comment 8 Philipe Mulet CLA 2006-01-31 10:21:58 EST
Resolved annotations are NOT type bindings, they denote some sort of references to an annotation type, with attributes.

The parallel with parameterized types doesn't work as one cannot use an annotation ref as variable type. So in essence, it is not the same.

I agree, though, that current naming is confusing. I would suggest:
IAnnotationBinding
since it is:
- a binding
- any binding is resolved intrinsically

Comment 9 Philipe Mulet CLA 2006-01-31 10:23:00 EST
Internally, I use "attribute" for memberPairValue. Is this something which would help decoding the API ?
Comment 10 Martin Aeschlimann CLA 2006-02-01 09:35:48 EST
We discussed a lot here in the JDT.UI team: Here are our findings:
- We agree with Philipps comment 9: this new construct is not related to ITypeBinding. IAnnotationBinding is a good name.

Here are our findings and an additional, more lightweigth alternative:
a.) we call it a binding (IAnnotationBinding): it won't extend IBinding for compatibility reasons but behaves like one:  
- identity
- a unique key
- only there if 100% complete (no bindings for unresolved cases)
- mapping from binding to ASTNode (CompilationUnit.findDeclaringNode(binding))
- mapping from nodes to binding (Annotation.resolveAnnotationBinding())
- binding should also provide the containing node (IAnnotationBinding.getAnnotatedBinding), similar like an IMethodBinding.getDeclaringType
- if e.g 2 methods are annotated with @X, these 2 annotations have different bindings (reason: CompilationUnit.findDeclaringNode(binding) must return the correct ASTNode)

If IAnnotationBinding has a IResolvedMemberValuePair, then also IResolvedMemberValuePair should be a binding, with the same properties

b.) we only treat it as a attibute of binding. Similar to IBinding.getModifiers(), you just get some information from a binding (in int), but it isn't one. Name could be 'IAnnotationDescription'
- no mapping API from IAnnotationDescription to ASTNode
(no CompilationUnit.findDeclaringNode(annotDesc). Instead: CompilationUnit.findDeclaringNode(annotDesc.getAnnotatedBinding()) and find the annotation on the returned ASTNode)

- no other way to get that IAnnotationDescription than from the declaring binding
(IBinding.getAnnotations(), but no 'annotation.resolveAnnotation()'. Instead clients can use: annotation.resolveTypeBinding().getAnnotaion(name)
- idendity not required, no key

solution a.) is more or less what we have now: Except of the name, there is no key, you can't access the parent, IResolvedMemberValuePair doesn't follow identity rules, you can't find a IResolvedMemberValuePair in an AST. It also seems to be possible that its parts can be null (APT does a null check after IResolvedAnnotation.getAnnotationType).

the weaknesses of this solution are: it's called and behaves like a binding but can't be one (for compatibility reasons): Ugly duplicated APIs:
  CompilationUnit.findDeclaringNode(IResolvedAnnotation resolvedAnnotation)
  Annotation.resolveAnnotationBinding
-> AST inconsistencies

solution b.) is less intrusive but more work for the user to map from an IAnnotationDescription to a ASTNode. We are not aware of a need for this however.

As an additional proposal, valid for both alternatives:
Merge IResolvedMemberValuePair in IAnnotationBinding

IAnnotationBinding
   getAnnotationType() : ITypeBinding
   getValues() : Object
   getValue(key, useDefault) : Object

The values are returned in the same order as getAnnotationType().getDeclaredMethods()
The name and 'isDefault' can all be accessed through the IMethodBinding
getValue(name) : Object is a convenience method to access an value for a given key. 'useDefault' decides if the default value or 'null' will be returned if the annotation has not defined a value. 
Comment 11 Jerome Lanneluc CLA 2006-02-01 16:28:18 EST
Why IAnnotationBinding cannot extend IBinding ? When you say "for compatibility reason", I'm not sure to understand with 'what' we have to be compatible with since this is being introduced in 3.2.
Comment 12 Philipe Mulet CLA 2006-02-01 19:05:54 EST
I would also consider these to implement IBinding (since they are bindings).
Comment 13 Martin Aeschlimann CLA 2006-02-02 03:19:21 EST
Everybody who assumes that there are exactly 4 diffenrent binding kinds will fail, e.g. in switch statement.
There are several of these cases also in our code.
I'd be ready to update our code, but I think this change would have to be sold as breaking API change in 3.2.
IBinding:
	/**
	 * Returns the kind of bindings this is.
	 * 
	 * @return one of the kind constants:
	 * 	<code>PACKAGE</code>,
	 * 	<code>TYPE</code>,
	 * 	<code>VARIABLE</code>,
	 * 	or <code>METHOD</code>.
	 */
	public int getKind();
Comment 14 Philipe Mulet CLA 2006-02-02 06:30:43 EST
How different is that from 3.1 additions for param types, wildcards, type parameters etc... ?
Comment 15 Martin Aeschlimann CLA 2006-02-02 08:12:36 EST
For all these changes we introduced the new AST level (AST.JLS3) so that clients have to migrate.
Comment 16 Olivier Thomann CLA 2006-02-06 14:51:50 EST
(In reply to comment #13)
> Everybody who assumes that there are exactly 4 diffenrent binding kinds will
> fail, e.g. in switch statement.
> There are several of these cases also in our code.
> I'd be ready to update our code, but I think this change would have to be sold
> as breaking API change in 3.2.
I don't see how this would be an API breakage. If we add new constants, the existing users would not miss any new or old bindings. They would still treat all the existing bindings.
The new constants would point to instances of the new binding types. The only problem I am seeing is for user who add a default case statement in switch statement. Those default could not be reached.
But since this wasn't specified before, this is not really an API breakage.
The problem is more for users that used a default for the last sets of bindings.
For example:
switch(binding.getKind()) {
case IBinding.PACKAGE :
...;
break;
case IBinding.TYPE :
....;
break;
case IBinding.VARIABLE :
...
break;
default:
   // Assuming IBinding.METHOD
....
}

We could document this one. You are the one that requested a more consistent model. So this looks like a fair trade-off.
Comment 17 Martin Aeschlimann CLA 2006-02-07 07:08:22 EST
If everybody agrees with Olivier's opinion, JDT would be glad to update their code to not make sure nobody makes any assumptions of the possible bindings kinds.
Jim, what's your opinion here?
Comment 18 Jerome Lanneluc CLA 2006-02-07 07:12:12 EST
I also agree with Olivier. Note we had the same dilema in the Java model when adding new IJavaElements. But experience shows that clients are doing the right thing since nobody reported a problem because of these new IJavaElements.
Comment 19 Jim des Rivieres CLA 2006-02-07 09:25:33 EST
I agree with comment #13 that this must be considered a breaking API change. But I also agree with comment #16 that the real risk of breaking existing clients is low. So I'd be ok with making this API change even though it is breaking (there would need to be a 3.2 migration guide entry explaining the change and its impact).

While updating the spec for IBinding.getKind() to include the new constant, the spec could warn clients that additional binding types might be added in the future, so that they can program defensively.
Comment 20 Markus Keller CLA 2006-02-07 15:27:34 EST
Just to make sure we agree on the implications of these new IAnnotationBindings:

I would expect that e.g. SimpleName#resolveBinding() would then return such an IAnnotationBinding for the name of an Annotation node.

And if IMemberValuePair is not merged into IAnnotationBinding, then we should have MemberValuePair#resolveMemberValuePair() returning an IMemberValuePairBinding, etc.

But I think Martin's proposal to drop IMemberValuePairBinding would be a cleaner API anyway.
Comment 21 Martin Aeschlimann CLA 2006-02-08 04:13:34 EST
Regarding Markus' comment 20 about SimpleName#resolveBinding() on the annotation name:

For compatibility reasons I would not change this behaviour, but let it return the type binding of the annotation type.
To get the annotation binding use Annotation.resolveAnnotationBinding()

We have a similar case for the constructor invocation: the binding on the name is the type binding. 'ConstructorInvocation.resolveConstructorBinding' gives you the method binding.
Comment 22 Jerome Lanneluc CLA 2006-02-09 10:10:30 EST
Created attachment 34415 [details]
Patch for jdt.core projects
Comment 23 Jerome Lanneluc CLA 2006-02-09 10:11:15 EST
Created attachment 34416 [details]
Patch for jdt.apt projects
Comment 24 Jerome Lanneluc CLA 2006-02-09 10:19:27 EST
I everyone agrees with these changes, we need to coordinate with APT to release the 2 patches at the same time so as to not break a build.

Jess please let me know when it is a good time for you. Idealy we would like to do it before the end of the week so that we're ready for the M5 builds.
Comment 25 Martin Aeschlimann CLA 2006-02-09 11:39:57 EST
May I suggest the following:
rename 'Annotation.resolveAnnotation' to 'Annotation.resolveAnnotationBinding'
This would be conistent with all other resolve methods, they always are called
resolveXXBinding

CompilationUnit.findDeclaringNode(IAnnotationBinding annotationBinding) not required (it is covered by CompilationUnit.findDeclaringNode(IBinding binding))

getAllMemberValuePairs must not create new elements every time. Bindings must be idendity comparable

I still suggest to simplify and get rid of IMemberValuePairBinding. The annotation type returned with getAnnotationType gives you all the information you need about member value pairs (name and default).

IAnnotationBinding
   getAnnotationType() : ITypeBinding
   getValues() : Object[]    null if value is inherited from default
   getValue(key, useDefault) : Object    

Comment 26 Jess Garms CLA 2006-02-09 12:37:19 EST
Jerome - your patch to APT looks good. You're welcome to apply it directly to APT for the M5 build, or I can do so once you've made the change to jdt.core. Anytime today or tomorrow would be great.
Comment 27 Jerome Lanneluc CLA 2006-02-09 12:56:24 EST
Thanks for the suggestions Martin. However I still think we should keep IMemberPairValueBinding to be consistent since we have the AST node MemberPairValue.

I'll provides new patches shortly.
Comment 28 Jerome Lanneluc CLA 2006-02-09 13:32:20 EST
Created attachment 34435 [details]
Improved patch for jdt.core projects
Comment 29 Jerome Lanneluc CLA 2006-02-09 13:33:05 EST
Created attachment 34436 [details]
Improved patch for jdt.apt projects
Comment 30 Jerome Lanneluc CLA 2006-02-09 13:40:51 EST
Commited both patches to HEAD.
Comment 31 Markus Keller CLA 2006-02-09 14:23:00 EST
You should add this to the javadoc of IBinding#getJavaElement():
* For annotations, this method returns the Java element of the annotation type.

And there are still some "missing links"
- MemberValuePair#resolveMemberValuePairBinding(): IMemberValuePairBinding
- IMemberValuePairBinding#getDeclaringAnnotation(): IAnnotationBinding
- IAnnotationBinding#getDeclaringBinding(): IBinding

CompilationUnit#findDeclaringNode(IBinding) promises to return a MemberValuePair for a member value pair binding. What can clients expect if they pass an IMemberValuePairBinding for a default value or for a SingleMemberAnnotation's value?
Comment 32 Philipe Mulet CLA 2006-02-10 09:29:22 EST
re:- MemberValuePair#resolveMemberValuePairBinding(): IMemberValuePairBinding
Sounds fair.

re:- IMemberValuePairBinding#getDeclaringAnnotation(): IAnnotationBinding
An annotation is homogenous to a paramType or a message send. Arguments are not connected to the parent container. So I wouldn't add this one.

re:- IAnnotationBinding#getDeclaringBinding(): IBinding
What is that ? Isn't it a dup of #getAnnotationType() ?
Comment 33 Jerome Lanneluc CLA 2006-02-10 09:59:27 EST
Thanks for your comment Markus.
- Updated getJavaElement() spec as suggested.
- Added MemberValuePair#resolveMemberValuePairBinding()
- Updated CompilationUnit#findDeclaringNode(IBinding) spec to say that null is returned in the default value and single member cases
Comment 34 Markus Keller CLA 2006-02-10 12:23:32 EST
> re:- IMemberValuePairBinding#getDeclaringAnnotation(): IAnnotationBinding
> An annotation is homogenous to a paramType or a message send. Arguments are
> not connected to the parent container. So I wouldn't add this one.

I disagree. An IMemberValuePairBinding has a unique parent, and there should be a query to get that parent. We already have this for all other containment relations, namely ITypeBinding#getPackage() and IType/Variable/MethodBinding#getDeclaringClass/Method().
typeArgument -> parameterizedType and parameterType -> methodBinding are not one-to-one mappings, and therefore it's clear that there's no link to a parent.

> re:- IAnnotationBinding#getDeclaringBinding(): IBinding
> What is that ? Isn't it a dup of #getAnnotationType() ?

No, like IType/Variable/MethodBinding#getDeclaringClass/Method(), this would return the parent binding that has this IAnnotationBinding attached, i.e binding.getAnnotations() would contain the receiver.
Comment 35 Markus Keller CLA 2006-02-13 09:58:13 EST
Sorry, I think I accidentally removed the target milestone - resetting to 3.2 M5.

I filed bug 127462 for the enhancement requests from comments 32 and 34.
Comment 36 Frederic Fusier CLA 2006-02-14 12:33:48 EST
Verified for 3.2 M5 using build I20060214-0010.