Archive

Posts Tagged ‘Xtext’

Xtext and Strings as Cross-References

Cross References are a often used concept in Xtext. They ususally work like this

Model:
    definitions+=Definition*
    usages+=Usage*
;

Definition:
    "define" name=ID
;

Usage:
   "use" definition=[Definition]
;

They can be used in the model like this

define Thing
use Thing

but what if i want to write something like

define "This is a Thing"
use "This is a Thing"

well the definition part is easily changed

Definition:
    "define" name=STRING
;

But what about the usage part?
well it is quite easy as well. refName=[Type] is short for refName=[Type|ID] which means ‘Refererence a Type and parse an ID. So to use another Terminal or Data Type Rule we change it to refName=[Type|RULENAME]

Usage:
   "use" definition=[Definition|STRING]
;

Now the cross refs are working fine. but if we try the editor we find out what autoedit and content assist disturb each other. We type " and auto edit gets us to "|". If we now type Crtl+Space for content assist we finally get "This is a Thing"" with an extra " at the end.
To avoid this we have to tweak the proposal provider a bit.

package org.xtext.example.mydsl.ui.contentassist

import org.xtext.example.mydsl.ui.contentassist.AbstractMyDslProposalProvider
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.Assignment
import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext
import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor
import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor.Delegate
import org.eclipse.jface.text.contentassist.ICompletionProposal
import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal

class MyDslProposalProvider extends AbstractMyDslProposalProvider {

	override completeUsage_Definition(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		super.completeUsage_Definition(model, assignment, context, new  StringProposalDelegate(acceptor, context))
	}

	static class StringProposalDelegate extends Delegate {

		ContentAssistContext ctx

		new(ICompletionProposalAcceptor delegate, ContentAssistContext ctx) {
			super(delegate)
			this.ctx = ctx
		}

		override accept(ICompletionProposal proposal) {
			if (proposal instanceof ConfigurableCompletionProposal) {
				val endPos = proposal.replacementOffset + proposal.replacementLength 
				if (ctx.document != null && ctx.document.length > endPos) {
					// We are not at the end of the file
					if ("\"" == ctx.document.get(endPos, 1)) {
						proposal.replacementLength = proposal.replacementLength-1
						proposal.replacementString = proposal.replacementString.substring(0,proposal.replacementString.length-1)
					}
				}
			}
			super.accept(proposal)
		}

	}

}

what we basically do is detecting the situation and remove the extra "

Xtext and Dot/Path-Expressions

If you have a DSL that describes structure (e.g. like an Entity DSL) you often need to “walk” on this structure using dot/path-expressions.

Let us asume we have a grammar like

Model:
	entities+=Entity*
;

Entity:
	"entity" name=ID "{"
		features+=Feature*
	"}"
;

Feature:
	Attribute | Reference
;

Attribute:
	"attr" name=ID ":" type=DataType
;

enum DataType:
	string | int
;

Reference:
	"ref" name=ID ":" type=[Entity]
;

and a model like

entity A {
	attr a1 : int
	attr a2 : string
	ref b : B
	ref c : C
}

entity B {
	attr b1 : string
	attr b2 : string
	ref a : A
	ref c : C
}

entity C {
	attr c1 : string
	attr c2 : int
}

and want to have expressions like

use A.b.b2
use A.b.c.c1
use A.a1
use A.b.a.a1

but how to do this with Xtext?
there are several possibility but the following was working well in my usecase:

Model:
	entities+=Entity*
	usages+=Usage*
;
Usage:
	"use" ref=DotExpression
;

DotExpression returns Ref:
	EntityRef ({DotExpression.ref=current}  "." tail=[Feature])*
;

EntityRef returns Ref:
	{EntityRef} entity=[Entity]
; 

a bit of scoping

class MyDslScopeProvider extends AbstractDeclarativeScopeProvider {

	def IScope scope_DotExpression_tail(DotExpression exp, EReference ref) {
		val head = exp.ref;
		switch (head) {
			EntityRef : Scopes::scopeFor(head.entity.features)
			DotExpression : {
				val tail = head.tail
				switch (tail) {
					Attribute : IScope::NULLSCOPE
					Reference : Scopes::scopeFor(tail.type.features)
					default: IScope::NULLSCOPE
				}
			}
			
			default: IScope::NULLSCOPE
		}
	}

}

