Skip to content

Instantly share code, notes, and snippets.

Created January 30, 2013 08:13
Show Gist options
  • Save anonymous/4671578 to your computer and use it in GitHub Desktop.
Save anonymous/4671578 to your computer and use it in GitHub Desktop.
An Xtext workflow written as an Xtend class.
package org.xtext.example.mydsl
import org.eclipse.emf.mwe.utils.DirectoryCleaner
import org.eclipse.emf.mwe.utils.StandaloneSetup
import org.eclipse.xtext.generator.Generator
import org.eclipse.xtext.generator.LanguageConfig
import org.eclipse.xtext.generator.builder.BuilderIntegrationFragment
import org.eclipse.xtext.generator.ecore.EcoreGeneratorFragment
import org.eclipse.xtext.generator.exporting.QualifiedNamesFragment
import org.eclipse.xtext.generator.formatting.FormatterFragment
import org.eclipse.xtext.generator.generator.GeneratorFragment
import org.eclipse.xtext.generator.grammarAccess.GrammarAccessFragment
import org.eclipse.xtext.generator.junit.Junit4Fragment
import org.eclipse.xtext.generator.parser.antlr.XtextAntlrGeneratorFragment
import org.eclipse.xtext.generator.parser.antlr.XtextAntlrUiGeneratorFragment
import org.eclipse.xtext.generator.resourceFactory.ResourceFactoryFragment
import org.eclipse.xtext.generator.scoping.ImportNamespacesScopingFragment
import org.eclipse.xtext.generator.serializer.SerializerFragment
import org.eclipse.xtext.generator.types.TypesGeneratorFragment
import org.eclipse.xtext.generator.validation.ValidatorFragment
import org.eclipse.xtext.generator.xbase.XbaseGeneratorFragment
import org.eclipse.xtext.ui.generator.compare.CompareFragment
import org.eclipse.xtext.ui.generator.contentAssist.ContentAssistFragment
import org.eclipse.xtext.ui.generator.labeling.LabelProviderFragment
import org.eclipse.xtext.ui.generator.outline.OutlineTreeProviderFragment
import org.eclipse.xtext.ui.generator.outline.QuickOutlineFragment
import org.eclipse.xtext.ui.generator.quickfix.QuickfixProviderFragment
import org.eclipse.xtext.ui.generator.refactoring.RefactorElementNameFragment
import org.eclipse.xtext.ui.generator.templates.CodetemplatesGeneratorFragment
class GenerateMyDsl extends XtendWorkflow {
String grammarURI = "classpath:/org/xtext/example/mydsl/MyDsl.xtext"
String fileExtensions = "mydsl"
String projectName = "org.xtext.example.mydsl"
String runtimeProject = "../${projectName}".resolve
boolean generateXtendStub = true
new() {
beans += new StandaloneSetup => [
scanClassPath = true
platformUri = "${runtimeProject}/..".resolve
// The following two lines can be removed, if Xbase is not used.
addRegisterGeneratedEPackage("org.eclipse.xtext.xbase.XbasePackage")
addRegisterGenModelFile("platform:/resource/org.eclipse.xtext.xbase/model/Xbase.genmodel")
]
components += new DirectoryCleaner => [
directory = "${runtimeProject}/src-gen".resolve
]
components += new DirectoryCleaner => [
directory = "${runtimeProject}.ui/src-gen".resolve
]
components += new Generator => [
pathRtProject = runtimeProject
pathUiProject = "${runtimeProject}.ui".resolve
pathTestProject = "${runtimeProject}.tests".resolve
projectNameRt = projectName
projectNameUi = "${projectName}.ui".resolve
addLanguage(
new LanguageConfig => [
autoInject
uri = grammarURI
// Java API to access grammar elements (required by several other fragments)
addFragment(new GrammarAccessFragment => [autoInject])
// generates Java API for the generated EPackages
addFragment(
new EcoreGeneratorFragment => [
autoInject
// referencedGenModels = "
// platform:/resource/org.eclipse.xtext.xbase/model/Xbase.genmodel,
// platform:/resource/org.eclipse.xtext.common.types/model/JavaVMTypes.genmodel
// "
])
// the old serialization component
// fragment = parseTreeConstructor.ParseTreeConstructorFragment auto-inject {}
// serializer 2.0
addFragment(
new SerializerFragment => [
autoInject
generateStub = false
])
// a custom ResourceFactory for use with EMF
addFragment(
new ResourceFactoryFragment => [
autoInject
])
// The antlr parser generator fragment.
addFragment(
new XtextAntlrGeneratorFragment => [
autoInject
// options = {
// backtrack = true
// }
])
// Xtend-based API for validation
addFragment(
new ValidatorFragment => [
autoInject
// composedCheck = "org.eclipse.xtext.validation.ImportUriValidator"
// composedCheck = "org.eclipse.xtext.validation.NamesAreUniqueValidator"
])
// old scoping and exporting API
// fragment = scoping.ImportURIScopingFragment auto-inject {}
// fragment = exporting.SimpleNamesFragment auto-inject {}
// scoping and exporting API
addFragment(
new ImportNamespacesScopingFragment => [
autoInject
])
addFragment(
new QualifiedNamesFragment => [
autoInject
])
addFragment(
new BuilderIntegrationFragment => [
autoInject
])
// generator API
addFragment(
new GeneratorFragment => [
autoInject
])
// formatter API
addFragment(
new FormatterFragment => [
autoInject
])
// labeling API
addFragment(
new LabelProviderFragment => [
autoInject
])
// outline API
addFragment(
new OutlineTreeProviderFragment => [
autoInject
])
addFragment(
new QuickOutlineFragment => [
autoInject
])
// quickfix API
addFragment(
new QuickfixProviderFragment => [
autoInject
])
// content assist API
addFragment(
new ContentAssistFragment => [
autoInject
])
// generates a more lightweight Antlr parser and lexer tailored for content assist
addFragment(
new XtextAntlrUiGeneratorFragment => [
autoInject
])
// generates junit test support classes into Generator#pathTestProject
addFragment(
new Junit4Fragment => [
autoInject
])
// project wizard (optional)
// fragment = projectWizard.SimpleProjectWizardFragment auto-inject {
// generatorProjectName = "${projectName}"
// }
// rename refactoring
addFragment(
new RefactorElementNameFragment => [
autoInject
])
// provides the necessary bindings for java types integration
addFragment(
new TypesGeneratorFragment => [
autoInject
])
// generates the required bindings only if the grammar inherits from Xbase
addFragment(
new XbaseGeneratorFragment => [
autoInject
])
// provides a preference page for template proposals
addFragment(
new CodetemplatesGeneratorFragment => [
autoInject
])
// provides a compare view
addFragment(
new CompareFragment => [
autoInject
])
])
]
}
def static void main(String[] args) {
new GenerateMyDsl().run(null)
}
}
package org.xtext.example.mydsl;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.emf.mwe2.runtime.workflow.IWorkflowComponent;
import org.eclipse.emf.mwe2.runtime.workflow.Workflow;
import org.eclipse.xtext.util.Strings;
import com.google.common.collect.Lists;
public class XtendWorkflow extends Workflow {
protected List<Object> getBeans() {
return Lists.newArrayList();
}
protected List<IWorkflowComponent> getComponents() {
return getChildren();
}
protected <T> T autoInject(T t) {
Class<?> tClass = t.getClass();
for (Field field : getClass().getFields()) {
String name = field.getName();
Class<?> type = field.getType();
try {
Method setter = tClass.getMethod(
"set" + Strings.toFirstUpper(name), type);
field.setAccessible(true);
setter.setAccessible(true);
setter.invoke(t, field.get(this));
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
// no problem
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return t;
}
private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\{([^\\}]+)\\}");
protected String resolve(String s) {
Matcher matcher = VAR_PATTERN.matcher(s);
StringBuilder result = new StringBuilder();
int last = 0;
while (matcher.find()) {
result.append(s.substring(last, matcher.start()));
String group = matcher.group(1);
result.append(getFieldValue(group));
last = matcher.end();
}
result.append(s.substring(last));
return result.toString();
}
private Object getFieldValue(String fieldName) {
try {
Field field = getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(this);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return null;
}
}
@JanKoehnlein
Copy link

Great! Looks like we can deprecate MWE2 in the long run...
Some comments:

  • The list of imports looks frightening, but it is nicely managed by the editor as of Xtend 2.4
  • Using ''' ''' and guillemets instead of " ", ${} and resolve can save some characters
  • I'd try make auto-inject default, e.g. by overriding methods like addFragment() with extensions
  • Using 'val' for the properties you can skip the types
  • XtendWorkflow should be written in Xtend, too :-)

@knutwannheden
Copy link

Yes, you are right, there would be some ways to improve this:

  • I tried using wildcard imports at first, but these now result in warnings and with Eclipse by default showing imports in collapsed mode, it's hardly an issue. Also most classes are imported from different packages anyways. What is interesting here is that quite a few of the generator fragment classes result in "Discouraged access" warnings.
  • Using rich strings is definitely the way to go instead of custom string interpolation. Didn't even think of that.
  • I suppose auto-inject by default would make sense. Or possibly something like a getInstance() like a Guice injector has it. See revised example.
  • I think rather than magically having the workflow class fields being injected by autoInject() it would probably make more sense if there were a more explicit way to declare what should be injected. E.g. adding an annotation to these class fields or something.
  • Can you think of a way to get the "+=" operator automatically map to the corresponding JavaBean style addXyz() method? I implemented another hack to be able to use "fragments +=".

As a first step I tried to create something which would look as similar as an MWE2 workflow as possible. And I think the result is a surprisingly close match, even with some small benefits over the MWE2 original (not to mention the far better tooling of the editor).

I would as a next step like to see how this can be improved with the additional features offered by Xtend. In the area of modularization I am quite convinced that Xtend would beat MWE2 hands down.

@knutwannheden
Copy link

Somehow I managed to create this Gist in anonymous mode, so I've created a fork for the updated example: https://gist.github.com/4676978.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment