Skip to main content



      Home
Home » Modeling » TMF (Xtext) » Initialization of member JvmFields in constructor of a parent JvmGenericType(Initialization of fields with XExpression not via 'JvmTypesBuilder#setInitializer' method but at some other place)
Initialization of member JvmFields in constructor of a parent JvmGenericType [message #1841241] Fri, 07 May 2021 09:26 Go to next message
Eclipse UserFriend
In short, a file of my DSL is compiled to a java class with a constructor that may receive some dependencies. This java class also has some fields - those are actually added during inferring a model. Each field (JvmField) maps to some ArbitraryTypeEntity entity that is described in the file. These fields are dynamically typed - my DSL should obtain types of these fields from their corresponding initialization expressions (here they are called 'constructor' but the real expression might be just a java method, not necessarily a java constructor).

The problem: I'd like to initialize JvmFields not directly (via 'JvmTypesBuilder#setInitializer' method) but from the other place - constructor of my model. I cannot use XExpression constructor directly in the body of my model constructor (it is shown in 'generated code for not working version of the inferrer' code block). May there be some kind of a workaround for this particular problem - reuse default type computation and initialize a field from the other place?

I've made a sample project to reproduce the case and added a snippet of 'desired generated code'.

The grammar of my language:
grammar org.xtext.stackoverflow.initializer_dsl.InitializeDsl with org.eclipse.xtext.xbase.Xbase

generate initializeDsl "http://www.xtext.org/stackoverflow/initializer_dsl/InitializeDsl"

InitializeModel:
	{InitializeModel}
	elements+=ArbitraryTypeEntity*;

ArbitraryTypeEntity:
	'arbitraty entity' name=ID '=' constructor=XOrExpression ';'?
; 


My inferrer (I'd like it to be like this):
/*
 * generated by Xtext 2.25.0
 */
package org.xtext.stackoverflow.initializer_dsl.jvmmodel

import com.google.inject.Inject
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.stackoverflow.initializer_dsl.initializeDsl.InitializeModel
import org.eclipse.xtext.common.types.JvmVisibility
import java.util.ArrayList
import java.util.HashMap

class InitializeDslJvmModelInferrer extends AbstractModelInferrer {

	@Inject extension JvmTypesBuilder

	def dispatch void infer(InitializeModel element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
		acceptor.accept(element.toClass("InitializeModel")) [
			for (nestedElement : element.elements) {
				members += nestedElement.toField(nestedElement.name, nestedElement.constructor.inferredType) [
					final = true
					static = false
				// don't want to use initializer here as the element will later be initialized in constructor 
				]
			}
			members += element.toConstructor [
				visibility = JvmVisibility.PUBLIC
// I'd like to use the code of constructor XExpression here but I can't - it is compiled to not what I want
				body = '''
				«FOR nestedElement : element.elements»
				this.«nestedElement.name» = «nestedElement.constructor»;
				«ENDFOR»
				'''
			]
		]
	}
}


But if use this version of my inferrer (with ArbitraryTypeEntity constructor not directly used as initializer of the field), I would get this generated code:
generated code for not working version of the inferrer :
@SuppressWarnings("all")
public class InitializeModel {
  private final Object/* type is 'null' */ entity;
  
  public InitializeModel() {
    this.entity = org.eclipse.xtext.xbase.impl.XConstructorCallImplCustom@16098231 (invalidFeatureIssueCode: null, validFeature: false, explicitConstructorCall: true, anonymousClassConstructorCall: false);
  }
}



Here the type of entity is lost (of course, I didn't associate it in any way with the expression).
Then, if use the JvmTypeBuilder setInitializer function directly and connect the XExpression to JvmField as a logicalChild, it will, of course, work.

working version of inferrer, but not what I'd like to achieve:
/*
 * generated by Xtext 2.25.0
 */
package org.xtext.stackoverflow.initializer_dsl.jvmmodel

import com.google.inject.Inject
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.stackoverflow.initializer_dsl.initializeDsl.InitializeModel
import org.eclipse.xtext.common.types.JvmVisibility
import java.util.ArrayList
import java.util.HashMap

class InitializeDslJvmModelInferrer extends AbstractModelInferrer {

	@Inject extension JvmTypesBuilder

	def dispatch void infer(InitializeModel element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
		acceptor.accept(element.toClass("InitializeModel")) [
			
			for (nestedElement : element.elements) {
				members += nestedElement.toField(nestedElement.name, nestedElement.constructor.inferredType) [
					final = true
					static = false
					initializer = nestedElement.constructor
				]
			}
			members += element.toConstructor [
				visibility = JvmVisibility.PUBLIC
				// now no body is set
//				body = '''
//				«FOR nestedElement : element.elements»
//				this.«nestedElement.name» = «nestedElement.constructor»;
//				«ENDFOR»
//				'''

			]
		]
	}
}


generated code with the working inferrer version:
import java.util.ArrayList;

@SuppressWarnings("all")
public class InitializeModel {
  private final ArrayList<String> entity = new ArrayList<String>();
}


but what I'd like to have is this desired generated code :
import java.util.ArrayList;

@SuppressWarnings("all")
public class InitializeModel {
  private final ArrayList<String> entity;
  public InitializeModel() {
    this.entity = new ArrayList<String>();
  }
}


A possible solution that I thought of is:
1. Modify the compiler to not generate the initializing part of code for fields that are mapped to entities of ArbitraryTypeEntity type, so the fields will only be declared
2. Set the initializer - but use a somehow overridden version of XExpression
3. Use in the model constructor body this 'somehow overridden version of XExpression that could be appended to ITreeAppendable of the constructor body.

I could possibly imagine what to do for the first step, but steps 2 and 3 seem to me not realizable for now.
Re: Initialization of member JvmFields in constructor of a parent JvmGenericType [message #1841242 is a reply to message #1841241] Fri, 07 May 2021 09:39 Go to previous messageGo to next message
Eclipse UserFriend
is there any reason you insist to do it in constructor?
if yes what about move the init to a (static)element/method and call it from contructor?

[Updated on: Fri, 07 May 2021 09:47] by Moderator

Re: Initialization of member JvmFields in constructor of a parent JvmGenericType [message #1841245 is a reply to message #1841242] Fri, 07 May 2021 11:26 Go to previous message
Eclipse UserFriend
Hi

The EMF way is for EObjects to be default constructed and then initialized by setXXX methods. This is unfortunate in terms of having solid @NonNull fields, but pretty unavoidable if you want to able to load from XML.

If you insist on rich construction, you will throw away many of the facilities that EMF offers.

Regards

Ed Willink
Previous Topic:Proper way to install antlr-generator-3.2.0-patch.jar
Next Topic:Xtext Resource Storage
Goto Forum:
  


Current Time: Tue May 13 17:11:30 EDT 2025

Powered by FUDForum. Page generated in 0.03748 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top