Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » How to override and extend the JvmModelGenerator to generate complex enumerations(Is this the right way to solve the Problem?)
How to override and extend the JvmModelGenerator to generate complex enumerations [message #1747355] Sun, 13 November 2016 14:16 Go to next message
Thomas Kohler is currently offline Thomas KohlerFriend
Messages: 7
Registered: November 2016
Junior Member
Hi,

I'm working on a xbase based dsl using the JvmModelInferrer and I'm surprised how much functionallity works out of the box when using this concept!

Unfortunately I found out that the JvmModel and JvmModelGenerator does not support more complex Java Enumerations (also in xtend), so I started to enhance the framework to support them as I expect it.

To generate Java code for the content of the additional values for an enumeration instance I need to transform a XExpression to Java code when defining the body of a JvmEnumerationLiteral. To hook into this point I started overriding the JvmModelGenerator and I got following problem:

Question 1)

When using the Type org.eclipse.xtext.xbase.compiler.JvmModelGenerator I always get the following warning:

Discouraged access: The type JvmModelGenerator is not accessible due to restriction on required project
com.generali.fipos.dsl


This happens in

/*
 * generated by Xtext 2.10.0
 */
package my.dsl

import org.eclipse.xtext.xbase.compiler.JvmModelGenerator

/**
 * Use this class to register components to be used at runtime / without the Equinox extension registry.
 */
class MyRuntimeModule extends AbstractFiposRuntimeModule {
	override bindIGenerator() {
		MyJvmModelGenerator
	}
}


as well as in

package my.dsl.compiler

import org.eclipse.xtext.xbase.compiler.JvmModelGenerator

class FiposJvmModelGenerator extends JvmModelGenerator {
}


Is this a problem and is the reason the restriction in the manifest of the plugin containing the original generator?

Question 2)

Is the a better way to solve my root problem? I try to describe enums as defined in the following grammar snippet:

EnumDef:
	'enum' name=ValidID
	('implements' interfaces+=(JvmParameterizedTypeReference)(',' interfaces+=(JvmParameterizedTypeReference))*)?
	'(' (params+=FullJvmFormalParameter (',' params+=FullJvmFormalParameter)*)? ')' '{'
		(enumEntries+=EnumEntry ','?)*';'?
		(enumInits+=EnumInit)*
		(ops+=Operation)*
	'}'
;

EnumEntry:
	name=ValidID (code=INT? & text=STRING?) ('(' values+=XExpression (',' values+=XExpression)* ')')?
;

EnumInit:
	'init' '(' (params+=FullJvmFormalParameter (',' params+=FullJvmFormalParameter)*)? ')' body=XBlockExpression
;

enum OperationType:
    OP='func' | STATIC='stat'
;

Operation:
	optype=OperationType (type=JvmTypeReference)? name=ValidID
	'(' (params+=FullJvmFormalParameter (',' params+=FullJvmFormalParameter)*)? ')' 
	body=XBlockExpression
;


The following DSL-Code matches this grammar:

	enum EModality(boolean visible, boolean enabled) {

		mandatory 1 "Pflicht"  (true) 
		optional  2 "Optional" (true, true)
		forbidden 0 "Verboten"
		dummy

		init(boolean visible) {
			// The following commented code would be better but JvmModelInferrer currently does
			// not define 'this' for calling an other defined constructor in constructor body..
			/* this(visible, false) */

			this.visible = visible
			this.enabled = false;
		}

		init() {
			// The following commented code would be better but JvmModelInferrer currently does
			// not define 'this' for calling an other defined constructor in constructor body..
			/* this(false, false) */

			visible = false
			enabled = false
		}
		
		func editable() {
			visible && enabled
		}
	}


And should produce a Java code like:

package core;

@SuppressWarnings("all")
public enum EModality {
  /* special columns code and text are handled during generation as additional enum columns and are set to null if not defined */
  mandatory(1, "Pflicht", true),
  optional(2, "Optional", true, true),
  forbidden(0, "Verboten"),
  dummy(null, null);

  private final Integer code;
  private final String text;
  private final boolean visible;
  private final boolean enabled;

  /* Main Constructor derived from signature extended with code and text */
  private EModality(final Integer code, final String text, final boolean visible, final boolean enabled) {
    this.code = code;
    this.text = text;
    this.visible = visible;
    this.enabled = enabled;
  }

  /* Constructor generated from init(boolean visible) with default for 'enabled' */
  private EModality(final Integer code, final String text, final boolean visible) {
    this(code, text, visible, false);
  }

  /* Constructor generated from init() with defaults for both signature parameters */
  private EModality(final Integer code, final String text) {
	this(code, text, false);
  }

  /* If not specified the negative ordinal is used for code */
  public Integer getCode() {
	return code == null ? -ordinal() : code;
  }
  
  /* If not specified the name is used for text */
  public String getText() {
	return text == null ? name() : text;
  }
  
  public boolean isVisible() {
    return this.visible;
  }
  
  public boolean isEnabled() {
    return this.enabled;
  }

  /* Additional defined function for enum entry */
  public boolean editable() {
    return (this.visible && this.enabled);
  }
}


The main problem is to generate the additional values (as typed in the signature) to the enumeration literals. I've defined them as a XExpression because there should be the possibility not only to enter primitive values as in the example above but also references to other enumeration entries or calls to conversion methods (either using extensions or static methods) to simple enter Dates etc.. I don't need an inner scope of the enumeration class itself, but the scope of the package the enumeration resists in.

What will be the correct Way to convert those XExpressions to Java and where?

Last but not least Question 3)

As mentioned in the comments of the dsl snippet I can't call a constructor in a constructor using the java this(...) - syntax. Xtext alwas says that there is no function called 'this'. Is this a known restriction or a bug?

Thank you in advance,
Thomas.

