Use Engine to evaluate Completion Templates

- Use a more flexible API to declare variables in template descriptors
- Use Velocity template engine internally. This provides e.g. if blocks
- Create inner type getters for Composites, FormExtensions and
  CompositeExtensions too
diff --git a/org.eclipse.scout.sdk.core.test/src/test/java/org/eclipse/scout/sdk/core/generator/transformer/WorkingCopyTransformerTest.java b/org.eclipse.scout.sdk.core.test/src/test/java/org/eclipse/scout/sdk/core/generator/transformer/WorkingCopyTransformerTest.java
index 4d1e468..9112ba6 100644
--- a/org.eclipse.scout.sdk.core.test/src/test/java/org/eclipse/scout/sdk/core/generator/transformer/WorkingCopyTransformerTest.java
+++ b/org.eclipse.scout.sdk.core.test/src/test/java/org/eclipse/scout/sdk/core/generator/transformer/WorkingCopyTransformerTest.java
@@ -270,11 +270,12 @@
   /**
    * If changes in this method are necessary also update the corresponding example on {@link IWorkingCopyTransformer}
    * class javadoc.
-   * 
+   *
    * @param env
    *          The environment passed
    */
   @Test
+  @SuppressWarnings("unused")
   public void testTransformerDocumentation(IJavaEnvironment env) {
     IWorkingCopyTransformer transformer = new DefaultWorkingCopyTransformer() {
       @Override
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateDescriptor.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateDescriptor.kt
index 2d8a33e..992330b 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateDescriptor.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateDescriptor.kt
@@ -10,39 +10,61 @@
  */
 package org.eclipse.scout.sdk.s2i.template
 
+import com.intellij.codeInsight.template.impl.TemplateImpl
+import org.eclipse.scout.sdk.core.s.IScoutRuntimeTypes
+import org.eclipse.scout.sdk.core.util.Ensure
 import org.eclipse.scout.sdk.core.util.Ensure.newFail
 import org.eclipse.scout.sdk.core.util.FinalValue
 import org.eclipse.scout.sdk.core.util.Strings
+import org.eclipse.scout.sdk.core.util.Strings.toStringLiteral
 import org.eclipse.scout.sdk.s2i.EclipseScoutBundle.Companion.message
+import org.eclipse.scout.sdk.s2i.nls.NlsKeysEnumMacro
+import org.eclipse.scout.sdk.s2i.template.variable.AbstractClassesEnumVariableAdapter
+import org.eclipse.scout.sdk.s2i.template.variable.BoolVariableAdapter
+import org.eclipse.scout.sdk.s2i.template.variable.EnumVariableAdapter
+import org.eclipse.scout.sdk.s2i.template.variable.VariableDescriptor
 
 class TemplateDescriptor(val id: String, private val resourceLoader: ClassLoader = TemplateDescriptor::class.java.classLoader) {
 
     companion object {
         const val VARIABLE_NAME = "name"
-        const val VARIABLE_PREFIX_NLS = "nls"
-        const val VARIABLE_PREFIX_BOOL = "bool"
-        const val PREDEFINED_VARIABLE_KEYSTROKES = "keystrokes"
+
         const val PREDEFINED_VARIABLE_SUPER = "super"
+        const val PREDEFINED_VARIABLE_MENU_TYPES = "menuTypes"
+        const val PREDEFINED_VARIABLE_KEYSTROKES = "keystrokes"
         const val PREDEFINED_VARIABLE_COMPLETE = "complete"
-        const val PREDEFINED_VARIABLE_CONFIGURED_MENU_TYPES = "getConfiguredMenuTypes"
-        const val PREDEFINED_VARIABLE_STATIC_IN_EXTENSION = "staticIfInExtension"
+
+        const val PREDEFINED_CONSTANT_IN_EXTENSION = "inExtension"
+        const val PREDEFINED_CONSTANT_MENU_SUPPORTED = "menuSupported"
     }
 
     private var m_name: String? = null
     private var m_description: String? = null
     private var m_superClassInfo: SuperClassInfo? = null
     private var m_orderDefinitionType: String? = null
-    private var m_innerTypeGetterContainer: InnerTypeGetterInfo? = null
     private val m_source = FinalValue<String>()
-    private val m_variables = HashMap<String, VariableDescriptor>()
+    private var m_innerTypeGetterInfos = LinkedHashSet<InnerTypeGetterInfo>()
+    private val m_variables = HashMap<String, (TemplateEngine) -> VariableDescriptor?>()
     private val m_alias = HashSet<String>()
 
+    init {
+        // append predefined variables
+        withVariable(PREDEFINED_VARIABLE_MENU_TYPES, EnumVariableAdapter(PREDEFINED_VARIABLE_MENU_TYPES, PsiExpressionEnumMacro.NAME) {
+            it.menuTypes.map { candidate -> "${IScoutRuntimeTypes.CollectionUtility}.hashSet($candidate)" }
+        })
+        withVariable(PREDEFINED_VARIABLE_KEYSTROKES, EnumVariableAdapter(PREDEFINED_VARIABLE_KEYSTROKES, PsiExpressionEnumMacro.NAME) {
+            it.keyStrokes
+        })
+        withVariable(PREDEFINED_VARIABLE_SUPER, AbstractClassesEnumVariableAdapter(PREDEFINED_VARIABLE_SUPER))
+        withVariable(PREDEFINED_VARIABLE_COMPLETE) { VariableDescriptor(PREDEFINED_VARIABLE_COMPLETE, "complete()") }
+    }
+
     constructor(original: TemplateDescriptor) : this(original.id, original.resourceLoader) {
         m_name = original.m_name
         m_description = original.m_description
         m_superClassInfo = original.m_superClassInfo
         m_orderDefinitionType = original.m_orderDefinitionType
-        m_innerTypeGetterContainer = original.m_innerTypeGetterContainer
+        m_innerTypeGetterInfos.addAll(original.m_innerTypeGetterInfos)
         m_variables.putAll(original.m_variables)
         m_alias.addAll(original.m_alias)
     }
@@ -50,12 +72,18 @@
     fun withName(name: String) = apply { m_name = name }
     fun name() = m_name ?: ""
 
-    @Suppress("unused")
     fun withDescription(description: String) = apply { m_description = description }
     fun description() = m_description ?: message("template.desc", name())
 
-    fun withVariable(name: String, vararg values: String) = withVariable(name, values.toList())
-    fun withVariable(name: String, values: Collection<String>) = apply { m_variables[name] = VariableDescriptor(name, values) }
+    fun withEnumVariable(name: String, values: Iterable<String>, macroName: String? = null) = withVariable(name, EnumVariableAdapter(name, macroName, values))
+    fun withBoolVariable(name: String, defaultValue: Boolean) = withVariable(name, BoolVariableAdapter(name, defaultValue.toString()))
+    fun withNlsVariable(name: String, defaultValue: String? = null) = withVariable(name) { VariableDescriptor(name, "${NlsKeysEnumMacro.NAME}()", toStringLiteral(defaultValue)?.toString()) }
+    fun withVariable(name: String, value: String) = withVariable(name) { VariableDescriptor(name, null, toStringLiteral(value)?.toString()) }
+    fun withVariable(name: String, variableAdapter: (TemplateEngine) -> VariableDescriptor?) = apply {
+        Ensure.isFalse(TemplateImpl.INTERNAL_VARS_SET.contains(name), "Variable name '{}' is reserved for internal use.", name)
+        m_variables[name] = variableAdapter
+    }
+
     fun variable(name: String) = m_variables[name]
 
     fun withAliasName(alias: String) = apply { m_alias.add(alias) }
@@ -68,8 +96,8 @@
     fun withOrderDefinitionType(fqn: String) = apply { m_orderDefinitionType = fqn }
     fun orderDefinitionType() = m_orderDefinitionType
 
-    fun withInnerTypeGetterContainer(containerFqn: String, methodName: String) = apply { m_innerTypeGetterContainer = InnerTypeGetterInfo(containerFqn, methodName) }
-    fun innerTypeGetterContainer() = m_innerTypeGetterContainer
+    fun withInnerTypeGetterInfo(containerFqn: String, methodName: String, lookupType: InnerTypeGetterLookupType = InnerTypeGetterLookupType.CLOSEST) = apply { m_innerTypeGetterInfos.add(InnerTypeGetterInfo(containerFqn, methodName, lookupType)) }
+    fun innerTypeGetterInfos(): Set<InnerTypeGetterInfo> = m_innerTypeGetterInfos
 
     fun source(): String = m_source.computeIfAbsentAndGet {
         val templatePath = id.replace('.', '/') + ".txt"
@@ -78,9 +106,9 @@
 
     fun copy() = TemplateDescriptor(this)
 
-    data class VariableDescriptor(val name: String, val values: Collection<String>)
-
     data class SuperClassInfo(val baseFqn: String, val defaultValue: String)
 
-    data class InnerTypeGetterInfo(val definitionClassFqn: String, val methodName: String)
+    data class InnerTypeGetterInfo(val definitionClassFqn: String, val methodName: String, val lookupType: InnerTypeGetterLookupType = InnerTypeGetterLookupType.CLOSEST)
+
+    enum class InnerTypeGetterLookupType { CLOSEST, FARTHEST }
 }
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateEngine.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateEngine.kt
new file mode 100644
index 0000000..05e8a31
--- /dev/null
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateEngine.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * 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:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.sdk.s2i.template
+
+import com.intellij.openapi.module.Module
+import com.intellij.psi.PsiClass
+import org.eclipse.scout.sdk.core.s.IScoutRuntimeTypes
+import org.eclipse.scout.sdk.core.s.classid.ClassIds
+import org.eclipse.scout.sdk.core.s.uniqueid.UniqueIds
+import org.eclipse.scout.sdk.core.util.Ensure
+import org.eclipse.scout.sdk.core.util.JavaTypes
+import org.eclipse.scout.sdk.core.util.Strings
+import org.eclipse.scout.sdk.s2i.*
+import org.eclipse.scout.sdk.s2i.util.VelocityRunner
+import java.util.Collections.emptyList
+import java.util.regex.MatchResult
+import java.util.regex.Pattern
+
+class TemplateEngine(val templateDescriptor: TemplateDescriptor, val context: TemplateContext) {
+
+    data class TemplateContext(val declaringClass: PsiClass, val module: Module, val fileInsertPosition: Int)
+
+    companion object {
+        const val VALUE_OPTION_BOX = "box"
+        const val VALUE_OPTION_UNBOX = "unbox"
+        const val VALUE_OPTION_DEFAULT_VAL = "default"
+        const val VALUE_OPTION_UNCHANGED = "unchanged"
+
+        private val DECLARING_TYPE_ARG_REGEX = Pattern.compile("\\\$declaringTypeArg\\(([\\w.]+),\\s*(\\d+),\\s*(${VALUE_OPTION_BOX}|${VALUE_OPTION_UNBOX}|${VALUE_OPTION_DEFAULT_VAL}|${VALUE_OPTION_UNCHANGED})\\)\\\$")
+        private val ENCLOSING_INSTANCE_FQN_REGEX = Pattern.compile("\\\$enclosingInstanceInScopeFqn\\((.*)\\)\\\$")
+        private val CLASS_ID_REGEX = Pattern.compile("\\\$newClassId\\(\\)\\\$")
+        private val UNIQUE_ID_REGEX = Pattern.compile("\\\$newUniqueId\\((.+)\\)\\\$")
+        private val SCOUT_RT_CONSTANTS = IScoutRuntimeTypes::class.java
+                .fields
+                .map { it.name to it.get(null).toString() }
+                .toMap()
+        private val MENU_TYPE_MAPPING = buildMenuTypeMapping()
+
+        private fun buildMenuTypeMapping(): Map<String, List<String>> {
+            val mapping = HashMap<String, List<String>>()
+
+            val tableEmpty = "${IScoutRuntimeTypes.TableMenuType}.${IScoutRuntimeTypes.TableMenuType_EmptySpace}"
+            val tableHeader = "${IScoutRuntimeTypes.TableMenuType}.${IScoutRuntimeTypes.TableMenuType_Header}"
+            val tableMulti = "${IScoutRuntimeTypes.TableMenuType}.${IScoutRuntimeTypes.TableMenuType_MultiSelection}"
+            val tableSingle = "${IScoutRuntimeTypes.TableMenuType}.${IScoutRuntimeTypes.TableMenuType_SingleSelection}"
+            val treeEmpty = "${IScoutRuntimeTypes.TreeMenuType}.${IScoutRuntimeTypes.TreeMenuType_EmptySpace}"
+            val treeHeader = "${IScoutRuntimeTypes.TreeMenuType}.${IScoutRuntimeTypes.TreeMenuType_Header}"
+            val treeMulti = "${IScoutRuntimeTypes.TreeMenuType}.${IScoutRuntimeTypes.TreeMenuType_MultiSelection}"
+            val treeSingle = "${IScoutRuntimeTypes.TreeMenuType}.${IScoutRuntimeTypes.TreeMenuType_SingleSelection}"
+            val calendarComponent = "${IScoutRuntimeTypes.CalendarMenuType}.${IScoutRuntimeTypes.CalendarMenuType_CalendarComponent}"
+            val calendarEmpty = "${IScoutRuntimeTypes.CalendarMenuType}.${IScoutRuntimeTypes.CalendarMenuType_EmptySpace}"
+            val valueFieldEmpty = "${IScoutRuntimeTypes.ValueFieldMenuType}.${IScoutRuntimeTypes.ValueFieldMenuType_Null}"
+            val valueFieldNotEmpty = "${IScoutRuntimeTypes.ValueFieldMenuType}.${IScoutRuntimeTypes.ValueFieldMenuType_NotNull}"
+            val imageFieldEmpty = "${IScoutRuntimeTypes.ImageFieldMenuType}.${IScoutRuntimeTypes.ImageFieldMenuType_Null}"
+            val imageFieldImageId = "${IScoutRuntimeTypes.ImageFieldMenuType}.${IScoutRuntimeTypes.ImageFieldMenuType_ImageId}"
+            val imageFieldImageUrl = "${IScoutRuntimeTypes.ImageFieldMenuType}.${IScoutRuntimeTypes.ImageFieldMenuType_ImageUrl}"
+            val imageFieldImage = "${IScoutRuntimeTypes.ImageFieldMenuType}.${IScoutRuntimeTypes.ImageFieldMenuType_Image}"
+            val tileGridEmpty = "${IScoutRuntimeTypes.TileGridMenuType}.${IScoutRuntimeTypes.TileGridMenuType_EmptySpace}"
+            val tileGridMulti = "${IScoutRuntimeTypes.TileGridMenuType}.${IScoutRuntimeTypes.TileGridMenuType_MultiSelection}"
+            val tileGridSingle = "${IScoutRuntimeTypes.TileGridMenuType}.${IScoutRuntimeTypes.TileGridMenuType_SingleSelection}"
+
+            val treeMappings = listOf("$treeSingle, $treeMulti", "$treeEmpty, $treeHeader", "$treeSingle, $treeMulti, $treeHeader, $treeEmpty")
+            val calendarMappings = listOf(calendarComponent, calendarEmpty, "$calendarComponent, $calendarEmpty")
+
+            mapping[IScoutRuntimeTypes.ITable] = listOf("$tableSingle, $tableMulti", "$tableEmpty, $tableHeader", "$tableSingle, $tableMulti, $tableHeader, $tableEmpty")
+            mapping[IScoutRuntimeTypes.ITree] = treeMappings
+            mapping[IScoutRuntimeTypes.ITreeNode] = treeMappings
+            mapping[IScoutRuntimeTypes.ITabBox] = listOf("${IScoutRuntimeTypes.TabBoxMenuType}.${IScoutRuntimeTypes.TabBoxMenuType_Header}")
+            mapping[IScoutRuntimeTypes.ICalendarItemProvider] = calendarMappings
+            mapping[IScoutRuntimeTypes.ICalendar] = calendarMappings
+            mapping[IScoutRuntimeTypes.IValueField] = listOf(valueFieldNotEmpty, valueFieldEmpty, "$valueFieldNotEmpty, $valueFieldEmpty")
+            mapping[IScoutRuntimeTypes.IImageField] = listOf("$imageFieldImageId, $imageFieldImageUrl, $imageFieldImage", imageFieldEmpty, "$imageFieldImageId, $imageFieldImageUrl, $imageFieldImage, $imageFieldEmpty")
+            mapping[IScoutRuntimeTypes.ITileGrid] = listOf("$tileGridSingle, $tileGridMulti", tileGridEmpty, "$tileGridSingle, $tileGridMulti, $tileGridEmpty")
+            return mapping
+        }
+
+        fun getKeyStrokeOptions(): List<String> {
+            val fKeys = (1..12).map { "${IScoutRuntimeTypes.IKeyStroke}.F$it" }
+            val actionKeys = listOf("${IScoutRuntimeTypes.IKeyStroke}.ENTER", "${IScoutRuntimeTypes.IKeyStroke}.INSERT", "${IScoutRuntimeTypes.IKeyStroke}.DELETE",
+                    "${IScoutRuntimeTypes.IKeyStroke}.ESCAPE", "${IScoutRuntimeTypes.IKeyStroke}.SPACE", "${IScoutRuntimeTypes.IKeyStroke}.TAB", "${IScoutRuntimeTypes.IKeyStroke}.BACKSPACE")
+            val cursors = listOf("${IScoutRuntimeTypes.IKeyStroke}.LEFT", "${IScoutRuntimeTypes.IKeyStroke}.RIGHT", "${IScoutRuntimeTypes.IKeyStroke}.UP", "${IScoutRuntimeTypes.IKeyStroke}.DOWN")
+            val ctrl = "${IScoutRuntimeTypes.IKeyStroke}.CONTROL"
+            val shift = "${IScoutRuntimeTypes.IKeyStroke}.SHIFT"
+            val alt = "${IScoutRuntimeTypes.IKeyStroke}.ALT"
+            val combinedSamples = listOf(
+                    "$ctrl, \"C\"",
+                    "$ctrl, $shift, \"E\"",
+                    "$alt, ${IScoutRuntimeTypes.IKeyStroke}.F11")
+                    .map { "${IScoutRuntimeTypes.KeyStroke}.combineKeyStrokes($it)" }
+            return fKeys + actionKeys + cursors + combinedSamples
+        }
+    }
+
+    private val m_resolvedTypeArgs = HashMap<Pair<String, Int>, String>()
+    private val m_superClassBase = templateDescriptor.superClassInfo()
+            ?.baseFqn
+            ?.let { context.module.findTypeByName(it) }
+    val menuTypes: List<String> = MENU_TYPE_MAPPING.entries
+            .firstOrNull { context.declaringClass.isInstanceOf(it.key) }
+            ?.value ?: emptyList()
+    val keyStrokes = getKeyStrokeOptions()
+
+    fun buildTemplate(): String {
+        val templateSource = VelocityRunner()
+                .withProperties(SCOUT_RT_CONSTANTS)
+                .withProperty(TemplateDescriptor.PREDEFINED_CONSTANT_IN_EXTENSION, context.declaringClass.isInstanceOf(IScoutRuntimeTypes.IExtension))
+                .withProperty(TemplateDescriptor.PREDEFINED_CONSTANT_MENU_SUPPORTED, menuTypes.isNotEmpty())
+                .withPostProcessor(DECLARING_TYPE_ARG_REGEX, this::resolveDeclaringTypeArgument)
+                .withPostProcessor(UNIQUE_ID_REGEX) { UniqueIds.next(it.group(1)) }
+                .withPostProcessor(ENCLOSING_INSTANCE_FQN_REGEX, this::resolveEnclosingInstanceFqn)
+                .withPostProcessor(CLASS_ID_REGEX) { ClassIds.next(context.declaringClass.qualifiedName) }
+                .eval(templateDescriptor.source())
+
+        val classId = createClassIdAnnotationIfNecessary() ?: ""
+        val order = createOrderAnnotationIfNecessary() ?: ""
+        return "$order $classId $templateSource"
+    }
+
+    private fun resolveEnclosingInstanceFqn(match: MatchResult): String {
+        val queryType = match.group(1)
+        return context.declaringClass.findEnclosingClass(queryType, true)
+                ?.qualifiedName ?: ""
+    }
+
+    private fun resolveDeclaringTypeArgument(match: MatchResult): String {
+        val typeArgDeclaringClassFqn = match.group(1)
+        val typeArgIndex = Integer.valueOf(match.group(2))
+        val postProcessing = match.group(3)
+        val type = m_resolvedTypeArgs.computeIfAbsent(typeArgDeclaringClassFqn to typeArgIndex) {
+            val owner = context.declaringClass.findEnclosingClass(typeArgDeclaringClassFqn, true)
+            owner?.resolveTypeArgument(typeArgIndex, typeArgDeclaringClassFqn)
+                    ?.getCanonicalText(false)
+                    ?: Object::class.java.name
+        }
+        return when (postProcessing) {
+            VALUE_OPTION_BOX -> JavaTypes.boxPrimitive(type)
+            VALUE_OPTION_UNBOX -> JavaTypes.unboxToPrimitive(type)
+            VALUE_OPTION_DEFAULT_VAL -> JavaTypes.defaultValueOf(type)
+            else -> type
+        }
+    }
+
+    private fun createOrderAnnotationIfNecessary(): String? {
+        if (!isOrderSupported()) {
+            return null
+        }
+        val siblings = findOrderSiblings()
+        val first = OrderAnnotation.valueOf(siblings[0])
+        val second = OrderAnnotation.valueOf(siblings[1])
+        val orderValue = org.eclipse.scout.sdk.core.s.annotation.OrderAnnotation.convertToJavaSource(org.eclipse.scout.sdk.core.s.annotation.OrderAnnotation.getNewViewOrderValue(first, second))
+        return "@${IScoutRuntimeTypes.Order}($orderValue)"
+    }
+
+    private fun findOrderSiblings(): Array<PsiClass?> {
+        val orderDefinitionType = templateDescriptor.orderDefinitionType()
+                ?: throw Ensure.newFail("Super class supports the Order annotation but no order annotation definition type has been specified.")
+        val candidates = context.declaringClass.innerClasses.filter { it.isInstanceOf(orderDefinitionType) }
+
+        var prev: PsiClass? = null
+        for (t in candidates) {
+            if (t.textOffset > context.fileInsertPosition) {
+                return arrayOf(prev, t)
+            }
+            prev = t
+        }
+        return arrayOf(prev, null)
+    }
+
+    private fun createClassIdAnnotationIfNecessary(): String? {
+        if (!isClassIdSupported() || !ClassIds.isAutomaticallyCreateClassIdAnnotation()) {
+            return null
+        }
+        val classIdValue = Strings.toStringLiteral(ClassIds.next(context.declaringClass.qualifiedName)) ?: return null
+        return "@${IScoutRuntimeTypes.ClassId}($classIdValue)"
+    }
+
+    private fun isOrderSupported() = m_superClassBase?.isInstanceOf(IScoutRuntimeTypes.IOrdered) ?: false
+
+    private fun isClassIdSupported() = m_superClassBase?.isInstanceOf(IScoutRuntimeTypes.ITypeWithClassId) ?: false
+}
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateInsertHandler.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateInsertHandler.kt
index 5a9257d..7c105da 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateInsertHandler.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/TemplateInsertHandler.kt
@@ -23,7 +23,6 @@
 import com.intellij.openapi.command.WriteCommandAction.writeCommandAction
 import com.intellij.openapi.editor.Document
 import com.intellij.openapi.editor.Editor
-import com.intellij.openapi.module.Module
 import com.intellij.openapi.util.Conditions.alwaysTrue
 import com.intellij.psi.*
 import com.intellij.psi.codeStyle.CodeStyleSettings
@@ -36,21 +35,13 @@
 import org.eclipse.scout.sdk.core.log.SdkLog
 import org.eclipse.scout.sdk.core.log.SdkLog.onTrace
 import org.eclipse.scout.sdk.core.model.api.PropertyBean
-import org.eclipse.scout.sdk.core.s.IScoutRuntimeTypes
-import org.eclipse.scout.sdk.core.s.annotation.OrderAnnotation.convertToJavaSource
-import org.eclipse.scout.sdk.core.s.annotation.OrderAnnotation.getNewViewOrderValue
-import org.eclipse.scout.sdk.core.s.classid.ClassIds
-import org.eclipse.scout.sdk.core.s.uniqueid.UniqueIds
 import org.eclipse.scout.sdk.core.util.Ensure.newFail
 import org.eclipse.scout.sdk.core.util.FinalValue
-import org.eclipse.scout.sdk.core.util.JavaTypes
 import org.eclipse.scout.sdk.core.util.Strings
-import org.eclipse.scout.sdk.core.util.Strings.toStringLiteral
-import org.eclipse.scout.sdk.s2i.*
-import org.eclipse.scout.sdk.s2i.nls.NlsKeysEnumMacro
+import org.eclipse.scout.sdk.s2i.containingModule
+import org.eclipse.scout.sdk.s2i.findTypeByName
+import org.eclipse.scout.sdk.s2i.isInstanceOf
 import java.lang.reflect.Method
-import java.util.regex.MatchResult
-import java.util.regex.Pattern
 
 /**
  * Handler that inserts a selected [TemplateDescriptor].
@@ -58,69 +49,8 @@
 class TemplateInsertHandler(val templateDescriptor: TemplateDescriptor, val prefix: String) : InsertHandler<LookupElement> {
 
     companion object {
-
-        const val VALUE_OPTION_BOX = "box"
-        const val VALUE_OPTION_UNBOX = "unbox"
-        const val VALUE_OPTION_DEFAULT_VAL = "default"
-        const val VALUE_OPTION_UNCHANGED = "unchanged"
-        const val CONSTANT_IDENTIFIER = "#" // marker for constants in IScoutRuntimeTypes that may be used in the template
-        private const val VARIABLE_MENU_TYPES = "menuTypes"
         private val CREATE_TEMP_SETTINGS_METHOD = FinalValue<Method>()
 
-        private val DECLARING_TYPE_ARG_REGEX = Pattern.compile("\\\$declaringTypeArg\\(([\\w.]+),\\s*(\\d+),\\s*($VALUE_OPTION_BOX|$VALUE_OPTION_UNBOX|$VALUE_OPTION_DEFAULT_VAL|$VALUE_OPTION_UNCHANGED)\\)\\\$")
-        private val ENCLOSING_INSTANCE_FQN_REGEX = Pattern.compile("\\\$enclosingInstanceInScopeFqn\\((.*)\\)\\\$")
-        private val CLASS_ID_REGEX = Pattern.compile("\\\$newClassId\\(\\)\\\$")
-        private val UNIQUE_ID_REGEX = Pattern.compile("\\\$newUniqueId\\((.+)\\)\\\$")
-        private val SCOUT_RT_CONSTANTS = buildScoutRtConstants()
-        private val MENU_TYPE_MAPPING = buildMenuTypeMapping()
-
-        private fun buildScoutRtConstants(): Pair<Array<String>, Array<String>> {
-            val map = IScoutRuntimeTypes::class.java.fields
-                    .map { CONSTANT_IDENTIFIER + it.name + CONSTANT_IDENTIFIER to it.get(null).toString() }
-                    .toMap()
-            val keyArray = map.keys.toTypedArray()
-            val valArray = map.values.toTypedArray()
-            return keyArray to valArray
-        }
-
-        private fun buildMenuTypeMapping(): Map<String, List<String>> {
-            val mapping = HashMap<String, List<String>>()
-
-            val tableEmpty = "${IScoutRuntimeTypes.TableMenuType}.${IScoutRuntimeTypes.TableMenuType_EmptySpace}"
-            val tableHeader = "${IScoutRuntimeTypes.TableMenuType}.${IScoutRuntimeTypes.TableMenuType_Header}"
-            val tableMulti = "${IScoutRuntimeTypes.TableMenuType}.${IScoutRuntimeTypes.TableMenuType_MultiSelection}"
-            val tableSingle = "${IScoutRuntimeTypes.TableMenuType}.${IScoutRuntimeTypes.TableMenuType_SingleSelection}"
-            val treeEmpty = "${IScoutRuntimeTypes.TreeMenuType}.${IScoutRuntimeTypes.TreeMenuType_EmptySpace}"
-            val treeHeader = "${IScoutRuntimeTypes.TreeMenuType}.${IScoutRuntimeTypes.TreeMenuType_Header}"
-            val treeMulti = "${IScoutRuntimeTypes.TreeMenuType}.${IScoutRuntimeTypes.TreeMenuType_MultiSelection}"
-            val treeSingle = "${IScoutRuntimeTypes.TreeMenuType}.${IScoutRuntimeTypes.TreeMenuType_SingleSelection}"
-            val calendarComponent = "${IScoutRuntimeTypes.CalendarMenuType}.${IScoutRuntimeTypes.CalendarMenuType_CalendarComponent}"
-            val calendarEmpty = "${IScoutRuntimeTypes.CalendarMenuType}.${IScoutRuntimeTypes.CalendarMenuType_EmptySpace}"
-            val valueFieldEmpty = "${IScoutRuntimeTypes.ValueFieldMenuType}.${IScoutRuntimeTypes.ValueFieldMenuType_Null}"
-            val valueFieldNotEmpty = "${IScoutRuntimeTypes.ValueFieldMenuType}.${IScoutRuntimeTypes.ValueFieldMenuType_NotNull}"
-            val imageFieldEmpty = "${IScoutRuntimeTypes.ImageFieldMenuType}.${IScoutRuntimeTypes.ImageFieldMenuType_Null}"
-            val imageFieldImageId = "${IScoutRuntimeTypes.ImageFieldMenuType}.${IScoutRuntimeTypes.ImageFieldMenuType_ImageId}"
-            val imageFieldImageUrl = "${IScoutRuntimeTypes.ImageFieldMenuType}.${IScoutRuntimeTypes.ImageFieldMenuType_ImageUrl}"
-            val imageFieldImage = "${IScoutRuntimeTypes.ImageFieldMenuType}.${IScoutRuntimeTypes.ImageFieldMenuType_Image}"
-            val tileGridEmpty = "${IScoutRuntimeTypes.TileGridMenuType}.${IScoutRuntimeTypes.TileGridMenuType_EmptySpace}"
-            val tileGridMulti = "${IScoutRuntimeTypes.TileGridMenuType}.${IScoutRuntimeTypes.TileGridMenuType_MultiSelection}"
-            val tileGridSingle = "${IScoutRuntimeTypes.TileGridMenuType}.${IScoutRuntimeTypes.TileGridMenuType_SingleSelection}"
-
-            val treeMappings = listOf("$treeSingle, $treeMulti", "$treeEmpty, $treeHeader", "$treeSingle, $treeMulti, $treeHeader, $treeEmpty")
-            val calendarMappings = listOf(calendarComponent, calendarEmpty, "$calendarComponent, $calendarEmpty")
-
-            mapping[IScoutRuntimeTypes.ITable] = listOf("$tableSingle, $tableMulti", "$tableEmpty, $tableHeader", "$tableSingle, $tableMulti, $tableHeader, $tableEmpty")
-            mapping[IScoutRuntimeTypes.ITree] = treeMappings
-            mapping[IScoutRuntimeTypes.ITreeNode] = treeMappings
-            mapping[IScoutRuntimeTypes.ITabBox] = listOf("${IScoutRuntimeTypes.TabBoxMenuType}.${IScoutRuntimeTypes.TabBoxMenuType_Header}")
-            mapping[IScoutRuntimeTypes.ICalendarItemProvider] = calendarMappings
-            mapping[IScoutRuntimeTypes.ICalendar] = calendarMappings
-            mapping[IScoutRuntimeTypes.IValueField] = listOf(valueFieldNotEmpty, valueFieldEmpty, "$valueFieldNotEmpty, $valueFieldEmpty")
-            mapping[IScoutRuntimeTypes.IImageField] = listOf("$imageFieldImageId, $imageFieldImageUrl, $imageFieldImage", imageFieldEmpty, "$imageFieldImageId, $imageFieldImageUrl, $imageFieldImage, $imageFieldEmpty")
-            mapping[IScoutRuntimeTypes.ITileGrid] = listOf("$tileGridSingle, $tileGridMulti", tileGridEmpty, "$tileGridSingle, $tileGridMulti, $tileGridEmpty")
-            return mapping
-        }
-
         private fun createTempSettings(origSettings: CodeStyleSettings, settingsManager: CodeStyleSettingsManager): CodeStyleSettings {
             val createTemporarySettings = CREATE_TEMP_SETTINGS_METHOD.computeIfAbsentAndGet { createTemporarySettingsMethod() }
             if (createTemporarySettings != null) {
@@ -144,24 +74,13 @@
                 }
     }
 
-    private lateinit var m_declaringClass: PsiClass
-    private lateinit var m_containingModule: Module
-    private val m_resolvedTypeArgs = HashMap<Pair<String, Int>, String>()
-    private var m_superClassInfo: TemplateDescriptor.SuperClassInfo? = null
-    private var m_superClassBase: PsiClass? = null
-    private var m_insertPosition: Int = -1
-    private var m_menuTypes: List<String>? = null
+    private lateinit var m_engine: TemplateEngine
 
     override fun handleInsert(context: InsertionContext, item: LookupElement) {
         val editor = context.editor
-        m_declaringClass = item.getObject() as PsiClass
-        m_containingModule = m_declaringClass.containingModule() ?: return
-        m_superClassInfo = templateDescriptor.superClassInfo()
-        m_superClassBase = m_superClassInfo?.baseFqn?.let { m_containingModule.findTypeByName(it) }
-        m_insertPosition = editor.caretModel.offset
-        m_menuTypes = MENU_TYPE_MAPPING.entries
-                .firstOrNull { m_declaringClass.isInstanceOf(it.key) }
-                ?.value
+        val declaringClass = item.getObject() as PsiClass
+        val containingModule = declaringClass.containingModule() ?: return
+        m_engine = TemplateEngine(templateDescriptor, TemplateEngine.TemplateContext(declaringClass, containingModule, editor.caretModel.offset))
 
         startTemplateWithTempSettings(buildTemplate(), editor)
     }
@@ -207,7 +126,7 @@
     private fun isAlphaChar(char: Char) = char in 'a'..'z' || char in 'A'..'Z'
 
     private fun buildTemplate(): TemplateImpl {
-        val source = buildTemplateSource()
+        val source = m_engine.buildTemplate()
         val template = TemplateImpl(templateDescriptor.id, source, "Scout")
         template.id = this.templateDescriptor.id
         template.description = templateDescriptor.description()
@@ -221,162 +140,15 @@
         return template
     }
 
-    private fun buildTemplateSource(): String {
-        val templateSource = replaceConstants(templateDescriptor.source())
-        val classId = createClassIdAnnotationIfNecessary() ?: ""
-        val order = createOrderAnnotationIfNecessary() ?: ""
-        return "$order $classId $templateSource"
-    }
-
-    /**
-     * Replace constants in the template. The values depend on the execution context (the declaring class in which the template is invoked) and must therefore be evaluated at insertion time.
-     * But the values are constant from the perspective of a single template execution. The user cannot modify these constants.
-     */
-    private fun replaceConstants(src: CharSequence): String {
-        val staticIfExtension = if (m_declaringClass.isInstanceOf(IScoutRuntimeTypes.IExtension)) "static" else ""
-        val getConfiguredMenuTypes = if (isMenuTypesSupported()) {
-            "@${Override::class.java.name} protected ${Set::class.java.name}<? extends ${IScoutRuntimeTypes.IMenuType}> getConfiguredMenuTypes() { return \$$VARIABLE_MENU_TYPES\$; }"
-        } else {
-            ""
-        }
-
-        val stage1 = Strings.replaceEach(src,
-                arrayOf("\$${TemplateDescriptor.PREDEFINED_VARIABLE_STATIC_IN_EXTENSION}\$", "\$${TemplateDescriptor.PREDEFINED_VARIABLE_CONFIGURED_MENU_TYPES}\$"),
-                arrayOf(staticIfExtension, getConfiguredMenuTypes))
-        val stage2 = Strings.replaceEach(stage1, SCOUT_RT_CONSTANTS.first, SCOUT_RT_CONSTANTS.second)
-        val stage3 = DECLARING_TYPE_ARG_REGEX.replaceAll(stage2) { resolveDeclaringTypeArgument(it) }
-        val stage4 = UNIQUE_ID_REGEX.replaceAll(stage3) { UniqueIds.next(it.group(1)) }
-        val stage5 = ENCLOSING_INSTANCE_FQN_REGEX.replaceAll(stage4) { resolveEnclosingInstanceFqn(it) }
-        return CLASS_ID_REGEX.replaceAll(stage5) { ClassIds.next(m_declaringClass.qualifiedName) }
-    }
-
-    private fun resolveEnclosingInstanceFqn(match: MatchResult): String {
-        val queryType = match.group(1)
-        return m_declaringClass.findEnclosingClass(queryType, true)
-                ?.qualifiedName ?: ""
-    }
-
-    private fun resolveDeclaringTypeArgument(match: MatchResult): String {
-        val typeArgDeclaringClassFqn = match.group(1)
-        val typeArgIndex = Integer.valueOf(match.group(2))
-        val postProcessing = match.group(3)
-        val type = m_resolvedTypeArgs.computeIfAbsent(typeArgDeclaringClassFqn to typeArgIndex) {
-            val owner = m_declaringClass.findEnclosingClass(typeArgDeclaringClassFqn, true)
-            owner?.resolveTypeArgument(typeArgIndex, typeArgDeclaringClassFqn)
-                    ?.getCanonicalText(false)
-                    ?: Object::class.java.name
-        }
-        return when (postProcessing) {
-            VALUE_OPTION_BOX -> JavaTypes.boxPrimitive(type)
-            VALUE_OPTION_UNBOX -> JavaTypes.unboxToPrimitive(type)
-            VALUE_OPTION_DEFAULT_VAL -> JavaTypes.defaultValueOf(type)
-            else -> type
-        }
-    }
-
-    private fun createOrderAnnotationIfNecessary(): String? {
-        if (!isOrderSupported()) {
-            return null
-        }
-        val siblings = findOrderSiblings()
-        val first = OrderAnnotation.valueOf(siblings[0])
-        val second = OrderAnnotation.valueOf(siblings[1])
-        val orderValue = convertToJavaSource(getNewViewOrderValue(first, second))
-        return "@${IScoutRuntimeTypes.Order}($orderValue)"
-    }
-
-    private fun findOrderSiblings(): Array<PsiClass?> {
-        val orderDefinitionType = templateDescriptor.orderDefinitionType()
-                ?: throw newFail("Super class supports the Order annotation but no order annotation definition type has been specified.")
-        val candidates = m_declaringClass.innerClasses.filter { it.isInstanceOf(orderDefinitionType) }
-
-        var prev: PsiClass? = null
-        for (t in candidates) {
-            if (t.textOffset > m_insertPosition) {
-                return arrayOf(prev, t)
-            }
-            prev = t
-        }
-        return arrayOf(prev, null)
-    }
-
-    private fun createClassIdAnnotationIfNecessary(): String? {
-        if (!isClassIdSupported() || !ClassIds.isAutomaticallyCreateClassIdAnnotation()) {
-            return null
-        }
-        val classIdValue = toStringLiteral(ClassIds.next(m_declaringClass.qualifiedName)) ?: return null
-        return "@${IScoutRuntimeTypes.ClassId}($classIdValue)"
-    }
-
-    private fun isOrderSupported() = m_superClassBase?.isInstanceOf(IScoutRuntimeTypes.IOrdered) ?: false
-
-    private fun isClassIdSupported() = m_superClassBase?.isInstanceOf(IScoutRuntimeTypes.ITypeWithClassId) ?: false
-
-    private fun isMenuTypesSupported() = m_menuTypes != null
-
     private fun addVariable(name: String, target: TemplateImpl) {
+        // com.intellij.codeInsight.Template internal variables (like "END")
         if (TemplateImpl.INTERNAL_VARS_SET.contains(name)) {
             return
         }
-        if (VARIABLE_MENU_TYPES == name) {
-            val options = m_menuTypes!!.map { "${IScoutRuntimeTypes.CollectionUtility}.hashSet($it)" }
-            target.addVariable(name, toEnum(options, PsiExpressionEnumMacro.NAME), toStringLiteral(options[0]).toString(), true)
-            return
-        }
-        if (TemplateDescriptor.PREDEFINED_VARIABLE_KEYSTROKES == name) {
-            val options = getKeyStrokeOptions()
-            target.addVariable(name, toEnum(options, PsiExpressionEnumMacro.NAME), toStringLiteral(options[0]).toString(), true)
-            return
-        }
-        if (TemplateDescriptor.PREDEFINED_VARIABLE_SUPER == name) {
-            val baseFqn = toStringLiteral(m_superClassInfo?.baseFqn) ?: throw newFail("Variable '{}' is used in the template but no valid super class base was specified.", TemplateDescriptor.PREDEFINED_VARIABLE_SUPER)
-            val defaultValue = toStringLiteral(m_superClassInfo?.defaultValue) ?: throw newFail("Variable '{}' is used in the template but no valid super class default was specified.", TemplateDescriptor.PREDEFINED_VARIABLE_SUPER)
-            target.addVariable(name, "${DescendantAbstractClassesEnumMacro.NAME}($baseFqn, $defaultValue)", defaultValue.toString(), true)
-            return
-        }
-        if (TemplateDescriptor.PREDEFINED_VARIABLE_COMPLETE == name) {
-            target.addVariable(name, "complete()", null, true)
-            return
-        }
-        addVariableFromDescriptorDefinition(name, target)
-    }
 
-    private fun toEnum(options: Iterable<String>, enumMacroName: String = "enum") = options.joinToString(", ", "$enumMacroName(", ")") { toStringLiteral(it) }
-
-    private fun addVariableFromDescriptorDefinition(name: String, target: TemplateImpl) {
-        val variableDescriptor = templateDescriptor.variable(name) ?: throw newFail("Variable '{}' is used in the template but not declared in the template descriptor.", name)
-        var expression: String? = null
-        var defaultValue = variableDescriptor.values.firstOrNull() ?: ""
-
-        if (name.startsWith(TemplateDescriptor.VARIABLE_PREFIX_NLS)) {
-            expression = "${NlsKeysEnumMacro.NAME}()"
-        } else if (name.startsWith(TemplateDescriptor.VARIABLE_PREFIX_BOOL)) {
-            val other = if ("true" == defaultValue) "false" else "true"
-            expression = toEnum(listOf(defaultValue, other))
-        } else {
-            val options = variableDescriptor.values
-            defaultValue = options.firstOrNull() ?: ""
-            if (options.size > 1) {
-                expression = toEnum(options)
-            }
-        }
-        target.addVariable(name, expression, toStringLiteral(defaultValue).toString(), true)
-    }
-
-    private fun getKeyStrokeOptions(): List<String> {
-        val fKeys = (1..12).map { "${IScoutRuntimeTypes.IKeyStroke}.F$it" }
-        val cursors = listOf("${IScoutRuntimeTypes.IKeyStroke}.LEFT", "${IScoutRuntimeTypes.IKeyStroke}.RIGHT", "${IScoutRuntimeTypes.IKeyStroke}.UP", "${IScoutRuntimeTypes.IKeyStroke}.DOWN")
-        val actionKeys = listOf("${IScoutRuntimeTypes.IKeyStroke}.ENTER", "${IScoutRuntimeTypes.IKeyStroke}.INSERT", "${IScoutRuntimeTypes.IKeyStroke}.DELETE",
-                "${IScoutRuntimeTypes.IKeyStroke}.ESCAPE", "${IScoutRuntimeTypes.IKeyStroke}.SPACE", "${IScoutRuntimeTypes.IKeyStroke}.TAB", "${IScoutRuntimeTypes.IKeyStroke}.BACKSPACE")
-        val ctrl = "${IScoutRuntimeTypes.IKeyStroke}.CONTROL"
-        val shift = "${IScoutRuntimeTypes.IKeyStroke}.SHIFT"
-        val alt = "${IScoutRuntimeTypes.IKeyStroke}.ALT"
-        val combinedSamples = listOf(
-                "$ctrl, \"C\"",
-                "$ctrl, $shift, \"E\"",
-                "$alt, ${IScoutRuntimeTypes.IKeyStroke}.F11")
-                .map { "${IScoutRuntimeTypes.KeyStroke}.combineKeyStrokes($it)" }
-        return fKeys + actionKeys + cursors + combinedSamples
+        val adapter = templateDescriptor.variable(name) ?: throw newFail("Variable '{}' is used in the template source but not declared in the template descriptor.", name)
+        val descriptor = adapter.invoke(m_engine) ?: return
+        target.addVariable(descriptor.name, descriptor.expression, descriptor.defaultValueExpression, true)
     }
 
     private class TemplateListener(private val templateDescriptor: TemplateDescriptor, private val settingsManager: CodeStyleSettingsManager, private val origSettings: CodeStyleSettings?) : TemplateEditingAdapter() {
@@ -401,8 +173,25 @@
             }
         }
 
+        private fun resolveInnerTypeGetterContainer(createdClass: PsiClass): Pair<PsiClass, String>? {
+            val containingModule = createdClass.containingModule() ?: return null
+            for (info in templateDescriptor.innerTypeGetterInfos()) {
+                val container = if (info.lookupType == TemplateDescriptor.InnerTypeGetterLookupType.CLOSEST) {
+                    val innerTypeGetterBase = containingModule.findTypeByName(info.definitionClassFqn) ?: continue
+                    findEnclosingInstanceInScope(innerTypeGetterBase, createdClass, alwaysTrue(), false)
+                } else {
+                    PsiTreeUtil.collectParents(createdClass, PsiClass::class.java, false) { it is PsiFile }
+                            .lastOrNull { it.isInstanceOf(info.definitionClassFqn) }
+                }
+
+                if (container != null) {
+                    return container to info.methodName
+                }
+            }
+            return null
+        }
+
         private fun insertInnerTypeGetter(state: TemplateState) {
-            val innerTypeGetterContainerInfo = templateDescriptor.innerTypeGetterContainer() ?: return
             val editor = state.editor
             val project = editor.project ?: return
             val document = editor.document
@@ -411,15 +200,14 @@
             val file = psiDocumentManager.getPsiFile(document) ?: return
             val element = file.findElementAt(nameRange.startOffset) ?: return
             val createdClass = PsiTreeUtil.getParentOfType(element, PsiClass::class.java) ?: return
-            val containingModule = file.containingModule() ?: return
-            val innerTypeGetterBase = containingModule.findTypeByName(innerTypeGetterContainerInfo.definitionClassFqn) ?: return
-            val innerTypeGetterContainer = findEnclosingInstanceInScope(innerTypeGetterBase, element, alwaysTrue(), false) ?: return
+            val innerTypeGetterInfo = resolveInnerTypeGetterContainer(createdClass) ?: return
 
             val createdClassFqn = createdClass.qualifiedName
             val createdClassSimpleName = Strings.ensureStartWithUpperCase(createdClass.name)
             val psiElementFactory = JavaPsiFacade.getElementFactory(project)
             val methodName = PropertyBean.GETTER_PREFIX + createdClassSimpleName
-            val innerTypeGetterMethodName = innerTypeGetterContainerInfo.methodName
+            val innerTypeGetterContainer = innerTypeGetterInfo.first
+            val innerTypeGetterMethodName = innerTypeGetterInfo.second
             val innerTypeGetter = psiElementFactory.createMethodFromText("public $createdClassFqn $methodName() { return $innerTypeGetterMethodName($createdClassFqn.class);}", innerTypeGetterContainer)
             if (innerTypeGetterContainer.findMethodBySignature(innerTypeGetter, false) != null) {
                 return // method already exists
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/Templates.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/Templates.kt
index 0d253e2..b0514e8 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/Templates.kt
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/Templates.kt
@@ -53,7 +53,7 @@
                 .withName(message("template.CalendarField"))
                 .withSuperClassInfo(IScoutRuntimeTypes.ICalendarField, IScoutRuntimeTypes.AbstractCalendarField)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MyCalendar")
-                .withVariable("boolLabelVisible", "false"))
+                .withBoolVariable("boolLabelVisible", false))
         registerFormFieldTemplate(IScoutRuntimeTypes.IDateField, TemplateDescriptor("templates.DateField")
                 .withName(message("template.DateField"))
                 .withAliasNames(message("template.DateTimeField"), message("template.TimeField"))
@@ -79,18 +79,18 @@
                 .withName(message("template.ListBox"))
                 .withSuperClassInfo(IScoutRuntimeTypes.IListBox, IScoutRuntimeTypes.AbstractListBox)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MyList")
-                .withVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes()))
+                .withEnumVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes(), PsiExpressionEnumMacro.NAME))
         registerFormFieldTemplate(IScoutRuntimeTypes.IProposalField, TemplateDescriptor("templates.ProposalField")
                 .withName(message("template.ProposalField"))
                 .withSuperClassInfo(IScoutRuntimeTypes.IProposalField, IScoutRuntimeTypes.AbstractProposalField)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MyProposal")
-                .withVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes()))
+                .withEnumVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes(), PsiExpressionEnumMacro.NAME))
         registerFormFieldTemplate(IScoutRuntimeTypes.ISmartField, TemplateDescriptor("templates.SmartField")
                 .withName(message("template.SmartField"))
                 .withAliasNames(message("template.ComboBox"))
                 .withSuperClassInfo(IScoutRuntimeTypes.ISmartField, IScoutRuntimeTypes.AbstractSmartField)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MySmart")
-                .withVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes()))
+                .withEnumVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes(), PsiExpressionEnumMacro.NAME))
         registerFormFieldTemplate(IScoutRuntimeTypes.ILongField, TemplateDescriptor("templates.LongField")
                 .withName(message("template.LongField"))
                 .withAliasNames(message("template.NumberField"), message("template.IntegerField"))
@@ -102,12 +102,12 @@
                 .withName(message("template.RadioButtonGroup"))
                 .withSuperClassInfo(IScoutRuntimeTypes.IRadioButtonGroup, IScoutRuntimeTypes.AbstractRadioButtonGroup)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MyRadioButton")
-                .withVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes()))
+                .withEnumVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes(), PsiExpressionEnumMacro.NAME))
         registerFormFieldTemplate(IScoutRuntimeTypes.ISequenceBox, TemplateDescriptor("templates.SequenceBox")
                 .withName(message("template.SequenceBox"))
                 .withSuperClassInfo(IScoutRuntimeTypes.ISequenceBox, IScoutRuntimeTypes.AbstractSequenceBox)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MySequence")
-                .withVariable("boolAutoCheckFromTo", "false"))
+                .withBoolVariable("boolAutoCheckFromTo", false))
         registerFormFieldTemplate(IScoutRuntimeTypes.ITabBox, TemplateDescriptor("templates.TabBox")
                 .withName(message("template.TabBox"))
                 .withSuperClassInfo(IScoutRuntimeTypes.ITabBox, IScoutRuntimeTypes.AbstractTabBox)
@@ -129,8 +129,8 @@
                 .withSuperClassInfo(IScoutRuntimeTypes.IImageField, IScoutRuntimeTypes.AbstractImageField)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MyImage")
                 .withVariable("width", "2")
-                .withVariable("boolAutoFit", "true")
-                .withVariable("boolLabelVisible", "false"))
+                .withBoolVariable("boolAutoFit", true)
+                .withBoolVariable("boolLabelVisible", false))
         registerFormFieldTemplate(IScoutRuntimeTypes.IFileChooserButton, TemplateDescriptor("templates.FileChooserButton")
                 .withName(message("template.FileChooserButton"))
                 .withSuperClassInfo(IScoutRuntimeTypes.IFileChooserButton, IScoutRuntimeTypes.AbstractFileChooserButton)
@@ -144,15 +144,15 @@
                 .withSuperClassInfo(IScoutRuntimeTypes.IModeSelectorField, IScoutRuntimeTypes.AbstractModeSelectorField)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MyModeSelector")
                 .withVariable("width", "2")
-                .withVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes()))
+                .withEnumVariable(VALUE_FIELD_TYPE_VARIABLE_NAME, valueFieldTypes(), PsiExpressionEnumMacro.NAME))
         registerFormFieldTemplate(IScoutRuntimeTypes.IBrowserField, TemplateDescriptor("templates.BrowserField")
                 .withName(message("template.BrowserField"))
                 .withSuperClassInfo(IScoutRuntimeTypes.IBrowserField, IScoutRuntimeTypes.AbstractBrowserField)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MyBrowser")
                 .withVariable("width", "2")
                 .withVariable("height", "6")
-                .withVariable("boolLabelVisible", "false")
-                .withVariable("boolScrollBarEnabled", "true"))
+                .withBoolVariable("boolLabelVisible", false)
+                .withBoolVariable("boolScrollBarEnabled", true))
         registerFormFieldTemplate(IScoutRuntimeTypes.ITileField, TemplateDescriptor("templates.TileField")
                 .withName(message("template.TileField"))
                 .withSuperClassInfo(IScoutRuntimeTypes.ITileField, IScoutRuntimeTypes.AbstractTileField)
@@ -161,14 +161,14 @@
                 .withName(message("template.AccordionField"))
                 .withSuperClassInfo(IScoutRuntimeTypes.IAccordionField, IScoutRuntimeTypes.AbstractAccordionField)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "MyAccordion")
