blob: df8ec91df6a427717bf2032f621c335ef675868f [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*
*
* This copyright notice shows up in the generated Java code
*
*/
package org.eclipse.osbp.xtext.menu.jvmmodel
import com.vaadin.ui.Accordion
import com.vaadin.ui.Component
import com.vaadin.ui.TabSheet
import com.vaadin.ui.VerticalLayout
import com.vaadin.ui.Window
import java.util.HashMap
import java.util.Locale
import javax.annotation.PostConstruct
import javax.annotation.PreDestroy
import javax.inject.Inject
import javax.inject.Named
import org.eclipse.e4.core.contexts.IContextFunction
import org.eclipse.e4.core.contexts.IEclipseContext
import org.eclipse.e4.core.di.annotations.Optional
import org.eclipse.e4.core.services.events.IEventBroker
import org.eclipse.emf.common.util.EList
import org.eclipse.osbp.bpm.api.IBPMEngine
import org.eclipse.osbp.bpm.api.IBlipBPMFunctionProvider
import org.eclipse.osbp.ui.api.contextfunction.IUserMenuProvider
import org.eclipse.osbp.ui.api.contextfunction.IVaadinDialogProvider
import org.eclipse.osbp.ui.api.contextfunction.IViewEmbeddedProvider
import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService
import org.eclipse.osbp.ui.api.themes.IThemeResourceService
import org.eclipse.osbp.ui.api.user.IUser
import org.eclipse.osbp.ui.api.useraccess.IUserAccessService
import org.eclipse.osbp.utils.annotation.CommonUtils
import org.eclipse.osbp.utils.constants.GeneratorConstants
import org.eclipse.osbp.utils.vaadin.YesNoDialog
import org.eclipse.osbp.xtext.menu.MenuEntry
import org.eclipse.osbp.xtext.menu.MenuPackage
import org.eclipse.osbp.xtext.menu.common.UserMenuItem
import org.eclipse.xtext.Constants
import org.eclipse.xtext.common.types.JvmDeclaredType
import org.eclipse.xtext.common.types.JvmField
import org.eclipse.xtext.common.types.JvmGenericType
import org.eclipse.xtext.common.types.JvmOperation
import org.eclipse.xtext.common.types.JvmTypeReference
import org.eclipse.xtext.common.types.JvmVisibility
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.osgi.service.event.EventHandler
import org.slf4j.Logger
class MenuDslJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
@Inject extension IQualifiedNameProvider
@Inject
@Named(Constants.FILE_EXTENSIONS) String fileExtension
@Inject extension CommonUtils
var String binderClassName = ""
var String contextClassName = ""
var String clsName = ""
def dispatch void infer(MenuPackage pkg, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
clsName = pkg.eResource.URI.lastSegment.replace("."+fileExtension,"")+fileExtension.toFirstUpper
val cls = pkg.toClass(pkg.name+"."+clsName)
cls.superTypes.add(_typeReferenceBuilder.typeRef(IUserMenuProvider))
cls.superTypes.add(_typeReferenceBuilder.typeRef(IUser.UserLocaleListener))
acceptor.accept(cls,
[
documentation = GeneratorConstants.GENERATED_CLASSES_DOCUMENTATION
it.toFields(pkg)
it.toGetterSetter(pkg)
it.toInjectedFields(pkg)
it.toConstructor(pkg)
it.toOperations(pkg)
])
// the context function class
contextClassName = pkg.name+"."+clsName+"ContextFunction"
val contextCls = pkg.toClass(contextClassName)
contextCls.getSuperTypes().add(_typeReferenceBuilder.typeRef(IContextFunction))
contextCls.simpleName = contextCls.simpleName.toFirstUpper
acceptor.accept(contextCls,
[
var annotationRef = _annotationTypesBuilder.annotationRef(typeof(org.osgi.service.component.annotations.Component))
annotationRef.addAnnAttr(pkg, "service", _typeReferenceBuilder.typeRef(IContextFunction) as JvmTypeReference)
var propContent = '''«IContextFunction.SERVICE_CONTEXT_KEY»=Menu''' //«MenuModelGenerator.clsName»
annotationRef.addAnnAttr(pkg, "property", propContent)
annotations += annotationRef
packageName = pkg.fullyQualifiedName.toString
documentation = GeneratorConstants.GENERATED_CLASSES_DOCUMENTATION
it.toContextOperations(pkg)
])
}
def void toContextOperations(JvmGenericType type, MenuPackage pkg) {
// compute override
type.members += pkg.toMethod("compute", _typeReferenceBuilder.typeRef(Object), [
annotations += _annotationTypesBuilder.annotationRef(Override)
visibility = JvmVisibility.PUBLIC
parameters += pkg.toParameter("context", _typeReferenceBuilder.typeRef(IEclipseContext))
parameters += pkg.toParameter("contextKey", _typeReferenceBuilder.typeRef(String))
body = [append(
'''
MApplication application = context.get(MApplication.class);
IEclipseContext appCtx = application.getContext();
IUserMenuProvider provider = ContextInjectionFactory.make(«clsName».class, appCtx);
appCtx.set(IUserMenuProvider.class, provider);
return provider;''')]
])
}
def void toInjectedFields(JvmDeclaredType type, MenuPackage pkg) {
for(entry:pkg.entries) {
entry.iterateFieldEntry(type, pkg)
}
}
def void iterateFieldEntry(MenuEntry entry, JvmDeclaredType type, MenuPackage pkg) {
var JvmField field = null
if(entry.hasTable || entry.hasDialog) {
var name = ""
var list = <String>newArrayList
if (entry.hasTable) {
name = entry.table.name
} else if(entry.hasDialog) {
name = entry.dialog.name
}
if ((name != null) && !name.isEmpty) {
list.add(name.toFirstUpper)
val annotationRef = _annotationTypesBuilder.annotationRef(Named)
annotationRef.addStringValuesToAnnotation(list)
field = pkg.toField(name.toFirstLower, _typeReferenceBuilder.typeRef(IViewEmbeddedProvider)) [annotations += _annotationTypesBuilder.annotationRef(Inject) annotations += annotationRef]
if (field != null) {
field.visibility = JvmVisibility::PRIVATE
type.members += field
}
}
}
if(entry.subentries != null && entry.subentries.size > 0) {
for(e:entry.subentries) {
e.iterateFieldEntry(type, pkg)
}
}
}
def void toConstructor(JvmDeclaredType type, MenuPackage pkg) {
type.members += pkg.toConstructor([
annotations += _annotationTypesBuilder.annotationRef(Inject)
body = [ append('''''')]
])
}
def void toFields(JvmGenericType type, MenuPackage pkg) {
var JvmField field = null
// create logger
field = pkg.toField("log", _typeReferenceBuilder.typeRef(Logger))[setInitializer([append('''org.slf4j.LoggerFactory.getLogger("menu")''')])]
field.static = true
field.visibility = JvmVisibility::PRIVATE
type.members += field
field = pkg.toField("themeResourceService", _typeReferenceBuilder.typeRef(IThemeResourceService)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = pkg.toField("userAccessService", _typeReferenceBuilder.typeRef(IUserAccessService)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = pkg.toField("dslMetadataService", _typeReferenceBuilder.typeRef(IDSLMetadataService)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = pkg.toField("user", _typeReferenceBuilder.typeRef(IUser)) [annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = pkg.toField("locale", _typeReferenceBuilder.typeRef(Locale))
type.members += field
field = pkg.toField("bpmEngine", _typeReferenceBuilder.typeRef(IBPMEngine)) [
annotations += _annotationTypesBuilder.annotationRef(Inject)
annotations += _annotationTypesBuilder.annotationRef(Optional)
]
type.members += field
field = pkg.toField("selectedMap", _typeReferenceBuilder.typeRef(HashMap, _typeReferenceBuilder.typeRef(Accordion), _typeReferenceBuilder.typeRef(Component)))
type.members += field
field = pkg.toField("menuComponent", _typeReferenceBuilder.typeRef(VerticalLayout))
type.members += field
field = pkg.toField("yesnoDialog", _typeReferenceBuilder.typeRef(YesNoDialog))
type.members += field
field = pkg.toField("dispatchYesNo", _typeReferenceBuilder.typeRef(EventHandler))
type.members += field
field = pkg.toField("startProcessText", _typeReferenceBuilder.typeRef(String))
type.members += field
field = pkg.toField("answerYesText", _typeReferenceBuilder.typeRef(String))
type.members += field
field = pkg.toField("answerNoText", _typeReferenceBuilder.typeRef(String))
type.members += field
field = pkg.toField("blipSuccessful", _typeReferenceBuilder.typeRef(boolean))
type.members += field
field = pkg.toField("yesnoWindow", _typeReferenceBuilder.typeRef(Window))
type.members += field
field = pkg.toField("eventBroker", _typeReferenceBuilder.typeRef(IEventBroker))[
annotations += _annotationTypesBuilder.annotationRef(Inject)]
type.members += field
field = pkg.toField("tabs",
_typeReferenceBuilder.typeRef(HashMap, _typeReferenceBuilder.typeRef(TabSheet.Tab), _typeReferenceBuilder.typeRef(Pair, _typeReferenceBuilder.typeRef(String), _typeReferenceBuilder.typeRef(String))))
type.members += field
field = pkg.toField("items",
_typeReferenceBuilder.typeRef(HashMap, _typeReferenceBuilder.typeRef(UserMenuItem), _typeReferenceBuilder.typeRef(String)))
type.members += field
// inject blip context function
var list = <String>newArrayList
list.add("Blip")
val annotationRef = _annotationTypesBuilder.annotationRef(Named)
annotationRef.addStringValuesToAnnotation(list)
field = pkg.toField("blip", _typeReferenceBuilder.typeRef(IBlipBPMFunctionProvider)) [annotations += _annotationTypesBuilder.annotationRef(Inject) annotations += annotationRef]
field.visibility = JvmVisibility::PRIVATE
type.members += field
// inject UserFilter context function
list.clear
list.add("UserFilter")
val annotationRef2 = _annotationTypesBuilder.annotationRef(Named)
annotationRef2.addStringValuesToAnnotation(list)
field = pkg.toField("userFilter", _typeReferenceBuilder.typeRef(IVaadinDialogProvider)) [annotations += _annotationTypesBuilder.annotationRef(Inject) annotations += annotationRef2]
field.visibility = JvmVisibility::PRIVATE
type.members += field
}
/**
* <p>build the getters and setters from class variables.</p>
*
*/
def void toGetterSetter(JvmDeclaredType type, MenuPackage pkg) {
var JvmOperation operation = null
operation = pkg.toGetter("startProcessText", _typeReferenceBuilder.typeRef(String))
operation.visibility = JvmVisibility::PUBLIC
type.members += operation
operation = pkg.toSetter("startProcessText", _typeReferenceBuilder.typeRef(String))
operation.visibility = JvmVisibility::PUBLIC
type.members += operation
operation = pkg.toGetter("answerYesText", _typeReferenceBuilder.typeRef(String))
operation.visibility = JvmVisibility::PUBLIC
type.members += operation
operation = pkg.toSetter("answerYesText", _typeReferenceBuilder.typeRef(String))
operation.visibility = JvmVisibility::PUBLIC
type.members += operation
operation = pkg.toGetter("answerNoText", _typeReferenceBuilder.typeRef(String))
operation.visibility = JvmVisibility::PUBLIC
type.members += operation
operation = pkg.toSetter("answerNoText", _typeReferenceBuilder.typeRef(String))
operation.visibility = JvmVisibility::PUBLIC
type.members += operation
}
def void toOperations(JvmDeclaredType type, MenuPackage pkg) {
// init
type.members += pkg.toMethod("init", _typeReferenceBuilder.typeRef(Void::TYPE), [
annotations += _annotationTypesBuilder.annotationRef(PostConstruct)
body = [ append('''«pkg.init»''')]
])
// create menu
type.members += pkg.toMethod("createMenu", _typeReferenceBuilder.typeRef(VerticalLayout), [
body = [ append('''«pkg.createMenu»''')]
])
// setIcon
type.members += pkg.toMethod("setIcon", _typeReferenceBuilder.typeRef(Void::TYPE), [
parameters += pkg.toParameter("accordion", _typeReferenceBuilder.typeRef(Accordion))
parameters += pkg.toParameter("open", _typeReferenceBuilder.typeRef(boolean))
body = [ append('''«pkg.setIcon»''')]
])
// create menu
type.members += pkg.toMethod("getMenu", _typeReferenceBuilder.typeRef(VerticalLayout), [
annotations += _annotationTypesBuilder.annotationRef(Override)
body = [ append('''«pkg.getMenu»''')]
])
// unsubscribe from eventBroker
type.members += pkg.toMethod("unsubscribe", _typeReferenceBuilder.typeRef(Void::TYPE),
[
visibility = JvmVisibility.PROTECTED
annotations += _annotationTypesBuilder.annotationRef(PreDestroy)
body = [append('''«pkg.unsubscribe»''')]
])
// locale notification
type.members += pkg.toMethod("localeChanged", _typeReferenceBuilder.typeRef(Void::TYPE),
[
visibility = JvmVisibility.PUBLIC
annotations += _annotationTypesBuilder.annotationRef(Override)
parameters += pkg.toParameter("locale", _typeReferenceBuilder.typeRef(Locale))
body = [append('''«pkg.localeChanged»''')]
])
}
def init(MenuPackage pkg)
'''
tabs = new HashMap<>();
items = new HashMap<>();
user.addUserLocaleListener(this);
blipSuccessful = blip.initBlip();
if(!blipSuccessful) {
Notification.show(org.apache.commons.lang.StringUtils.join(blip.getErrors().toArray(), "\n"), Notification.TYPE_ERROR_MESSAGE);
}
yesnoDialog = new YesNoDialog();
startProcessText = "Start process?";
answerYesText = "yes";
answerNoText = "no";
dispatchYesNo = «pkg.dispatchYesNo»
eventBroker.subscribe(EventBrokerMsg.QUERY_ANSWER, dispatchYesNo);
menuComponent = createMenu();
localeChanged(locale);'''
def unsubscribe(MenuPackage pkg)
'''
eventBroker.unsubscribe(dispatchYesNo);
'''
def getMenu(MenuPackage pkg)
'''
return menuComponent;
'''
def createMenu(MenuPackage pkg)
'''
selectedMap = new HashMap<Accordion,Component>();
VerticalLayout tabRoot = new VerticalLayout();
«pkg.entries.iterateEntries("Root", 0, pkg)»
return tabRoot;
'''
def iterateEntries(EList <MenuEntry> entries, String parentName, int depth, MenuPackage pkg)
'''
// start depth: «depth»
«IF pkg.subMenuIsAccordion || depth == 0»
Accordion acc«parentName» = new Accordion();
acc«parentName».setWidth(«IF pkg.width != null»"«pkg.width»"«ELSE»"400px"«ENDIF»);
acc«parentName».addStyleName("os-accordion-level«depth»");
«selectedHandler('''acc«parentName»''')»
tab«parentName».addComponent(acc«parentName»);
«FOR entry:entries»
«IF entry.hasUserFilter»if (userAccessService.isSuperuser()) {«ENDIF»
VerticalLayout tab«entry.entryid.toFirstUpper» = new VerticalLayout();
«IF entry.hasTable»
«entry.table.name.toFirstLower».createView(tab«entry.entryid.toFirstUpper»);
tab«entry.entryid.toFirstUpper».setData(«entry.table.name.toFirstLower»);
«ELSEIF entry.hasDialog»
«entry.dialog.name.toFirstLower».createView(tab«entry.entryid.toFirstUpper»);
tab«entry.entryid.toFirstUpper».setData(«entry.dialog.name.toFirstLower»);
«ELSEIF entry.hasUserFilter»
userFilter.createView(tab«entry.entryid.toFirstUpper»);
«ELSE»
«entry.iterateTabs(depth, pkg, null)»
«ENDIF»
acc«parentName».addTab(tab«entry.entryid.toFirstUpper», "«entry.entryid»", themeResourceService.getThemeResource("«pkg.collapsedImage»", ThemeResourceType.ICON));
tabs.put(acc«parentName».getTab(tab«entry.entryid.toFirstUpper»), new Pair("«entry.entryid»","«entry.descriptionValue»"));
«IF entry.hasUserFilter»}«ENDIF»
«ENDFOR»
selectedMap.put(acc«parentName», acc«parentName».getSelectedTab());
«ELSE»
«IF depth == 1»
Tree tree«parentName» = new Tree();
tree«parentName».setWidth("300px");
tree«parentName».addStyleName("os-menutree-level«depth»");
tree«parentName».setImmediate(true);
«valueChangeHandler('''tree«parentName»''')»
tab«parentName».addComponent(tree«parentName»);
«ENDIF»
«FOR entry:entries»
«IF entry.hasTable»
«binderClassName».get«entry.table.name.toFirstUpper»().createView(tab«entry.entryid.toFirstUpper»);
«ELSEIF entry.hasDialog»
«binderClassName».get«entry.dialog.name.toFirstUpper»().createView(tab«entry.entryid.toFirstUpper»);
«ELSEIF entry.hasUserFilter»
«binderClassName».getUserFilter().createView(tab«entry.entryid.toFirstUpper»);
«ELSE»
«entry.iterateMenu(null, depth, pkg, '''tree«parentName»''')»
«ENDIF»
«ENDFOR»
«ENDIF»
// end depth: «depth»
'''
def iterateMenu(MenuEntry entry, MenuEntry root, int depth, MenuPackage pkg, String treeName)
'''
«IF entry.hasProcess»
if (bpmEngine != null && userAccessService.isPermitted(Permission.forBlipProcess("«entry.process.name»", Action.startable)).isPermitted()) {
UserMenuItem «treeName»«entry.entryid» = new UserMenuItem("«entry.entryid»", "«entry.process.name»", UserMenuItemType.PROCESS);
items.put(«treeName»«entry.entryid», "«entry.entryid»");
«treeName».addItem(«treeName»«entry.entryid»);
«IF depth > 1 && root != null»
«treeName».setParent(«treeName»«entry.entryid», «treeName»«root.entryid»);
«ENDIF»
«IF entry.subentries != null && entry.subentries.size > 0»
«treeName».setChildrenAllowed(«treeName»«entry.entryid», true);
«ELSE»
«treeName».setChildrenAllowed(«treeName»«entry.entryid», false);
«ENDIF»
}
«ELSEIF entry.hasUserFilter»
// userFilter
if (userAccessService.isSuperuser()) {
UserMenuItem «treeName»«entry.entryid» = new UserMenuItem(«treeName»«entry.entryid» , "", UserMenuItemType.USERFILTER);
items.put(«treeName»«entry.entryid», "«treeName»«entry.entryid»");
treeTasks.addItem(«treeName»«entry.entryid»);
treeTasks.setParent(«treeName»«entry.entryid», «treeName»«root.entryid»);
treeTasks.setChildrenAllowed(«treeName»«entry.entryid», false);
}
«ELSE»
UserMenuItem «treeName»«entry.entryid» = new UserMenuItem("«entry.entryid»", «IF entry.hasProcess»"«entry.process.name»"«ELSEIF entry.hasPerspective»"«entry.perspective.fullyQualifiedName»"«ELSE»""«ENDIF», UserMenuItemType.«IF entry.hasProcess»PROCESS«ELSEIF entry.hasPerspective»PERSPECTIVE«ELSE»NONE«ENDIF»);
items.put(«treeName»«entry.entryid», "«entry.entryid»");
«treeName».addItem(«treeName»«entry.entryid»);
«IF depth > 1 && root != null»
«treeName».setParent(«treeName»«entry.entryid», «treeName»«root.entryid»);
«ENDIF»
«IF entry.subentries != null && entry.subentries.size > 0»
«treeName».setChildrenAllowed(«treeName»«entry.entryid», true);
«ELSE»
«treeName».setChildrenAllowed(«treeName»«entry.entryid», false);
«ENDIF»
«ENDIF»
«FOR e:entry.subentries»
«e.iterateMenu(entry, depth+1, pkg, treeName)»
«ENDFOR»
'''
def iterateTabs(MenuEntry root, int depth, MenuPackage pkg, String treeName)
'''
«FOR entry:root.subentries»
«IF entry.subentries != null && entry.subentries.size != 0 && (depth == 0 || pkg.subMenuIsAccordion)»
«entry.subentries.iterateEntries(root.entryid.toFirstUpper, depth+1, pkg)»
«ENDIF»
«ENDFOR»
'''
def setIcon(MenuPackage pkg) {
'''
Tab tab = accordion.getTab(selectedMap.get(accordion));
if (tab != null) {
if(open) {
tab.setIcon(themeResourceService.getThemeResource("«pkg.expandedImage»", ThemeResourceType.ICON));
} else {
tab.setIcon(themeResourceService.getThemeResource("«pkg.collapsedImage»", ThemeResourceType.ICON));
}
}
'''
}
def selectedHandler(String accName) {
'''
«accName».addSelectedTabChangeListener(new SelectedTabChangeListener() {
@Override
public void selectedTabChange(SelectedTabChangeEvent event) {
setIcon(«accName», false);
selectedMap.put(«accName», «accName».getSelectedTab());
setIcon(«accName», true);
Component component = accRoot.getSelectedTab();
if(component instanceof AbstractOrderedLayout) {
if(((AbstractOrderedLayout)component).getData() instanceof IViewEmbeddedProvider) {
IViewEmbeddedProvider view = ((IViewEmbeddedProvider)((AbstractOrderedLayout)component).getData());
view.createComponents();
eventBroker.send(EventBrokerMsg.AUTOWIRED_ENTITY_PREFIX+userAccessService.getUser().getClass().getSimpleName().replace("Dto", ""), userAccessService.getUser());
}
}
}
});
'''
}
def valueChangeHandler(String entryName) {
'''
«entryName».addValueChangeListener(new ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
if («entryName».getValue() != null) {
UserMenuItem item = (UserMenuItem)«entryName».getValue();
if(item.getType() == UserMenuItemType.PROCESS) {
log.debug("start process "+item.getId());
yesnoWindow = yesnoDialog.init(eventBroker, item, startProcessText, answerYesText, answerNoText);
UI.getCurrent().addWindow(yesnoWindow);
}
else if(item.getType() == UserMenuItemType.PERSPECTIVE) {
log.debug("start perspective "+item.getId());
eventBroker.send(EventBrokerMsg.MENU_CLOSE, null);
EventBrokerMsg msg = new EventBrokerMsg(0, item.getCallId());
eventBroker.send(EventBrokerMsg.OPEN_PERSPECTIVE, msg);
}
}
}
});
'''
}
def dispatchYesNo(MenuPackage pkg) {
'''
new EventHandler() {
@Override
public void handleEvent(Event event) {
if(yesnoWindow != null) {
yesnoWindow.close();
UI.getCurrent().removeWindow(yesnoWindow);
yesnoWindow = null;
}
EventBrokerMsg msg = (EventBrokerMsg)event.getProperty(EventUtils.DATA);
UserMenuItem item = (UserMenuItem)msg.getObject1();
if(item.getType() == UserMenuItemType.PROCESS && EventBrokerMsg.QUERY_YES.equals(msg.getId())) {
log.debug("start process "+item.getId());
eventBroker.send(EventBrokerMsg.MENU_CLOSE, null);
blip.startProcess(item.getCallId());
} else {
eventBroker.send(EventBrokerMsg.MENU_CLOSE, null);
}
}
};
'''
}
def localeChanged(MenuPackage pkg)
'''
this.locale = locale;
for(TabSheet.Tab tab: tabs.keySet()) {
tab.setCaption(dslMetadataService.translate(locale.toLanguageTag(),tabs.get(tab).getKey()));
tab.setDescription(dslMetadataService.translate(locale.toLanguageTag(),tabs.get(tab).getValue()));
}
for(UserMenuItem item: items.keySet()) {
item.setI18nId(dslMetadataService.translate(locale.toLanguageTag(),items.get(item)));
}
startProcessText = dslMetadataService.translate(locale.toLanguageTag(),"Start process?");
answerYesText = dslMetadataService.translate(locale.toLanguageTag(),"yes");
answerNoText = dslMetadataService.translate(locale.toLanguageTag(),"no");
'''
}