PS: Is the enhancement of enumerations planned in future xtend versions? I've found some (very old) CRs and comments in the generator code related to this.
Especially for commercial Systems (e.g. Insurances) enums and may be efficient lookups in small (in memory) tables are very important...
Re: How to override and extend the JvmModelGenerator to generate complex enumerations [message #1747434 is a reply to message #1747355] Mon, 14 November 2016 16:17 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 10423
Registered: July 2009
Senior Member
hi,

this task wont be that easy.

- currently nobody i working on it nor has it any high priority
- the constructors are not working cause they are filtered at org.eclipse.xtext.xbase.scoping.batch.ConstructorDelegateScope.getLocalElementsByName(QualifiedName)
this can be fixed i think

public class MyFeatureScopes extends FeatureScopes {

@Override
protected IScope createConstructorDelegateScope(EObject featureCall, LightweightTypeReference type, IScope parent,
IFeatureScopeSession session) {
return new ConstructorDelegateScope(parent, type, session, asAbstractFeatureCall(featureCall)) {
@Override
protected List<IEObjectDescription> getLocalElementsByName(QualifiedName name) {
if (THIS.equals(name) || SUPER.equals(name)) {
IEObjectDescription description = getSession().getLocalElement(name);
if (description != null) {
EObject objectOrProxy = description.getEObjectOrProxy();
if (objectOrProxy instanceof JvmGenericType && !objectOrProxy.eIsProxy()) {
return createConstructorDescriptions(name, (JvmGenericType) objectOrProxy, SUPER.equals(name));
} else if (objectOrProxy instanceof JvmEnumerationType && !objectOrProxy.eIsProxy()) {
return createConstructorDescriptions(name, (JvmEnumerationType) objectOrProxy, SUPER.equals(name));
}
}
}
return Collections.emptyList();
}

protected List<IEObjectDescription> createConstructorDescriptions(QualifiedName name, JvmEnumerationType type, boolean superType) {
Iterable<JvmConstructor> constructors = type.getDeclaredConstructors();
List<IEObjectDescription> result = Lists.newArrayListWithCapacity(3);
for(JvmConstructor constructor: constructors) {
addToList(createDescription(name, constructor, superType), result);
}
return result;
}
};
}

}

- for the literals i think the easiest is to inferr for each literal and parmeter a own method and simpy call these methods in the initializer of the literal

e.g.

public enum Demo {
	
	X(init_x_1()),
	Y(init_y_1());
	
	private Demo(String x) {
		
	}

	private static String init_y_1() {
		return "x";
	}

	private static String init_x_1() {
		return "y";
	}

}


alternatives you can adapt the type computer to compute the types for the expressions
and use xbasecompiler to generator the code for it
that will be tricky and noone has a idea how to make this "nice"
Re: How to override and extend the JvmModelGenerator to generate complex enumerations [message #1747448 is a reply to message #1747434] Mon, 14 November 2016 20:37 Go to previous messageGo to next message
Thomas Kohler is currently offline Thomas KohlerFriend
Messages: 7
Registered: November 2016
Junior Member
Hello Christian,
Thank you for your relpy. I'll try to implement the Scope extension.

I know that generating (static) Methods for each expression could be done, but some of our Enumerations are really big and this would be a disaster (Thousends of methods returning mostly a literal).
I managed to come to the place where I could generate the additional values to the Literal:

class MyDslJvmModelGenerator extends JvmModelGenerator {

	@Inject extension JvmModelAssociator
	@Inject XbaseCompiler compiler
	
	override generateEnumLiteral(JvmEnumerationLiteral it, ITreeAppendable appendable, GeneratorConfig config) {
		super.generateEnumLiteral(it, appendable, config)
		val pse = primarySourceElement
		switch pse {
			EnumEntry: {
				/* Special handling for EnumEntries */
				appendable.append("(")
				/* Handle the primary 'code' and 'text' columns */
				appendable.append(if(pse.code.empty) "null" else pse.code.get(0).toString);
				appendable.append(",")
				appendable.append(if(pse.text == null) "null" else {"\"" + pse.text + "\""})
				/* Use the Parameter types together with additional values for the other columns */
				val enumDef = pse.eContainer as EnumDef
				val parameterIterator = enumDef.params.iterator
				for(expression : pse.values) {
					if(parameterIterator.hasNext) {
						val type = parameterIterator.next.parameterType
						appendable.append(",")
						// XXX this call causes Lightweight Type NPE problems in many constellations ...
						// I need at least support for expressions like:
						// - null
						// - boolean values (true, false)
						// - Numeric values (1, 0xFF, 123.45) or maybe Arithmetic expressions (60 * 24, 2^3)
						// - String values ("Hello", "World")
						// - Enumeration values (EModality.mandatory, EVisibility.invisible)
						// - Date values, build using library extensions ("2016-11-14".isodate could generate DateLib.isodate(String isodate))
						compiler.compileAsJavaExpression(expression, appendable, type)
					}
				}
				appendable.append(")")
			}
		}
	}
}


I need at least support for expressions like:


  • null
  • boolean values (true, false)
  • Numeric values (1, 0xFF, 123.45) or maybe Arithmetic expressions (60 * 24, 2^3)
  • String values ("Hello", "World")
  • Enumeration values (EModality.mandatory, EVisibility.invisible)
  • Date values, build using library extensions ("2016-11-14".isodate could generate DateLib.isodate(String isodate))


All of them result in a simple Java expression with no need for an own method. Which would be the right XExpression Subclass for that?
The Code shown above works for boolean values but not for number values (literals) or enumeration literals. I always get the NPE

900910 [Worker-55] ERROR org.eclipse.xtext.builder.BuilderParticipant - Error during compilation of 'platform:/resource/core.fipos/src/my.dsl'.
java.lang.NullPointerException
at org.eclipse.xtext.xbase.compiler.LiteralsCompiler._toJavaExpression(LiteralsCompiler.java:134)
at org.eclipse.xtext.xbase.compiler.LiteralsCompiler.internalToConvertedExpression(LiteralsCompiler.java:43)
at org.eclipse.xtext.xbase.compiler.FeatureCallCompiler.internalToConvertedExpression(FeatureCallCompiler.java:102)
at org.eclipse.xtext.xbase.compiler.XbaseCompiler.internalToConvertedExpression(XbaseCompiler.java:317)
at org.eclipse.xtext.xbase.compiler.TypeConvertingCompiler.internalToConvertedExpression(TypeConvertingCompiler.java:104)
at org.eclipse.xtext.xbase.compiler.TypeConvertingCompiler.internalToJavaExpression(TypeConvertingCompiler.java:47)
at org.eclipse.xtext.xbase.compiler.AbstractXbaseCompiler.compileAsJavaExpression(AbstractXbaseCompiler.java:225)
at org.eclipse.xtext.xbase.compiler.AbstractXbaseCompiler.compileAsJavaExpression(AbstractXbaseCompiler.java:148)
at mydsl.compiler.MyDslJvmModelGenerator.generateEnumLiteral(MyDslJvmModelGenerator.java:82)

at

	public void _toJavaExpression(XNumberLiteral expr, ITreeAppendable b) {
		LightweightTypeReference type = getLightweightType(expr);
		if(type.isType(BigInteger.class)) { [...]


because getLightweightType() returns null for almost every expression. Do you have a hint for me what the problem could be here?

By the way: What is the reason to call ".copyWithProxies" on JvmTypeReferences in inferrer methods before using them? I saw that in many examples but I did not know the reason.

Thank you,
Thomas
Re: How to override and extend the JvmModelGenerator to generate complex enumerations [message #1747456 is a reply to message #1747448] Tue, 15 November 2016 05:30 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 10423
Registered: July 2009
Senior Member
This was my hint on the type computer
Make sure you recursively walk down to these expressions
Re: How to override and extend the JvmModelGenerator to generate complex enumerations [message #1747457 is a reply to message #1747456] Tue, 15 November 2016 05:31 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 10423
Registered: July 2009
Senior Member
And the reason for cloning is: in EMF Taking ohne Contained EObject and set it at a Place of another contained eobject is actually a move
Re: How to override and extend the JvmModelGenerator to generate complex enumerations [message #1748513 is a reply to message #1747456] Wed, 23 November 2016 20:55 Go to previous messageGo to next message
Thomas Kohler is currently offline Thomas KohlerFriend
Messages: 7
Registered: November 2016
Junior Member
Quote:
alternatives you can adapt the type computer to compute the types for the expressions


Please help me once more! If I try to compile the XExpression when generating the additional values in the Enumeration Literal the method

getLightweightType(expression)


always returns null. When I use an expression with the same content in an field initializer the evaluation of the lightweight type works. I debugged the generator and compiler, but I did'nt realize the difference...

I have the correct type as a JvmTypeReference - how can I convert a JvmTypeReference to a LightweightType? What is the meaning of the Lightweight Types?

An ugly fix for Numbers and Strings is to catch the NPE and do the same as the last else which does not need the lightweight type:

	@Override
	public void _toJavaExpression(XNumberLiteral expr, ITreeAppendable b) {
		try {
			super._toJavaExpression(expr, b);
		} catch(NullPointerException npx) {
			GeneratorConfig config = b.getGeneratorConfig();
			if (config != null && config.getJavaSourceVersion().isAtLeast(JAVA7)) {
				b.append(numberLiterals.toJavaLiteral(expr, false));
			} else {
				b.append(numberLiterals.toJavaLiteral(expr, true));
			}
		}
	}

	@Override
	protected void toJavaExpression(XStringLiteral literal, ITreeAppendable appendable, boolean useUnicodeEscapes) {
		try {
			super.toJavaExpression(literal, appendable, useUnicodeEscapes);
		} catch(NullPointerException npx) {
			String javaString = Strings.convertToJavaString(literal.getValue(), useUnicodeEscapes);
			appendable.append("\"").append(javaString).append("\"");
		}
	}


but that is not a solution ...

thx in advance,
Tom
Re: How to override and extend the JvmModelGenerator to generate complex enumerations [message #1748530 is a reply to message #1748513] Thu, 24 November 2016 03:47 Go to previous message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 10423
Registered: July 2009
Senior Member
How does your adaption of type computer look like
Previous Topic:Another problem with left recursion
Next Topic:Folding package/ file
Goto Forum:
  


Current Time: Mon Apr 24 00:15:04 GMT 2017

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

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