-                .withVariable("boolLabelVisible", "false"))
+                .withBoolVariable("boolLabelVisible", false))
 
         registerTemplate(IScoutRuntimeTypes.IMenu, TemplateDescriptor("templates.Menu")
                 .withName(message("template.Menu"))
                 .withOrderDefinitionType(IScoutRuntimeTypes.IMenu)
                 .withSuperClassInfo(IScoutRuntimeTypes.IMenu, IScoutRuntimeTypes.AbstractMenu)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "My")
-                .withVariable(TemplateDescriptor.VARIABLE_PREFIX_NLS, "MyMenuName"))
+                .withNlsVariable("nls", "MyMenuName"))
         registerTemplate(IScoutRuntimeTypes.IKeyStroke, TemplateDescriptor("templates.KeyStroke")
                 .withName(message("template.KeyStroke"))
                 .withOrderDefinitionType(IScoutRuntimeTypes.IKeyStroke)
@@ -179,18 +179,18 @@
                 .withOrderDefinitionType(IScoutRuntimeTypes.ICode)
                 .withSuperClassInfo(IScoutRuntimeTypes.ICode, IScoutRuntimeTypes.AbstractCode)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "My")
-                .withVariable(TemplateDescriptor.VARIABLE_PREFIX_NLS, "MyCodeName"))
+                .withNlsVariable("nls", "MyCodeName"))
         registerTemplate(IScoutRuntimeTypes.IFormHandler, TemplateDescriptor("templates.FormHandler")
                 .withName(message("template.FormHandler"))
                 .withSuperClassInfo(IScoutRuntimeTypes.IFormHandler, IScoutRuntimeTypes.AbstractFormHandler)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "My"))
         registerTemplate(IScoutRuntimeTypes.IColumn, TemplateDescriptor("templates.Column")
                 .withName(message("template.Column"))
-                .withInnerTypeGetterContainer(IScoutRuntimeTypes.ITable, "getColumnSet().getColumnByClass")
+                .withInnerTypeGetterInfo(IScoutRuntimeTypes.ITable, "getColumnSet().getColumnByClass", TemplateDescriptor.InnerTypeGetterLookupType.CLOSEST)
                 .withOrderDefinitionType(IScoutRuntimeTypes.IColumn)
                 .withSuperClassInfo(IScoutRuntimeTypes.IColumn, IScoutRuntimeTypes.AbstractStringColumn)
                 .withVariable(TemplateDescriptor.VARIABLE_NAME, "My")
-                .withVariable(TemplateDescriptor.VARIABLE_PREFIX_NLS, "MyColumnName")
+                .withNlsVariable("nls", "MyColumnName")
                 .withVariable("width", "100"))
         registerTemplate(IScoutRuntimeTypes.IExtension, TemplateDescriptor("templates.Extension")
                 .withName(message("template.Extension"))
@@ -212,9 +212,12 @@
 
     private fun registerFormFieldTemplate(definitionInterface: String, descriptor: TemplateDescriptor) =
             registerTemplate(definitionInterface, descriptor
-                    .withInnerTypeGetterContainer(IScoutRuntimeTypes.IForm, "getFieldByClass")
+                    .withInnerTypeGetterInfo(IScoutRuntimeTypes.IForm, "getFieldByClass", TemplateDescriptor.InnerTypeGetterLookupType.CLOSEST)
+                    .withInnerTypeGetterInfo(IScoutRuntimeTypes.ICompositeField, "getFieldByClass", TemplateDescriptor.InnerTypeGetterLookupType.FARTHEST)
+                    .withInnerTypeGetterInfo(IScoutRuntimeTypes.IFormExtension, "getOwner().getFieldByClass", TemplateDescriptor.InnerTypeGetterLookupType.CLOSEST)
+                    .withInnerTypeGetterInfo(IScoutRuntimeTypes.ICompositeFieldExtension, "getOwner().getFieldByClass", TemplateDescriptor.InnerTypeGetterLookupType.FARTHEST)
                     .withOrderDefinitionType(IScoutRuntimeTypes.IFormField)
-                    .withVariable(TemplateDescriptor.VARIABLE_PREFIX_NLS, "MyNlsKey"))
+                    .withNlsVariable("nls", "MyNlsKey"))
 
     private fun registerTemplate(definitionInterface: String, descriptor: TemplateDescriptor) = m_builtInTemplates.put(definitionInterface, descriptor)
 
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/AbstractClassesEnumVariableAdapter.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/AbstractClassesEnumVariableAdapter.kt
new file mode 100644
index 0000000..607fde5
--- /dev/null
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/AbstractClassesEnumVariableAdapter.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * 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:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.sdk.s2i.template.variable
+
+import org.eclipse.scout.sdk.core.util.Ensure.newFail
+import org.eclipse.scout.sdk.core.util.Strings
+import org.eclipse.scout.sdk.s2i.template.DescendantAbstractClassesEnumMacro
+import org.eclipse.scout.sdk.s2i.template.TemplateEngine
+
+open class AbstractClassesEnumVariableAdapter(val name: String) : (TemplateEngine) -> VariableDescriptor? {
+
+    override fun invoke(context: TemplateEngine): VariableDescriptor? {
+        val superClassInfo = context.templateDescriptor.superClassInfo() ?: throw newFail("No super class info specified for variable '{}'.", name)
+        val baseFqn = Strings.toStringLiteral(superClassInfo.baseFqn)
+        val defaultValue = Strings.toStringLiteral(superClassInfo.defaultValue)
+        return VariableDescriptor(name, "${DescendantAbstractClassesEnumMacro.NAME}($baseFqn, $defaultValue)", defaultValue.toString())
+    }
+}
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/BoolVariableAdapter.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/BoolVariableAdapter.kt
new file mode 100644
index 0000000..2daab2a
--- /dev/null
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/BoolVariableAdapter.kt
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * 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:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.sdk.s2i.template.variable
+
+class BoolVariableAdapter(name: String, defaultValue: String) : EnumVariableAdapter(name, null, listOf(defaultValue, if ("true" == defaultValue) "false" else "true"))
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/EnumVariableAdapter.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/EnumVariableAdapter.kt
new file mode 100644
index 0000000..6c10f66
--- /dev/null
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/EnumVariableAdapter.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * 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:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.sdk.s2i.template.variable
+
+import org.eclipse.scout.sdk.core.util.Strings
+import org.eclipse.scout.sdk.s2i.template.TemplateEngine
+
+open class EnumVariableAdapter(val name: String, val enumMacroName: CharSequence? = null, val optionsSupplier: (TemplateEngine) -> Iterable<CharSequence>) : (TemplateEngine) -> VariableDescriptor? {
+
+    constructor(name: String, enumMacroName: CharSequence?, options: Iterable<CharSequence>) : this(name, enumMacroName, { options })
+
+    override fun invoke(context: TemplateEngine): VariableDescriptor? {
+        val options = optionsSupplier.invoke(context)
+        val defaultValue = Strings.toStringLiteral(options.firstOrNull())?.toString() ?: ""
+        return VariableDescriptor(name, toEnum(options, enumMacroName ?: "enum"), defaultValue)
+    }
+
+    protected fun toEnum(options: Iterable<CharSequence>, enumMacroName: CharSequence) = options.joinToString(", ", "$enumMacroName(", ")") { Strings.toStringLiteral(it) }
+}
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/VariableDescriptor.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/VariableDescriptor.kt
new file mode 100644
index 0000000..92846de
--- /dev/null
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/template/variable/VariableDescriptor.kt
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * 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:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.sdk.s2i.template.variable
+
+data class VariableDescriptor(val name: String, val expression: String?, val defaultValueExpression: String? = null)
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/util/VelocityRunner.kt b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/util/VelocityRunner.kt
new file mode 100644
index 0000000..ca711af
--- /dev/null
+++ b/org.eclipse.scout.sdk.s2i/src/main/kotlin/org/eclipse/scout/sdk/s2i/util/VelocityRunner.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * 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:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.sdk.s2i.util
+
+import org.apache.velocity.VelocityContext
+import org.eclipse.scout.sdk.core.util.Strings
+import org.eclipse.scout.sdk.s2i.replaceAll
+import org.jetbrains.java.generate.velocity.VelocityFactory
+import java.io.StringWriter
+import java.util.regex.Pattern
+
+class VelocityRunner {
+
+    companion object {
+        const val DOLLAR = "dollar"
+    }
+
+    private val m_properties = HashMap<String, Any>()
+    private val m_postProcessors = HashMap<Pattern, (java.util.regex.MatchResult) -> String>()
+
+    init {
+        withProperty(DOLLAR, "$")
+    }
+
+    fun withPostProcessor(pattern: Pattern, replacer: (java.util.regex.MatchResult) -> String): VelocityRunner {
+        m_postProcessors[pattern] = replacer
+        return this
+    }
+
+    fun withProperty(name: String, value: Any): VelocityRunner {
+        m_properties[name] = value
+        return this
+    }
+
+    fun withProperties(props: Map<String, Any>): VelocityRunner {
+        m_properties.putAll(props)
+        return this
+    }
+
+    fun eval(template: String): CharSequence {
+        var evaluated = evalVelocity(template)
+        for (postProcessor in m_postProcessors) {
+            val pattern = postProcessor.key
+            val replacer = postProcessor.value
+            evaluated = pattern.replaceAll(evaluated, replacer)
+        }
+        return evaluated
+    }
+
+    private fun evalVelocity(template: String): CharSequence {
+        val out = StringWriter()
+        val context = VelocityContext(m_properties)
+        VelocityFactory.getVelocityEngine().evaluate(context, out, VelocityRunner::class.java.name, template)
+        return Strings.replace(out.buffer, "\r", "")
+    }
+}
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/AccordionField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/AccordionField.txt
index 565120a..4e4cd13 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/AccordionField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/AccordionField.txt
@@ -1,7 +1,7 @@
-public class $name$Field extends $super$<$name$Field.Accordion> {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar}<${dollar}name${dollar}Field.Accordion> {
   @java.lang.Override
   protected boolean getConfiguredLabelVisible() {
-    return $boolLabelVisible$;
+    return ${dollar}boolLabelVisible${dollar};
   }
 
   @java.lang.Override
@@ -9,8 +9,8 @@
     return FULL_WIDTH;
   }
 
-  @#ClassId#("$newClassId()$")
-  public class Accordion extends #AbstractAccordion# {
-    $END$
+  @${ClassId}("${dollar}newClassId()${dollar}")
+  public class Accordion extends ${AbstractAccordion} {
+    ${dollar}END${dollar}
   }
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BigDecimalField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BigDecimalField.txt
index a5fbb8f..240ffd5 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BigDecimalField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BigDecimalField.txt
@@ -1,16 +1,16 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected java.math.BigDecimal getConfiguredMinValue() {
-    return new java.math.BigDecimal("$min$");
+    return new java.math.BigDecimal("${dollar}min${dollar}");
   }
 
   @java.lang.Override
   protected java.math.BigDecimal getConfiguredMaxValue() {
-    return new java.math.BigDecimal("$max$");
-  }$END$
+    return new java.math.BigDecimal("${dollar}max${dollar}");
+  }${dollar}END${dollar}
 }
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BooleanField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BooleanField.txt
index 025885e..68f33d7 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BooleanField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BooleanField.txt
@@ -1,6 +1,6 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BrowserField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BrowserField.txt
index 28e5b4b..0cbfd3d 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BrowserField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/BrowserField.txt
@@ -1,21 +1,21 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected int getConfiguredGridH() {
-    return $height$;
+    return ${dollar}height${dollar};
   }
 
   @java.lang.Override
   protected int getConfiguredGridW() {
-    return $width$;
+    return ${dollar}width${dollar};
   }
 
   @java.lang.Override
   protected boolean getConfiguredLabelVisible() {
-    return $boolLabelVisible$;
+    return ${dollar}boolLabelVisible${dollar};
   }
 
   @java.lang.Override
   protected boolean getConfiguredScrollBarEnabled() {
-    return $boolScrollBarEnabled$;
-  }$END$
+    return ${dollar}boolScrollBarEnabled${dollar};
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Button.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Button.txt
index 4d8c9c3..87b8b8b 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Button.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Button.txt
@@ -1,11 +1,11 @@
-public class $name$Button extends $super$ {
+public class ${dollar}name${dollar}Button extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected void execClickAction() {
-    $END$
+    ${dollar}END${dollar}
   }
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/CalendarField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/CalendarField.txt
index a8c9fb7..1ebaeae 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/CalendarField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/CalendarField.txt
@@ -1,9 +1,9 @@
-public class $name$Field extends $super$<$name$Field.Calendar> {
-  @#ClassId#("$newClassId()$")
-  public class Calendar extends #AbstractCalendar# {
-    @#Order#(1000)
-    public class MyCalendarItemProvider extends #AbstractCalendarItemProvider# {
-      $END$
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar}<${dollar}name${dollar}Field.Calendar> {
+  @${ClassId}("${dollar}newClassId()${dollar}")
+  public class Calendar extends ${AbstractCalendar} {
+    @${Order}(1000)
+    public class MyCalendarItemProvider extends ${AbstractCalendarItemProvider} {
+      ${dollar}END${dollar}
     }
   }
 
@@ -14,6 +14,6 @@
 
   @java.lang.Override
   protected boolean getConfiguredLabelVisible() {
-    return $boolLabelVisible$;
+    return ${dollar}boolLabelVisible${dollar};
   }
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Code.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Code.txt
index c8a6fb1..6d87bba 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Code.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Code.txt
@@ -1,14 +1,14 @@
-public static class $name$Code extends $super$<$declaringTypeArg(#ICodeType#, #TYPE_PARAM_CODETYPE__CODE_ID#, box)$> {
+public static class ${dollar}name${dollar}Code extends ${dollar}super${dollar}<${dollar}declaringTypeArg(${ICodeType}, ${TYPE_PARAM_CODETYPE__CODE_ID}, box)${dollar}> {
   private static final long serialVersionUID = 1L;
-  public static final $declaringTypeArg(#ICodeType#, #TYPE_PARAM_CODETYPE__CODE_ID#, unbox)$ ID = $declaringTypeArg(#ICodeType#, #TYPE_PARAM_CODETYPE__CODE_ID#, default)$;
+  public static final ${dollar}declaringTypeArg(${ICodeType}, ${TYPE_PARAM_CODETYPE__CODE_ID}, unbox)${dollar} ID = ${dollar}declaringTypeArg(${ICodeType}, ${TYPE_PARAM_CODETYPE__CODE_ID}, default)${dollar};
 
   @java.lang.Override
   protected java.lang.String getConfiguredText() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
-  public $declaringTypeArg(#ICodeType#, #TYPE_PARAM_CODETYPE__CODE_ID#, box)$ getId() {
+  public ${dollar}declaringTypeArg(${ICodeType}, ${TYPE_PARAM_CODETYPE__CODE_ID}, box)${dollar} getId() {
     return ID;
-  }$END$
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Column.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Column.txt
index bef46fe..6e2b48b 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Column.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Column.txt
@@ -1,11 +1,11 @@
-public class $name$Column extends $super$ {
+public class ${dollar}name${dollar}Column extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredHeaderText() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected int getConfiguredWidth() {
-    return $width$;
-  }$END$
+    return ${dollar}width${dollar};
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/DateField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/DateField.txt
index 025885e..68f33d7 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/DateField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/DateField.txt
@@ -1,6 +1,6 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Extension.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Extension.txt
index 8742f3c..305d98d 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Extension.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Extension.txt
@@ -1,5 +1,5 @@
-public class $name$Extension extends $super$<$complete$> {
-  public $name$Extension($complete$ owner) {
+public class ${dollar}name${dollar}Extension extends ${dollar}super${dollar}<${dollar}complete${dollar}> {
+  public ${dollar}name${dollar}Extension(${dollar}complete${dollar} owner) {
     super(owner);
-  }$END$
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FileChooserButton.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FileChooserButton.txt
index d47e553..84539f8 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FileChooserButton.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FileChooserButton.txt
@@ -1,6 +1,6 @@
-public class $name$Button extends $super$ {
+public class ${dollar}name${dollar}Button extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FileChooserField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FileChooserField.txt
index 025885e..68f33d7 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FileChooserField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FileChooserField.txt
@@ -1,6 +1,6 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FormHandler.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FormHandler.txt
index 969f346..475b124 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FormHandler.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/FormHandler.txt
@@ -1,7 +1,7 @@
-public $staticIfInExtension$ class $name$Handler extends $super$ {
+public #if( $inExtension ) static #end class ${dollar}name${dollar}Handler extends ${dollar}super${dollar} {
   @java.lang.Override
   protected void execLoad() {
-    $END$
+    ${dollar}END$
   }
 
   @java.lang.Override
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Group.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Group.txt
index 890af72..a2542b0 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Group.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Group.txt
@@ -1,6 +1,6 @@
-public class $name$Group extends $super$ {
-  @#ClassId#("$newClassId()$")
-  public class Body extends #AbstractTileGrid#<#ITile#> {
-   $END$
+public class ${dollar}name${dollar}Group extends ${dollar}super${dollar} {
+  @${ClassId}("${dollar}newClassId()${dollar}")
+  public class Body extends ${AbstractTileGrid}<${ITile}> {
+   ${dollar}END${dollar}
   }
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/GroupBox.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/GroupBox.txt
index 6320368..812fdde 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/GroupBox.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/GroupBox.txt
@@ -1,6 +1,6 @@
-public class $name$Box extends $super$ {
+public class ${dollar}name${dollar}Box extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/HtmlField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/HtmlField.txt
index 025885e..68f33d7 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/HtmlField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/HtmlField.txt
@@ -1,6 +1,6 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ImageField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ImageField.txt
index 58c80d4..8b5aab9 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ImageField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ImageField.txt
@@ -1,21 +1,21 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected boolean getConfiguredAutoFit() {
-    return $boolAutoFit$;
+    return ${dollar}boolAutoFit${dollar};
   }
 
   @java.lang.Override
   protected int getConfiguredGridH() {
-    return $width$;
+    return ${dollar}width${dollar};
   }
 
   @java.lang.Override
   protected boolean getConfiguredLabelVisible() {
-    return $boolLabelVisible$;
-  }$END$
+    return ${dollar}boolLabelVisible${dollar};
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/KeyStroke.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/KeyStroke.txt
index f811ed0..d4981aa 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/KeyStroke.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/KeyStroke.txt
@@ -1,11 +1,11 @@
-public class $name$KeyStroke extends $super$ {
+public class ${dollar}name${dollar}KeyStroke extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredKeyStroke() {
-    return $keystrokes$;
+    return ${dollar}keystrokes${dollar};
   }
 
   @java.lang.Override
   protected void execAction() {
-    $END$
+    ${dollar}END${dollar}
   }
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/LabelField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/LabelField.txt
index a5c2025..e0c9b67 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/LabelField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/LabelField.txt
@@ -1,7 +1,7 @@
-@#FormData#(sdkCommand = #FormData#.SdkCommand.IGNORE)
-public class $name$Field extends $super$ {
+@${FormData}(sdkCommand = ${FormData}.SdkCommand.IGNORE)
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ListBox.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ListBox.txt
index 54462dd..9d59c3e 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ListBox.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ListBox.txt
@@ -1,11 +1,11 @@
-public class $name$Box extends $super$<$type$> {
+public class ${dollar}name${dollar}Box extends ${dollar}super${dollar}<${dollar}type${dollar}> {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected int getConfiguredGridH() {
     return 6;
-  }$END$
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/LongField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/LongField.txt
index bc8375c..6a29fbf 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/LongField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/LongField.txt
@@ -1,16 +1,16 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected java.lang.Long getConfiguredMinValue() {
-    return $min$L;
+    return ${dollar}min${dollar}L;
   }
 
   @java.lang.Override
   protected java.lang.Long getConfiguredMaxValue() {
-    return $max$L;
-  }$END$
+    return ${dollar}max${dollar}L;
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Menu.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Menu.txt
index 0a6f105..21c7a96 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Menu.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Menu.txt
@@ -1,12 +1,18 @@
-public class $name$Menu extends $super$ {
+public class ${dollar}name${dollar}Menu extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredText() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
-  $getConfiguredMenuTypes$
+  #if( $menuSupported )
+  @java.lang.Override
+  protected java.util.Set<? extends ${IMenuType}> getConfiguredMenuTypes() {
+    return ${dollar}menuTypes${dollar};
+  }
+
+  #end
   @java.lang.Override
   protected void execAction() {
-    $END$
+    ${dollar}END$
   }
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ModeSelectorField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ModeSelectorField.txt
index 27302c1..a5370ab 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ModeSelectorField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ModeSelectorField.txt
@@ -1,29 +1,29 @@
-public class $name$Field extends $super$<$type$> {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar}<${dollar}type${dollar}> {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected int getConfiguredGridW() {
-    return $width$;
+    return ${dollar}width${dollar};
   }
 
-  @#Order#(1000)
-  @#ClassId#("$newClassId()$")
-  public class Mode1 extends #AbstractMode#<$type$> {
+  @${Order}(1000)
+  @${ClassId}("${dollar}newClassId()${dollar}")
+  public class Mode1 extends ${AbstractMode}<${dollar}type${dollar}> {
     @java.lang.Override
     protected java.lang.String getConfiguredText() {
       return "Mode 1";
     }
   }
 
-  @#Order#(2000)
-  @#ClassId#("$newClassId()$")
-  public class Mode2 extends #AbstractMode#<$type$> {
+  @${Order}(2000)
+  @${ClassId}("${dollar}newClassId()${dollar}")
+  public class Mode2 extends ${AbstractMode}<${dollar}type${dollar}> {
     @java.lang.Override
     protected java.lang.String getConfiguredText() {
       return "Mode 2";
     }
-  }$END$
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ProposalField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ProposalField.txt
index de470b7..0c51d70 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ProposalField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/ProposalField.txt
@@ -1,6 +1,6 @@
-public class $name$Field extends $super$<$type$> {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar}<${dollar}type${dollar}> {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/RadioButton.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/RadioButton.txt
index d0231b0..bc2ae31 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/RadioButton.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/RadioButton.txt
@@ -1,11 +1,11 @@
-public class $name$Button extends $super$<$declaringTypeArg(#IValueField#, #TYPE_PARAM_VALUEFIELD__VALUE#, box)$> {
+public class ${dollar}name${dollar}Button extends ${dollar}super${dollar}<${dollar}declaringTypeArg(${IValueField}, ${TYPE_PARAM_VALUEFIELD__VALUE}, box)${dollar}> {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @Override
-  protected $declaringTypeArg(#IValueField#, #TYPE_PARAM_VALUEFIELD__VALUE#, box)$ getConfiguredRadioValue() {
-    return $declaringTypeArg(#IValueField#, #TYPE_PARAM_VALUEFIELD__VALUE#, default)$;
-  }$END$
+  protected ${dollar}declaringTypeArg(${IValueField}, ${TYPE_PARAM_VALUEFIELD__VALUE}, box)${dollar} getConfiguredRadioValue() {
+    return ${dollar}declaringTypeArg(${IValueField}, ${TYPE_PARAM_VALUEFIELD__VALUE}, default)${dollar};
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/RadioButtonGroup.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/RadioButtonGroup.txt
index 9275da2..749afdd 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/RadioButtonGroup.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/RadioButtonGroup.txt
@@ -1,6 +1,6 @@
-public class $name$Group extends $super$<$type$> {
+public class ${dollar}name${dollar}Group extends ${dollar}super${dollar}<${dollar}type${dollar}> {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/SequenceBox.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/SequenceBox.txt
index 0b3696e..e40c678 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/SequenceBox.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/SequenceBox.txt
@@ -1,11 +1,11 @@
-public class $name$Box extends $super$ {
+public class ${dollar}name${dollar}Box extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected boolean getConfiguredAutoCheckFromTo() {
-    return $boolAutoCheckFromTo$;
-  }$END$
+    return ${dollar}boolAutoCheckFromTo${dollar};
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/SmartField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/SmartField.txt
index de470b7..0c51d70 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/SmartField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/SmartField.txt
@@ -1,6 +1,6 @@
-public class $name$Field extends $super$<$type$> {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar}<${dollar}type${dollar}> {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/StringField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/StringField.txt
index 9349061..3b7056c 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/StringField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/StringField.txt
@@ -1,11 +1,11 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
   protected int getConfiguredMaxLength() {
-    return $max$;
-  }$END$
+    return ${dollar}max${dollar};
+  }${dollar}END${dollar}
 }
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TabBox.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TabBox.txt
index ac72199..06bd00c 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TabBox.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TabBox.txt
@@ -1,3 +1,3 @@
-public class $name$Box extends $super$ {
-  $END$
+public class ${dollar}name${dollar}Box extends ${dollar}super${dollar} {
+  ${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TableField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TableField.txt
index e2bfe4d..c2b083b 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TableField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TableField.txt
@@ -1,7 +1,7 @@
-public class $name$Field extends $super$<$name$Field.Table> {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar}<${dollar}name${dollar}Field.Table> {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
@@ -9,8 +9,8 @@
     return 6;
   }
 
-  @#ClassId#("$newClassId()$")
-  public class Table extends #AbstractTable# {
-    $END$
+  @${ClassId}("${dollar}newClassId()${dollar}")
+  public class Table extends ${AbstractTable} {
+    ${dollar}END${dollar}
   }
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TagField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TagField.txt
index 025885e..68f33d7 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TagField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TagField.txt
@@ -1,6 +1,6 @@
-public class $name$Field extends $super$ {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
-  }$END$
+    return ${TEXTS}.get("${dollar}nls${dollar}");
+  }${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Tile.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Tile.txt
index 507cbce..be1670f 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Tile.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/Tile.txt
@@ -1,3 +1,3 @@
-public class $name$Tile extends $super$ {
-  $END$
+public class ${dollar}name${dollar}Tile extends ${dollar}super${dollar} {
+  ${dollar}END${dollar}
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TileField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TileField.txt
index bb97ec8..4f6270f 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TileField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TileField.txt
@@ -1,7 +1,7 @@
-public class $name$Field extends $super$<$name$Field.TileGrid> {
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar}<${dollar}name${dollar}Field.TileGrid> {
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
@@ -9,8 +9,8 @@
     return FULL_WIDTH;
   }
 
-  @#ClassId#("$newClassId()$")
-  public class TileGrid extends #AbstractTileGrid#<#ITile#> {
-   $END$
+  @${ClassId}("${dollar}newClassId()${dollar}")
+  public class TileGrid extends ${AbstractTileGrid}<${ITile}> {
+   ${dollar}END${dollar}
   }
 }
\ No newline at end of file
diff --git a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TreeField.txt b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TreeField.txt
index 73f7ff9..d29de1b 100644
--- a/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TreeField.txt
+++ b/org.eclipse.scout.sdk.s2i/src/main/resources/templates/TreeField.txt
@@ -1,12 +1,12 @@
-public class $name$Field extends $super$ {
-  @#ClassId#("$newClassId()$")
-  public class Tree extends #AbstractTree# {
-    $END$
+public class ${dollar}name${dollar}Field extends ${dollar}super${dollar} {
+  @${ClassId}("${dollar}newClassId()${dollar}")
+  public class Tree extends ${AbstractTree} {
+    ${dollar}END${dollar}
   }
 
   @java.lang.Override
   protected java.lang.String getConfiguredLabel() {
-    return #TEXTS#.get("$nls$");
+    return ${TEXTS}.get("${dollar}nls${dollar}");
   }
 
   @java.lang.Override
diff --git a/org.eclipse.scout.sdk.s2i/src/test/kotlin/org/eclipse/scout/sdk/s2i/util/VelocityRunnerTest.kt b/org.eclipse.scout.sdk.s2i/src/test/kotlin/org/eclipse/scout/sdk/s2i/util/VelocityRunnerTest.kt
new file mode 100644
index 0000000..2fbdc7d
--- /dev/null
+++ b/org.eclipse.scout.sdk.s2i/src/test/kotlin/org/eclipse/scout/sdk/s2i/util/VelocityRunnerTest.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010-2020 BSI Business Systems Integration AG.
+ * 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:
+ *     BSI Business Systems Integration AG - initial API and implementation
+ */
+package org.eclipse.scout.sdk.s2i.util
+
+import junit.framework.TestCase
+import org.eclipse.scout.sdk.core.s.IScoutRuntimeTypes
+import java.util.regex.Pattern
+
+class VelocityRunnerTest : TestCase() {
+
+    /**
+     * tests that properties are replaced and that if variables can be used
+     */
+    fun testReplaceAndIf() {
+        val result = VelocityRunner()
+                .withProperty("TEXTS", IScoutRuntimeTypes.TEXTS)
+                .withProperty("dd", false)
+                .eval("start\$nls\$end\n\${TEXTS}.get()\n\$dd\n#if( \$dd )static #end class\$dollar")
+        assertEquals("start\$nls\$end\n${IScoutRuntimeTypes.TEXTS}.get()\nfalse\n class$", result.toString())
+    }
+
+    /**
+     * Tests that the post processors are executed after the velocity evaluation
+     */
+    fun testWithPostProcessor() {
+        val result = VelocityRunner()
+                .withProperty("xx", "zz")
+                .withPostProcessor(Pattern.compile("zz(\\d+)zz")) { '>' + it.group(1) + '<' }
+                .eval("\${xx}123\${xx}\${xx}456\${xx}")
+        assertEquals(">123<>456<", result.toString())
+    }
+}
\ No newline at end of file