and it works fine.

as an additional note: the ast of the expressions look like

Sample AST

Sample AST

Categories: Xtext Tags: , ,

Xtext: Referencing Elements of one DSL from another DSL

This is a blog post on Inter-Language-Cross-References in Xtext. Let us asume we have a DSL that contains Definitions. Now we want to create another DSL that references (uses) the Definitions defined in the the first DSL.

So let us start with the first DSL: We create a new Xtext Project

And here is the (for demonstration purpose oversimplyfied) Grammar

grammar org.xtext.example.definitions.Definitions with org.eclipse.xtext.common.Terminals

generate definitions "http://www.xtext.org/example/definitions/Definitions"

Model:
	definitions+=Definition*;

Definition:
	'define' name=ID;

We run GenerateDefinitions.mwe2 to generate the language.
This is all for the first DSL.

Now let us create a project for the second DSL.

To be able to reference the first DSL from the second we add a bundle dependency to the Manifest of the second

Here the resulting manifest

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: org.xtext.example.usages
Bundle-Vendor: My Company
Bundle-Version: 1.0.0.qualifier
Bundle-SymbolicName: org.xtext.example.usages; singleton:=true
Bundle-ActivationPolicy: lazy
Require-Bundle: org.eclipse.xtext;visibility:=reexport,
 org.eclipse.xtext.xbase;resolution:=optional;visibility:=reexport,
 org.eclipse.xtext.generator;resolution:=optional,
 org.apache.commons.logging;bundle-version="1.0.4";resolution:=optional,
 org.eclipse.emf.codegen.ecore;resolution:=optional,
 org.eclipse.emf.mwe.utils;resolution:=optional,
 org.eclipse.emf.mwe2.launch;resolution:=optional,
 org.xtext.example.definitions;bundle-version="1.0.0"
Import-Package: org.apache.log4j
Bundle-RequiredExecutionEnvironment: J2SE-1.5

Now we can create the Grammar from the Usage DSL

grammar org.xtext.example.usages.Usages with org.eclipse.xtext.common.Terminals

generate usages "http://www.xtext.org/example/usages/Usages"

import "http://www.xtext.org/example/definitions/Definitions" as def

Model:
	usages+=Usage*;
	
Usage:
	'use' definition=[def::Definition];

With "http://www.xtext.org/example/definitions/Definitions" as def we import the metamodel of our Define DSL to our Usage DSL.
Thus the Type Definition is available and can be used to define the cross reference definition=[def::Definition]

To get the thing running we have to do some adjustments to the workflow of the Usages language.

module org.xtext.example.usages.GenerateUsages

import org.eclipse.emf.mwe.utils.*
import org.eclipse.xtext.generator.*
import org.eclipse.xtext.ui.generator.*

var grammarURI = "classpath:/org/xtext/example/usages/Usages.xtext"
var file.extensions = "use"
var projectName = "org.xtext.example.usages"
var runtimeProject = "../${projectName}"

Workflow {
    bean = StandaloneSetup {
        scanClassPath = true
        platformUri = "${runtimeProject}/..";
        registerGeneratedEPackage = "org.xtext.example.definitions.definitions.DefinitionsPackage"
        registerGenModelFile = "platform:/resource/org.xtext.example.definitions/src-gen/org/xtext/example/definitions/Definitions.genmodel"
    }
    
        ...
}

we generate our Usage DSL (GenerateUsages.mwe2)

finally we start a new runtime eclipse and give it a try

Update Xtext 2.9.2+:

Here is how the workflow looks like with the new Xtext 2.9.x generator

module org.xtext.example.usages.GenerateUsages

import org.eclipse.xtext.xtext.generator.*
import org.eclipse.xtext.xtext.generator.model.project.*

var rootPath = ".."

Workflow {
	
	component = XtextGenerator {
		configuration = {
			project = StandardProjectConfig {
				baseName = "org.xtext.example.usages"
				rootPath = rootPath
				runtimeTest = {
					enabled = true
				}
				eclipsePlugin = {
					enabled = true
				}
				eclipsePluginTest = {
					enabled = true
				}
				createEclipseMetaData = true
			}
			code = {
				encoding = "UTF-8"
				fileHeader = "/*\n * generated by Xtext \${version}\n */"
			}
		}
		language = StandardLanguage {
			name = "org.xtext.example.usages.Usages"
			referencedResource = "platform:/resource/org.xtext.example.definitions/model/generated/Definitions.genmodel"
			fileExtensions = "use"

			serializer = {
				generateStub = false
			}
			validator = {
			}
		}
	}
}

Xtext: Calling the Generator from a Context Menu

October 15, 2011 41 comments

Xtext offers the user to implement an Xtend Class that implements the org.eclipse.xtext.generator.IGenerator Interface. By Default this Generator is called by the Builder through a org.eclipse.xtext.builder.IXtextBuilderParticipant when saving the file. But what to do if I want to call the Generator explicitely through a context menu entry on the file as shown in the screenshot below? This will be shown in the Following example.

Grammar & IGenerator

We first start with writing the Grammar and after generating the language we implement the Generator Stub Xtext created for us.

grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals

generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"

Model:
	greetings+=Greeting*;
	
Greeting:
	'Hello' name=ID ('from' from=[Greeting])?'!';
package org.xtext.example.mydsl.generator

import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.IGenerator
import org.eclipse.xtext.generator.IFileSystemAccess
import org.xtext.example.mydsl.myDsl.Greeting

import static extension org.eclipse.xtext.xtend2.lib.ResourceExtensions.*

class MyDslGenerator implements IGenerator {
	
	override void doGenerate(Resource resource, IFileSystemAccess fsa) {
		for (g : resource.allContentsIterable.filter(typeof(Greeting))) {
			fsa.generateFile(g.name+".txt",'''
				Hello «g.name» «IF g.from != null»from «g.from.name»«ENDIF»!
			''')
		}
	}
}

Disabling the IBuilderParticipant

Then we go to the UI Projects Plugin.xml and disable the default registration of an IXtextBuilderParticipant

<!--
   <extension
         point="org.eclipse.xtext.builder.participant">
      <participant
            class="org.xtext.example.mydsl.ui.MyDslExecutableExtensionFactory:org.eclipse.xtext.builder.IXtextBuilderParticipant">
      </participant>
   </extension>
-->

Setting up the Context Menu

Then we setup the context menu with Eclipse means

<extension
        point="org.eclipse.ui.handlers">
     <handler
           class="org.xtext.example.mydsl.ui.MyDslExecutableExtensionFactory:org.xtext.example.mydsl.ui.handler.GenerationHandler"
           commandId="org.xtext.example.mydsl.ui.handler.GenerationCommand">
     </handler>
     
  </extension>
  
  <extension
        point="org.eclipse.ui.commands">
        <command name="Generate Code"
              id="org.xtext.example.mydsl.ui.handler.GenerationCommand">
        </command>
  </extension>
  
  <extension point="org.eclipse.ui.menus">
    <menuContribution locationURI="popup:org.eclipse.jdt.ui.PackageExplorer">
        <command
            commandId="org.xtext.example.mydsl.ui.handler.GenerationCommand"
            style="push">
            <visibleWhen
                  checkEnabled="false">
                  <iterate>
       <adapt type="org.eclipse.core.resources.IResource">
          <test property="org.eclipse.core.resources.name" 
                value="*.mydsl"/>
       </adapt>
    </iterate>
            </visibleWhen>
        </command>
    </menuContribution>
	</extension>

Implementing the Handler / Calling the Generator

The last thing we have to do is to call the IGenerator from the handler class.
this could look like

public class GenerationHandler extends AbstractHandler implements IHandler {
	
	@Inject
	private IGenerator generator;

	@Inject
	private Provider<EclipseResourceFileSystemAccess> fileAccessProvider;
	
	@Inject
	IResourceDescriptions resourceDescriptions;
	
