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
Showing
with 230 additions and 47 deletions
......@@ -174,3 +174,4 @@ local.properties
.idea/
.classpath
.project
output/
[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 emfecoreVersion = "2.12.0"
val scrollVersion = "1.6"
name := "CodeGenerator"
version := "0.1"
scalaVersion := "2.12.6"
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.max-leuthaeuser" %% "scroll" % scrollVersion
)
),
scalacOptions ++= Seq(
"-language:implicitConversions"
)
).dependsOn(syncProvider)
Subproject commit ace8bdf5799dd89bd66d01782237123172bb0ec0
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
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.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 {
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 = {
val factory: EcoreFactory = EcoreFactory.eINSTANCE
val ecorePackage: EcorePackage = EcorePackage.eINSTANCE
val ePackage: EPackage = factory.createEPackage()
val importer = new ECoreImporter {
def lm(): Resource = loadModel()
}
importer.path = "assets/ttc17.ecore"
val res = importer.lm()
println(s"We done: $res")
val ePerson = factory.createEClass()
ePerson.setName("Person")
ePackage.getEClassifiers().add(ePerson)
res.getContents.toArray(new Array[EObject](0)).toList.find(_.isInstanceOf[EPackage]).map((p: EObject) => p.asInstanceOf[EPackage]).orNull
}
val eName: EAttribute = factory.createEAttribute()
eName.setName("name")
eName.setEType(ecorePackage.getEString)
ePerson.getEStructuralFeatures.add(eName)
def prepareModel(sModel: SModel): Unit = {
val getterSetterVisitor = new GetterSetterGeneratingVisitor
val syncNotificationVisitor = new SyncEnhancingVisitor
ePackage
sModel.accept(getterSetterVisitor)
sModel.accept(syncNotificationVisitor)
}
}
......@@ -7,12 +7,16 @@ import org.rosi_project.model_sync.generator.sync_model.SModelVisitor
* @author Rico Bergmann
*/
class SClass(val name: String,
val attributes: Seq[SAttribute],
var attributes: Seq[SAttribute] = Seq.empty,
val sPackage: String = "",
var parent: STypedElement = null,
var methods: Seq[SMethod] = Seq.empty)
extends STypedElement {
private var constructorStatements: Seq[SMethodStatement] = Seq.empty
def getAdditionalConstructorStatements: Seq[SMethodStatement] = constructorStatements
def isDefaultPackage: Boolean = sPackage == ""
def isRootClass: Boolean = parent == null
......@@ -50,8 +54,12 @@ class SClass(val name: String,
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 setAttributes(attrs: Seq[SAttribute]): Unit = this.attributes = attrs
override def getName: String = name
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] {
contents.foreach {
case ec: EClass =>
model.addModelClass(new SClassConverter convert ec)
STypeRegistry.addType(SType(ec.getName, packageName))
println(s"Found class $ec")
case et: EGenericType=>
STypeRegistry.addType(SType(et.eClass().getName, packageName))
//STypeRegistry.addType(SType(et.eClass().getName, packageName))
println(s"Found type $et")
case obj =>
println(s"Found something else: $obj")
}
println(s"The TypeRegistry:\n${STypeRegistry.allTypes}")
println(s"The model:\n$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) {
|
|class $clazzFixture {
|
| ${modelClass.getAdditionalConstructorStatements.map(_.content).mkString("\n")}
|
| ${modelClass.methods.map(stringifyMethod).mkString("\n")}
|
|}
......
......@@ -21,9 +21,6 @@ class SModelFSWriter(
outputDir: Directory = File("output").toDirectory,
keepClassFiles: Boolean = false) extends SModelVisitor {
// TODO enable different output dirs
// TODO enable retaining the .scala files
workingDir.jfile.mkdirs()
outputDir.jfile.mkdirs()
......
......@@ -11,4 +11,6 @@ trait SModel extends SModelElement {
def getAllClasses: Set[SClass]
override def toString: String = s"SModel: pckg=$getPackageName, classes=$getAllClasses"
}
......@@ -11,9 +11,12 @@ object STypeRegistry {
registerDefaultTypes()
def addType(theType: STypedElement): Unit = {
def addType(theType: STypedElement): STypedElement = {
if (!registeredTypes.contains(theType)) {
registeredTypes += theType
}
theType
}
def query(name: String, sPackage: String): Option[STypedElement] = {
registeredTypes.find(t => t.getName == name && t.getPackage == sPackage)
......
package org.rosi_project.model_sync.generator.sync_model
import org.rosi_project.model_sync.generator.acr._
import scroll.internal.Compartment
import org.rosi_project.model_sync.sync.PlayerSync
/**
* @author Rico Bergmann
......@@ -12,14 +12,12 @@ class SyncEnhancingVisitor extends SModelVisitor {
}
override def visit(sClass: SClass): Unit = {
// TODO extend PlayerSync and call setup method in constructor
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")
}
println(s"Class hierarchy for $sClass : ${sClass.getInheritanceHierarchy}")
sClass.augmentConstructor(SyncEnhancingVisitor.PLAYER_SYNC_INIT)
}
......@@ -37,7 +35,7 @@ class SyncEnhancingVisitor extends SModelVisitor {
private def extractSetterAttr(sMethod: SMethod): Option[String] = {
sMethod.name match {
case SyncEnhancingVisitor.SetterRegex(attrName) =>
case SyncEnhancingVisitor.Setter(attrName) =>
Option(attrName)
case _ =>
None
......@@ -49,11 +47,11 @@ class SyncEnhancingVisitor extends SModelVisitor {
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 COMPARTMENT_STYPE = SType(COMPARTMENT_CLASS.getSimpleName, COMPARTMENT_CLASS.getPackage.getName)
private val PLAYER_SYNC_INIT = SMethodStatement("buildClass()")
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment