Jump to: navigation, search

Difference between revisions of "Xtext/FAQ"

m
m (Corrected formatting)
(37 intermediate revisions by 5 users not shown)
Line 3: Line 3:
 
== How do I load my model in a standalone Java application ?  ==
 
== How do I load my model in a standalone Java application ?  ==
  
Assuming you have a grammar Foo.xtext the Java code to load a corresponding model from a resource should look something like this:  
+
Assuming you have the standard example grammar ''MyDsl.xtext'' the Java code to load a corresponding top-level Model object from a resource (here with URI ''platform:/resource/org.xtext.example.mydsl/src/example.mydsl'') should look something like this:
 +
  
<source lang="java">
+
 
Injector injector = new FooStandaloneSetup().createInjectorAndDoEMFRegistration();
+
<source lang="java">
 +
new org.eclipse.emf.mwe.utils.StandaloneSetup().setPlatformUri("../");
 +
Injector injector = new MyDslStandaloneSetup().createInjectorAndDoEMFRegistration();
 
XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class);
 
XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class);
 
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
 
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
Resource resource = resourceSet.getResource(URI.createURI("platform:/resource/myproject/my.foo"), true);
+
Resource resource = resourceSet.getResource(
Foo foo = (Foo) resource.getContents().get(0);
+
    URI.createURI("platform:/resource/org.xtext.example.mydsl/src/example.mydsl"), true);
 +
Model model = (Model) resource.getContents().get(0);
 
</source>
 
</source>
 +
 +
 +
 +
Note that the argument in the first line is the path to your workspace root and is only required if you use '''platform:/resource''' URIs (as in the example) to reference your model files. I.e. if you for instance use '''file:''' URIs instead this line is not required.
 +
 +
 +
 +
Also note that the <code>MyDslStandaloneSetup.createInjectorAndDoEMFRegistration()</code> call only registers Ecore models ''generated'' by the grammar. ''Imported'' grammars must be [http://wiki.eclipse.org/EMF/FAQ#How_do_I_use_EMF_in_standalone_applications_.28such_as_an_ordinary_main.29.3F registered manually] by e.g. by adding the following statement <code>MyDslPackage.eINSTANCE.eClass();</code>.
 +
 +
 +
 +
You can also load a model from a <tt>String</tt> or an <tt>InputStream</tt>, yet you still need a resource. As the resource's URI you can any legal dummy URI (here we use ''dummy:/example.mydsl''), just make sure it has the correct file extension, as that is required to look up your DSL's EMF ResourceFactory. Building on the previous example you just need to replace the last two lines with the following:
 +
  
To load a model from a String or an InputStream replace the last two lines with:
 
  
 
<source lang="java">
 
<source lang="java">
Resource resource = resourceSet.createResource(URI.createURI("platform:/resource/myproject/dummy.foo"));
+
Resource resource = resourceSet.createResource(URI.createURI("dummy:/example.mydsl"));
InputStream in = new ByteArrayInputStream("foo model goes here".getBytes());
+
InputStream in = new ByteArrayInputStream("type foo type bar".getBytes());
 
resource.load(in, resourceSet.getLoadOptions());
 
resource.load(in, resourceSet.getLoadOptions());
Foo foo = (Foo) resource.getContents().get(0);
+
Model model = (Model) resource.getContents().get(0);
 
</source>
 
</source>
 +
 +
 +
 +
Potential parse errors are available as <code>resource.getErrors()</code>.
 +
 +
== How can I load my model in the EMF reflective model editor&nbsp;?  ==
 +
 +
Simply select your model and click the context action ''Open With &gt; Sample Reflective Ecore Model Editor''. This works because Xtext generates and registers a standard EMF resource factory for your DSL (see also previous question) and thus complies with the EMF resource API.
 +
 +
 +
<br>
 +
  
 
= Workflow / Generator  =
 
= Workflow / Generator  =
Line 27: Line 55:
  
 
Here's an example of the full error message:  
 
Here's an example of the full error message:  
 +
  
 +
<br>
 
<source lang="text">
 
<source lang="text">
 
warning(200): InternalFoo.g:42:3: Decision can match input such as "FOO" using multiple alternatives: 1, 2
 
warning(200): InternalFoo.g:42:3: Decision can match input such as "FOO" using multiple alternatives: 1, 2
 
As a result, alternative(s) 2 were disabled for that input
 
As a result, alternative(s) 2 were disabled for that input
 
</source>  
 
</source>  
 +
  
 
These warnings are generated by the ANTLR code generator. Based on the rules in your grammar the ANTLR generated parser cannot disambiguate what rules to apply for a given input. You should try to refactor your grammar (see [http://www.antlr.org/pipermail/antlr-interest/2008-November/031697.html example]) or you can enable backtracking for your parser (see next question).  
 
These warnings are generated by the ANTLR code generator. Based on the rules in your grammar the ANTLR generated parser cannot disambiguate what rules to apply for a given input. You should try to refactor your grammar (see [http://www.antlr.org/pipermail/antlr-interest/2008-November/031697.html example]) or you can enable backtracking for your parser (see next question).  
 +
 +
 +
<br/>
 +
  
 
== OK, but I didn't get these warnings in oAW Xtext&nbsp;!  ==
 
== OK, but I didn't get these warnings in oAW Xtext&nbsp;!  ==
  
 
Unlike in oAW Xtext the ANTLR grammar generated by TMF Xtext doesn't have [http://www.antlr.org/wiki/display/ANTLR3/Grammar+options backtracking] enabled by default. To enable backtracking you have to add a nested element <code>&lt;options backtrack="true"/&gt;</code> to the ANTLR generator fragments in your Xtext project's MWE workflow. So replace:  
 
Unlike in oAW Xtext the ANTLR grammar generated by TMF Xtext doesn't have [http://www.antlr.org/wiki/display/ANTLR3/Grammar+options backtracking] enabled by default. To enable backtracking you have to add a nested element <code>&lt;options backtrack="true"/&gt;</code> to the ANTLR generator fragments in your Xtext project's MWE workflow. So replace:  
 
+
 
<source lang="xml">
 
<source lang="xml">
 
<!-- Antlr Generator fragment -->
 
<!-- Antlr Generator fragment -->
 
<fragment class="org.eclipse.xtext.generator.AntlrDelegatingFragment"/>
 
<fragment class="org.eclipse.xtext.generator.AntlrDelegatingFragment"/>
 
</source>  
 
</source>  
 
+
 
with:  
 
with:  
  
Line 52: Line 87:
 
</fragment>
 
</fragment>
 
</source>  
 
</source>  
 
+
 
Further down in the workflow you will find another use of the <code>AntlrDelegatingFragment</code> fragment (used for content assist) you have to replace with:  
 
Further down in the workflow you will find another use of the <code>AntlrDelegatingFragment</code> fragment (used for content assist) you have to replace with:  
 
+
 
<source lang="xml">
 
<source lang="xml">
 
<fragment class="de.itemis.xtext.antlr.XtextAntlrUiGeneratorFragment">
 
<fragment class="de.itemis.xtext.antlr.XtextAntlrUiGeneratorFragment">
Line 60: Line 95:
 
</fragment>
 
</fragment>
 
</source>  
 
</source>  
 +
  
 
Note that you can in both cases also specify <code>memoize="true"</code> as an additional option.  
 
Note that you can in both cases also specify <code>memoize="true"</code> as an additional option.  
 +
 +
 +
<br>
 +
  
 
== Why are generated packages from an imported grammar A duplicated in dependent grammar B&nbsp;?  ==
 
== Why are generated packages from an imported grammar A duplicated in dependent grammar B&nbsp;?  ==
  
In addition to the import statement in B.xtext you must also configure your GenerateB.mwe workflow to let it know about the corresponding GenModels of grammar A. You do this by [http://www.eclipse.org/Xtext/documentation/latest/xtext.html#UsingresourceURIstoimportexistingEPackages setting the genModels attribute] of the EcoreGeneratorFragment:  
+
In addition to the import statement in ''B.xtext'' you must also configure your ''GenerateB.mwe'' workflow to let it know about the corresponding GenModels of grammar A. You do this by [http://www.eclipse.org/Xtext/documentation/latest/xtext.html#UsingresourceURIstoimportexistingEPackages setting the genModels attribute] of the EcoreGeneratorFragment:  
 
+
 
<source lang="xml">
 
<source lang="xml">
 
   <fragment class="org.eclipse.xtext.generator.ecore.EcoreGeneratorFragment"
 
   <fragment class="org.eclipse.xtext.generator.ecore.EcoreGeneratorFragment"
       genModels="platform:/resource/project/src/my/pack/SecretCompartments.genmodel"/>
+
       genModels="platform:/resource/my.b.project/src-gen/my/b/B.genmodel"/>
 
</source>  
 
</source>  
 +
 +
 +
  
== How can I control the Xtext meta model inference ?  ==
+
== How can I control the Xtext meta model inference&nbsp;?  ==
  
 
The typical use case is to let Xtext automatically [http://www.eclipse.org/Xtext/documentation/latest/xtext.html#metamodelInference infer a meta model] corresponding to the Xtext grammar. Quite often this meta model is exactly what you want. If you on the other hand want to make some small changes to the inferred meta model (e.g. set attribute default values, add operations, features, or enumeration literals, etc.) you must implement a ''post processor''. Please refer to the [http://www.eclipse.org/Xtext/documentation/latest/xtext.html#customPostProcessing relevant documentation] and the following example for more details.  
 
The typical use case is to let Xtext automatically [http://www.eclipse.org/Xtext/documentation/latest/xtext.html#metamodelInference infer a meta model] corresponding to the Xtext grammar. Quite often this meta model is exactly what you want. If you on the other hand want to make some small changes to the inferred meta model (e.g. set attribute default values, add operations, features, or enumeration literals, etc.) you must implement a ''post processor''. Please refer to the [http://www.eclipse.org/Xtext/documentation/latest/xtext.html#customPostProcessing relevant documentation] and the following example for more details.  
 +
  
 
The following example shows how to add an enumeration literal (here ''NULL'') to an enumeration (here ''VisibilityModifier'') and sets it as the default value for all attributes of that type.  
 
The following example shows how to add an enumeration literal (here ''NULL'') to an enumeration (here ''VisibilityModifier'') and sets it as the default value for all attributes of that type.  
 +
 +
 +
('''Note''': You'll have to configure the JavaBeansMetamodel for your Eclipse project via Project-&gt;Properties-&gt;Xpand. Otherwise you'll get false error markers in your xtend-file.)
 +
  
 
<source lang="text">
 
<source lang="text">
Line 113: Line 161:
 
;
 
;
 
</source>  
 
</source>  
 +
 +
 +
Here is another example where EOperations (here "doFoo" and "getBar") with method bodies are added to types (here "Foo") in the model. The method body simply delegates to a static method with the same name in another class (here "com.mycompany.FooHelper"):
 +
 +
 +
<source lang="text">
 +
import ecore;
 +
 +
process(xtext::GeneratedMetamodel this) :
 +
process(ePackage)
 +
;
 +
 +
process(EPackage this) :
 +
eClassifiers.typeSelect(EClass).process()
 +
;
 +
 +
process(EClass this) :
 +
switch (name) {
 +
case "Foo": (addOperation("doFoo", getEcoreDataType("EString")) -> addOperation("getBar", ePackage.getEClassifier("Bar")))
 +
default: null
 +
}
 +
;
 +
 +
EDataType getEcoreDataType(String name) :
 +
    org::eclipse::emf::ecore::EcorePackage::eINSTANCE.getEClassifier(name)
 +
;
 +
 +
addOperation(EClass this, String name, EClassifier type) :
 +
let op  = newOperation(name, type) :
 +
newDelegatingBodyAnnotation(op)
 +
;
 +
 +
create EOperation newOperation(EClass owner, String name, EClassifier type) :
 +
setName(name) -> setEType(type) -> owner.eOperations.add(this)
 +
;
 +
 +
create EAnnotation newDelegatingBodyAnnotation(EOperation op) :
 +
let d = new EStringToStringMapEntry :
 +
setSource("http://www.eclipse.org/emf/2002/GenModel") ->
 +
d.setKey("body") ->
 +
d.setValue((op.eType != null ? "return " : "") + op.delegateMethodName() + "(this);") ->
 +
details.add(d) ->
 +
op.eAnnotations.add(this)
 +
;
 +
 +
delegateMethodName(EOperation this) :
 +
"com.mycompany." + eContainingClass.name + "Helper." + name
 +
;
 +
</source>
 +
 +
 +
<br>
 +
 +
 +
== How can I get rid of the ecore model generation (and use only imported EMF models)?  ==
 +
 +
Let's start with this simple dsl:
 +
 +
 +
<source lang="text">
 +
grammar org.xtext.example.MyDsl with org.eclipse.xtext.common.Terminals
 +
generate myDsl "http://www.xtext.org/example/MyDsl"
 +
Model:
 +
(elements+=Type)*;
 +
Type:
 +
'type' name=ID;
 +
 +
</source> Here is the EMF model that we want to use (in [[Emfatic]] syntax):
 +
<source lang="text">
 +
@namespace(uri="http://org.other.model/example/OtherModel", prefix="otherModel")
 +
package otherModel;
 +
class OtherModel {
 +
  val SimpleType[*] elements;
 +
}
 +
class SimpleType {
 +
  attr String name;
 +
}
 +
</source> Make sure you have imported the packages that contain the EMF model in the <code>MANIFEST.MF</code>
 +
 +
 +
Change the workflow and add the following attributes to the existing workflow elements. Add <code>genModels</code> to the <code>EcoreGeneratorFragment</code> fragment. This is needed so that the generator finds your genmodel:
 +
 +
 +
<source lang="xml">
 +
  <fragment class="org.eclipse.xtext.generator.ecore.EcoreGeneratorFragment"
 +
      genModels="platform:/resource/org.xtext.example.mydsl/src/org/other/model/OtherModel.genmodel"/>         
 +
</source> Add <code>registerGeneratedEPackage</code> to the <code>StandaloneSetup</code> bean. This is needed so that xtext finds your package. If there are more then one model, use a comma separated list in the registerGeneratedEPackage attribute: <source lang="xml">
 +
  <bean class="org.eclipse.emf.mwe.utils.StandaloneSetup" platformUri="${runtimeProject}/.."
 +
    registerGeneratedEPackage="otherModel.MyDslPackage"/>
 +
 +
</source>
 +
 +
 +
Import the ecore model(s) and use it in your grammar. You know that you are ready for the next step if the generated <code>Mydsl.ecore</code> file contains only an empty package.
 +
 +
 +
<source lang="text">
 +
grammar org.xtext.example.MyDsl with org.eclipse.xtext.common.Terminals
 +
import "platform:/resource/org.xtext.example.mydsl/src/org/other/model/OtherModel.ecore" as other
 +
generate myDsl "http://www.xtext.org/example/MyDsl"
 +
Model returns other::OtherModel:
 +
(elements+=Type)*;
 +
Type returns other::SimpleType:
 +
'type' name=ID;
 +
</source>
 +
 +
 +
Remove the <code>generate</code> statement form your grammar:
 +
 +
 +
<source lang="text">
 +
grammar org.xtext.example.MyDsl with org.eclipse.xtext.common.Terminals
 +
import "platform:/resource/org.xtext.example.mydsl/src/org/other/model/OtherModel.ecore" as other
 +
Model returns other::OtherModel:
 +
(elements+=Type)*;
 +
Type returns other::SimpleType:
 +
'type' name=ID;
 +
</source>
 +
 +
 +
You might remove the <code>genModels</code> from the <code>EcoreGeneratorFragment</code> because you are not generating any EMF anymore!
 +
 +
 +
<br>
 +
 +
 +
== Why do I get compilation errors when trying to import an existing Xtext grammar using the with statement?  ==
 +
 +
Consider the following grammars:
 +
 +
 +
<br>
 +
<source lang="text">
 +
grammar my.grammar.Terminals
 +
 +
terminal ID : ('a'..'z'|'A'..'Z'|'_'|'0'..'9')+;
 +
 +
</source>
 +
 +
 +
<br>
 +
<source lang="text">
 +
grammar my.grammar.DomainModel with my.grammar.Terminals
 +
 +
DomainModel: name=ID;
 +
 +
</source>
 +
 +
 +
After you run the default MWE2 workflow, you will most likely encounter compilation errors such as:
 +
 +
 +
<source lang="text">
 +
AbstractTerminalsProposalProvider cannot be resolved to a type
 +
TerminalsGrammarAccess cannot be resolved to a type
 +
...
 +
</source>
 +
 +
 +
To resolve this, you need to update your MWE2 workflow so that these classes do get generated.
 +
 +
 +
Try add the following block to the "Generator" workflow in the MWE2 file:
 +
 +
 +
<source lang="text">
 +
language = {
 +
uri = terminalURI
 +
fileExtensions="___terms"
 +
fragment = grammarAccess.GrammarAccessFragment {}
 +
fragment = parseTreeConstructor.ParseTreeConstructorFragment {}
 +
fragment = contentAssist.JavaBasedContentAssistFragment {}
 +
}
 +
</source>
 +
 +
 +
== How do I enable multiple file extensions for a single DSL?  ==
 +
 +
Open up the plugin.xml for your mydsl.ui project.<br>
 +
 +
For the org.eclipse.ui.editors extension point, enter in a comma-separated list of the file extensions which should be handled by your DSL.
 +
E.g.:
 +
 +
<source lang="text">
 +
<editor
 +
  class="ansa.autotest.ui.HtmlExecutableExtensionFactory:org.eclipse.xtext.ui.editor.XtextEditor"
 +
  contributorClass="org.eclipse.ui.editors.text.TextEditorActionContributor"
 +
  default="true"
 +
  extensions="htm,html"
 +
  id="mydsl.html"
 +
  name="HTML Editor">
 +
</editor>
 +
</source>
 +
 +
 +
Make a copy of the existing org.eclipse.emf.ecore.extension_parser and org.eclipse.xtext.extension_resourceServiceProvider extension points, replacing the type and uriExtension attributes with each additional file extension.<br><br>
 +
 +
= UI  =
 +
 +
== How do I implement a generic label provider&nbsp;?  ==
 +
 +
The [http://www.eclipse.org/Xtext/documentation/latest/xtext.html#labelProvider official way] is to put the icons into a folder <code>icons</code> in the
 +
 +
 +
UI project and add a method to your <code>LabelProvider</code> for each of your Ecore classes:
 +
 +
 +
<source lang="java">
 +
    String image(MyModel ele) {
 +
      return "MyModel.gif";
 +
    }
 +
    String image(Foo ele) {
 +
      return "Foo.gif";
 +
    }
 +
    ....
 +
</source>
 +
 +
 +
If the name of your icons corresponds to the name of your Ecore classes you can declare a generic method:
 +
 +
 +
<br>
 +
<source lang="java">
 +
    public String image(EObject obj) {
 +
        return obj.eClass().getName() + ".gif";
 +
    }
 +
</source>
 +
 +
 +
You may also want to look for a PNG as a fallback. The following code will first look for the image as determined by the label provider (see above) and in case this is a non-existing GIF then as a fallback for a PNG with the same name:
 +
 +
 +
<br>
 +
<source lang="java">
 +
    @Override
 +
    public Image getImage(Object element) {
 +
    Image image = super.getImage(element);
 +
    if (image == null) {
 +
        String imageName = getImageDispatcher().invoke(element);
 +
        if (imageName != null && imageName.endsWith(".gif"))
 +
        image = getImageHelper().getImage(imageName.substring(0, imageName.length() - 3) + "png");
 +
    }
 +
    return image;
 +
    }
 +
</source>
 +
  
 
[[Category:FAQ]] [[Category:Xtext]]
 
[[Category:FAQ]] [[Category:Xtext]]

Revision as of 02:59, 25 September 2012

General

How do I load my model in a standalone Java application ?

Assuming you have the standard example grammar MyDsl.xtext the Java code to load a corresponding top-level Model object from a resource (here with URI platform:/resource/org.xtext.example.mydsl/src/example.mydsl) should look something like this:


new org.eclipse.emf.mwe.utils.StandaloneSetup().setPlatformUri("../");
Injector injector = new MyDslStandaloneSetup().createInjectorAndDoEMFRegistration();
XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class);
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
Resource resource = resourceSet.getResource(
    URI.createURI("platform:/resource/org.xtext.example.mydsl/src/example.mydsl"), true);
Model model = (Model) resource.getContents().get(0);


Note that the argument in the first line is the path to your workspace root and is only required if you use platform:/resource URIs (as in the example) to reference your model files. I.e. if you for instance use file: URIs instead this line is not required.


Also note that the MyDslStandaloneSetup.createInjectorAndDoEMFRegistration() call only registers Ecore models generated by the grammar. Imported grammars must be registered manually by e.g. by adding the following statement MyDslPackage.eINSTANCE.eClass();.


You can also load a model from a String or an InputStream, yet you still need a resource. As the resource's URI you can any legal dummy URI (here we use dummy:/example.mydsl), just make sure it has the correct file extension, as that is required to look up your DSL's EMF ResourceFactory. Building on the previous example you just need to replace the last two lines with the following:


Resource resource = resourceSet.createResource(URI.createURI("dummy:/example.mydsl"));
InputStream in = new ByteArrayInputStream("type foo type bar".getBytes());
resource.load(in, resourceSet.getLoadOptions());
Model model = (Model) resource.getContents().get(0);


Potential parse errors are available as resource.getErrors().

How can I load my model in the EMF reflective model editor ?

Simply select your model and click the context action Open With > Sample Reflective Ecore Model Editor. This works because Xtext generates and registers a standard EMF resource factory for your DSL (see also previous question) and thus complies with the EMF resource API.




Workflow / Generator

Why do I get warnings like "warning(200): InternalFoo.g:42:3: Decision can match ..." when running the generator ?

Here's an example of the full error message:



warning(200): InternalFoo.g:42:3: Decision can match input such as "FOO" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input


These warnings are generated by the ANTLR code generator. Based on the rules in your grammar the ANTLR generated parser cannot disambiguate what rules to apply for a given input. You should try to refactor your grammar (see example) or you can enable backtracking for your parser (see next question).




OK, but I didn't get these warnings in oAW Xtext !

Unlike in oAW Xtext the ANTLR grammar generated by TMF Xtext doesn't have backtracking enabled by default. To enable backtracking you have to add a nested element <options backtrack="true"/> to the ANTLR generator fragments in your Xtext project's MWE workflow. So replace:

<!-- Antlr Generator fragment -->
<fragment class="org.eclipse.xtext.generator.AntlrDelegatingFragment"/>

with:

<!-- Antlr Generator fragment -->
<fragment class="de.itemis.xtext.antlr.XtextAntlrGeneratorFragment">
   <options backtrack="true"/>
</fragment>

Further down in the workflow you will find another use of the AntlrDelegatingFragment fragment (used for content assist) you have to replace with:

<fragment class="de.itemis.xtext.antlr.XtextAntlrUiGeneratorFragment">
   <options backtrack="true"/>
</fragment>


Note that you can in both cases also specify memoize="true" as an additional option.




Why are generated packages from an imported grammar A duplicated in dependent grammar B ?

In addition to the import statement in B.xtext you must also configure your GenerateB.mwe workflow to let it know about the corresponding GenModels of grammar A. You do this by setting the genModels attribute of the EcoreGeneratorFragment:

   <fragment class="org.eclipse.xtext.generator.ecore.EcoreGeneratorFragment"
      genModels="platform:/resource/my.b.project/src-gen/my/b/B.genmodel"/>



How can I control the Xtext meta model inference ?

The typical use case is to let Xtext automatically infer a meta model corresponding to the Xtext grammar. Quite often this meta model is exactly what you want. If you on the other hand want to make some small changes to the inferred meta model (e.g. set attribute default values, add operations, features, or enumeration literals, etc.) you must implement a post processor. Please refer to the relevant documentation and the following example for more details.


The following example shows how to add an enumeration literal (here NULL) to an enumeration (here VisibilityModifier) and sets it as the default value for all attributes of that type.


(Note: You'll have to configure the JavaBeansMetamodel for your Eclipse project via Project->Properties->Xpand. Otherwise you'll get false error markers in your xtend-file.)


import ecore;
 
process(xtext::GeneratedMetamodel this) :
     ePackage.process()
;
 
process(EPackage this) :
     eClassifiers.process()
;
 
process(EClassifier this) :
     null
;
 
process(EClass this) :
     eStructuralFeatures.process()
;
 
process(EEnum this) :
     if name == 'VisibilityModifier' then eLiterals.add(newLiteral('null', 'NULL', eLiterals.size))
;
 
create EEnumLiteral newLiteral(String literal, String name, int value) :
     setLiteral(literal) -> setName(name) -> setValue(value)
;
 
process(EStructuralFeature this) :
     null
;
 
process(EAttribute this) :
     if eAttributeType.name == 'VisibilityModifier' then setDefaultValueLiteral('NULL')
;


Here is another example where EOperations (here "doFoo" and "getBar") with method bodies are added to types (here "Foo") in the model. The method body simply delegates to a static method with the same name in another class (here "com.mycompany.FooHelper"):


import ecore;
 
process(xtext::GeneratedMetamodel this) :
	process(ePackage)
;
 
process(EPackage this) :
	eClassifiers.typeSelect(EClass).process()
;
 
process(EClass this) :
	switch (name) {
		case "Foo": (addOperation("doFoo", getEcoreDataType("EString")) -> addOperation("getBar", ePackage.getEClassifier("Bar")))
		default:	null
	}
;
 
EDataType getEcoreDataType(String name) :
    org::eclipse::emf::ecore::EcorePackage::eINSTANCE.getEClassifier(name)
;
 
addOperation(EClass this, String name, EClassifier type) :
	let op  = newOperation(name, type) :
		newDelegatingBodyAnnotation(op)
;
 
create EOperation newOperation(EClass owner, String name, EClassifier type) :
	setName(name) -> setEType(type) -> owner.eOperations.add(this)
;
 
create EAnnotation newDelegatingBodyAnnotation(EOperation op) :
	let d = new EStringToStringMapEntry :
		setSource("http://www.eclipse.org/emf/2002/GenModel") ->
		d.setKey("body") ->
		d.setValue((op.eType != null ? "return " : "") + op.delegateMethodName() + "(this);") ->
		details.add(d) ->
		op.eAnnotations.add(this)
;
 
delegateMethodName(EOperation this) :
	"com.mycompany." + eContainingClass.name + "Helper." +	name
;




How can I get rid of the ecore model generation (and use only imported EMF models)?

Let's start with this simple dsl:


grammar org.xtext.example.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "http://www.xtext.org/example/MyDsl"
Model:
	(elements+=Type)*;
Type:
	'type' name=ID;
Here is the EMF model that we want to use (in Emfatic syntax):
@namespace(uri="http://org.other.model/example/OtherModel", prefix="otherModel")
package otherModel;
class OtherModel {
  val SimpleType[*] elements;
}
class SimpleType {
  attr String name;
}
Make sure you have imported the packages that contain the EMF model in the MANIFEST.MF


Change the workflow and add the following attributes to the existing workflow elements. Add genModels to the EcoreGeneratorFragment fragment. This is needed so that the generator finds your genmodel:


  <fragment class="org.eclipse.xtext.generator.ecore.EcoreGeneratorFragment"
      genModels="platform:/resource/org.xtext.example.mydsl/src/org/other/model/OtherModel.genmodel"/>
Add registerGeneratedEPackage to the StandaloneSetup bean. This is needed so that xtext finds your package. If there are more then one model, use a comma separated list in the registerGeneratedEPackage attribute:
   <bean class="org.eclipse.emf.mwe.utils.StandaloneSetup" platformUri="${runtimeProject}/.."
    registerGeneratedEPackage="otherModel.MyDslPackage"/>


Import the ecore model(s) and use it in your grammar. You know that you are ready for the next step if the generated Mydsl.ecore file contains only an empty package.


grammar org.xtext.example.MyDsl with org.eclipse.xtext.common.Terminals
import "platform:/resource/org.xtext.example.mydsl/src/org/other/model/OtherModel.ecore" as other
generate myDsl "http://www.xtext.org/example/MyDsl"
Model returns other::OtherModel:
	(elements+=Type)*;
Type returns other::SimpleType:
	'type' name=ID;


Remove the generate statement form your grammar:


grammar org.xtext.example.MyDsl with org.eclipse.xtext.common.Terminals
import "platform:/resource/org.xtext.example.mydsl/src/org/other/model/OtherModel.ecore" as other
Model returns other::OtherModel:
	(elements+=Type)*;
Type returns other::SimpleType:
	'type' name=ID;


You might remove the genModels from the EcoreGeneratorFragment because you are not generating any EMF anymore!




Why do I get compilation errors when trying to import an existing Xtext grammar using the with statement?

Consider the following grammars:



grammar my.grammar.Terminals
 
terminal ID : ('a'..'z'|'A'..'Z'|'_'|'0'..'9')+;



grammar my.grammar.DomainModel with my.grammar.Terminals
 
DomainModel: name=ID;


After you run the default MWE2 workflow, you will most likely encounter compilation errors such as:


AbstractTerminalsProposalProvider cannot be resolved to a type
TerminalsGrammarAccess cannot be resolved to a type
...


To resolve this, you need to update your MWE2 workflow so that these classes do get generated.


Try add the following block to the "Generator" workflow in the MWE2 file:


language = {
	uri = terminalURI
	fileExtensions="___terms"
	fragment = grammarAccess.GrammarAccessFragment {}
	fragment = parseTreeConstructor.ParseTreeConstructorFragment {}
	fragment = contentAssist.JavaBasedContentAssistFragment {}
}


How do I enable multiple file extensions for a single DSL?

Open up the plugin.xml for your mydsl.ui project.

For the org.eclipse.ui.editors extension point, enter in a comma-separated list of the file extensions which should be handled by your DSL. E.g.:

<editor
   class="ansa.autotest.ui.HtmlExecutableExtensionFactory:org.eclipse.xtext.ui.editor.XtextEditor"
   contributorClass="org.eclipse.ui.editors.text.TextEditorActionContributor"
   default="true"
   extensions="htm,html"
   id="mydsl.html"
   name="HTML Editor">
</editor>


Make a copy of the existing org.eclipse.emf.ecore.extension_parser and org.eclipse.xtext.extension_resourceServiceProvider extension points, replacing the type and uriExtension attributes with each additional file extension.

UI

How do I implement a generic label provider ?

The official way is to put the icons into a folder icons in the


UI project and add a method to your LabelProvider for each of your Ecore classes:


    String image(MyModel ele) {
      return "MyModel.gif";
    }
    String image(Foo ele) {
      return "Foo.gif";
    }
    ....


If the name of your icons corresponds to the name of your Ecore classes you can declare a generic method:



    public String image(EObject obj) {
        return obj.eClass().getName() + ".gif";
    }


You may also want to look for a PNG as a fallback. The following code will first look for the image as determined by the label provider (see above) and in case this is a non-existing GIF then as a fallback for a PNG with the same name:



    @Override
    public Image getImage(Object element) {
    	Image image = super.getImage(element);
    	if (image == null) {
        	String imageName = getImageDispatcher().invoke(element);
        	if (imageName != null && imageName.endsWith(".gif"))
        		image = getImageHelper().getImage(imageName.substring(0, imageName.length() - 3) + "png");
    	}
    	return image;
    }