diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a5b301de2e3f121b8dd7a09ea9fe0b667da4c369
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,178 @@
+#### joe made this: http://goel.io/joe
+
+#### scala ####
+*.class
+*.log
+
+
+#### sbt ####
+# Simple Build Tool
+# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
+
+dist/*
+target/
+lib_managed/
+src_managed/
+project/boot/
+project/plugins/project/
+.history
+.cache
+.lib/
+
+
+#### jetbrains ####
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn.  Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+
+#### java ####
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+
+#### eclipse ####
+
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# CDT- autotools
+.autotools
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Annotation Processing
+.apt_generated/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+#### Custom (user defined) insertions ####
+.idea/
+.classpath
+.project
+output/
+doc/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..5ed635731bd2541c2765d6ab654a00c411e18bbc
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,9 @@
+[submodule "lib/ModelSyncProvider"]
+	path = lib/ModelSyncProvider
+	url = https://git-st.inf.tu-dresden.de/cwerner/role_model_synchronization_provider.git
+[submodule "lib/SCROLL"]
+	path = lib/SCROLL
+	url = https://github.com/portux/SCROLL.git
+[submodule "src/main/asciidoc"]
+	path = src/main/doc
+	url = https://git-st.inf.tu-dresden.de/cwerner/code_generator.wiki.git
diff --git a/assets/family.png b/assets/family.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2d93ecd8b345770d27b6847e4af6ca1aff673d0
Binary files /dev/null and b/assets/family.png differ
diff --git a/assets/ttc17.ecore b/assets/ttc17.ecore
new file mode 100644
index 0000000000000000000000000000000000000000..4768ec7123e7e93faa9a3a93a1d283b0fd7f84e5
--- /dev/null
+++ b/assets/ttc17.ecore
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="ttc17" nsURI="http://www.example.org/ttc17" nsPrefix="ttc17">
+  <eClassifiers xsi:type="ecore:EClass" name="Person">
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="fullName" unique="false"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="birthday" unique="false"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDate"/>
+  </eClassifiers>
+  <eClassifiers xsi:type="ecore:EClass" name="Male" eSuperTypes="#//Person"/>
+  <eClassifiers xsi:type="ecore:EClass" name="Female" eSuperTypes="#//Person"/>
+  <eClassifiers xsi:type="ecore:EClass" name="Family">
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="lastName" unique="false"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="mother" eType="#//Member"
+        containment="true" eOpposite="#//Member/familyMother"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="father" eType="#//Member"
+        containment="true" eOpposite="#//Member/familyFather"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="familySons" upperBound="-1"
+        eType="#//Member" containment="true" eOpposite="#//Member/familySon"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="familyDaughters" upperBound="-1"
+        eType="#//Member" containment="true" eOpposite="#//Member/familyDaughter"/>
+  </eClassifiers>
+  <eClassifiers xsi:type="ecore:EClass" name="Member">
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="firstName" unique="false"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="familyMother" eType="#//Family"
+        eOpposite="#//Family/mother"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="familyFather" eType="#//Family"
+        eOpposite="#//Family/father"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="familySon" eType="#//Family"
+        eOpposite="#//Family/familySons"/>
+    <eStructuralFeatures xsi:type="ecore:EReference" name="familyDaughter" eType="#//Family"
+        eOpposite="#//Family/familyDaughters"/>
+  </eClassifiers>
+  <eClassifiers xsi:type="ecore:EClass" name="SimplePerson">
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="completeName" unique="false"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="address" unique="false"
+        eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
+    <eStructuralFeatures xsi:type="ecore:EAttribute" name="male" unique="false" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
+  </eClassifiers>
+</ecore:EPackage>
diff --git a/assets/ttc17.sync.json b/assets/ttc17.sync.json
new file mode 100644
index 0000000000000000000000000000000000000000..2315bd5c9df40c6f2b5277acd599417901897f40
--- /dev/null
+++ b/assets/ttc17.sync.json
@@ -0,0 +1,31 @@
+{
+  "name": "TTC 17",
+  "init": {
+    "name": "ModelB",
+    "primaryClass": "ttc17.Family",
+    "image": "family.png",
+    "nested": [
+      {
+        "name": "Model B - members",
+        "primaryClass": "ttc17.Member"
+      }
+    ]
+  },
+  "integration": [
+    {
+      "name": "ModelA",
+      "primaryClass": "ttc17.Person",
+      "additionalClasses": [
+        "ttc17.Female",
+        "ttc17.Male"
+      ],
+      "image": null,
+      "rule": "person.atl"
+    },
+    {
+      "name": "ModelC",
+      "primaryClass": "ttc17.SimplePerson",
+      "image": null
+    }
+  ]
+}
diff --git a/build.sbt b/build.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..24e4b959b4ea8a41f46d545de3fd7d7da6e30203
--- /dev/null
+++ b/build.sbt
@@ -0,0 +1,40 @@
+import sbt.Keys.{libraryDependencies, scalacOptions, version}
+
+val emfcommonVersion = "2.12.0"
+val emfecoreVersion = "2.12.0"
+val scrollVersion = "1.6"
+val scoptVersion = "3.7.0"
+val liftVersion = "3.3.0"
+
+val syncProvider = RootProject(file("lib/ModelSyncProvider"))
+
+lazy val generator = (project in file("."))
+  .settings(
+    name := "CodeGenerator",
+    version := "0.1",
+    scalaVersion := "2.12.6",
+    libraryDependencies ++= Seq(
+      "org.scala-lang" % "scala-reflect" % scalaVersion.value,
+      "org.scala-lang" % "scala-compiler" % scalaVersion.value,
+      "org.eclipse.emf" % "org.eclipse.emf.common" % emfcommonVersion,
+      "org.eclipse.emf" % "org.eclipse.emf.ecore" % emfecoreVersion,
+      "com.github.scopt" %% "scopt" % scoptVersion,
+      "net.liftweb" %% "lift-json" % liftVersion
+    ),
+    scalacOptions ++= Seq(
+      "-language:implicitConversions"
+    ),
+    mainClass in assembly := Some("org.rosi_project.model_sync.generator.Generator"),
+    assemblyMergeStrategy in assembly := {
+      case "MANIFEST.MF" => MergeStrategy.first
+      case "plugin.xml" => MergeStrategy.discard
+      case "plugin.properties" => MergeStrategy.discard
+      case "generated_package.exsd" => MergeStrategy.discard
+      case "dynamic_package.exsd" => MergeStrategy.discard
+      case PathList("schema", ps @ _ *) if ps.lastOption.exists(_.endsWith("generated_package.exsd")) => MergeStrategy.discard
+      case PathList("schema", ps @ _ *) if ps.lastOption.exists(_.endsWith("dynamic_package.exsd")) => MergeStrategy.discard
+      case x =>
+        val oldStrategy = (assemblyMergeStrategy in assembly).value
+        oldStrategy(x)
+    }
+  ).dependsOn(syncProvider)
diff --git a/lib/ModelSyncProvider b/lib/ModelSyncProvider
new file mode 160000
index 0000000000000000000000000000000000000000..ace8bdf5799dd89bd66d01782237123172bb0ec0
--- /dev/null
+++ b/lib/ModelSyncProvider
@@ -0,0 +1 @@
+Subproject commit ace8bdf5799dd89bd66d01782237123172bb0ec0
diff --git a/lib/SCROLL b/lib/SCROLL
new file mode 160000
index 0000000000000000000000000000000000000000..623765816af6d27387efb82dfb890bc4318c58a2
--- /dev/null
+++ b/lib/SCROLL
@@ -0,0 +1 @@
+Subproject commit 623765816af6d27387efb82dfb890bc4318c58a2
diff --git a/project/assembly.sbt b/project/assembly.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..d95475f16ffa0d9a0670795c8296b31fa24c76ec
--- /dev/null
+++ b/project/assembly.sbt
@@ -0,0 +1 @@
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")
diff --git a/project/build.properties b/project/build.properties
new file mode 100644
index 0000000000000000000000000000000000000000..8db5ca22266ea113c2b39c92dddb73c83e61ffee
--- /dev/null
+++ b/project/build.properties
@@ -0,0 +1 @@
+sbt.version = 1.2.1
\ No newline at end of file
diff --git a/project/plugins.sbt b/project/plugins.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..45a6f02d1880ea4c9bff889a194ab87adf467611
--- /dev/null
+++ b/project/plugins.sbt
@@ -0,0 +1,2 @@
+addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4")
+addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0")
diff --git a/src/main/doc b/src/main/doc
new file mode 160000
index 0000000000000000000000000000000000000000..89e2af4e060ae7c39c261fde55ba9140caf85853
--- /dev/null
+++ b/src/main/doc
@@ -0,0 +1 @@
+Subproject commit 89e2af4e060ae7c39c261fde55ba9140caf85853
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/CLIGenerator.scala b/src/main/scala/org/rosi_project/model_sync/generator/CLIGenerator.scala
new file mode 100644
index 0000000000000000000000000000000000000000..9f6af4d784e94a44459d35eb17e5c33159c54a02
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/CLIGenerator.scala
@@ -0,0 +1,50 @@
+package org.rosi_project.model_sync.generator
+
+import java.{io => jio}
+
+import scopt.OptionParser
+
+/** Main entry into the application. It is intended to run as a console application and should be
+  * called like the following: `generator [-o DIR] [-c] ECORE` with the following options:
+  *
+  *  - `-o | --outdir DIR` will write the generated `model.jar` to the directory `DIR`
+  *  - `-c | --cleanup` will remove the generated `.scala` files and only keep the `.class` in the
+  *   generated `.jar` file
+  *  - `ECORE` should specify the ecore (XML) files which contains the model to create the scala
+  *   files for
+  *
+  * @author Rico Bergmann
+  */
+object CLIGenerator extends App {
+
+  val parser = new OptionParser[GeneratorConfig](programName="modelgen") {
+    head("modelgen", "0.1")
+
+    arg[jio.File]("ecore").required().action( (ef, conf) =>
+      conf.copy(source = ef.getAbsolutePath)
+    ).text("The ecore (XML) file of the model")
+
+    opt[jio.File]('o', "outdir").optional().action( (dir, conf) =>
+      conf.copy(outDir =  dir)
+    ).text("The directory to place the generated model in (current dir by default)")
+
+    opt[jio.File]('w', "workdir").optional().action( (dir, conf) =>
+      conf.copy(workDir = dir)
+    ).text("The directory to place the generated .scala files in (temp dir by default)")
+
+    opt[jio.File]('m', "model").optional().action( (mj, conf) =>
+      conf.copy(modelFile = mj.getAbsolutePath)
+    ).text("The description of the model's components (mapped to the name of the ecore with .sync.json extension by default)")
+
+    opt[Unit]('c', "cleanup").optional().action( (_, conf) =>
+      conf.copy(cleanUp = true)
+    ).text("Remove the generated .scala files and only keep the compiled .class files")
+  }
+
+  parser.parse(args, GeneratorConfig()) match {
+    case Some(config) =>
+      new Generator(config).run()
+    case None =>
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/EcoreLoader.scala b/src/main/scala/org/rosi_project/model_sync/generator/EcoreLoader.scala
new file mode 100644
index 0000000000000000000000000000000000000000..6bd33b84fa74494b769718936aa11b759e4c5bea
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/EcoreLoader.scala
@@ -0,0 +1,29 @@
+package org.rosi_project.model_sync.generator
+
+import org.eclipse.emf.ecore.{EObject, EPackage}
+import scroll.internal.ecore.ECoreImporter
+
+/** Simple service to load an ECORE model from a file.
+  *
+  * @author Rico Bergmann
+  */
+class EcoreLoader extends ECoreImporter {
+
+  /** Fetches an ecore model from XML.
+    *
+    * @param path where to find the model
+    * @return the model described by the XML
+    */
+  def loadEcore(path: String = "assets/ttc17.ecore"): EPackage = {
+    this.path = path
+    val res = loadModel()
+    res.getContents.toArray(new Array[EObject](0)).toList.find(_.isInstanceOf[EPackage]).map((p: EObject) => p.asInstanceOf[EPackage]).orNull
+  }
+
+}
+
+/** Exception to indicate that the model images may not be copied into the JAR.
+  *
+  * @param cause the causing exception
+  */
+class EcoreLoadException(cause: Exception) extends RuntimeException(cause)
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/Generator.scala b/src/main/scala/org/rosi_project/model_sync/generator/Generator.scala
new file mode 100644
index 0000000000000000000000000000000000000000..69c91044992b22cb5d4733834f2136c4360e7409
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/Generator.scala
@@ -0,0 +1,74 @@
+package org.rosi_project.model_sync.generator
+
+import net.liftweb.json._
+import org.eclipse.emf.ecore._
+import org.rosi_project.model_sync.generator.conversion.SModelGenerator
+import org.rosi_project.model_sync.generator.env.{CompilationException, JarPackaginException}
+import org.rosi_project.model_sync.generator.io.{ClassWritingException, ModelImagePreparationException, SModelFSWriter}
+
+import scala.io.Source
+import scala.reflect.io.File
+
+/** The `Generator` runs the whole workflow of generating a JAR of compiled Scala source files
+  * based on an ECORE file of some sync model.
+  *
+  * @param config the configuration that tells the generator where to locate the necessary files.
+  *
+  * @author Rico Bergmann
+  */
+class Generator(config: GeneratorConfig) {
+
+  /**
+    * Starts the generation.
+    */
+  def run(): Unit = {
+    try {
+      val modelJson = Source.fromFile(config.getModelFile).getLines.mkString
+      implicit val jsonFormats: Formats = DefaultFormats
+      val modelCfg = parse(modelJson).extract[ModelConfig]
+
+      var ecoreModel: EPackage = null
+      try {
+        ecoreModel = new EcoreLoader loadEcore config.source
+      }  catch {
+        case e: Exception => throw new EcoreLoadException(e)
+      }
+
+      val sModel = new SModelGenerator convert ecoreModel
+      new SModelSyncPreparationService prepareModel(sModel, modelCfg)
+      // write the model and create the JAR
+      println("... Writing model")
+
+      if (config.hasWorkDir) {
+        sModel.accept(
+          new SModelFSWriter(
+            outputDir = File(config.outDir).toDirectory,
+            workingDir = File(config.workDir),
+            keepClassFiles = !config.cleanUp,
+            modelCfg = modelCfg,
+            currentDir = config.getModelPath))
+      } else {
+        sModel.accept(
+          new SModelFSWriter(
+            outputDir = File(config.outDir).toDirectory,
+            keepClassFiles = !config.cleanUp,
+            modelCfg = modelCfg,
+            currentDir = config.getModelPath))
+      }
+
+    }
+    catch {
+      case ele: EcoreLoadException =>
+        println(s"** ERROR ** could not load ecore model: $ele")
+      case mipe: ModelImagePreparationException =>
+        println(s"** ERROR ** could not prepare model images: $mipe")
+      case cwe: ClassWritingException =>
+        println(s"** ERROR ** could not write classes: $cwe")
+      case ce: CompilationException =>
+        println(s"** ERROR ** could not compile classes: $ce")
+      case jpe: JarPackaginException =>
+        println(s"** ERROR ** could not package JAR: $jpe")
+    }
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/GeneratorConfig.scala b/src/main/scala/org/rosi_project/model_sync/generator/GeneratorConfig.scala
new file mode 100644
index 0000000000000000000000000000000000000000..2d1a971fc94cf9febb32cd153100518c26525d98
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/GeneratorConfig.scala
@@ -0,0 +1,17 @@
+package org.rosi_project.model_sync.generator
+
+import java.io.File
+
+/** Wrapper for the different command line options for the [[Generator]]
+  *
+  * @author Rico Bergmann
+  */
+case class GeneratorConfig(source: String = "", cleanUp: Boolean = false, outDir: File = new File(System.getProperty("user.dir")), workDir: File = null, modelFile: String = "") {
+
+  def hasWorkDir: Boolean = workDir != null
+
+  def getModelFile: File = if (modelFile.isEmpty) new File(source.replace(".ecore", ".sync.json")) else new File(modelFile)
+
+  def getModelPath: File = new File(source).getParentFile
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/ModelConfig.scala b/src/main/scala/org/rosi_project/model_sync/generator/ModelConfig.scala
new file mode 100644
index 0000000000000000000000000000000000000000..abcd564426912f437e3bdb022225dcd0a16e372f
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/ModelConfig.scala
@@ -0,0 +1,53 @@
+package org.rosi_project.model_sync.generator
+
+import org.rosi_project.model_sync.provider.DisplayableModel
+import org.rosi_project.model_sync.provider.ModelSyncProvider
+
+/** Representation of a model configuration (i.e. a number of corresponding models). It tells the
+  * generator how the model looks like, which types are needed, etc.
+  *
+  * This class as well as the derived types form a a reification of ''JSON'' files.
+  *
+  * @param name the name of the model
+  * @param init the initial sub-model to display
+  * @param integration sub-models which should be integrable
+  *
+  * @see [[ModelSyncProvider]]
+  *
+  * @author Rico Bergmann
+  */
+case class ModelConfig(name: String, init: Model, integration: Option[List[Model]])
+
+/** Representation of a single model.
+  *
+  * Classes are expected to look like `package.ClassName`
+  *
+  * @param name the model's name
+  * @param primaryClass the main class of the model (see [[DisplayableModel.getInstanceClass]]
+  * @param additionalClasses derived classes of the model
+  * @param image graphical (e.g. UML) representation
+  * @param nested nested models
+  *
+  * @see [[DisplayableModel]]
+  */
+case class Model(name: String, primaryClass: String, additionalClasses: Option[List[String]], image: Option[String], nested: Option[List[Model]])
+
+/** The companion provides some utility methods.
+  */
+object Model {
+
+  /** Decomposes a model name into its package and class name components.
+    */
+  def parseClass(qualifiedName: String): (String, String) = {
+    val lastSeparator = qualifiedName.lastIndexOf(".")
+    val pckg = qualifiedName.substring(0, lastSeparator)
+    val className = qualifiedName.substring(lastSeparator + 1, qualifiedName.length)
+    (pckg, className)
+  }
+
+}
+
+/** Exception to indicate that a (''JSON'') model does not contain the expected data for the
+  * Generator to work correctly.
+  */
+class ModelDataException extends RuntimeException
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/SModelSyncPreparationService.scala b/src/main/scala/org/rosi_project/model_sync/generator/SModelSyncPreparationService.scala
new file mode 100644
index 0000000000000000000000000000000000000000..130f2527eae224b7fed30dcf4295e0b1ed7fa876
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/SModelSyncPreparationService.scala
@@ -0,0 +1,26 @@
+package org.rosi_project.model_sync.generator
+
+import org.rosi_project.model_sync.generator.acr_model.SModel
+import org.rosi_project.model_sync.generator.sync.{GetterSetterGeneratingVisitor, SyncEnhancingVisitor}
+
+/** Simple service to perform all the necessary adaptions of an [[SModel]] to make applicable for
+  * synchronization.
+  *
+  * @author Rico Bergmann
+  */
+class SModelSyncPreparationService {
+
+  /** Augments a [[SModel]] with the necessary methods and statements to make it usable in a
+    * synchronization context.
+    *
+    * @param sModel the model to augment
+    */
+  def prepareModel(sModel: SModel, modelCfg: ModelConfig): Unit = {
+    val getterSetterVisitor = new GetterSetterGeneratingVisitor
+    val syncNotificationVisitor = new SyncEnhancingVisitor(modelCfg)
+
+    sModel.accept(getterSetterVisitor)
+    sModel.accept(syncNotificationVisitor)
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/GenericSType.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/GenericSType.scala
new file mode 100644
index 0000000000000000000000000000000000000000..0a1cb48ef2b5a2fbbf3b46639e6ffc2a0e6f81ab
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/GenericSType.scala
@@ -0,0 +1,25 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Adds support for generic types to [[SType]] instances.
+  *
+  * @param name `this` type's name
+  * @param sPackage the package in which `this` type resides
+  * @param typeParam the type parameter (a.k.a.  the ''Generic'')
+  *
+  * @author Rico Bergmann
+  */
+class GenericSType(name: String, sPackage: String = "", val typeParam: STypedElement) extends SType(name, sPackage) {
+
+  override def getName: String = s"$name[${typeParam.getName}]"
+
+  override def getNecessaryImports: Set[SImport] = if (typeParam.getPackage.isEmpty || typeParam.getPackage == sPackage)
+    typeParam.getNecessaryImports + SImport(typeParam.getPackage, typeParam.getName) else typeParam.getNecessaryImports
+}
+
+/** The companion provides an `unapply` implementation.
+  */
+object GenericSType {
+
+  def unapply(arg: GenericSType): Option[(String, String, STypedElement)] = Some((arg.name, arg.sPackage, arg.typeParam))
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SAttribute.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SAttribute.scala
new file mode 100644
index 0000000000000000000000000000000000000000..72c7beaace32d66527a258b06268b1361bdb40dc
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SAttribute.scala
@@ -0,0 +1,21 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Representation of class attributes. This abstraction is a simplification of an actual scala
+  * attribute: it is impossible to distinguish between ''final'' and ''non-final'' fields. Instead,
+  * all attributes will be treated mutable.
+  *
+  * @author Rico Bergmann
+  */
+case class SAttribute(name: String, attrType: STypedElement) extends SModelElement {
+
+  /** Provides the type of `this` attribute as a `String`.
+    *
+    * @return the type. Will never be `null` nor empty.
+    */
+  def getType: String = attrType.getName
+
+  override def accept(visitor: SModelVisitor): Unit = visitor.visit(this)
+
+  override def toString: String = s"$name: $getType"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SClass.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SClass.scala
new file mode 100644
index 0000000000000000000000000000000000000000..fe3882754e85ae49eef024d526df59bf15d71a73
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SClass.scala
@@ -0,0 +1,219 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+import java.util.Objects
+
+/** Representation of Scala classes. Attributes, methods, etc. have their own wrappers which should
+  * be used to modify these individual parts of the class.
+  *
+  * @see [[SType]]
+  * @author Rico Bergmann
+  */
+class SClass(val name: String,
+             val sPackage: String = "",
+             var attributes: Seq[SAttribute] = Seq.empty,
+             var parent: STypedElement = null,
+             protected var methods: Seq[SMethod] = Seq.empty)
+  extends STypedElement {
+
+  protected var constructorStatements: Seq[SMethodStatement] = Seq.empty
+
+  /** Provides code that should be executed when invoking the constructor.
+    */
+  def getAdditionalConstructorStatements: Seq[SMethodStatement] = constructorStatements
+
+  /** Checks, whether `this` is part of the default package
+    */
+  def isDefaultPackage: Boolean = sPackage == ""
+
+  /** Checks, whether `this` extends any other classes. If this is the case, it is '''not'''
+    * considered a ''root class'', otherwise it is.
+    */
+  def isRootClass: Boolean = parent == null
+
+  /** Provides the set of all symbols that have to be imported in order to use `this` complete
+    * class.
+    */
+  def collectImports: Set[String] = getNecessaryImports.map(imp => s"${imp.pckg}.${imp.name}")
+
+  /** Augments `this` class with another method.
+    *
+    * @param m the additional method. May not be `null`
+    */
+  def addMethod(m: SMethod): Unit = {
+    Objects.requireNonNull(m, "Method to add may not be null")
+    if (methods.contains(m)) {
+      return
+    }
+    methods = methods :+ m
+  }
+
+  def getMethods: Seq[SMethod] = {
+    if (methods.exists(_.name == "toString")) {
+      methods
+    } else {
+      methods :+ new SMethod(
+        name = "toString",
+        result = SType.String,
+        params = Seq.empty,
+        implementation = Seq(SMethodStatement((List(s"""  "$name: " """) ++ attributes.map(attr => s""" "${attr.name}=" + ${attr.name} + " " """)).mkString(" + "))),
+        overrides = true
+      )
+    }
+  }
+
+  /** Augments the constructor by another statement. The statement will be executed lastly (though
+    * this may change due to further modifications of the constructor).
+    *
+    * @param statement the new statement. May not be `null`
+    */
+  def augmentConstructor(statement: SMethodStatement): Unit = {
+    Objects.requireNonNull(statement, "Statement to add may not be null")
+    constructorStatements = constructorStatements :+ statement
+  }
+
+  /** Makes `this` class a subclass of `parent`.
+    *
+    * If `parent == this` nothing will happen.
+    *
+    * @param parent the superclass. May be `null` to indicate that there is no superclass (other
+    *               than `AnyRef`)
+    */
+  def setParent(parent: STypedElement): Unit = {
+    if (parent == this) {
+      return
+    }
+    this.parent = parent
+  }
+
+  /** Sets the attributes of `this` class.
+    *
+    * @param attrs the attributes. May be `null` to remove all current attributes.
+    */
+  def setAttributes(attrs: Seq[SAttribute]): Unit = {
+    if (attrs == null) {
+      this.attributes = Seq.empty
+    } else {
+      this.attributes = attrs
+    }
+  }
+
+  /** Provides all types `this` classes accesses in some way.
+    */
+  def getUsedTypes: Set[STypedElement] = {
+    val parentImport: Seq[STypedElement] = if (isRootClass) List() else List(parent)
+
+    val parentConstructorParamImports: Seq[STypedElement] = if (isRootClass) List() else parent match {
+      case parent: SClass => parent.attributes.map(_.attrType)
+      case _ => List()
+    }
+
+    val attrTypeImports: Seq[STypedElement] = attributes.map(_.attrType)
+
+    val methodResultImports: Seq[STypedElement] = methods.map(_.result)
+
+    val methodParamImports: Seq[STypedElement] = methods.flatMap(_.params).map(_.paramType)
+
+    val methodImplImports: Seq[STypedElement] = methods.flatMap(_.getUsedTypes.toList)
+
+    // create a set to eliminate duplicates
+    (parentImport
+      ++ parentConstructorParamImports
+      ++ attrTypeImports
+      ++ methodResultImports
+      ++ methodParamImports
+      ++ methodImplImports).toSet
+  }
+
+  override def getName: String = name
+
+  override def getPackage: String = sPackage
+
+  override def getConstructorParameters: Seq[SMethodParameter] = {
+    val ownParams = attributes.map(attr => SMethodParameter(attr.name, attr.attrType))
+    val parentParams = if (isRootClass) List() else parent.getConstructorParameters
+    ownParams ++ parentParams
+  }
+
+  override def getInheritanceHierarchy: Seq[STypedElement] = if (isRootClass) List(this) else this +: parent.getInheritanceHierarchy
+
+  override def getNecessaryImports: Set[SImport] = {
+    val parentImport: List[SImport] = if (isRootClass) List() else includeImportIfNecessary(parent.getPackage, parent.getName)
+
+    val parentConstructorParamImports: List[SImport] = if (isRootClass) List() else parent match {
+      case parent: SClass =>
+        parent.attributes
+          .map(attr => includeImportIfNecessary(attr.attrType.getPackage, attr.attrType.getName))
+          .fold(List())((l1, l2) => l1 ++ l2)
+      case _ => List()
+    }
+
+    val attrTypeImports: List[SImport] =
+      attributes
+        .map(attr => includeImportIfNecessary(attr.attrType.getPackage, attr.getType))
+        .fold(List())((l1, l2) => l1 ++ l2)
+
+    val methodResultImports: List[SImport] =
+      methods
+        .map(_.result)
+        .map(res => includeImportIfNecessary(res.getPackage, res.getName))
+        .fold(List())((l1, l2) => l1 ++ l2)
+
+    val methodParamImports: List[SImport] =
+      methods
+        .map(_.params)
+        .fold(List())((l1, l2) => l1 ++ l2)
+        .map(param => includeImportIfNecessary(param.paramType.getPackage, param.paramType.getName))
+        .fold(List())((l1, l2) => l1 ++ l2)
+
+    val methodImplImports: List[SImport] =
+      methods
+        .map(_.getUsedTypes.toList)
+        .fold(List())((l1, l2) => l1 ++ l2)
+        .map(typ => includeImportIfNecessary(typ.getPackage, typ.getName))
+        .fold(List())((l1, l2) => l1 ++ l2)
+
+    // create a set to eliminate duplicates
+    (parentImport
+      ++ parentConstructorParamImports
+      ++ attrTypeImports
+      ++ methodResultImports
+      ++ methodParamImports
+      ++ methodImplImports).toSet
+  }
+
+  override def accept(visitor: SModelVisitor): Unit = {
+    attributes.foreach(_.accept(visitor))
+    methods.foreach(_.accept(visitor))
+    visitor.visit(this)
+  }
+
+  protected def canEqual(other: Any): Boolean = other.isInstanceOf[SClass]
+
+  /** Checks if some class has to be imported in order to be usable from `this` class. If this
+    * is the case it will wrap the necessary import in a `List` for further usage.
+    *
+    * @param sPackage the package of the class
+    * @param sClass the class
+    * @return an empty list if the class does not need to be imported, or the necessary import
+    *         otherwise
+    */
+  private def includeImportIfNecessary(sPackage: String, sClass: String): List[SImport] = {
+    if (sPackage != this.sPackage && sPackage != "") List(SImport(sPackage, sClass)) else List()
+  }
+
+  override def equals(other: Any): Boolean = other match {
+    case that: SClass =>
+      (that canEqual this) &&
+        name == that.name &&
+        sPackage == that.sPackage
+    case _ => false
+  }
+
+  override def hashCode(): Int = {
+    val state = Seq(name, sPackage)
+    state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
+  }
+
+  override def toString: String = s"$name(${attributes.map(_.name).mkString(", ")})"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SGetter.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SGetter.scala
new file mode 100644
index 0000000000000000000000000000000000000000..3cb1dce28b2cca0c736031693bc0c10a392f6988
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SGetter.scala
@@ -0,0 +1,16 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+import org.rosi_project.model_sync.generator.support.ExtendedString.stringToExtended
+
+
+/** Simple representation of a getter method.
+  *
+  * @author Rico Bergmann
+  */
+class SGetter(attr: SAttribute) extends SMethod(
+  name = s"get${attr.name.firstLetterToUpperCase}",
+  result = attr.attrType,
+  params = Seq.empty,
+  implementation = Seq(SMethodStatement(content = attr.name, usedTypes = Set(attr.attrType)))) {
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SImport.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SImport.scala
new file mode 100644
index 0000000000000000000000000000000000000000..b3511ce453d0d8b02a42e8625e8fda954c4faf22
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SImport.scala
@@ -0,0 +1,34 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Wraps a type that needs to be imported in order for some class to compile.
+  *
+  * @param pckg the package that contains the type to import
+  * @param name the name of the type to import
+  * @author Rico Bergmann
+  */
+case class SImport(pckg: String, name: String)
+
+/** The companion provides convenience methods to deal with sets of imports.
+  */
+object SImport {
+
+  /** Creates the imports necessary to access the specified types.
+    */
+  def generateImports(types: STypedElement*): Set[SImport] = removeUnnecessaryImports(types.map(t => SImport(t.getPackage, t.getName)).toSet)
+
+  /** Creates the imports necessary to access the specified types. Necessity to imports will be
+    * assessed from a base package.
+    */
+  def generateImportsForBasePackage(basePckg: String, types: STypedElement*) = removeUnnecessaryImports(generateImports(types: _*), basePckg)
+
+  /** Deletes all the imports which refer to types in the default package and are therefore not
+    * necessary.
+    */
+  def removeUnnecessaryImports(imports: Set[SImport]): Set[SImport] = imports.filter(_.pckg != "")
+
+  /** Deletes all the imports which refer to types in the default package or the same package as
+    * specified and are therefore not necessary.
+    */
+  def removeUnnecessaryImports(imports: Set[SImport], basePckg: String): Set[SImport] = imports.filter(imp => imp.pckg != "" && imp.pckg != basePckg)
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethod.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethod.scala
new file mode 100644
index 0000000000000000000000000000000000000000..e17dd79cbe0f5b8a3c2f1e88c1e14bc994cd9526
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethod.scala
@@ -0,0 +1,79 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+import java.util.Objects
+
+// TODO use a simple DSL to represent the method body?
+
+/** Abstraction of a method.
+  *
+  * @author Rico Bergmann
+  */
+class SMethod(val name: String,
+              val result: STypedElement,
+              val params: Seq[SMethodParameter],
+              var implementation: Seq[SMethodStatement],
+              var overrides: Boolean = false)
+  extends SModelElement {
+
+  /** Provides the return-type as a `String`.
+    *
+    * @return the type. Will never be `null` nor empty.
+    */
+  def getResultType: String = result.getName
+
+  /** Replaces the current body of `this` method.
+    *
+    * @param impl the new body. May be `null` to remove all current statements, leaving the body
+    *             empty.
+    */
+  def updateImplementation(impl: Seq[SMethodStatement]): Unit = {
+    if (impl == null) {
+      this.implementation = Seq.empty
+    } else {
+      this.implementation = impl
+    }
+  }
+
+  /** Adds another statement to the current method body.
+    *
+    * @param statement the statement. May not be `null`
+    */
+  def augmentImplementation(statement: SMethodStatement): Unit = {
+    Objects.requireNonNull(statement, "Statement to add may not be null")
+    this.implementation = this.implementation :+ statement
+  }
+
+  /** Collects all types which need to be imported for `this` method to use.
+    */
+  def getUsedTypes: Set[STypedElement] = {
+    val resultType = result match {
+      case GenericSType(_, _, typeParam) => Seq(result, typeParam)
+      case SType.Unit => Seq.empty
+      case _ => Seq(result)
+    }
+    val paramTypes = params.map(_.paramType)
+    val implementationTypes = implementation.map(_.usedTypes).fold(Set.empty)((s1, s2) => s1 ++ s2)
+    (resultType ++ paramTypes).toSet ++ implementationTypes
+  }
+
+  override def accept(visitor: SModelVisitor): Unit = visitor.visit(this)
+
+  protected def canEqual(other: Any): Boolean = other.isInstanceOf[SMethod]
+
+  override def equals(other: Any): Boolean = other match {
+    case that: SMethod =>
+      (that canEqual this) &&
+        name == that.name &&
+        result == that.result &&
+        params == that.params
+    case _ => false
+  }
+
+  override def hashCode(): Int = {
+    val state = Seq(name, result, params)
+    state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
+  }
+
+  override def toString: String = s"$name(${params.map(_.name).mkString(", ")}): $getResultType"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethodParameter.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethodParameter.scala
new file mode 100644
index 0000000000000000000000000000000000000000..6ac68dd5e24b1733742e43a3fbbf5f1dc7f2b3bb
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethodParameter.scala
@@ -0,0 +1,17 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Wrapper for parameters that need to be passed to a method.
+  *
+  * @author Rico Bergmann
+  */
+case class SMethodParameter(name: String, paramType: STypedElement) {
+
+  /** Provides the type of `this` parameter as a `String`.
+    *
+    * @return the type. Will never be `null` nor empty.
+    */
+  def getType: String = paramType.getName
+
+  override def toString: String = s"$name: $getType"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethodStatement.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethodStatement.scala
new file mode 100644
index 0000000000000000000000000000000000000000..c3b7c213d9fec9f83b1947f9d3b9c4dbf54b0677
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SMethodStatement.scala
@@ -0,0 +1,30 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Wrapper for the statements that form a method.
+  *
+  * As for now we represent the actual statement as a simple `String` the types used in the
+  * statement need to be given explicitly and cannot be derived as e.g. in the case of [[SClass]]
+  * or [[SMethod]].
+  *
+  * To get the actual statement [[getContent]] should be used instead of accessing `content`
+  * directly. This will make usage more robust if the content will be replaced by a more
+  * sophisticated structure later on (which is likely to happen).
+  *
+  * @author Rico Bergmann
+  */
+case class SMethodStatement(content: String, usedTypes: Set[STypedElement] = Set.empty) {
+
+  /** The actual statement as a `String`.
+    *
+    * We provide this method as a stable API to the statement. Accessing the content directly
+    * through the corresponding property will couple code too tightly to this current
+    * implementation of `content` -  which is likely to be replaced by a more sophisticated
+    * structure to represent method statements.
+    *
+    * @return the statement. Will never be `null` nor empty.
+    */
+  def getContent: String = content
+
+  override def toString: String = getContent
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModel.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModel.scala
new file mode 100644
index 0000000000000000000000000000000000000000..280d69f0f4631ce8f75a2845ed3ec768d9fb7e5f
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModel.scala
@@ -0,0 +1,22 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** A model is a collection of classes that represents a specific data-driven view on the
+  * application.
+  *
+  * @author Rico Bergmann
+  */
+trait SModel extends SModelElement {
+
+  /** Provides all the classes in `this` model.
+    */
+  def getAllClasses: Set[SClass]
+
+  /** Extends the model by a new class.
+    *
+    * @param mClass the class to add. May never `null`.
+    */
+  def addModelClass(mClass: SClass): Unit
+
+  override def toString: String = s"SModel: classes=$getAllClasses"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModelElement.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModelElement.scala
new file mode 100644
index 0000000000000000000000000000000000000000..cf4aa07916483f80a0d3cef3d2c60070fe9ed3f3
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModelElement.scala
@@ -0,0 +1,19 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Base class for all parts of the ''ACR''. It merely exists to enable the ''Visitor pattern''.
+  *
+  * @see [[https://en.wikipedia.org/wiki/Visitor_pattern Visitor pattern @ Wikipedia]]
+  * @author Rico Bergmann
+  */
+trait SModelElement {
+
+  /** Executes the `visitor` on the structure of `this` element.
+    *
+    * Implementations will differ naturally but the most common approach is to recursively call
+    * `accept` on all composed elements and run `visitor.visit(this)` at the end.
+    *
+    * @param visitor the visitor. Might not be `null`.
+    */
+  def accept(visitor: SModelVisitor)
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModelVisitor.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModelVisitor.scala
new file mode 100644
index 0000000000000000000000000000000000000000..f4c43e6c3dbe89fdeadb23a54a2cedbad6d434ab
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SModelVisitor.scala
@@ -0,0 +1,46 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+// TODO fix cyclic imports with classes from acr package
+
+/** Runs an algorithm on model instances.
+  *
+  * @see [[https://en.wikipedia.org/wiki/Visitor_pattern Visitor pattern @ Wikipedia]]
+  * @author Rico Bergmann
+  */
+trait SModelVisitor {
+
+  /** Runs the algorithm as appropriate for the whole model.
+    *
+    * @param sModel the model to run the algorithm on. Whether it may be `null` is implementation
+    *               specific.
+    */
+  def visit(sModel: SModel): Unit
+
+  /** Runs the algorithm as appropriate for a single class of the model.
+    *
+    * @param sClass the class to run the algorithm on
+    */
+  def visit(sClass: SClass): Unit
+
+  /** Runs the algorithm as appropriate for an attribute of a model class.
+    *
+    * @param sAttr the attribute to run the algorithm on. Whether it may be `null` is implementation
+    *              specific.
+    */
+  def visit(sAttr: SAttribute): Unit
+
+  /** Runs the algorithm as appropriate for a method of a model class.
+    *
+    * @param sMethod the method to run the algorithm on. Whether it may be `null` is implementation
+    *                specific.
+    */
+  def visit(sMethod: SMethod): Unit
+
+  /** Runs the algorithm as appropriate for a type of the model.
+    *
+    * @param sType the type to run the algorithm on. Whether it may be `null` is implementation
+    *              specific.
+    */
+  def visit(sType: SType): Unit
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SSetter.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SSetter.scala
new file mode 100644
index 0000000000000000000000000000000000000000..563c9a8d114b1d7752a059ae79dba181286885e4
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SSetter.scala
@@ -0,0 +1,15 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+import org.rosi_project.model_sync.generator.support.ExtendedString.stringToExtended
+
+/** Simple representation of a setter method.
+  *
+  * @author Rico Bergmann
+  */
+class SSetter(attr: SAttribute) extends SMethod(
+  name = s"set${attr.name.firstLetterToUpperCase}",
+  result = SType.Unit,
+  params = Seq(SMethodParameter(attr.name.head.toString, attr.attrType)),
+  implementation = Seq(SMethodStatement(content = s"${attr.name} = ${attr.name.head}", usedTypes = Set(attr.attrType)))) {
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SType.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SType.scala
new file mode 100644
index 0000000000000000000000000000000000000000..a3a15f4f511ef046fa9fad55d026a46c5566828f
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SType.scala
@@ -0,0 +1,40 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Represents types provided by the Java runtime (either as part of the Java/Scala standard
+  * libraries or other third-party projects).
+  *
+  * In general as a rule of thumb when loading a model the classes defined by the model should be
+  * mapped to instances of [[SClass]] whereas all those types that are used (but not defined) within
+  * the model and therefore function as some kind of "support-type" should become instances of this
+  * class.
+  *
+  * @see [[SClass]]
+  * @author Rico Bergmann
+  */
+case class SType(name: String, sPackage: String = "") extends STypedElement {
+
+  override def getName: String = name
+
+  override def getPackage: String = sPackage
+
+  override def accept(visitor: SModelVisitor): Unit = visitor.visit(this)
+
+}
+
+/** The companion defines frequently used types.
+  */
+object SType {
+
+  /** The empty type.
+    */
+  val Unit = SType("Unit")
+
+  /** Wrapper for `AnyRef`.
+    */
+  val AnyRef = SType("AnyRef")
+
+  /** Wrapper for `String`.
+    */
+  val String = SType("String")
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/STypeRegistry.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/STypeRegistry.scala
new file mode 100644
index 0000000000000000000000000000000000000000..345d9b8b7b13d8a2c53fdde1423736865ddad4f6
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/STypeRegistry.scala
@@ -0,0 +1,68 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Repository to keep track of all types and classes of model. It ensures that all attributes,
+  * methods, etc. reference the same type instances and thus prevent duplication and conflicting
+  * states. The registry should be treated as "''single point of truth''".
+  *
+  * @author Rico Bergmann
+  */
+object STypeRegistry {
+
+  private var registeredTypes: Set[STypedElement] = Set()
+
+  registerDefaultTypes()
+
+  /** Registers a type if it is not already known.
+    *
+    * @param theType the new type. May never be `null`.
+    * @return `theType` if it was indeed unknown before. Otherwise the currently registered type.
+    */
+  def addType(theType: STypedElement): STypedElement = {
+    val existing: Option[STypedElement] = registeredTypes.find(existing => existing.getName == theType.getName && existing.getPackage == theType.getPackage)
+    if (existing.isEmpty) {
+      registeredTypes += theType
+      theType
+    } else {
+      existing.get
+    }
+  }
+
+  /** Searches for a type.
+    *
+    * @param name the type's name
+    * @param sPackage the package that contains the type
+    * @return the type if it was found
+    */
+  def query(name: String, sPackage: String): Option[STypedElement] = {
+    registeredTypes.find(t => t.getName == name && t.getPackage == sPackage)
+  }
+
+  /** Searches for a type based on its name. It may reside in any package.
+    *
+    * @param name the type's name
+    * @return the type if it was found
+    */
+  def queryForName(name: String): Option[STypedElement] = {
+    registeredTypes.find(_.getName == name)
+  }
+
+  /** Provides all types that are currently in the repository.
+    */
+  def allTypes: Set[STypedElement] = registeredTypes
+
+  private def registerDefaultTypes(): Unit = {
+    registeredTypes ++= Seq(
+      SType("Boolean"),
+      SType("Byte"),
+      SType("Short"),
+      SType("Integer"),
+      SType("Long"),
+      SType("Float"),
+      SType("Double"),
+      SType("String"),
+    )
+  }
+
+  override def toString: String = s"Registry: $registeredTypes"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/STypedElement.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/STypedElement.scala
new file mode 100644
index 0000000000000000000000000000000000000000..d89878c254515cac3bf962435253300052e1135f
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/STypedElement.scala
@@ -0,0 +1,41 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+/** Abstraction of "elements" that may be used in source code where a type is expected.
+  *
+  * Currently there are two different kinds of such elements:
+  *
+  *  - instances of [[SClass]] are supplied by the user
+  *  - instances of [[SType]] are provided by the Java runtime (they are classes of the standard
+  *    Java libraries or others)
+  *
+  * @author Rico Bergmann
+  */
+trait STypedElement extends SModelElement {
+
+  /** The name of `this` type.
+    *
+    * @return the name. Will never be `null` nor empty.
+    */
+  def getName: String
+
+  /** The package containing `this` type.
+    *
+    * @return
+    */
+  def getPackage: String
+
+  /** The parameters necessary to create an instance of `this` type.
+    */
+  def getConstructorParameters: Seq[SMethodParameter] = Seq.empty
+
+  /** The inheritance hierarchy provides information about the types `this` extends. It will start
+    * with `this` (thus the hierarchy will 'never' be empty) and end with the root type. `AnyRef`
+    * will be ignored however.
+    */
+  def getInheritanceHierarchy: Seq[STypedElement] = Seq.empty
+
+  /** Provides all classes that need to be imported for `this` type.
+    */
+  def getNecessaryImports: Set[SImport] = Set.empty
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SimpleSModel.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SimpleSModel.scala
new file mode 100644
index 0000000000000000000000000000000000000000..4722a9ad758e180a69e4fc59cf9c92ffa927471c
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SimpleSModel.scala
@@ -0,0 +1,39 @@
+package org.rosi_project.model_sync.generator.acr_model
+
+import org.rosi_project.model_sync.generator.support.Assert
+
+/** Default implementation of the [[SModel]].
+  *
+  * @author Rico Bergmann
+  */
+class SimpleSModel extends SModel {
+
+  private var sClasses: Set[SClass] = Set.empty
+
+  override def addModelClass(mClass: SClass): Unit = {
+    Assert.notNull(mClass, "Class may not be null")
+    sClasses += mClass
+  }
+
+  override def getAllClasses: Set[SClass] = sClasses
+
+  override def accept(visitor: SModelVisitor): Unit = {
+    sClasses.foreach(_.accept(visitor))
+    visitor.visit(this)
+  }
+
+  def canEqual(other: Any): Boolean = other.isInstanceOf[SimpleSModel]
+
+  override def equals(other: Any): Boolean = other match {
+    case that: SimpleSModel =>
+      (that canEqual this) &&
+        sClasses == that.sClasses
+    case _ => false
+  }
+
+  override def hashCode(): Int = {
+    val state = Seq(sClasses)
+    state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/package.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/package.scala
new file mode 100644
index 0000000000000000000000000000000000000000..1be79e6a19ce1c5a1a18f968e2ab516cd2bcb175
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/package.scala
@@ -0,0 +1,16 @@
+package org.rosi_project.model_sync.generator
+
+import org.rosi_project.model_sync.generator.acr_model.{SClass, SModel}
+
+/** Abstract class representation of the Scala code used within a model.
+  *
+  * This package specifies a simplified meta-model of scala classes. Most parts of (our take on) a
+  * Scala class have a corresponding wrapper class that may be modified for further adaptations.
+  *
+  * @see [[SClass]]
+  * @see [[SModel]]
+  * @author Rico Bergmann
+  */
+package object acr_model {
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/PredefTypes.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/PredefTypes.scala
new file mode 100644
index 0000000000000000000000000000000000000000..d181569e09da6186cb0ba1aa877b4d1e4f586a3a
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/PredefTypes.scala
@@ -0,0 +1,38 @@
+package org.rosi_project.model_sync.generator.acr_model.types
+
+import org.rosi_project.model_sync.generator.acr_model.{GenericSType, SType, STypedElement}
+
+/** Contains a number of types that are part of the JSE as well as some more advanced Scala types.
+  *
+  * This should prevent creating them over and over again every time a `SType` should be set to any
+  * of these types.
+  *
+  * @author Rico Bergmann
+  */
+object PredefTypes {
+
+  /** `java.lang.Object`
+    */
+  val Object = SType("Object")
+
+  /** `java.io.File`
+    */
+  val File = SType("File", "java.io")
+
+  /**  `java.lang.reflect.Parameter`
+    */
+  val Parameter = SType("Parameter", "java.lang.reflect")
+
+  /** `java.lang.Class[typ]`
+    *
+    * @param typ the type parameter for the class type
+    */
+  def classOf(typ: STypedElement): GenericSType = new GenericSType("Class", typeParam = typ)
+
+  /** `Option[typ]`
+    *
+    * @param typ the type parameter for the Option type
+    */
+  def option(typ: STypedElement): GenericSType = new GenericSType("Option", typeParam = typ)
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/SList.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/SList.scala
new file mode 100644
index 0000000000000000000000000000000000000000..a7d11b35fa3045539727f7d5ddc5df9ea01603f8
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/SList.scala
@@ -0,0 +1,21 @@
+package org.rosi_project.model_sync.generator.acr_model.types
+
+import org.rosi_project.model_sync.generator.acr_model._
+
+/** Wraps a Scala `List` for some type.
+  */
+class SList(elemType: STypedElement) extends SSeq(elemType) {
+
+  override def getConstructorParameters: Seq[SMethodParameter] = Seq(SMethodParameter("elems", elemType))
+
+}
+
+/** The companion provides `apply` and `unapply` methods.
+  */
+object SList {
+
+  def apply(elemType: STypedElement): SList = new SList(elemType)
+
+  def unapply(arg: SList): Option[STypedElement] = Some(arg.elemType)
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/SSeq.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/SSeq.scala
new file mode 100644
index 0000000000000000000000000000000000000000..e6a9f5ded7b0d289ec384126b9a20c07481f26aa
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/types/SSeq.scala
@@ -0,0 +1,21 @@
+package org.rosi_project.model_sync.generator.acr_model.types
+
+import org.rosi_project.model_sync.generator.acr_model.{GenericSType, SMethodParameter, STypedElement}
+
+/** Wraps a Scala `Seq` for some type.
+  */
+class SSeq(val elemType: STypedElement) extends GenericSType(name = "Seq", sPackage = "", typeParam = elemType) {
+
+  override def getConstructorParameters: Seq[SMethodParameter] = Seq(SMethodParameter("elems", elemType))
+
+}
+
+/** The companion provides `apply` and `unapply` methods.
+  */
+object SSeq {
+
+  def apply(elemType: STypedElement): SSeq = new SSeq(elemType)
+
+  def unapply(arg: SSeq): Option[STypedElement] = Some(arg.elemType)
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/conversion/Converter.scala b/src/main/scala/org/rosi_project/model_sync/generator/conversion/Converter.scala
new file mode 100644
index 0000000000000000000000000000000000000000..502465439eda93fe71934b67d95cc9eca6fd1d0e
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/conversion/Converter.scala
@@ -0,0 +1,18 @@
+package org.rosi_project.model_sync.generator.conversion
+
+/** A converter converts instance of a source type `S` to a target type `T`.
+  *
+  * @tparam S the source type
+  * @tparam T the target type
+  * @author Rico Bergmann
+  */
+trait Converter[S, T] {
+
+  /** Converts the source object.
+    *
+    * @param source the object to convert
+    * @return the converted object
+    */
+  def convert(source: S): T
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/conversion/EmfTypeTranslator.scala b/src/main/scala/org/rosi_project/model_sync/generator/conversion/EmfTypeTranslator.scala
new file mode 100644
index 0000000000000000000000000000000000000000..716e851ce938be96a22c2dc072cf257a0a29ce75
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/conversion/EmfTypeTranslator.scala
@@ -0,0 +1,49 @@
+package org.rosi_project.model_sync.generator.conversion
+
+import java.util.Objects
+
+import org.eclipse.emf.ecore.EDataType
+import org.rosi_project.model_sync.generator.acr_model.SType
+
+/** Service to map an instance of [[EDataType]] to its corresponding [[SType]] (which wrap Scala's
+  * native types).
+  *
+  * @author Rico Bergmann
+  */
+object EmfTypeTranslator {
+
+  private val typeMap: Map[String, SType] = Map(
+    "EBoolean" -> SType("Boolean"),
+    "EByte" -> SType("Byte"),
+    "EChar" -> SType("Char"),
+    "EDate" -> SType(name = "Date", sPackage = "java.util"),
+    "EDouble" -> SType("Double"),
+    "EFloat" -> SType("Float"),
+    "EInt" -> SType("Int"),
+    "EJavaObject" -> SType("Object"),
+    "ELong" -> SType("Long"),
+    "EShort" -> SType("Short"),
+    "EString" -> SType("String"),
+  )
+
+  /** Maps an EMF data type to its corresponding Scala type.
+    *
+    * @param dataType the source type. May not be `null`.
+    * @return the matching type. If there is none, the `Option` will be empty.
+    */
+  def getSClassFromEmf(dataType: EDataType): Option[SType] = {
+    Objects.requireNonNull(dataType, "Data type may not be null")
+    typeMap.get(dataType.getName)
+  }
+
+  /** Maps an EMF data type to its corresponding Scala type.
+    *
+    * @param dataType the source type. May not be `null`.
+    * @return the matching type. If there is none, the `Option` will be empty.
+    */
+  def getSClassFromEmf(dataType: String): Option[SType] = {
+    Objects.requireNonNull(dataType, "Data type may not be null")
+    typeMap.get(dataType)
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/conversion/SClassConverter.scala b/src/main/scala/org/rosi_project/model_sync/generator/conversion/SClassConverter.scala
new file mode 100644
index 0000000000000000000000000000000000000000..76b10c5c96d06b597cdbe8ec78dbe3896b66e75a
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/conversion/SClassConverter.scala
@@ -0,0 +1,135 @@
+package org.rosi_project.model_sync.generator.conversion
+
+import org.eclipse.emf.ecore.{EAttribute, EClass, EReference}
+import org.rosi_project.model_sync.generator.acr_model._
+import org.rosi_project.model_sync.generator.acr_model.types.SList
+
+/** Converter to generate instances of [[SClass]] based on [[EClass EClasses]].
+  *
+  * @author Rico Bergmann
+  */
+class SClassConverter extends Converter[EClass, SClass] {
+
+  override def convert(source: EClass): SClass = {
+    var attrs: List[SAttribute] = List.empty
+
+    // fetch the attributes of the SClass
+    (source.getEAttributes: List[EAttribute]).foreach(eAttr => {
+      val attrType: STypedElement = STypeRegistry
+
+        // check if the attribute type is already known and registered
+        .query(eAttr.getEAttributeType.getName, eAttr.getEAttributeType.getEPackage.getName)
+
+        // otherwise create a new type
+        .getOrElse {
+          val newAttr = EmfTypeTranslator
+
+            // if the type is wrapped by EMF (such as EString), use the corresponding scala type
+            .getSClassFromEmf(eAttr.getEAttributeType)
+
+            // otherwise create a new class (as the attribute should instance of a class rather than a type in this case)
+            .getOrElse(new SClass(eAttr.getEAttributeType.getName, sPackage = eAttr.getEAttributeType.getEPackage.getName))
+
+          // finally save the type
+          STypeRegistry.addType(newAttr)
+        }
+
+      attrs ::= SAttribute(eAttr.getName, attrType)
+    })
+
+    // TODO add support for multiple inheritance
+
+    (source.getEReferences: List[EReference])
+      .filter(_.isContainment)
+      .foreach(eRef => {
+        val attrName = eRef.getName
+        val attrType = eRef.getEReferenceType.getName
+        val attrPckg = eRef.getEReferenceType.getEPackage.getName
+
+        var sAttrType: STypedElement = STypeRegistry
+
+          // check if the attribute type is already known and registered
+          .query(attrType, attrPckg)
+
+          // otherwise create a new type
+          .getOrElse {
+            val newAttr = EmfTypeTranslator
+
+              // if the type is wrapped by EMF (such as EString), use the corresponding scala type
+              .getSClassFromEmf(attrType)
+
+              // otherwise create a new class (as the attribute should instance of a class rather than a type in this case)
+              .getOrElse(new SClass(attrType, sPackage = attrPckg))
+
+            // finally save the type
+            STypeRegistry.addType(newAttr)
+          }
+
+        if (eRef.getUpperBound > 1 || eRef.getUpperBound == -1) {
+          sAttrType = SList(sAttrType)
+        }
+
+        attrs ::= SAttribute(attrName, sAttrType)
+
+    })
+
+    val parents: List[EClass] = source.getESuperTypes.toArray(new Array[EClass](0)).toList
+
+    // we may not convert class hierarchies that utilize multiple inheritance yet
+    if (violatesSingleInheritance(parents)) {
+      throw new UnconvertibleEmfException(source.getEPackage, s"For class: $source")
+    }
+
+    val parent: STypedElement = parents.headOption.map((p: EClass) => {
+      STypeRegistry
+
+        // check if we already know the parent
+        .queryForName(p.getName)
+
+        // otherwise we need to create and register it
+        .getOrElse {
+          val parentSClass: SClass = new SClass(p.getName, sPackage = p.getEPackage.getName)
+
+          // register the parent (it will be visited and completely inflated later on)
+          STypeRegistry.addType(parentSClass)
+          parentSClass
+        }
+    }).orNull
+
+
+    // TODO add methods to SClass
+
+    /* `convert` may be called on two different occasions: either for a completely new type or
+     * for a type that was already created before when another type was being inflated by `convert`.
+     *  In the first case we need to set up all the necessary attributes of the SClass and therefore
+     *  create it from scratch.
+     *  Otherwise the most basic attributes (i.e. name and package) have already been set up and
+     *  have to be left unchanged (they are values, actually). However parent and attributes of the
+     *  type have not been inflated during the first visit (as the only important part then was to
+     *  have the type known). Therefore we will set these now.
+     */
+
+    val currentClass: Option[STypedElement] = STypeRegistry.query(source.getName, source.getEPackage.getName)
+
+    currentClass.map {
+      case clazz: SClass =>
+        clazz.attributes = attrs
+        clazz.parent = parent
+        clazz
+      case sType =>
+        sys.error(s"sType should have been a class: $sType")
+    }.getOrElse {
+      val createdClass: SClass = new SClass(source.getName, source.getEPackage.getName, attrs, parent)
+      STypeRegistry.addType(createdClass)
+      createdClass
+    }
+
+  }
+
+  /** Checks whether there is more than one parent class.
+    *
+    * This method is just to express the constraint more explicitly.
+    */
+  private def violatesSingleInheritance(parents: List[EClass]): Boolean = parents.length > 1
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/conversion/SModelGenerator.scala b/src/main/scala/org/rosi_project/model_sync/generator/conversion/SModelGenerator.scala
new file mode 100644
index 0000000000000000000000000000000000000000..51057830ce435267e5f7f8b15f34cb14b4ef870e
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/conversion/SModelGenerator.scala
@@ -0,0 +1,35 @@
+package org.rosi_project.model_sync.generator.conversion
+
+import org.eclipse.emf.ecore.{EClass, EGenericType, EPackage}
+import org.rosi_project.model_sync.generator.acr_model.{SModel, SType, STypeRegistry, SimpleSModel}
+
+import scala.collection.JavaConverters._
+
+/** Converter to generate an [[SModel]] from ecore.
+  *
+  * @author Rico Bergmann
+  */
+class SModelGenerator extends Converter[EPackage, SModel] {
+
+  override def convert(source: EPackage): SModel = {
+    val packageName = if (source.getName != null) source.getName else ""
+    val contents = source.eAllContents().asScala
+
+    val model = new SimpleSModel
+
+    println("... Converting ecore model")
+    contents.foreach {
+      case ec: EClass =>
+        model.addModelClass(new SClassConverter convert ec)
+        STypeRegistry.addType(SType(ec.getName, packageName))
+      case _ =>
+        // we only care about classes. Types will be registered as soon as they are needed as
+        // attributes
+    }
+    println("... Conversion finished")
+    println(s"Generated model: $model")
+
+    model
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/conversion/SModelGeneratorTest.scala b/src/main/scala/org/rosi_project/model_sync/generator/conversion/SModelGeneratorTest.scala
new file mode 100644
index 0000000000000000000000000000000000000000..16cee3fbd4f8dee34a5278703d94aa3dc352bc64
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/conversion/SModelGeneratorTest.scala
@@ -0,0 +1,21 @@
+package org.rosi_project.model_sync.generator.conversion
+
+import org.eclipse.emf.ecore.{EObject, EPackage}
+import org.eclipse.emf.ecore.resource.Resource
+import scroll.internal.ecore.ECoreImporter
+
+/**
+  * @author Rico Bergmann
+  */
+object SModelGeneratorTest/* extends App */{
+
+  val importer = new ECoreImporter {
+    def lm(): Resource = loadModel()
+  }
+  importer.path = "assets/ttc17.ecore"
+  val res = importer.lm()
+  println(s"We done: $res")
+
+  res.getContents.toArray(new Array[EObject](0)).toList.find(_.isInstanceOf[EPackage]).foreach(e => (new SModelGenerator).convert(e.asInstanceOf[EPackage]))
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/conversion/UnconvertibleEmfException.scala b/src/main/scala/org/rosi_project/model_sync/generator/conversion/UnconvertibleEmfException.scala
new file mode 100644
index 0000000000000000000000000000000000000000..12093b2899c94002cee12b9775937659cf511caf
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/conversion/UnconvertibleEmfException.scala
@@ -0,0 +1,15 @@
+package org.rosi_project.model_sync.generator.conversion
+
+import org.eclipse.emf.ecore.EPackage
+import org.rosi_project.model_sync.generator.acr_model.SModel
+
+/** Exception to indicate that a EMF model may not be converted to an [[SModel]] because some
+  * constraints are violated.
+  *
+  * @author Rico Bergmann
+  */
+class UnconvertibleEmfException(val model: EPackage, val message: String = "") extends RuntimeException(message) {
+
+  override def toString: String = s"UnconvertibleException: $model ${if (message.isEmpty) "" else s"($message)"}"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/conversion/package.scala b/src/main/scala/org/rosi_project/model_sync/generator/conversion/package.scala
new file mode 100644
index 0000000000000000000000000000000000000000..46f952645700b7610e8b23d1389d831e862e6845
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/conversion/package.scala
@@ -0,0 +1,32 @@
+package org.rosi_project.model_sync.generator
+
+import org.eclipse.emf.common.util.EList
+import org.rosi_project.model_sync.generator.acr_model.SModel
+
+/** Provides services to convert EMF ecore-models to corresponding instances of [[SModel]].
+  *
+  * @author Rico Bergmann
+  */
+package object conversion {
+
+  /** Converts an EMF list instance to a corresponding Scala `List`.
+    *
+    * This converter is necessary as `EList` is an instance of `java.util.List` and may thus not be
+    * used like a Scala list conveniently.
+    *
+    * @param eList the EMF list. May be `null`.
+    * @tparam T the elements in the EMF list
+    * @return the Scala list containing all the elements of the original list. Will be `null` if
+    *         `elist` is.
+    */
+  implicit def elist2slist[T](eList: EList[T]): List[T] = {
+    if (eList == null) {
+      null
+    } else {
+      var res: List[T] = List()
+      eList.forEach(elem => res = res :+ elem)
+      res
+    }
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/env/FilesCompiler.scala b/src/main/scala/org/rosi_project/model_sync/generator/env/FilesCompiler.scala
new file mode 100644
index 0000000000000000000000000000000000000000..516f2859e01e910e66f57eaeef2d17b0a28f814c
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/env/FilesCompiler.scala
@@ -0,0 +1,42 @@
+package org.rosi_project.model_sync.generator.env
+
+import java.io.PrintWriter
+
+import scala.reflect.io.File
+import scala.tools.nsc.reporters.ConsoleReporter
+import scala.tools.nsc.{GenericRunnerSettings, Global, Settings}
+
+/** Utility to compile all set of Scala source files to Java byte code.
+  *
+  * @param outDir the directory where the compiled files should placed
+  * @author Rico Bergmann
+  */
+class FilesCompiler(outDir: File) {
+
+  /** Compiles all given files.
+    */
+  def run(filesToCompile: List[File]): Unit = {
+    // see: https://stackoverflow.com/a/20323371/5161760
+    try {
+      val out = new PrintWriter(System.out)
+      val compilationSettings: Settings = new GenericRunnerSettings(out.println)
+      // just re-use the whole classpath
+      compilationSettings.classpath.value = JClassPath.adaptClassPathToOSEnv(JClassPath.fetchCurrentClassPath).distinct.mkString(File.pathSeparator)
+      println(s"Using classpath ${compilationSettings.classpath.value}")
+      compilationSettings.outdir.value = outDir.toAbsolute.toString
+      val reporter = new ConsoleReporter(compilationSettings)
+      val compiler = new Global(compilationSettings, reporter)
+      val runner = new compiler.Run
+      runner.compile(filesToCompile.map(_.toAbsolute.toString))
+    } catch {
+      case e: Exception => throw new CompilationException(e)
+    }
+  }
+
+}
+
+/** Exception to indicate that the compilation process failed.
+  *
+  * @param cause the causing exception
+  */
+class CompilationException(cause: Exception) extends RuntimeException(cause)
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/env/JClassPath.scala b/src/main/scala/org/rosi_project/model_sync/generator/env/JClassPath.scala
new file mode 100644
index 0000000000000000000000000000000000000000..fa936742e12179c6c168dab65432b6f220969982
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/env/JClassPath.scala
@@ -0,0 +1,56 @@
+package org.rosi_project.model_sync.generator.env
+
+import java.net.URLClassLoader
+
+/** Utility to provide high level access to the application's ''classpath'' as well as some basic
+  * transformations.
+  *
+  * @author Rico Bergmann
+  */
+object JClassPath {
+
+  /** Provides a platform-specific version of the ''classpath''. All entries may than be resolved as
+    * files on the current OS.
+    *
+    * @param cp the ''classpath'' to adapt
+    */
+  def adaptClassPathToOSEnv(cp: List[String]): List[String] = {
+    sys.props("os.name").toLowerCase match {
+      case win if win contains "windows" =>
+        cp.map(entry => {
+          val WinUrl = """file:/(.*)""".r
+          entry match {
+            case WinUrl(path) => path
+            case p => p
+          }
+        }).map(_.replaceAll("/", "\\\\").replaceAll("%20", " "))
+      case unix =>
+        val UnixUrl = """file:(.*)""".r
+        cp.map {
+          case UnixUrl(path) => path
+          case p => p
+        }
+    }
+  }
+
+  /** Gets the ''classpath'' as it is currently used by this application.
+    */
+  def fetchCurrentClassPath: List[String] = {
+    val ctxLoader = Thread.currentThread().getContextClassLoader
+    ctxLoader match {
+      case urlCL: URLClassLoader =>
+        urlCL.getURLs.toList.map {
+          _.toString
+        }
+      case wrappedCL =>
+        wrappedCL.getParent match {
+          case urlCL: URLClassLoader =>
+            urlCL.getURLs.toList.map {
+              _.toString
+            }
+        }
+      case something => sys.error(s"Could not unwrap class loader: $something")
+    }
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/env/JarPackager.scala b/src/main/scala/org/rosi_project/model_sync/generator/env/JarPackager.scala
new file mode 100644
index 0000000000000000000000000000000000000000..3d1478f542696ad245c92c73ba770637a9f0ad6b
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/env/JarPackager.scala
@@ -0,0 +1,79 @@
+package org.rosi_project.model_sync.generator.env
+
+import java.io.{BufferedInputStream, FileInputStream, FileOutputStream}
+import java.util.jar.{Attributes, JarEntry, JarOutputStream, Manifest}
+
+import scala.reflect.io.{Directory, File}
+import scala.util.control.Breaks.{break, breakable}
+
+/** Utility to put all files from some directory into a JAR.
+  *
+  * This will simply copy all files from the source directory into the JAR at the target directory.
+  *
+  * @param inputDir  the directory where all source files reside.
+  * @param outputDir the target directory where the JAR will be created
+  * @param jarName   the name of the JAR file to create
+  * @author Rico Bergmann
+  */
+class JarPackager(inputDir: File, outputDir: File, jarName: String = "model.jar") {
+
+  /** Starts the packaging process.
+    */
+  def run(): Unit = {
+    // see: https://stackoverflow.com/a/1281295/5161760
+    try {
+      val mf = new Manifest
+      mf.getMainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0")
+      val targetJar = new JarOutputStream(new FileOutputStream(outputDir + File.separator + jarName), mf)
+      var filesToAdd: List[java.io.File] = inputDir.jfile.listFiles.toList
+
+      while (filesToAdd.nonEmpty) {
+        val file = filesToAdd.head
+        var fname = file.getAbsolutePath.replace(inputDir.jfile.getAbsolutePath + File.separator, "").replaceAll("\\\\", "/")
+        println(s"Adding to JAR: $fname")
+        if (file.isDirectory) {
+          if (!fname.endsWith("/")) {
+            fname += "/"
+          }
+          filesToAdd ++= file.listFiles.toList
+
+          val entry = new JarEntry(fname)
+          entry.setTime(file.lastModified)
+          targetJar.putNextEntry(entry)
+          targetJar.closeEntry()
+        } else {
+          val entry = new JarEntry(fname)
+          entry.setTime(file.lastModified)
+          targetJar.putNextEntry(entry)
+
+          val in = new BufferedInputStream(new FileInputStream(file))
+          var buffer = new Array[Byte](1024)
+          breakable {
+            while (true) {
+              val bytesRead = in.read(buffer)
+              if (bytesRead == -1) {
+                break
+              }
+              targetJar.write(buffer, 0, bytesRead)
+            }
+          }
+          targetJar.closeEntry()
+          in.close()
+        }
+
+        filesToAdd = filesToAdd.tail
+      }
+
+      targetJar.close()
+    } catch {
+      case e: Exception => throw new JarPackaginException(e)
+    }
+  }
+
+}
+
+/** Exception to indicate that the packaging process failed.
+  *
+  * @param cause the causing exception
+  */
+class JarPackaginException(cause: Exception) extends RuntimeException(cause)
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/io/SClassWriter.scala b/src/main/scala/org/rosi_project/model_sync/generator/io/SClassWriter.scala
new file mode 100644
index 0000000000000000000000000000000000000000..ca1d49cefc4bcbb85b8656b7c42ac925366eb984
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/io/SClassWriter.scala
@@ -0,0 +1,71 @@
+package org.rosi_project.model_sync.generator.io
+
+import org.rosi_project.model_sync.generator.acr_model.{SAttribute, SMethod, SClass}
+import org.rosi_project.model_sync.generator.support.ExtendedString.stringToExtended
+
+/** The `Writer` generates the source code of a [[SClass]] and provides it as a `String`.
+  *
+  * @author Rico Bergmann
+  */
+class SClassWriter(val modelClass: SClass) {
+
+  private val pckg: String = if (modelClass.isDefaultPackage) "" else s"package ${modelClass.sPackage}"
+  private val imports: Seq[String] = modelClass.collectImports.toSeq
+
+  private val clazzFixture: String = generateClassFixture
+
+  /** Provides a source code representation of the `modelClass` as a `String`.
+    *
+    * When writing it to a file it will be able to be compiled with the `scalac` (assuming the
+    * ''classpath'' is set-up correctly).
+    */
+  def stringify: String = {
+    s"""$pckg
+       |
+       |${imports.map(i => s"import $i").mkString("\n")}
+       |
+       |class $clazzFixture {
+       |
+       |  ${modelClass.getAdditionalConstructorStatements.map(_.getContent).mkString("\n")}
+       |
+       |  ${modelClass.getMethods.map(stringifyMethod).mkString("\n")}
+       |
+       |}
+    """.stripMargin
+  }
+
+  /** Writes a method as source code.
+    *
+    * @param m the method to write
+    * @return the `String` representation of `m`
+    */
+  protected def stringifyMethod(m: SMethod): String = {
+    s"""${if (m.overrides) "override" else ""} def ${m.name}(${m.params.map(param => s"${param.name}: ${param.getType}").mkString(", ")}): ${m.getResultType} = {
+       |  ${m.implementation.map(_.getContent).mkString("\n")}
+       |}
+     """.stripMargin
+  }
+
+  /** Writes the "''class fixture''", i.e. the `class` identifier followed by the constructor and
+    * optionally a parent class. The parent constructor will be called correctly.
+    */
+  protected def generateClassFixture: String = {
+    var params: List[String] = modelClass.attributes.map(attr => s"var ${attr.name}: ${attr.getType}").toList
+    val parentConstructorParams: String =
+      if (modelClass.isRootClass)
+        ""
+      else
+        modelClass.parent.getConstructorParameters.map(param => s"${param.name}: ${param.getType}").mkString(", ")
+
+    val parentConstructor: String = if (modelClass.isRootClass) "" else s"(${modelClass.parent.getConstructorParameters.map(_.name).toList.mkString(", ")})"
+
+    if (parentConstructorParams != "")
+      params ::= parentConstructorParams
+
+    val constructor = s"(${params.mkString(", ")})"
+    val baseFixture = s"${modelClass.getName}$constructor"
+
+    if (modelClass.isRootClass) baseFixture else s"$baseFixture extends ${modelClass.parent.getName}$parentConstructor"
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/io/SClassWriterTest.scala b/src/main/scala/org/rosi_project/model_sync/generator/io/SClassWriterTest.scala
new file mode 100644
index 0000000000000000000000000000000000000000..528232225f239873446d478ce402be9a6a76a604
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/io/SClassWriterTest.scala
@@ -0,0 +1,26 @@
+package org.rosi_project.model_sync.generator.io
+
+import org.rosi_project.model_sync.generator.acr_model._
+import org.rosi_project.model_sync.generator.sync.{GetterSetterGeneratingVisitor, SyncEnhancingVisitor}
+
+/**
+  * @author Rico Bergmann
+  */
+object SClassWriterTest/* extends App */{
+
+  val stringType = SType("String")
+  val attrs = Seq(SAttribute("name", stringType))
+  val sayHelloMethod = new SMethod("sayHello", stringType, Seq.empty, Seq(SMethodStatement("s\"Hello $name\"")))
+  val modelClass = new SClass("Person", attributes = attrs, methods = Seq(sayHelloMethod))
+
+  val getterSetterVisitor = new GetterSetterGeneratingVisitor
+  modelClass.accept(getterSetterVisitor)
+
+  val syncNotificationVisitor = new SyncEnhancingVisitor(null)
+  modelClass.accept(syncNotificationVisitor)
+
+  val writer = new SClassWriter(modelClass)
+
+  println(writer.stringify)
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/io/SModelFSWriter.scala b/src/main/scala/org/rosi_project/model_sync/generator/io/SModelFSWriter.scala
new file mode 100644
index 0000000000000000000000000000000000000000..5a42da3e582afb901a5f5bcca1955b888d58f829
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/io/SModelFSWriter.scala
@@ -0,0 +1,143 @@
+package org.rosi_project.model_sync.generator.io
+
+import java.nio.file.{Files, StandardCopyOption}
+import java.{io => jio}
+
+import org.rosi_project.model_sync.generator.ModelConfig
+import org.rosi_project.model_sync.generator.acr_model._
+import org.rosi_project.model_sync.generator.env.{FilesCompiler, JarPackager}
+
+import scala.reflect.io.{Directory, File, Path}
+
+/** The `FSWriter` writes a [[SModel]] as a single compiled ''JAR'' file to the File System.
+  *
+  * @param workingDir directory to write the `.scala` files to. Will default to a temporary dir.
+  * @param outputDir directory to write the generated `model.jar` to
+  * @param keepClassFiles whether the `.scala` files should be added to the ''JAR'' as well
+  *
+  * @author Rico Bergmann
+  */
+class SModelFSWriter(
+    workingDir: File = File(Files.createTempDirectory("model").toFile),
+    outputDir: Directory = File("output").toDirectory,
+    keepClassFiles: Boolean = false,
+    modelCfg: ModelConfig,
+    currentDir: jio.File = new jio.File("").getAbsoluteFile) extends SModelVisitor {
+
+  workingDir.jfile.mkdirs()
+  outputDir.jfile.mkdirs()
+
+  private var sFilesToCompile: List[File] = List.empty
+
+  println(s"Temp dir (sources) is $workingDir")
+  println(s"Output dir is $outputDir")
+
+  println("... Copying model images")
+  private var images: List[jio.File] = _
+  private var imagesTargetDir: jio.File = _
+  try {
+    images = collectAllModelImages(modelCfg, currentDir)
+    imagesTargetDir = new jio.File(workingDir.toAbsolute.toString() + File.separator + "res")
+    imagesTargetDir.mkdirs()
+    images.foreach(img => {
+      Files.copy(img.toPath, imagesTargetDir.toPath.resolve(img.getName), StandardCopyOption.REPLACE_EXISTING)
+    })
+  } catch {
+    case e: Exception => throw new ModelImagePreparationException(e)
+  }
+
+
+  override def visit(sModel: SModel): Unit = {
+    println(s"... Wrote files (sources) $sFilesToCompile")
+
+    println("... Starting compilation")
+    new FilesCompiler(workingDir).run(sFilesToCompile)
+    println("... Compilation done")
+
+    if (!keepClassFiles) {
+      println("... Cleaning up")
+      sFilesToCompile.foreach {
+        _.delete
+      }
+    } else {
+      println("... No clean-up requested")
+    }
+
+    println("... Generating JAR")
+    new JarPackager(workingDir, outputDir.toFile).run()
+    println("... Done")
+  }
+
+  override def visit(sClass: SClass): Unit = {
+    try {
+      println(s"Writing class $sClass")
+      val classNameWithPath = workingDir.toAbsolute.toString() + File.separator + pckg2Path(sClass.getPackage) + File.separator + s"${sClass.name}.scala"
+      val writer = new SClassWriter(sClass)
+
+      val classFile = File(classNameWithPath)
+
+      classFile.jfile.getParentFile.mkdirs()
+      classFile.writeAll(writer.stringify)
+      sFilesToCompile ::= classFile
+    } catch {
+      case e: Exception => throw new ClassWritingException(e)
+    }
+  }
+
+  override def visit(sAttr: SAttribute): Unit = {
+    // pass
+  }
+
+  override def visit(sMethod: SMethod): Unit = {
+    // pass
+  }
+
+  override def visit(sType: SType): Unit = {
+    // pass
+  }
+
+  /** Maps a Scala package to an relative path on the File System.
+    *
+    * @return the OS-specific path
+    */
+  private def pckg2Path(pckg: String): Path = Path(pckg.replace(".", File.separator))
+
+  private def collectAllModelImages(modelConfig: ModelConfig, workingPath: jio.File): List[jio.File] = {
+    var images: List[jio.File] = List.empty
+
+    modelConfig.init.image.foreach(img => {
+      images = images :+ new jio.File(workingPath.getPath + File.separator + img)
+    })
+
+    modelConfig.init.nested.foreach(nested => {
+      nested.foreach(model => {
+        model.image.foreach(img => {
+          images = images :+ new jio.File(workingPath.getPath + File.separator + img)
+        })
+      })
+    })
+
+    modelConfig.integration.foreach(integration => {
+      integration.foreach(model => {
+        model.image.foreach(img => {
+          images = images :+ new jio.File(workingPath.getPath + File.separator + img)
+        })
+      })
+    })
+
+    images
+  }
+
+}
+
+/** Exception to indicate that the model images may not be copied into the JAR.
+  *
+  * @param cause the causing exception
+  */
+class ModelImagePreparationException(cause: Exception) extends RuntimeException(cause)
+
+/** Exception to indicate that a class may not be written.
+  *
+  * @param cause the causing exception
+  */
+class ClassWritingException(cause: Exception) extends RuntimeException(cause)
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/io/SModelFSWriterTest.scala b/src/main/scala/org/rosi_project/model_sync/generator/io/SModelFSWriterTest.scala
new file mode 100644
index 0000000000000000000000000000000000000000..266077240831ebcfb08efbc09ea975e656a2c3b4
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/io/SModelFSWriterTest.scala
@@ -0,0 +1,28 @@
+package org.rosi_project.model_sync.generator.io
+
+import org.rosi_project.model_sync.generator.acr_model.{SimpleSModel, _}
+import org.rosi_project.model_sync.generator.sync.{GetterSetterGeneratingVisitor, SyncEnhancingVisitor}
+
+import scala.reflect.io.File
+
+/** @author Rico Bergmann
+  */
+object SModelFSWriterTest/* extends App */{
+
+  val model = new SimpleSModel
+
+  val stringType = SType("String")
+
+  val personAttrs = Seq(SAttribute("name", stringType))
+  val personSayHelloMethod = new SMethod("sayHello", stringType, Seq.empty, Seq(SMethodStatement("s\"Hello $name\"")))
+  val personClass = new SClass("Person", sPackage = "foo", attributes = personAttrs, methods = Seq(personSayHelloMethod))
+
+  val getterSetterVisitor = new GetterSetterGeneratingVisitor
+  val syncNotificationVisitor = new SyncEnhancingVisitor(null)
+
+  model.addModelClass(personClass)
+
+  model.accept(getterSetterVisitor)
+  model.accept(syncNotificationVisitor)
+  //model.accept(new SModelFSWriter(workingDir = File("output/raw"), keepClassFiles = true))
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/io/package.scala b/src/main/scala/org/rosi_project/model_sync/generator/io/package.scala
new file mode 100644
index 0000000000000000000000000000000000000000..cbc3d55c39e06f54594441cf2281c5b997283795
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/io/package.scala
@@ -0,0 +1,11 @@
+package org.rosi_project.model_sync.generator
+
+import org.rosi_project.model_sync.generator.acr_model.SModel
+
+/** Contains services to write [[SModel]] instances to the File system.
+  *
+  * @author Rico Bergmann
+  */
+package object io {
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/support/Assert.scala b/src/main/scala/org/rosi_project/model_sync/generator/support/Assert.scala
new file mode 100644
index 0000000000000000000000000000000000000000..ffc599f8bbcb39a78eb42b1e353d01f19029010a
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/support/Assert.scala
@@ -0,0 +1,35 @@
+package org.rosi_project.model_sync.generator.support
+
+/** Utilities to enforce some domain constraints. If the constraint fails, most methods will throw
+  * an `AssertionError` (unless stated otherwise).
+  *
+  * @author Rico Bergmann
+  */
+object Assert {
+
+  /** Ensures that an object is not `null`. Will throw an `AssertionError` if it is.
+    *
+    * @param obj the object to check
+    * @param msg the message to display in case the constraint fails
+    */
+  def notNull(obj: AnyRef, msg: String): Unit = isTrue(obj != null, msg)
+
+  /** Ensures that some condition holds (i.e. evaluates to `true`). Will throw an `AssertionError`
+    * if it `false`.
+    *
+    * @param cond the condition to evaluate
+    * @param msg the message to display in case the constraint fails
+    */
+  def isTrue(cond: Boolean, msg: String): Unit = if (!cond) {
+      throw new AssertionError(msg)
+  }
+
+  /** Ensures that some condition does '''not''' hold (i.e. evaluates to `false`). Will throw an
+    * `AssertionError` if it is `true`.
+    *
+    * @param cond the condition to evaluate
+    * @param msg the message to display in case the constraint fails
+    */
+  def isFalse(cond: Boolean, msg: String): Unit = isTrue(!cond, msg)
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/support/ExtendedString.scala b/src/main/scala/org/rosi_project/model_sync/generator/support/ExtendedString.scala
new file mode 100644
index 0000000000000000000000000000000000000000..3a277849ff038f05615b79da9100ce785a08c900
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/support/ExtendedString.scala
@@ -0,0 +1,25 @@
+package org.rosi_project.model_sync.generator.support
+
+/** Adds more methods to a `String`.
+  *
+  * @author Rico Bergmann
+  */
+class ExtendedString(wrappedString: String) {
+
+  /** Turns the first character of a `String` into its upper-case equivalent.
+    *
+    * If no such equivalent exists, nothing will happen. If the `String` is empty neither.
+    */
+  def firstLetterToUpperCase: String =
+    wrappedString.headOption.map(h => h.toUpper + wrappedString.tail).getOrElse("")
+
+}
+
+/** The companion provides the implicit conversion.
+  */
+object ExtendedString {
+
+  implicit def stringToExtended(str: String): ExtendedString = new ExtendedString(str)
+
+}
+
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/support/package.scala b/src/main/scala/org/rosi_project/model_sync/generator/support/package.scala
new file mode 100644
index 0000000000000000000000000000000000000000..3be377dd9a52ca67ded41a8506b3d6d2b549e622
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/support/package.scala
@@ -0,0 +1,9 @@
+package org.rosi_project.model_sync.generator
+
+/** Contains various helper classes that are independent of the actual domain business logic.
+  *
+  * @author Rico Bergmann
+  */
+package object support {
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/ConstructorTemplate.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/ConstructorTemplate.scala
new file mode 100644
index 0000000000000000000000000000000000000000..c1dfbbec7dee61f50deceb0b3ad7a26c2c215ce8
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/ConstructorTemplate.scala
@@ -0,0 +1,51 @@
+package org.rosi_project.model_sync.generator.sync
+
+import org.rosi_project.model_sync.instances.ModelInstanceConstructor
+import org.rosi_project.model_sync.generator.acr_model.{SClass, SImport, SMethod, SMethodParameter, SMethodStatement, SType, types => acr}
+
+/** Creates implementations of [[ModelInstanceConstructor]] for model classes.
+  *
+  * @param modelClass the class for which the constructor should be wrapped
+  * @author Rico Bergmann
+  */
+class ConstructorTemplate(modelClass: SClass) extends SClass(name = s"${modelClass.name}Constructor") {
+
+  parent = PredefTypes.ModelInstanceConstructor
+
+  private val constructorParams = modelClass.attributes.map(attr => s"classOf[${attr.attrType.getName}]").mkString(", ")
+
+  constructorStatements = constructorStatements :+ SMethodStatement(s"private val ${ConstructorTemplate.ConstructorAttr} = classOf[${modelClass.getName}].getConstructor($constructorParams)")
+
+  methods = Seq(
+    new SMethod(
+      name = "getParameters",
+      result = acr.SSeq(acr.PredefTypes.Parameter),
+      params = Seq.empty,
+      implementation = s"${ConstructorTemplate.ConstructorAttr}.getParameters",
+      overrides = true
+    ),
+    new SMethod(
+      name = "invoke",
+      result = SType.AnyRef,
+      params = Seq(SMethodParameter("args", acr.SSeq(acr.PredefTypes.Object))),
+      implementation = s"${ConstructorTemplate.ConstructorAttr}.newInstance(args: _*)",
+      overrides = true
+    ),
+    new SMethod(
+      name = "toString",
+      result = SType.String,
+      params = Seq.empty,
+      implementation = s""" "${modelClass.getName} Constructor" """,
+      overrides = true
+    )
+  )
+
+  override def getNecessaryImports: Set[SImport] = modelClass.getUsedTypes.filter(_.getPackage != "").map(elem => SImport(elem.getPackage, elem.getName)) ++ super.getNecessaryImports + SImport(modelClass.getPackage, modelClass.getName)
+
+}
+
+object ConstructorTemplate {
+
+  val ConstructorAttr = "constructor"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/GetterSetterGeneratingVisitor.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/GetterSetterGeneratingVisitor.scala
new file mode 100644
index 0000000000000000000000000000000000000000..dd6fd38c5c7cc1da67789de45328b9a876637bfb
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/GetterSetterGeneratingVisitor.scala
@@ -0,0 +1,36 @@
+package org.rosi_project.model_sync.generator.sync
+import org.rosi_project.model_sync.generator.acr_model._
+
+/** Service to extend [[SClass SClasses]] with getter and setter methods for all attributes.
+  *
+  * @author Rico Bergmann
+  */
+class GetterSetterGeneratingVisitor extends SModelVisitor {
+
+  override def visit(sModel: SModel): Unit = {
+    // pass
+  }
+
+  override def visit(sClass: SClass): Unit = {
+    sClass.attributes.foreach(attr => {
+      val getter = new SGetter(attr)
+      val setter = new SSetter(attr)
+      sClass.addMethod(getter)
+      sClass.addMethod(setter)
+    })
+
+  }
+
+  override def visit(sAttr: SAttribute): Unit = {
+    // pass
+  }
+
+  override def visit(sMethod: SMethod): Unit = {
+    // pass
+  }
+
+  override def visit(sType: SType): Unit = {
+    // pass
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/Imports.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/Imports.scala
new file mode 100644
index 0000000000000000000000000000000000000000..bf4ba3cf8cda50fdef4ec5f8b52bc201d1ae2d78
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/Imports.scala
@@ -0,0 +1,15 @@
+package org.rosi_project.model_sync.generator.sync
+
+import org.rosi_project.model_sync.generator.acr_model.SImport
+
+/** Contains a number of imports which are used throughout the sync infrastructure.
+  *
+  * @author Rico Bergmann
+  */
+object Imports {
+
+  /** Import for the `ModelRegistry` type.
+    */
+  val ModelRegistry = SImport(PredefTypes.ModelRegistry.sPackage, PredefTypes.ModelRegistry.name)
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/InitialModelTemplate.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/InitialModelTemplate.scala
new file mode 100644
index 0000000000000000000000000000000000000000..018824aa198f879de82decbd01bf37b5fbf06ac6
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/InitialModelTemplate.scala
@@ -0,0 +1,97 @@
+package org.rosi_project.model_sync.generator.sync
+
+import java.io.File
+
+import org.rosi_project.model_sync.generator.{Model, ModelDataException}
+import org.rosi_project.model_sync.generator.acr_model._
+import org.rosi_project.model_sync.generator.acr_model.{types => acr}
+import org.rosi_project.model_sync.generator.support.ExtendedString.stringToExtended
+import org.rosi_project.model_sync.provider.DisplayableModelForInitialization
+
+
+/** Creates implementations of [[DisplayableModelForInitialization]] for models.
+  *
+  * @param model the model which should be initialized
+  * @author Rico Bergmann
+  */
+class InitialModelTemplate(model: Model) extends SClass(name = s"Initialized${Model.parseClass(model.primaryClass)._2.firstLetterToUpperCase}") {
+
+  parent = SType("DisplayableModelForInitialization", sPackage = "org.rosi_project.model_sync.provider")
+
+  private val primaryClassComps = Model.parseClass(model.primaryClass)
+  private val primaryClass: SClass = STypeRegistry.query(primaryClassComps._2, primaryClassComps._1).getOrElse(throw new ModelDataException).asInstanceOf[SClass]
+  private val additionalClasses: Seq[SClass] = model.additionalClasses.getOrElse(List()).map(Model.parseClass).map(cls => STypeRegistry.query(cls._2, cls._1).getOrElse(throw new ModelDataException).asInstanceOf[SClass])
+  private val allModelClasses = primaryClass +: additionalClasses
+
+  private var modelImagePath = "None"
+  model.image.foreach(image => {
+    val imageFile = new File(image)
+    modelImagePath = s"""Some(new File("${RES_PATH + imageFile.getName}"))"""
+  })
+
+
+  methods = Seq(
+    new SMethod (
+      name = "getName",
+      result = SType.String,
+      params = Seq.empty,
+      implementation = s""" "${model.name}" """
+    ),
+    new SMethod (
+      name = "getInstanceClass",
+      result = SType("Class[_ <: AnyRef]"),
+      params = Seq.empty,
+      implementation = s"classOf[${primaryClass.getName}]"
+    ),
+    new SMethod (
+      name = "getModelDiagram",
+      result = acr.PredefTypes.option(acr.PredefTypes.File),
+      params = Seq.empty,
+      implementation = modelImagePath
+    ),
+
+    new SMethod (
+      name = "getConstructors",
+      result = acr.SSeq(PredefTypes.ModelInstanceConstructor),
+      params = Seq.empty,
+      implementation = s"Seq(${allModelClasses.map(cls => s"new ${cls.getName}Constructor").mkString(", ")})"
+    ),
+    new SMethod (
+      name = "getUpdateMethods",
+      result = acr.SSeq(PredefTypes.ModelInstanceModifier),
+      params = Seq.empty,
+      implementation = s"Seq(${allModelClasses.map(cls => cls.attributes.map(attr => s"new ${cls.name}${attr.name.firstLetterToUpperCase}Modifier").mkString(", ")).mkString(", ")})"
+    ),
+    new SMethod (
+      name = "getInstances",
+      result = acr.SSeq(SType.AnyRef),
+      params = Seq.empty,
+      implementation = "Seq.empty" // TODO
+    )
+  )
+
+  if (model.nested.nonEmpty) {
+
+    methods = methods :+ new SMethod(
+      name = "getNestedModels",
+      result = acr.SSeq(PredefTypes.DisplayableModel),
+      params = Seq.empty,
+      implementation = s"Seq( ${model.nested.getOrElse(List()).map(nested => "new Initialized"  + Model.parseClass(nested.primaryClass)._2.firstLetterToUpperCase).mkString(", ")} )",
+      overrides = true
+    )
+
+  }
+
+  constructorStatements = Statements.ModelRegistration +: constructorStatements
+
+  override def getNecessaryImports: Set[SImport] = {
+    val base = super.getNecessaryImports + Imports.ModelRegistry
+
+    if (primaryClass.isDefaultPackage) {
+      base
+    } else {
+      base + SImport(primaryClass.getPackage, primaryClass.getName)
+    }
+  }
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/ModelProviderTemplate.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/ModelProviderTemplate.scala
new file mode 100644
index 0000000000000000000000000000000000000000..9b912f66a65b930ceeb400206f4c89d5f195f283
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/ModelProviderTemplate.scala
@@ -0,0 +1,32 @@
+package org.rosi_project.model_sync.generator.sync
+
+import org.rosi_project.model_sync.generator.Model
+import org.rosi_project.model_sync.generator.acr_model.{SClass, SMethod, types => acr}
+import org.rosi_project.model_sync.generator.support.ExtendedString.stringToExtended
+import org.rosi_project.model_sync.provider.ModelSyncProvider
+
+/** Creates implementations of [[ModelSyncProvider]] for models.
+  *
+  * @param model the model which should be initialized
+  * @author Rico Bergmann
+  */
+class ModelProviderTemplate(model: Model) extends SClass(name = "ModelProvider") {
+
+  parent = PredefTypes.ModelProvider
+
+  methods = Seq(
+    new SMethod (
+      name = "getInitialDisplayableModel",
+      result = PredefTypes.DisplayableModelForInitialization,
+      params = Seq.empty,
+      implementation = s"new Initialized${Model.parseClass(model.primaryClass)._2.firstLetterToUpperCase}"
+    ),
+    new SMethod (
+      name = "getDisplayableModelsForIntegration",
+      result = acr.SSeq(PredefTypes.DisplayableModelForIntegration),
+      params = Seq.empty,
+      implementation = "Seq.empty"
+    )
+  )
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/ModifierTemplate.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/ModifierTemplate.scala
new file mode 100644
index 0000000000000000000000000000000000000000..065a04ca7a11ca0813534c284d4384eb951db4d0
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/ModifierTemplate.scala
@@ -0,0 +1,58 @@
+package org.rosi_project.model_sync.generator.sync
+
+import org.rosi_project.model_sync.generator.acr_model.{GenericSType, SAttribute, SClass, SImport, SMethod, SMethodParameter, SMethodStatement, SType, STypedElement, types => acr}
+import org.rosi_project.model_sync.generator.support.ExtendedString.stringToExtended
+import org.rosi_project.model_sync.instances.ModelInstanceModifier
+
+/** Creates implementations of [[ModelInstanceModifier]] for attributes of model classes.
+  *
+  * @param modelClass the class which contains the attribute
+  * @param attribute the attribute for which the modifier should be created
+  * @author Rico Bergmann
+  */
+class ModifierTemplate(modelClass: SClass, attribute: SAttribute) extends SClass(name = s"${modelClass.name}${attribute.name.firstLetterToUpperCase}Modifier") {
+
+  parent = PredefTypes.ModelInstanceModifier
+
+  constructorStatements = constructorStatements :+
+    SMethodStatement(s"""private val ${ModifierTemplate.MethodAttr} = classOf[${modelClass.getName}].getMethod("set${attribute.name.firstLetterToUpperCase}", classOf[${attribute.getType}])""")
+
+  methods = Seq(
+    new SMethod (
+      name = "getParameters",
+      result = acr.SSeq(acr.PredefTypes.Parameter),
+      params = Seq.empty,
+      implementation = s"${ModifierTemplate.MethodAttr}.getParameters",
+      overrides = true
+    ),
+    new SMethod (
+      name = "invoke",
+      result = SType.Unit,
+      params = Seq(SMethodParameter("instance", SType.AnyRef), SMethodParameter("args", acr.SSeq(SType.AnyRef))),
+      implementation = s"${ModifierTemplate.MethodAttr}.invoke(instance, args: _*)",
+      overrides = true
+    ),
+    new SMethod(
+      name = "toString",
+      result = SType.String,
+      params = Seq.empty,
+      implementation = s""" "Change ${attribute.name}" """,
+      overrides = true
+    )
+  )
+
+  override def getNecessaryImports: Set[SImport] = {
+    val attrImports = attribute.attrType match {
+      case gt : GenericSType => SImport.generateImports(gt, gt.typeParam)
+      case t : STypedElement => SImport.generateImports(t)
+    }
+    modelClass.getNecessaryImports ++ super.getNecessaryImports + SImport(modelClass.getPackage, modelClass.getName) ++ attrImports
+  }
+
+}
+
+object ModifierTemplate {
+
+  val MethodAttr = "method"
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/PredefTypes.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/PredefTypes.scala
new file mode 100644
index 0000000000000000000000000000000000000000..0491c444f2512bada9edabbedf5f803c9ab01ca2
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/PredefTypes.scala
@@ -0,0 +1,42 @@
+package org.rosi_project.model_sync.generator.sync
+
+import org.rosi_project.model_sync.generator.acr_model.SType
+
+/** Contains a number of types which are used throughout the synchronization.
+  *
+  * Equivalent to [[org.rosi_project.model_sync.generator.acr_model.types.PredefTypes]], just for
+  * the sync types.
+  *
+  * @author Rico Bergmann
+  */
+object PredefTypes {
+
+  /** `org.rosi_project.model_sync.provider.DisplayableModel`
+    */
+  val DisplayableModel = SType("DisplayableModel", "org.rosi_project.model_sync.provider")
+
+  /** `org.rosi_project.model_sync.provider.DisplayableModelForInitialization`
+    */
+  val DisplayableModelForInitialization = SType("DisplayableModelForInitialization", "org.rosi_project.model_sync.provider")
+
+  /** `org.rosi_project.model_sync.provider.DisplayableModelForIntegration`
+    */
+  val DisplayableModelForIntegration = SType("DisplayableModelForIntegration", "org.rosi_project.model_sync.provider")
+
+  /** `org.rosi_project.model_sync.provider.ModelSyncProvider`
+    */
+  val ModelProvider = SType("ModelSyncProvider", "org.rosi_project.model_sync.provider")
+
+  /** `org.rosi_project.model_sync.provider.ModelRegistry`
+    */
+  val ModelRegistry = SType("ModelRegistry", "org.rosi_project.model_sync.provider")
+
+  /** `org.rosi_project.model_sync.provider.instances.ModelInstanceConstructor`
+    */
+  val ModelInstanceConstructor = SType("ModelInstanceConstructor", "org.rosi_project.model_sync.instances")
+
+  /** `org.rosi_project.model_sync.provider.instances.ModelInstanceModifier`
+    */
+  val ModelInstanceModifier = SType("ModelInstanceModifier", "org.rosi_project.model_sync.instances")
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/Statements.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/Statements.scala
new file mode 100644
index 0000000000000000000000000000000000000000..59fa88d7d81fe94ed4dcccfbc1ae5a9b1eedf680
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/Statements.scala
@@ -0,0 +1,15 @@
+package org.rosi_project.model_sync.generator.sync
+
+import org.rosi_project.model_sync.generator.acr_model.SMethodStatement
+
+/** Contains method statements which are used for throughout the synchronization.
+  *
+  * @see [[SMethodStatement]]
+  *
+  * @author Rico Bergmann
+  */
+object Statements {
+
+  val ModelRegistration = SMethodStatement("ModelRegistry.registerNewModel(this)", Set(PredefTypes.ModelRegistry))
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/SyncEnhancingVisitor.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/SyncEnhancingVisitor.scala
new file mode 100644
index 0000000000000000000000000000000000000000..23e433740969af76c2c8dff3b0ff702283d61443
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/SyncEnhancingVisitor.scala
@@ -0,0 +1,94 @@
+package org.rosi_project.model_sync.generator.sync
+
+import org.rosi_project.model_sync.generator.ModelConfig
+import org.rosi_project.model_sync.generator.acr_model._
+import org.rosi_project.model_sync.sync.PlayerSync
+import org.rosi_project.model_sync.sync.ISynchronizationCompartment
+
+/** Augments [[SClass SClasses]] with the necessary method calls to make it usable in a
+  * synchronization context.
+  *
+  * The following modifications are performed:
+  *  - the class (or its furthest parent) becomes a subclass of [[PlayerSync]]
+  *  - [[PlayerSync.buildClass()]] will be called in the constructor
+  *  - each setter will notify the synchronization context about the change
+  *
+  * @see [[ISynchronizationCompartment]]
+  * @author Rico Bergmann
+  */
+class SyncEnhancingVisitor(val modelCfg: ModelConfig) extends SModelVisitor {
+
+  private var additionalSyncClasses: Seq[SClass] = Seq.empty
+
+  override def visit(sModel: SModel): Unit = {
+    additionalSyncClasses.foreach(sModel.addModelClass)
+
+    modelCfg.init.nested.foreach(_.foreach(model => sModel.addModelClass(new InitialModelTemplate(model))))
+    sModel.addModelClass(new InitialModelTemplate(modelCfg.init))
+    sModel.addModelClass(new ModelProviderTemplate(modelCfg.init))
+
+  }
+
+  override def visit(sClass: SClass): Unit = {
+    sClass.getInheritanceHierarchy.reverse.headOption.foreach{
+      case root: SClass => root.setParent(SyncEnhancingVisitor.PLAYER_SYNC_STYPE)
+      case tp => sys.error(s"May not enhance $tp as it is a type and not a class")
+    }
+
+    sClass.augmentConstructor(SyncEnhancingVisitor.PLAYER_SYNC_INIT)
+
+    additionalSyncClasses = additionalSyncClasses :+ new ConstructorTemplate(sClass)
+    sClass.attributes.foreach(attr => {
+      additionalSyncClasses = additionalSyncClasses :+ new ModifierTemplate(sClass, attr)
+    })
+  }
+
+  override def visit(sAttr: SAttribute): Unit = {
+    // pass
+  }
+
+  override def visit(sMethod: SMethod): Unit = {
+    extractSetterAttr(sMethod).foreach(attr => sMethod.augmentImplementation(SMethodStatement(s"+this change$attr ()")))
+  }
+
+  override def visit(sType: SType): Unit = {
+    // pass
+  }
+
+  /** Tries to get the attribute's name from a setter method.
+    *
+    * A ''valid'' setter will have the following signature: `setXyz(x: T): Unit` (the parameter's
+    * name does not matter).
+    *
+    * Mind that the first letter will be left uppercase (i.e. `Xyz` will be returned although the
+    * actual attribute may be `xyz`)
+    *
+    * @param sMethod the method to analyze. May be any method (not necessarily a setter) but never
+    *                `null`.
+    * @return the attribute's name if `sMethod` was a valid setter. '''The first letter will be left
+    *         uppercase.'''
+    */
+  private def extractSetterAttr(sMethod: SMethod): Option[String] = {
+    sMethod.name match {
+      case SyncEnhancingVisitor.Setter(attrName) =>
+        Option(attrName)
+      case _ =>
+        None
+    }
+
+  }
+
+}
+
+/** The companion contains some static values.
+  */
+object SyncEnhancingVisitor {
+
+  private val Setter = """set([A-Z][a-zA-z0-9]*)""".r
+
+  private val PLAYER_SYNC_CLASS = classOf[PlayerSync]
+  private val PLAYER_SYNC_STYPE = SType(PLAYER_SYNC_CLASS.getSimpleName, PLAYER_SYNC_CLASS.getPackage.getName)
+
+  private val PLAYER_SYNC_INIT = SMethodStatement("buildClass()")
+
+}
diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/package.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/package.scala
new file mode 100644
index 0000000000000000000000000000000000000000..4e0a694ebd741b85202f40dac9ad9ecf4b9298e4
--- /dev/null
+++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/package.scala
@@ -0,0 +1,17 @@
+package org.rosi_project.model_sync.generator
+
+import org.rosi_project.model_sync.generator.acr_model.{SMethodStatement, SModel}
+
+/** Contains services to adapt a [[SModel]] for usage in a ''Synchronization context''.
+  *
+  * @author Rico Bergmann
+  */
+package object sync {
+
+  val RES_PATH = "res/"
+
+  implicit def t2SeqT[T](t: T): Seq[T] = Seq(t)
+
+  implicit def string2SeqMethodImpl(impl: String): Seq[SMethodStatement] = Seq(SMethodStatement(impl))
+
+}