Skip to content
Snippets Groups Projects
Verified Commit 17ea5437 authored by Rico Bergmann's avatar Rico Bergmann
Browse files

[WIP] Add converter from EMF to SModel

Still WIP as not all features are available yet (e.g. some data type
mappings are still missing)
parent 2b8d9219
Branches
No related tags found
2 merge requests!2Publish v0.1,!1Move away from experimental changes
This commit is part of merge request !1. Comments created here will be created in the context of that merge request.
Showing
with 230 additions and 47 deletions
...@@ -174,3 +174,4 @@ local.properties ...@@ -174,3 +174,4 @@ local.properties
.idea/ .idea/
.classpath .classpath
.project .project
output/
.gitmodules 0 → 100644
[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
<?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" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Male" eSuperTypes="#//Person"/>
<eClassifiers xsi:type="ecore:EClass" name="Female" eSuperTypes="#//Person"/>
</ecore:EPackage>
import sbt.Keys.{libraryDependencies, scalacOptions, version}
val emfcommonVersion = "2.12.0" val emfcommonVersion = "2.12.0"
val emfecoreVersion = "2.12.0" val emfecoreVersion = "2.12.0"
val scrollVersion = "1.6" val scrollVersion = "1.6"
name := "CodeGenerator" val syncProvider = RootProject(file("lib/ModelSyncProvider"))
version := "0.1"
scalaVersion := "2.12.6"
lazy val generator = (project in file("."))
.settings(
name := "CodeGenerator",
version := "0.1",
scalaVersion := "2.12.6",
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value, "org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.scala-lang" % "scala-compiler" % scalaVersion.value, "org.scala-lang" % "scala-compiler" % scalaVersion.value,
"org.eclipse.emf" % "org.eclipse.emf.common" % emfcommonVersion, "org.eclipse.emf" % "org.eclipse.emf.common" % emfcommonVersion,
"org.eclipse.emf" % "org.eclipse.emf.ecore" % emfecoreVersion, "org.eclipse.emf" % "org.eclipse.emf.ecore" % emfecoreVersion,
"com.github.max-leuthaeuser" %% "scroll" % scrollVersion ),
)
scalacOptions ++= Seq( scalacOptions ++= Seq(
"-language:implicitConversions" "-language:implicitConversions"
) )
).dependsOn(syncProvider)
Subproject commit ace8bdf5799dd89bd66d01782237123172bb0ec0
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4") addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4")
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0")
package org.rosi_project.model_sync.generator package org.rosi_project.model_sync.generator
import org.eclipse.emf.ecore._ import org.eclipse.emf.ecore._
import org.eclipse.emf.ecore.resource.Resource
import org.rosi_project.model_sync.generator.conversion.SModelGenerator import org.rosi_project.model_sync.generator.conversion.SModelGenerator
import org.rosi_project.model_sync.generator.io.SModelFSWriter
import org.rosi_project.model_sync.generator.io.SModelFSWriterTest.{model, personClass}
import org.rosi_project.model_sync.generator.sync_model.{GetterSetterGeneratingVisitor, SModel, SyncEnhancingVisitor}
import scroll.internal.ecore.ECoreImporter
import scala.reflect.io.File
/** /**
...@@ -11,23 +18,29 @@ object Generator extends App { ...@@ -11,23 +18,29 @@ object Generator extends App {
val ecoreModel = loadEcore() val ecoreModel = loadEcore()
SModelGenerator.convert(ecoreModel) val sModel = SModelGenerator.convert(ecoreModel)
prepareModel(sModel)
sModel.accept(new SModelFSWriter(workingDir = File("output/raw"), keepClassFiles = true))
def loadEcore(): EPackage = { def loadEcore(): EPackage = {
val factory: EcoreFactory = EcoreFactory.eINSTANCE val importer = new ECoreImporter {
val ecorePackage: EcorePackage = EcorePackage.eINSTANCE def lm(): Resource = loadModel()
val ePackage: EPackage = factory.createEPackage() }
importer.path = "assets/ttc17.ecore"
val res = importer.lm()
println(s"We done: $res")
val ePerson = factory.createEClass() res.getContents.toArray(new Array[EObject](0)).toList.find(_.isInstanceOf[EPackage]).map((p: EObject) => p.asInstanceOf[EPackage]).orNull
ePerson.setName("Person") }
ePackage.getEClassifiers().add(ePerson)
val eName: EAttribute = factory.createEAttribute() def prepareModel(sModel: SModel): Unit = {
eName.setName("name") val getterSetterVisitor = new GetterSetterGeneratingVisitor
eName.setEType(ecorePackage.getEString) val syncNotificationVisitor = new SyncEnhancingVisitor
ePerson.getEStructuralFeatures.add(eName)
ePackage sModel.accept(getterSetterVisitor)
sModel.accept(syncNotificationVisitor)
} }
} }
...@@ -7,12 +7,16 @@ import org.rosi_project.model_sync.generator.sync_model.SModelVisitor ...@@ -7,12 +7,16 @@ import org.rosi_project.model_sync.generator.sync_model.SModelVisitor
* @author Rico Bergmann * @author Rico Bergmann
*/ */
class SClass(val name: String, class SClass(val name: String,
val attributes: Seq[SAttribute], var attributes: Seq[SAttribute] = Seq.empty,
val sPackage: String = "", val sPackage: String = "",
var parent: STypedElement = null, var parent: STypedElement = null,
var methods: Seq[SMethod] = Seq.empty) var methods: Seq[SMethod] = Seq.empty)
extends STypedElement { extends STypedElement {
private var constructorStatements: Seq[SMethodStatement] = Seq.empty
def getAdditionalConstructorStatements: Seq[SMethodStatement] = constructorStatements
def isDefaultPackage: Boolean = sPackage == "" def isDefaultPackage: Boolean = sPackage == ""
def isRootClass: Boolean = parent == null def isRootClass: Boolean = parent == null
...@@ -50,8 +54,12 @@ class SClass(val name: String, ...@@ -50,8 +54,12 @@ class SClass(val name: String,
def addMethod(m: SMethod): Unit = methods = methods :+ m def addMethod(m: SMethod): Unit = methods = methods :+ m
def augmentConstructor(statement: SMethodStatement): Unit = constructorStatements = constructorStatements :+ statement
def setParent(parent: STypedElement): Unit = this.parent = parent def setParent(parent: STypedElement): Unit = this.parent = parent
def setAttributes(attrs: Seq[SAttribute]): Unit = this.attributes = attrs
override def getName: String = name override def getName: String = name
override def getPackage: String = sPackage override def getPackage: String = sPackage
......
package org.rosi_project.model_sync.generator.conversion
import org.eclipse.emf.ecore.EDataType
import org.rosi_project.model_sync.generator.acr.SType
/**
* @author Rico Bergmann
*/
object EmfTypeTranslator {
private val typeMap: Map[String, SType] = Map(
"EString" -> SType("String")
)
def getSClassFromEmf(dataType: EDataType): Option[SType] = typeMap.get(dataType.getName)
}
package org.rosi_project.model_sync.generator.conversion
import org.eclipse.emf.ecore.{EAttribute, EClass}
import org.rosi_project.model_sync.generator.acr.{SAttribute, SClass, STypedElement}
import org.rosi_project.model_sync.generator.sync_model.STypeRegistry
/**
* @author Rico Bergmann
*/
class SClassConverter extends Converter[EClass, SClass] {
override def convert(source: EClass): SClass = {
var attrs: List[SAttribute] = List.empty
(source.getEAttributes: List[EAttribute]).foreach(eAttr => {
val attrType: STypedElement = STypeRegistry.query(eAttr.getEAttributeType.getName, eAttr.getEAttributeType.getEPackage.getName).getOrElse {
val newAttr = EmfTypeTranslator.getSClassFromEmf(eAttr.getEAttributeType).getOrElse(new SClass(eAttr.getEAttributeType.getName, sPackage = eAttr.getEAttributeType.getEPackage.getName))
STypeRegistry.addType(newAttr)
}
attrs ::= SAttribute(eAttr.getName, attrType)
})
val parents: List[EClass] = source.getESuperTypes.toArray(new Array[EClass](0)).toList
if (violatesSingleInheritance(parents)) {
throw new UnconvertibleEmfException(source.getEPackage, s"For class: $source")
}
val parent: STypedElement = parents.headOption.map((p: EClass) => {
STypeRegistry.queryForName(p.getName).getOrElse {
val parentSClass: SClass = new SClass(p.getName, sPackage = p.getEPackage.getName)
STypeRegistry.addType(parentSClass)
parentSClass
}
}).orNull
// TODO add methods to SClass
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, attrs, source.getEPackage.getName, parent)
STypeRegistry.addType(createdClass)
createdClass
}
}
private def violatesSingleInheritance(parents: List[EClass]): Boolean = parents.length > 1
}
...@@ -19,16 +19,19 @@ object SModelGenerator extends Converter[EPackage, SModel] { ...@@ -19,16 +19,19 @@ object SModelGenerator extends Converter[EPackage, SModel] {
contents.foreach { contents.foreach {
case ec: EClass => case ec: EClass =>
model.addModelClass(new SClassConverter convert ec)
STypeRegistry.addType(SType(ec.getName, packageName)) STypeRegistry.addType(SType(ec.getName, packageName))
println(s"Found class $ec") println(s"Found class $ec")
case et: EGenericType=> case et: EGenericType=>
STypeRegistry.addType(SType(et.eClass().getName, packageName)) //STypeRegistry.addType(SType(et.eClass().getName, packageName))
println(s"Found type $et") println(s"Found type $et")
case obj => case obj =>
println(s"Found something else: $obj") println(s"Found something else: $obj")
} }
println(s"The TypeRegistry:\n${STypeRegistry.allTypes}") println(s"The TypeRegistry:\n${STypeRegistry.allTypes}")
println(s"The model:\n$model")
model model
} }
......
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 => SModelGenerator.convert(e.asInstanceOf[EPackage]))
}
package org.rosi_project.model_sync.generator.conversion
import org.eclipse.emf.ecore.EPackage
/**
* @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)"}"
}
package org.rosi_project.model_sync.generator
import org.eclipse.emf.common.util.EList
/**
* @author Rico Bergmann
*/
package object conversion {
implicit def elist2slist[T](elst: EList[T]): List[T] = {
if (elst == null) {
null
} else {
var res: List[T] = List()
elst.forEach(elem => res = res :+ elem)
res
}
}
}
package org.rosi_project.model_sync.generator.io
/**
* @author Rico Bergmann
*/
class EmfImporter {
}
...@@ -19,6 +19,8 @@ class SClassWriter(val modelClass: SClass) { ...@@ -19,6 +19,8 @@ class SClassWriter(val modelClass: SClass) {
| |
|class $clazzFixture { |class $clazzFixture {
| |
| ${modelClass.getAdditionalConstructorStatements.map(_.content).mkString("\n")}
|
| ${modelClass.methods.map(stringifyMethod).mkString("\n")} | ${modelClass.methods.map(stringifyMethod).mkString("\n")}
| |
|} |}
......
...@@ -21,9 +21,6 @@ class SModelFSWriter( ...@@ -21,9 +21,6 @@ class SModelFSWriter(
outputDir: Directory = File("output").toDirectory, outputDir: Directory = File("output").toDirectory,
keepClassFiles: Boolean = false) extends SModelVisitor { keepClassFiles: Boolean = false) extends SModelVisitor {
// TODO enable different output dirs
// TODO enable retaining the .scala files
workingDir.jfile.mkdirs() workingDir.jfile.mkdirs()
outputDir.jfile.mkdirs() outputDir.jfile.mkdirs()
......
...@@ -11,4 +11,6 @@ trait SModel extends SModelElement { ...@@ -11,4 +11,6 @@ trait SModel extends SModelElement {
def getAllClasses: Set[SClass] def getAllClasses: Set[SClass]
override def toString: String = s"SModel: pckg=$getPackageName, classes=$getAllClasses"
} }
...@@ -11,9 +11,12 @@ object STypeRegistry { ...@@ -11,9 +11,12 @@ object STypeRegistry {
registerDefaultTypes() registerDefaultTypes()
def addType(theType: STypedElement): Unit = { def addType(theType: STypedElement): STypedElement = {
if (!registeredTypes.contains(theType)) {
registeredTypes += theType registeredTypes += theType
} }
theType
}
def query(name: String, sPackage: String): Option[STypedElement] = { def query(name: String, sPackage: String): Option[STypedElement] = {
registeredTypes.find(t => t.getName == name && t.getPackage == sPackage) registeredTypes.find(t => t.getName == name && t.getPackage == sPackage)
......
package org.rosi_project.model_sync.generator.sync_model package org.rosi_project.model_sync.generator.sync_model
import org.rosi_project.model_sync.generator.acr._ import org.rosi_project.model_sync.generator.acr._
import scroll.internal.Compartment import org.rosi_project.model_sync.sync.PlayerSync
/** /**
* @author Rico Bergmann * @author Rico Bergmann
...@@ -12,14 +12,12 @@ class SyncEnhancingVisitor extends SModelVisitor { ...@@ -12,14 +12,12 @@ class SyncEnhancingVisitor extends SModelVisitor {
} }
override def visit(sClass: SClass): Unit = { override def visit(sClass: SClass): Unit = {
// TODO extend PlayerSync and call setup method in constructor
sClass.getInheritanceHierarchy.reverse.headOption.foreach{ sClass.getInheritanceHierarchy.reverse.headOption.foreach{
case root: SClass => root.setParent(SyncEnhancingVisitor.COMPARTMENT_STYPE) 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") case tp => sys.error(s"May not enhance $tp as it is a type and not a class")
} }
println(s"Class hierarchy for $sClass : ${sClass.getInheritanceHierarchy}") sClass.augmentConstructor(SyncEnhancingVisitor.PLAYER_SYNC_INIT)
} }
...@@ -37,7 +35,7 @@ class SyncEnhancingVisitor extends SModelVisitor { ...@@ -37,7 +35,7 @@ class SyncEnhancingVisitor extends SModelVisitor {
private def extractSetterAttr(sMethod: SMethod): Option[String] = { private def extractSetterAttr(sMethod: SMethod): Option[String] = {
sMethod.name match { sMethod.name match {
case SyncEnhancingVisitor.SetterRegex(attrName) => case SyncEnhancingVisitor.Setter(attrName) =>
Option(attrName) Option(attrName)
case _ => case _ =>
None None
...@@ -49,11 +47,11 @@ class SyncEnhancingVisitor extends SModelVisitor { ...@@ -49,11 +47,11 @@ class SyncEnhancingVisitor extends SModelVisitor {
object SyncEnhancingVisitor { object SyncEnhancingVisitor {
private val SetterRegex = """set([A-Z][a-zA-z0-9]*)""".r private val Setter = """set([A-Z][a-zA-z0-9]*)""".r
private val COMPARTMENT_CLASS = classOf[Compartment] private val PLAYER_SYNC_CLASS = classOf[PlayerSync]
private val PLAYER_SYNC_STYPE = SType(PLAYER_SYNC_CLASS.getSimpleName, PLAYER_SYNC_CLASS.getPackage.getName)
// TODO enhance with player sync instead of compartment! (where to get the class from?) private val PLAYER_SYNC_INIT = SMethodStatement("buildClass()")
private val COMPARTMENT_STYPE = SType(COMPARTMENT_CLASS.getSimpleName, COMPARTMENT_CLASS.getPackage.getName)
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment