Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » JvmModelInferrer refer to a java class not in the model
JvmModelInferrer refer to a java class not in the model [message #759366] Mon, 28 November 2011 08:44 Go to next message
Denis Delangle is currently offline Denis Delangle
Messages: 2
Registered: November 2011
Junior Member
Hello,

I want to generate java code from a model using XBlockExpression from XBase. Here is a part of the grammar :

Parseur :
('acceptRow' acceptRow = XBlockExpression)?
('acceptCol' acceptCol = XBlockExpression)?



I want to generate a method from the XBlockExpression without having to declare method signature in my DSL.

In the DSL, I would have :
acceptRow {! row.isEmpty()}

that would generate :
public boolean acceptRow(com.myruntimelib.Row row) {
   return ! row.isEmpty();
}


I am stuck in the JvmModelInferrer, I can't find a way to declare a method taking a com.myruntimelib.Row parameter or returning a boolean value.

I try :
members += element.toMethod("acceptRow", null) [
   it.parameters += it.toParameter("row", element.toClass("com.myruntimelib.Row") )
   it.setBody ['''
...


element.toClass("com.myruntimelib.Row") returns a JvmGenericType but a JvmTypeReference is expected.

How can I transform a JvmGenericType to a JvmTypeReference ?

Thanks a lot,

Denis
Re: JvmModelInferrer refer to a java class not in the model [message #759372 is a reply to message #759366] Mon, 28 November 2011 08:49 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian Dietrich
Messages: 6205
Registered: July 2009
Senior Member
Have a look at the typerefererences class. It contains utilities for
that.
Re: JvmModelInferrer refer to a java class not in the model [message #759385 is a reply to message #759372] Mon, 28 November 2011 09:11 Go to previous messageGo to next message
Denis Delangle is currently offline Denis Delangle
Messages: 2
Registered: November 2011
Junior Member
Thanks a lot !! You saved me Smile

I add to inject TypeReferences instance in my generator
@Inject private TypeReferences typeReferences

And then use it like this.
members += element.toMethod("acceptRow", null) [
   it.parameters += it.toParameter("row",  typeReferences.createTypeRef(typeReferences.findDeclaredType("com.myruntimelib.Row", element)) )
   it.setBody ['''
Re: JvmModelInferrer refer to a java class not in the model [message #1005771 is a reply to message #759385] Mon, 28 January 2013 11:43 Go to previous messageGo to next message
Alan Alberghini is currently offline Alan Alberghini
Messages: 19
Registered: January 2013
Junior Member
I keep incurring in strange behavior with TypeReferences injected instances...
I'm trying to add Junit4 annotations to a couple of methods inside my inferrer and, as a test, I tried two "semantically equivalent" paths but with different results.
In my inferrer I have:
class TestInferrer {
	
	@Inject extension JvmTypesBuilder
	
	@Inject TypesFactory typesFactory
	@Inject TypeReferences typeRefs
	
	private JvmUtils jvmUtils
	
	/**
	 * Infers TestSpec to Junit4 test classes
	 * @param testSpec The TestSpec object containing the test specifcation for an Entity
	 * @param acceptor the IAcceptor used to infer the model
	 */
	
	def infer (TestSpec testSpec, IAcceptor<JvmDeclaredType> acceptor) {
		jvmUtils = JvmUtils::instance
  		acceptor.accept (testSpec.toClass(getNameForTestClass(testSpec.entity))[

			it.documentation = "Test class for Entity " + testSpec.entity.name

                        [...]
      			/*
   			 * setUp / tearDown implementation
   			 */
   			 
   			if (testSpec.setupBody != null)
   				members += testSpec.toMethod("setUp", testSpec.newTypeRef("void"))[
   					it.body = testSpec.setupBody
   					var anno = jvmUtils.getAnnotationReference(typeof(org.junit.Before), testSpec.entity)
   					if (anno != null)
   						it.annotations += anno 
   				]
   			
   			[...]
   			/*
   			 * Map every Test to a test method
   			 */
   			
   			for (test: testSpec.testCases){
   				members += test.toMethod(test.name, test.newTypeRef("void")) [
					var annotation = typesFactory.createJvmAnnotationReference
					annotation.annotation = typeRefs.findDeclaredType(typeof(org.junit.Test), testSpec.entity) as JvmAnnotationType
					it.annotations += annotation
   					it.body = test.body
   				]
   			}
   			
   		])
   	}
}

while the code inside JvmUtil to get an annotation reference is:
class JvmUtils {
	
	@Inject TypeReferences typeRefs

 	def getAnnotationReference (Class<?> annotationClass, EObject context){
		if (!annoRefs.containsKey(annotationClass.name)){
			if (typeRefs == null)
				typeRefs = new TypeReferences()
			var temp = TypesFactory::eINSTANCE.createJvmAnnotationReference
			temp.annotation = typeRefs.findDeclaredType(annotationClass, context) as JvmAnnotationType
			annoRefs.put(annotationClass.name, temp)
		}
		annoRefs.get(annotationClass.name)
   	}

Using the utility I wrote inside JvmUtils I keep getting NullPointerExceptions because typeRefs is null and manually instantiating it results in the TypeFactory inside typeRefs to be null.
The code inside the TestInferrer class getting a reference for the "@Test" annotation works flawlessly, instead.
How come the two "semantically equivalent" code paths behave differently? Am I doing something wrong with Guice's injection?
Re: JvmModelInferrer refer to a java class not in the model [message #1005807 is a reply to message #1005771] Mon, 28 January 2013 14:49 Go to previous messageGo to next message
Lorenzo Bettini is currently offline Lorenzo Bettini
Messages: 1321
Registered: July 2009
Senior Member
But JvmUtils is not injected, that's why you get null pointer
exceptions, as far as I understand...

On 01/28/2013 05:43 PM, Alan Alberghini wrote:
> I keep incurring in strange behavior with TypeReferences injected
> instances...
> I'm trying to add Junit4 annotations to a couple of methods inside my
> inferrer and, as a test, I tried two "semantically equivalent" paths but
> with different results.
> In my inferrer I have:
>
> class TestInferrer {
>
> @Inject extension JvmTypesBuilder
>
> @Inject TypesFactory typesFactory
> @Inject TypeReferences typeRefs
>
> private JvmUtils jvmUtils
>
> /**
> * Infers TestSpec to Junit4 test classes
> * @param testSpec The TestSpec object containing the test
> specifcation for an Entity
> * @param acceptor the IAcceptor used to infer the model
> */
>
> def infer (TestSpec testSpec, IAcceptor<JvmDeclaredType> acceptor) {
> jvmUtils = JvmUtils::instance
> acceptor.accept
> (testSpec.toClass(getNameForTestClass(testSpec.entity))[
>
> it.documentation = "Test class for Entity " +
> testSpec.entity.name
>
> [...]
> /*
> * setUp / tearDown implementation
> */
> if (testSpec.setupBody != null)
> members += testSpec.toMethod("setUp",
> testSpec.newTypeRef("void"))[
> it.body = testSpec.setupBody
> var anno =
> jvmUtils.getAnnotationReference(typeof(org.junit.Before), testSpec.entity)
> if (anno != null)
> it.annotations += anno ]
>
> [...]
> /*
> * Map every Test to a test method
> */
>
> for (test: testSpec.testCases){
> members += test.toMethod(test.name,
> test.newTypeRef("void")) [
> var annotation =
> typesFactory.createJvmAnnotationReference
> annotation.annotation =
> typeRefs.findDeclaredType(typeof(org.junit.Test), testSpec.entity) as
> JvmAnnotationType
> it.annotations += annotation
> it.body = test.body
> ]
> }
>
> ])
> }
> }
>
> while the code inside JvmUtil to get an annotation reference is:
>
> class JvmUtils {
>
> @Inject TypeReferences typeRefs
>
> def getAnnotationReference (Class<?> annotationClass, EObject context){
> if (!annoRefs.containsKey(annotationClass.name)){
> if (typeRefs == null)
> typeRefs = new TypeReferences()
> var temp = TypesFactory::eINSTANCE.createJvmAnnotationReference
> temp.annotation = typeRefs.findDeclaredType(annotationClass,
> context) as JvmAnnotationType
> annoRefs.put(annotationClass.name, temp)
> }
> annoRefs.get(annotationClass.name)
> }
>
> Using the utility I wrote inside JvmUtils I keep getting
> NullPointerExceptions because typeRefs is null and manually
> instantiating it results in the TypeFactory inside typeRefs to be null.
> The code inside the TestInferrer class getting a reference for the
> "@Test" annotation works flawlessly, instead.
> How come the two "semantically equivalent" code paths behave
> differently? Am I doing something wrong with Guice's injection?


--
Lorenzo Bettini, PhD in Computer Science, DI, Univ. Torino
HOME: http://www.lorenzobettini.it
Re: JvmModelInferrer refer to a java class not in the model [message #1005816 is a reply to message #1005807] Mon, 28 January 2013 15:27 Go to previous messageGo to next message
Alan Alberghini is currently offline Alan Alberghini
Messages: 19
Registered: January 2013
Junior Member
Thanks for your answer.
Of course I forgot to mention that I implemented the JvmUtils class as a singleton and that it gets correctly initialized beforehand:
jvmUtils = JvmUtils::instance

The issue that's not clear to me is why, inside the TestInferrer class, using
@Inject TypeReferences typeRefs

correctly instantiates a TypeReferences instance while using the exact same code inside JvmUtils gives me a null typeRefs variable (spotted while debugging the getAnnotationReference method).
It seems like there can't be two "live" instances of TypeReferences objects with the same name in different classes... Is it perhaps a Google Guice "feature"?
Re: JvmModelInferrer refer to a java class not in the model [message #1005821 is a reply to message #1005816] Mon, 28 January 2013 15:37 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian Dietrich
Messages: 6205
Registered: July 2009
Senior Member
HI,

if the singleton doesnt leverage guice you have no chance anyway.
Re: JvmModelInferrer refer to a java class not in the model [message #1005832 is a reply to message #1005821] Mon, 28 January 2013 17:00 Go to previous messageGo to next message
Alan Alberghini is currently offline Alan Alberghini
Messages: 19
Registered: January 2013
Junior Member
Hi Christian and thanks for your response.
I know near-to-nothing about Google Guice, but my TestInferrer class is a simple class much like JvmUtils (although not a singleton), so I don't understand the difference in behavior.
How should my singleton leverage guice?
Re: JvmModelInferrer refer to a java class not in the model [message #1005833 is a reply to message #1005832] Mon, 28 January 2013 17:02 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian Dietrich
Messages: 6205
Registered: July 2009
Senior Member
why singleton at all?
Re: JvmModelInferrer refer to a java class not in the model [message #1005838 is a reply to message #1005833] Mon, 28 January 2013 18:01 Go to previous messageGo to next message
Alan Alberghini is currently offline Alan Alberghini
Messages: 19
Registered: January 2013
Junior Member
I chose the "singleton way" because I preferred it over a bunch of static fields/methods to share some common data across inferrers, but it didn't work either in a static only environment.
I originally thought that injecting a static TypeReferences field didn't work for some reason, but now I see that it isn't working in a non-static environment too.
Am I under completely wrong assumptions about how I should create a new TypeReferences instance?

UPDATE:
I now bypassed the problem by passing the typeRefs instance from the TestInferrer class to JvmUtils and exploited the EcoreUtil2.cloneWithProxies() method on the returned JvmAnnotationReference, but I would still like to understand why the injected TypeReferences instance is created correctly inside TestInferrer but not in JvmUtils.
Any insight?

UPDATE2:
I now added another method to get a JvmTypeReference out of one of my domain elements (Entity). For this I'm using the newTypeRef method found in JvmTypesBuilder, which is injected in the JvmUtils class with:
@Inject extension JvmTypesBuilder

Obviously (not to me, but I'm not so shocked anymore by it...), the associated instance is never initialized, resulting in several NullPointerExceptions when trying to extract a JvmTypeReference from a String.
I can't really seem to get the hang of it...

[Updated on: Tue, 29 January 2013 09:43]

Report message to a moderator

Re: JvmModelInferrer refer to a java class not in the model [message #1006187 is a reply to message #1005838] Wed, 30 January 2013 06:52 Go to previous messageGo to next message
Ingo Meyer is currently offline Ingo Meyer
Messages: 115
Registered: July 2009
Senior Member
Hi Alan,

once you have Guice you never ever should create an own instance of a utility class!!!
Guice will recursivle resolve all @Inject, so just use in your TestInferrer:
@Inject private JvmUtils jvmUtils

and Guice will also resolve the injects there.

[Updated on: Wed, 30 January 2013 06:53]

Report message to a moderator

Re: JvmModelInferrer refer to a java class not in the model [message #1006200 is a reply to message #1006187] Wed, 30 January 2013 07:39 Go to previous message
Alan Alberghini is currently offline Alan Alberghini
Messages: 19
Registered: January 2013
Junior Member
That explains it, then.
I added a "@Singleton" annotation to my JvmUtils class, an "@Inject" annotation to its construtor and then injected instances of JvmUtils in the classes needing it and everything works as expected, finally Smile
If I understood it correctly, Guice relies on an "injection chain" to properly inject instances and my "custom" singleton was breaking this chain.
Thanks for your answers Ingo, Christian and Lorenzo. Much appreciated!
Previous Topic:Cross reference customisation
Next Topic:References from other file not working
Goto Forum:
  


Current Time: Thu Aug 21 18:20:45 EDT 2014

Powered by FUDForum. Page generated in 0.10228 seconds