Home » Modeling » TMF (Xtext) » Strange behavior of JvmTypeReference assignments
Strange behavior of JvmTypeReference assignments [message #987875] |
Wed, 28 November 2012 13:26 |
Espinosa CZ Messages: 27 Registered: July 2009 |
Junior Member |
|
|
Hi guys,
I would have a curious question about a strange behaviour of JvmTypeReference assignments I have recently observed.
Let's have this simple language definition fragment:
RootCloner:
type=JvmTypeReference name=ID? '{'
(fields += FieldCloner)*
'}'
// ..several assignments, not relevant..
;
FieldCloner:
// ..several assignments, not relevant..
;
AbstractClonerType:
{AbstractClonerType} (inferredType=JvmTypeReference)?
;
AbstractClonerRule returns AbstractClonerType:
// this cause RootCloner, FieldCloner be but under same interface - AbstractClonerType
// Nice! They will share inferredType property - getInferredType() / setInferredType()
RootCloner | FieldCloner
;
During validation phase - as a sort of post-processing - I fill compute inferredType recursively based on parent type and field name and fill them back to the model. User has to specify explicitly only the type of RootCloner. Everything else is inferred.
The issues is with RootCloner. For RootCloner inferredType == type. To keep whole model consistent, that is to have inferredType set for ALL cloners, root or not, I have to do this assignment:
JvmTypeReference type = rootCloner.getType()
rootCloner.setInferredType(type);
However, the second command, the setInferredType(), the setter of JvmTypeReference, also sets to null the original field, that is rootCloner.type.
Why?
As a consequence, when validation is repeatedly called, and it sure is, in integration tests it is called with every assertion (why??) then the validation does not work since the root type is now null.
I look inside generated setInferredType() code, and it is quite complex method.
What are these eInverseRemove() and eInverseAdd() and basicSetType() are?
What is the purpose?
I search internet and it seems it is quite normal for any JvmTypeReference value assignment setter.
Side note: "inferredType" is indeed transient type, and yes, it gave me headaches when dealing with serialization, until I implemented and plugged in ITransientValuesService and specified that "inferredType" really is transient and should be ignored when serializing.
I found a solution for my problem by creating a "defensive copy" JvmTypeReference:
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.util.TypeReferences;
@Inject TypeReferences typeReferences;
public JvmTypeReference createDefensiveCopyOfJvmTypeReference(JvmTypeReference typeReference) {
return typeReferences.createTypeRef(typeReference.getType());
}
...
JvmTypeReference type = createDefensiveCopyOfJvmTypeReference(rootCloner.getType());
rootCloner.setInferredType(type);
...
..and this works.
I am just curious why is that?
Guys, did I overseen some parts of your documentation?
Espinosa
|
|
|
Re: Strange behavior of JvmTypeReference assignments [message #988192 is a reply to message #987875] |
Wed, 28 November 2012 18:35 |
Hallvard Traetteberg Messages: 673 Registered: July 2009 Location: Trondheim, Norway |
Senior Member |
|
|
Hi,
I guess type and inferredType are both containment references, and since
an EObject cannot be contained in more than one container by more than
one feature, it is removed from the type feature when linked to by the
inferredType feature. You'll need to use EcoreUtil.copy or the create a
copy like you explained.
Hallvard
On 28.11.12 05.26, Espinosa CZ wrote:
> Hi guys,
>
> I would have a curious question about a strange behaviour of
> JvmTypeReference assignments I have recently observed.
>
> Let's have this simple language definition fragment:
>
>
> RootCloner:
> type=JvmTypeReference name=ID? '{'
> (fields += FieldCloner)*
> '}'
> // ..several assignments, not relevant..
> ;
>
> FieldCloner:
> // ..several assignments, not relevant..
> ;
>
> AbstractClonerType:
> {AbstractClonerType} (inferredType=JvmTypeReference)?
> ;
>
> AbstractClonerRule returns AbstractClonerType:
> // this cause RootCloner, FieldCloner be but under same
> interface - AbstractClonerType
> // Nice! They will share inferredType property -
> getInferredType() / setInferredType()
> RootCloner | FieldCloner
> ;
>
>
> During validation phase - as a sort of post-processing - I fill compute
> inferredType recursively based on parent type and field name and fill
> them back to the model. User has to specify explicitly only the type of
> RootCloner. Everything else is inferred.
>
> The issues is with RootCloner. For RootCloner inferredType == type. To
> keep whole model consistent, that is to have inferredType set for ALL
> cloners, root or not, I have to do this assignment:
>
> JvmTypeReference type = rootCloner.getType()
> rootCloner.setInferredType(type);
>
> However, the second command, the setInferredType(), the setter of
> JvmTypeReference, also sets to null the original field, that is
> rootCloner.type.
> Why?
>
> As a consequence, when validation is repeatedly called, and it sure is,
> in integration tests it is called with every assertion (why??) then the
> validation does not work since the root type is now null.
>
> I look inside generated setInferredType() code, and it is quite complex
> method.
> What are these eInverseRemove() and eInverseAdd() and basicSetType() are?
> What is the purpose?
>
> I search internet and it seems it is quite normal for any
> JvmTypeReference value assignment setter.
>
> Side note: "inferredType" is indeed transient type, and yes, it gave me
> headaches when dealing with serialization, until I implemented and
> plugged in ITransientValuesService and specified that "inferredType"
> really is transient and should be ignored when serializing.
>
> I found a solution for my problem by creating a "defensive copy"
> JvmTypeReference:
>
> import org.eclipse.xtext.common.types.JvmTypeReference;
> import org.eclipse.xtext.common.types.util.TypeReferences;
>
> @Inject TypeReferences typeReferences;
>
> public JvmTypeReference
> createDefensiveCopyOfJvmTypeReference(JvmTypeReference typeReference) {
> return typeReferences.createTypeRef(typeReference.getType());
> }
> ..
> JvmTypeReference type =
> createDefensiveCopyOfJvmTypeReference(rootCloner.getType());
> rootCloner.setInferredType(type);
> ..
>
> .and this works. I am just curious why is that?
> Guys, did I overseen some parts of your documentation?
>
> Espinosa
|
|
|
Re: Strange behavior of JvmTypeReference assignments [message #988198 is a reply to message #987875] |
Wed, 28 November 2012 18:35 |
Hallvard Traetteberg Messages: 673 Registered: July 2009 Location: Trondheim, Norway |
Senior Member |
|
|
Hi,
I guess type and inferredType are both containment references, and since
an EObject cannot be contained in more than one container by more than
one feature, it is removed from the type feature when linked to by the
inferredType feature. You'll need to use EcoreUtil.copy or the create a
copy like you explained.
Hallvard
On 28.11.12 05.26, Espinosa CZ wrote:
> Hi guys,
>
> I would have a curious question about a strange behaviour of
> JvmTypeReference assignments I have recently observed.
>
> Let's have this simple language definition fragment:
>
>
> RootCloner:
> type=JvmTypeReference name=ID? '{'
> (fields += FieldCloner)*
> '}'
> // ..several assignments, not relevant..
> ;
>
> FieldCloner:
> // ..several assignments, not relevant..
> ;
>
> AbstractClonerType:
> {AbstractClonerType} (inferredType=JvmTypeReference)?
> ;
>
> AbstractClonerRule returns AbstractClonerType:
> // this cause RootCloner, FieldCloner be but under same
> interface - AbstractClonerType
> // Nice! They will share inferredType property -
> getInferredType() / setInferredType()
> RootCloner | FieldCloner
> ;
>
>
> During validation phase - as a sort of post-processing - I fill compute
> inferredType recursively based on parent type and field name and fill
> them back to the model. User has to specify explicitly only the type of
> RootCloner. Everything else is inferred.
>
> The issues is with RootCloner. For RootCloner inferredType == type. To
> keep whole model consistent, that is to have inferredType set for ALL
> cloners, root or not, I have to do this assignment:
>
> JvmTypeReference type = rootCloner.getType()
> rootCloner.setInferredType(type);
>
> However, the second command, the setInferredType(), the setter of
> JvmTypeReference, also sets to null the original field, that is
> rootCloner.type.
> Why?
>
> As a consequence, when validation is repeatedly called, and it sure is,
> in integration tests it is called with every assertion (why??) then the
> validation does not work since the root type is now null.
>
> I look inside generated setInferredType() code, and it is quite complex
> method.
> What are these eInverseRemove() and eInverseAdd() and basicSetType() are?
> What is the purpose?
>
> I search internet and it seems it is quite normal for any
> JvmTypeReference value assignment setter.
>
> Side note: "inferredType" is indeed transient type, and yes, it gave me
> headaches when dealing with serialization, until I implemented and
> plugged in ITransientValuesService and specified that "inferredType"
> really is transient and should be ignored when serializing.
>
> I found a solution for my problem by creating a "defensive copy"
> JvmTypeReference:
>
> import org.eclipse.xtext.common.types.JvmTypeReference;
> import org.eclipse.xtext.common.types.util.TypeReferences;
>
> @Inject TypeReferences typeReferences;
>
> public JvmTypeReference
> createDefensiveCopyOfJvmTypeReference(JvmTypeReference typeReference) {
> return typeReferences.createTypeRef(typeReference.getType());
> }
> ..
> JvmTypeReference type =
> createDefensiveCopyOfJvmTypeReference(rootCloner.getType());
> rootCloner.setInferredType(type);
> ..
>
> .and this works. I am just curious why is that?
> Guys, did I overseen some parts of your documentation?
>
> Espinosa
|
|
| |
Goto Forum:
Current Time: Sat Sep 21 00:12:24 GMT 2024
Powered by FUDForum. Page generated in 0.04179 seconds
|