Is Xtext generation gap support a myth? [message #1073414] |
Wed, 24 July 2013 18:22 |
Ed Staub Messages: 12 Registered: July 2009 |
Junior Member |
|
|
After looking at a few dozen email threads, StackOverflow, etc., I've come to the conclusion that generation-gap-pattern support in Xtext is largely mythical. Several people have had started threads asking how to do it; none of them appeared to arrive at anything successful (at least, with a reasonable amount of work). Many of the responses have appeared to either not work for Xtext in particular, or be useless because they require a lot more Ecore/EMF/whatever knowledge than I (or the other posters) are likely to have.
In my particular case, I simply want to be able to add a few methods and fields to a few of the grammar-derived generated classes/interfaces.
I'm rarely this intentionally provocative, but after reading through so much wasted attempts at communication, I think that on this topic answerers need to pause to think whether what they're suggesting actually works in Xtext in particular, and whether it is comprehensible to the folks asking the questions. Sorry if I've just given offense!
|
|
|
Re: Is Xtext generation gap support a myth? [message #1073588 is a reply to message #1073414] |
Thu, 25 July 2013 05:43 |
|
Hi,
hmmm i dont have the feeling that i have seen any of this threads.
first: what is generation gap
Quote:Separate generated code from non-generated code by inheritance.
i know many many Xtext and non Xtext Projects that realize this.
so there are basically 2 options to realize this
(1) you generate only interfaces and abstract classes, use a naming convention for the concrete class.
(2) you do additionaly a generate once for the conctrete classes
the following took me a few minutes todo.
Model:
entitites+=Entity*;
Entity:
'entity' name=ID "{"
attributes+=Attribute*
"}";
Attribute:
name=ID ":" type = Type
;
enum Type :
String | int | boolean
;
class MyDslGenerator implements IGenerator {
override void doGenerate(Resource resource, IFileSystemAccess fsa) {
for (entity : resource.allContents.toIterable.filter(typeof(Entity))) {
fsa.generateFile("test/"+entity.name+".java", '''
package test;
public abstract class «entity.name» {
«FOR a : entity.attributes»
public abstract «a.type.literal» get«a.name.toFirstUpper»();
public abstract void set«a.name.toFirstUpper»(«a.type.literal» «a.name»);
«ENDFOR»
public static «entity.name» newInstance() {
return new «entity.name»Impl();
}
}
''')
}
}
}
so let us take five more minutes to get the generate once done
class MyDslGenerator implements IGenerator {
override void doGenerate(Resource resource, IFileSystemAccess fsa) {
for (entity : resource.allContents.toIterable.filter(typeof(Entity))) {
fsa.generateFile("test/"+entity.name+".java", '''
package test;
public abstract class «entity.name» {
«FOR a : entity.attributes»
public abstract «a.type.literal» get«a.name.toFirstUpper»();
public abstract void set«a.name.toFirstUpper»(«a.type.literal» «a.name»);
«ENDFOR»
public static «entity.name» newInstance() {
return new «entity.name»Impl();
}
}
''')
fsa.generateFile("test/"+entity.name+"Impl.java","gen_once", '''
package test;
public class «entity.name»Impl extends «entity.name» {
«FOR a : entity.attributes»
private «a.type.literal» «a.name»;
public «a.type.literal» get«a.name.toFirstUpper»(){
return this.«a.name»;
}
public void set«a.name.toFirstUpper»(«a.type.literal» «a.name»){
this.«a.name» = «a.name»;
}
«ENDFOR»
}
''')
}
}
}
public class MyDslOutputConfigurationProvider implements IOutputConfigurationProvider {
public Set<OutputConfiguration> getOutputConfigurations() {
OutputConfiguration defaultOutput = new OutputConfiguration(IFileSystemAccess.DEFAULT_OUTPUT);
defaultOutput.setDescription("Output Folder");
defaultOutput.setOutputDirectory("./src-gen");
defaultOutput.setOverrideExistingResources(true);
defaultOutput.setCreateOutputDirectory(true);
defaultOutput.setCleanUpDerivedResources(true);
defaultOutput.setSetDerivedProperty(true);
OutputConfiguration genOnce = new OutputConfiguration("gen_once");
genOnce.setDescription("Output Folder Gen Once");
genOnce.setOutputDirectory("./src");
genOnce.setOverrideExistingResources(false);
genOnce.setCreateOutputDirectory(true);
genOnce.setCleanUpDerivedResources(false);
genOnce.setSetDerivedProperty(false);
return newHashSet(defaultOutput, genOnce);
}
}
public class MyDslRuntimeModule extends org.xtext.example.mydsl2.AbstractMyDslRuntimeModule {
public Class<? extends IOutputConfigurationProvider> bindIOutputConfigurationProvider() {
return MyDslOutputConfigurationProvider.class;
}
}
Please note: in standalone mode you need a custom version of JavaIoFileSystemAccess
since it is lagging generate once support. (This is a stupid bug not a conceptual problem)
https://bugs.eclipse.org/bugs/show_bug.cgi?id=398979
~Christian
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
|
|
Re: Is Xtext generation gap support a myth? [message #1074075 is a reply to message #1073673] |
Fri, 26 July 2013 03:36 |
Ed Staub Messages: 12 Registered: July 2009 |
Junior Member |
|
|
I'm a newbie, so I'm following the directions in the documentation, and using the Xtext-generated mwe2 - so I'm using EcoreGeneratorFragment. Googling for info on EcoreGenerator has been fruitless - AFAICT, there's no way one would find out about it other than reading, e.g., the xbase implementation. I don't want to harp on it, but this is a good example of the kind of disconnect I was trying to point out - the documentation gap between experts and novices.
Christian, search the forum for "generation gap" and see whether it looks like folks got what they needed. I'm working from the Xtext documentation, and most of what you kindly provided is, frankly, beyond me, in that I don't understand the context - maybe I need to learn a lot of EMF to understand?
It sounds like I might be able to save off the EcoreGeneratorFragment-generated Ecore file, modifying the mwe2 file to use it instead of generate it, using the xbase project as an example?
|
|
|
Re: Is Xtext generation gap support a myth? [message #1074102 is a reply to message #1074075] |
Fri, 26 July 2013 05:50 |
|
Hi,
first i want to state am not a Xtext committer but rather a normal user like you.
second documentation is always a heavily discussed topic. with a framework as Xtext where you can customize everything it is tons of work that nobody pays for and maybe is not needed since customizing the generated emf javaclasses may not be considered as usecase for the std. user. (you did not say what the particular wish for customization in your case is)
depending on the usecase there are basically 3 options
(1) you use the IXtext2EcorePostProcessor to change the generated ecore e.g. by adding methods.
(2) you use a manually maintained metamodel anyway and use EMFs Protected Region Approach (JMerge)
(3) the EcoreGenerator frees you from doing (2) by moving the implementations to custom impl classes.
in all cases you have to be willing to read into emf since Xtext is an EMF based framework.
if documentation is insufficient users can always find a enhancement request maybe with some text already attached. (hey Xtext is Open Source)
maybe i find the time to blog about ecoregenerator but this depends on my time.
~Christian
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
|
Re: Is Xtext generation gap support a myth? [message #1075612 is a reply to message #1073414] |
Mon, 29 July 2013 18:41 |
Ed Staub Messages: 12 Registered: July 2009 |
Junior Member |
|
|
Thanks much.
The main thing I needed to hear was:
Quote:in all cases you have to be willing to read into emf since Xtext is an EMF based framework.
So, after reading up a bit, I ended up creating an ecore project, importing the .ecore that was produced by Xtext. I then created a new "Xtext from existing ecore model" project, pointing at the ecore project, with all of my code, resources, et al from the old Xtext project. I'm just using JMerge to apply hand-coded model modifications. At this point, that's a tenable solution, even though the border between generated and authored code is of course a bit murky.
|
|
|
Powered by
FUDForum. Page generated in 0.04041 seconds