	@Inject
	IResourceSetProvider resourceSetProvider;
	
	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {
		
		ISelection selection = HandlerUtil.getCurrentSelection(event);
		if (selection instanceof IStructuredSelection) {
			IStructuredSelection structuredSelection = (IStructuredSelection) selection;
			Object firstElement = structuredSelection.getFirstElement();
			if (firstElement instanceof IFile) {
				IFile file = (IFile) firstElement;
				IProject project = file.getProject();
				IFolder srcGenFolder = project.getFolder("src-gen");
				if (!srcGenFolder.exists()) {
					try {
						srcGenFolder.create(true, true,
								new NullProgressMonitor());
					} catch (CoreException e) {
						return null;
					}
				}

				final EclipseResourceFileSystemAccess fsa = fileAccessProvider.get();
				fsa.setOutputPath(srcGenFolder.getFullPath().toString());
				
				URI uri = URI.createPlatformResourceURI(file.getFullPath().toString(), true);
				ResourceSet rs = resourceSetProvider.get(project);
				Resource r = rs.getResource(uri, true);
				generator.doGenerate(r, fsa);
				
			}
		}
		return null;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}

}

Give it a try
We start a runtime app, create a bunch of model files that refernce each other,
try the genenerator – and hey – it works.

And how if i want to call the generator from the context menu of the open editor

I register the command for the editor and write a Handler like this one

<extension point="org.eclipse.ui.menus">
    <menuContribution locationURI="popup:#TextEditorContext?after=additions">
        <command
            commandId="org.xtext.example.mydsl.ui.handler.GenerationCommand"
            style="push">
            <visibleWhen
	                  checkEnabled="false">
	               <reference
	                     definitionId="org.xtext.example.mydsl.MyDsl.Editor.opened">
	               </reference>
	            </visibleWhen>
        </command>
    </menuContribution>
</extension>
public class GenerationHandler extends AbstractHandler implements IHandler {
	
	@Inject
	private IGenerator generator;

	@Inject
	private Provider<EclipseResourceFileSystemAccess> fileAccessProvider;
	
	@Inject
	IResourceDescriptions resourceDescriptions;
	
	@Inject
	IResourceSetProvider resourceSetProvider;
	
	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {
		
		IEditorPart activeEditor = HandlerUtil.getActiveEditor(event);
		IFile file = (IFile) activeEditor.getEditorInput().getAdapter(IFile.class);
		if (file != null) {
			IProject project = file.getProject();
			IFolder srcGenFolder = project.getFolder("src-gen");
			if (!srcGenFolder.exists()) {
				try {
					srcGenFolder.create(true, true,
							new NullProgressMonitor());
				} catch (CoreException e) {
					return null;
				}
			}
	
			final EclipseResourceFileSystemAccess fsa = fileAccessProvider.get();
			fsa.setOutputPath(srcGenFolder.getFullPath().toString());
			
			
			if (activeEditor instanceof XtextEditor) {
				((XtextEditor)activeEditor).getDocument().readOnly(new IUnitOfWork<Boolean, XtextResource>() {
				
					@Override
					public Boolean exec(XtextResource state)
							throws Exception {
						generator.doGenerate(state, fsa);
						return Boolean.TRUE;
					}
				});
				
			}
		}
		return null;
	}

	@Override
	public boolean isEnabled() {
		return true;
	}

}

Xtext Content Assist Auto Activation

September 19, 2011 16 comments

Xtext offers nice Content Assist facilities. JDT offers a nice additional feature: Content assist is autoactivated if a certain character (.) is typed. To activate this feature in Xtext simply customize your UiModule

	
public class MyDslUiModule extends org.xtext.example.mydsl.ui.AbstractMyDslUiModule {
public MyDslUiModule(AbstractUIPlugin plugin) {
		super(plugin);
	}
	
	@Override
	public void configure(Binder binder) {
		super.configure(binder);
	binder.bind(String.class)
.annotatedWith(com.google.inject.name.Names.named(
(XtextContentAssistProcessor.COMPLETION_AUTO_ACTIVATION_CHARS)))
.toInstance(".,:");
	}
}

In this case content assist is autoactivated on . , and :

Xtend2 Code Generators with Non-Xtext Models

In this blog post i want to show a simple example of how to use Xtend2 to generate code from Non-Xtext but EMF-based model.

Having a simple EMF Model i’ve created the genmodel + Model + Edit + Editor code.
Using the Editor i’ve created a bunch of .sample files and now want to
generate code using Xtend2.

Xtend comes with an IGenerator interface that i implement in my SampleGenerator Xtend file

package sample

import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.IGenerator
import org.eclipse.xtext.generator.IFileSystemAccess
import org.eclipse.emf.ecore.EObject

class SampleGenerator implements IGenerator {

	override void doGenerate(Resource resource, IFileSystemAccess fsa) {
		for (EObject o : resource.contents) {
			o.compile(fsa)
		}
	}

	def dispatch void compile(Model m, IFileSystemAccess fsa) {
		for (e : m.elements) {
			e.compile(fsa)
		}
	}

	def compile(Element e, IFileSystemAccess fsa) {
		fsa.generateFile(e.name+".txt", '''
		this is element «e.name»
		''')
	}

	def dispatch void compile(EObject m, IFileSystemAccess fsa) { }

}

The last step we need is a workflow that reads the model files and invokes the generator

First we need to create some java classes that exposes our .sample to the reader
(resourceseriveprovider) and
do some Guice Binding Stuff (Generator / ResourceSet ….)

package sample;

import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.xtext.generator.IGenerator;
import org.eclipse.xtext.resource.generic.AbstractGenericResourceRuntimeModule;

public class SampleGeneratorModule extends AbstractGenericResourceRuntimeModule {

	@Override
	protected String getLanguageName() {
		return "sample.presentation.SampleEditorID";
	}

	@Override
	protected String getFileExtensions() {
		return "sample";
	}

	public Class<? extends IGenerator> bindIGenerator() {
		return SampleGenerator.class;
	}

	public Class<? extends ResourceSet> bindResourceSet() {
		return ResourceSetImpl.class;
	}

}

 

package sample;

import org.eclipse.xtext.ISetup;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class SampleGeneratorSetup implements ISetup {

	@Override
	public Injector createInjectorAndDoEMFRegistration() {
		return Guice.createInjector(new SampleGeneratorModule());
	}

}

 

package sample;

import org.eclipse.xtext.resource.generic.AbstractGenericResourceSupport;

import com.google.inject.Module;

public class SampleGeneratorSupport extends AbstractGenericResourceSupport {

	@Override
	protected Module createGuiceModule() {
		return new SampleGeneratorModule();
	}

}

finally we wire this together in the workflow file

module sample.SampleGenerator

import org.eclipse.emf.mwe.utils.*

var targetDir = "src-gen"
var modelPath = "model"

Workflow {

	bean = StandaloneSetup {
		registerGeneratedEPackage = "sample.SamplePackage"
	}

	component = DirectoryCleaner {
		directory = targetDir
	}

	component = sample.SampleGeneratorSupport {}

	component = org.eclipse.xtext.mwe.Reader {
		path = modelPath
		register = sample.SampleGeneratorSetup {}
		loadResource = {
			slot = "model"
		}
	}

	component = org.eclipse.xtext.generator.GeneratorComponent {
		register = sample.SampleGeneratorSetup {}
		slot = 'model'
		outlet = {
			path = targetDir
		}
	}
}

running the workflow we get nice files generated

Categories: Xtext Tags: , ,

Customizing Xtext Metamodel Inference using Xtend2

Xtext has nice metamodel inference capabilities. But sometimes you have to do some customizations to the generated ecore metamodel, e.g. adding a derived operation. You have basically two options: (1) move to a manually maintained metamodel (2) use Customized Post Processing as described here http://www.eclipse.org/Xtext/documentation/2_0_0/020-grammar-language.php#customPostProcessing

The second possibility uses good old Xpand/Xtend1 extensions to do the postprocessing. But what if i want to use Xtend2 for that? A very simple solution i’d like to show in this post.

So lets start with the gramar

grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals

generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"

Model:
	persons+=Person*
;

Person:
	"person" firstname=STRING lastname=STRING
;

We now want to add a getFullname Operation to our person.
Xtext offers the Interface IXtext2EcorePostProcessor for the postprocessing.
So we write a Xtend2 class for that

package org.xtext.example.mydsl

import org.eclipse.xtext.xtext.ecoreInference.IXtext2EcorePostProcessor
import org.eclipse.xtext.GeneratedMetamodel
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.ecore.EClassifier
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EcoreFactory
import org.eclipse.emf.ecore.EcorePackage
import org.eclipse.emf.ecore.EcorePackage.Literals
import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage
import org.eclipse.emf.common.util.BasicEMap$Entry
import org.eclipse.emf.ecore.impl.EStringToStringMapEntryImpl

class MyXtext2EcorePostProcessor implements IXtext2EcorePostProcessor {
	
 	override void process(GeneratedMetamodel metamodel) {
		metamodel.EPackage.process
	}
	
	def process(EPackage p) {
		for (c : p.EClassifiers.filter(typeof(EClass))) {
			if (c.name == "Person") {
				c.handle
			}
		}
	}
	
	def handle (EClass c) {
		val op = EcoreFactory::eINSTANCE.createEOperation
		op.name = "getFullName"
		op.EType = EcorePackage::eINSTANCE.EString
		val body = EcoreFactory::eINSTANCE.createEAnnotation
		body.source = GenModelPackage::eNS_URI
		val map = EcoreFactory::eINSTANCE.create(EcorePackage::eINSTANCE.getEStringToStringMapEntry()) as BasicEMap$Entry<String,String>
		map.key = "body"
		map.value = "return getFirstname() + \" \" + getLastname();"
		body.details.add(map)
		op.EAnnotations += body
		c.EOperations += op
	}
	
}

The last Problem left is how to make the Generator use this class.
Xtext does not offer a explicit place to change the IXtext2EcorePostProcessor.
Its default Implementation is bound in the XtextRuntimeModule,
that is instantiated in the org.eclipse.xtext.generator.Generator
class. so we have to subclass the Generator to get it changed

package org.xtext.example.mydsl;

import org.eclipse.xtext.XtextRuntimeModule;
import org.eclipse.xtext.XtextStandaloneSetup;
import org.eclipse.xtext.generator.Generator;
import org.eclipse.xtext.xtext.ecoreInference.IXtext2EcorePostProcessor;

import com.google.inject.Guice;
import com.google.inject.Injector;

@SuppressWarnings("restriction")
public class ExtendedGenerator extends Generator {
	
	public ExtendedGenerator() {
		new XtextStandaloneSetup() {
			@Override
			public Injector createInjector() {
				return Guice.createInjector(new XtextRuntimeModule() {
					@Override
					public Class<? extends IXtext2EcorePostProcessor> bindIXtext2EcorePostProcessor() {
						return MyXtext2EcorePostProcessor.class;
					}
				});
			}
		}.createInjectorAndDoEMFRegistration();
	}

}

finally we use the in the Generator Workflow

Workflow {
    bean = StandaloneSetup {
        scanClassPath = true
        platformUri = "${runtimeProject}/.."
    }

    component = DirectoryCleaner {
        directory = "${runtimeProject}/src-gen"
    }

    component = DirectoryCleaner {
        directory = "${runtimeProject}.ui/src-gen"
    }

    component = ExtendedGenerator {
        pathRtProject = runtimeProject
        pathUiProject = "${runtimeProject}.ui"
        pathTestProject = "${runtimeProject}.tests"
        projectNameRt = projectName
        projectNameUi = "${projectName}.ui"
        language = {
....

as a result our person has the getFullname Operation

public interface Person extends EObject
{

  String getFirstname();


  void setFirstname(String value);


  String getLastname();


  void setLastname(String value);


  String getFullName();

} // Person
public class PersonImpl extends MinimalEObjectImpl.Container implements Person
{
 
  /**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  public String getFullName()
  {
    return getFirstname() + " " + getLastname();
  }



} //PersonImpl