diff --git a/assets/model_join/simple.modeljoin b/assets/model_join/simple.modeljoin index e414cc173dbc28fa788578354a35673e27991358..5c22f623aa25cfc5361290c89310c984e7f7abf4 100644 --- a/assets/model_join/simple.modeljoin +++ b/assets/model_join/simple.modeljoin @@ -1,5 +1,11 @@ -natural join imdbdatabase.Film with eclipselibrary.VideoCassette as joins.JoinMovie { +natural join imdbdatabase.Film with elib.VideoCassette as joins.JoinMovie { keep attributes imdbdatabase.Film.year keep attributes eclipselibrary.AudioVisualItem.minutesLength + keep outgoing imdbdatabase.Film.votes as type jointarget.Vote { + keep attributes imdbdatabase.Vote.score + } + keep outgoing elib.VideoCassette.cast as type jointarget.Person { + keep attributes elib.Person.lastName + } } diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SJoinClass.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SJoinClass.scala index 82f34532c16e675a2f4fcd0a3fd22b28a58e66b8..a9c9dcdc4185afe946ca50386ca9bb8b5ac9ba26 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SJoinClass.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SJoinClass.scala @@ -3,15 +3,14 @@ package org.rosi_project.model_sync.generator.acr_model import org.rosi_project.model_management.sum.join.RsumJoinType class SJoinClass(_name: String, - _sPackage: String = "", - _isAbstract: Boolean = false, - _isInterface: Boolean = false, + _sPackage: String, val base: SClass, val other: SClass, val joinType: RsumJoinType.Value, val joinAttributes: Set[SStructuralFeature], - val innerAttributes: Set[SStructuralFeature]) extends SClass(_name, _sPackage, _isAbstract, _isInterface) { - + val innerAttributes: Set[SStructuralFeature], + val joinObject: SClass) extends SClass(_name, _sPackage) { + override def getRootClassWithNameAndPackage(n: String, p: String): SClass = { val bparent = base.getRootClassWithNameAndPackage(n, p) val oparent = other.getRootClassWithNameAndPackage(n, p) diff --git a/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SViewClass.scala b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SViewClass.scala new file mode 100644 index 0000000000000000000000000000000000000000..fb2e69f86be8751677ad386d8ec6fbda67ad93e5 --- /dev/null +++ b/src/main/scala/org/rosi_project/model_sync/generator/acr_model/SViewClass.scala @@ -0,0 +1,14 @@ +package org.rosi_project.model_sync.generator.acr_model + +class SViewClass(_name: String, + _sPackage: String) extends SClass(_name, _sPackage) { + + private var joinObjects: Set[SJoinClass] = Set.empty + + def getJoinObject(): Set[SJoinClass] = joinObjects + + def addJoinObject(v: SJoinClass): Unit = { + require(v != null) + joinObjects += v + } +} \ No newline at end of file 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 index 541facb75b7fd6a0e90aea9a665acd65728faa7a..2765ba3ded1417883c05b881a4c4394602d428e0 100644 --- 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 @@ -7,30 +7,43 @@ import org.rosi_project.model_sync.generator.acr_model.types.GenericSequence import org.rosi_project.model_sync.generator.sync.HelperFunctions import org.rosi_project.model_sync.generator.PackageNames -/** The `Writer` generates the source code of a [[SClass]] and provides it as a `String`. - * - * @author Rico Bergmann - */ -class SClassWriter(val modelClass: SClass, val isView: Boolean, val isObject: Boolean) { - +/** + * The `Writer` generates the source code of a [[SClass]] and provides it as a `String`. + * + * @author Rico Bergmann + */ +class SClassWriter(val modelClass: SClass, val isObject: Boolean) { + + private val isView: Boolean = modelClass.isInstanceOf[SViewClass] private val pckg: String = if (modelClass.isDefaultPackage) "" else s"package ${modelClass.sPackage}" private val imports: Seq[String] = modelClass.collectImports.toSeq + private val viewImports: String = { + if (isView) { + s""" + | import ${PredefRsumTypes.VIEWTYPE_INFO_STYPE.getPackage}.${PredefRsumTypes.VIEWTYPE_INFO_STYPE.getName} + | import ${PredefRsumTypes.JOIN_INFO_STYPE.getPackage}.${PredefRsumTypes.JOIN_INFO_STYPE.getName} + | ${modelClass.asInstanceOf[SViewClass].getJoinObject().map(j => s"import ${j.joinObject.getPackage}.${j.joinObject.getName}").mkString(" \n")} + """.stripMargin + } else { + "" + } + } private val clazzFixture: String = generateClassFixture private val internalClazzFixture: String = generateInternalClazzFixture private val companionObject: String = generateCompanionFixture - - /** 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). - */ + + /** + * 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")} - |${if(isView) s"import ${PredefRsumTypes.VIEWTYPE_INFO_STYPE.getPackage}.${PredefRsumTypes.VIEWTYPE_INFO_STYPE.getName}" else ""} - |${if(isView) s"import ${PredefRsumTypes.JOIN_INFO_STYPE.getPackage}.${PredefRsumTypes.JOIN_INFO_STYPE.getName}" else ""} + |${viewImports} | |${clazzFixture} { | @@ -43,11 +56,14 @@ class SClassWriter(val modelClass: SClass, val isView: Boolean, val isObject: Bo | ${internalClazzFixture} |} | - |${if(isView) companionObject else ""} + |${if (isView) companionObject else ""} | """.stripMargin } + /** + * Provides a source code representation of an internal class used as role in SCROLL. + */ def internalStringify: String = { s""" |$clazzFixture { @@ -60,52 +76,60 @@ class SClassWriter(val modelClass: SClass, val isView: Boolean, val isObject: Bo """.stripMargin } - /** Writes a method as source code. - * - * @param m the method to write - * @return the `String` representation of `m` - */ + /** + * 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 ""}${getVisibilityString(m.getVisibility)}def ${m.getName}(${m.params.map(param => s"${param.getName}: ${param.getDeepTypeName}").mkString(", ")}): ${m.getResultType} = { | ${m.implementation.map(_.getContent).mkString("\n")} |} """.stripMargin - } - - /** Writes the "''companion fixture''" for views. - */ + } + + /** + * Writes the "''companion fixture''" for views. + */ protected def generateCompanionFixture: String = { - s"""object ${modelClass.getName} extends ${PredefRsumTypes.VIEWTYPE_INFO_STYPE.getName} { + if (isView) { + val viewClass = modelClass.asInstanceOf[SViewClass] + s"""object ${modelClass.getName} extends ${PredefRsumTypes.VIEWTYPE_INFO_STYPE.getName} { | | override def getViewName(): String = "${modelClass.getName}" | - | def getJoinInfos(): Set[${PredefRsumTypes.JOIN_INFO_STYPE.getName}] = Set.empty + | def getJoinInfos(): Set[${PredefRsumTypes.JOIN_INFO_STYPE.getName}] = ${if (viewClass.getJoinObject.isEmpty) "Set.empty" else s"Set(${viewClass.getJoinObject.map(_.joinObject.getName).mkString(", ")})"} | | protected def getNewInstance(): ${PredefRsumTypes.IVIEW_COMPARTMENT_STYPE.getName} = new ${modelClass.getName}() | | def getNewView(): ${modelClass.getName} = getNewViewTypeInstance().asInstanceOf[${modelClass.getName}] |} - """.stripMargin + """.stripMargin + } else { + "" + } } - /** Writes the internal classes. - */ + /** + * Writes the internal classes. + */ protected def generateInternalClazzFixture: String = { var result = ""; modelClass.getInternalClasses.foreach(intCls => { - var sw: SClassWriter = new SClassWriter(intCls, false, false) + var sw: SClassWriter = new SClassWriter(intCls, false) var s = sw.internalStringify; result = result + s + "\n"; }) result } - + private def getVisibilityString(vis: MethodVisibility.Value): String = { var visibility = "" vis match { - case MethodVisibility.privateVis => visibility = "private " + case MethodVisibility.privateVis => visibility = "private " case MethodVisibility.protectedVis => visibility = "protected " - case MethodVisibility.privateExternalClass => + case MethodVisibility.privateExternalClass => if (modelClass.isInstanceOf[SInnerClass]) { visibility = s"private[${modelClass.asInstanceOf[SInnerClass].externalClass.getName}] " } @@ -113,53 +137,54 @@ class SClassWriter(val modelClass: SClass, val isView: Boolean, val isObject: Bo } visibility } - + /** * Generate the attribute fixtures to add them in the body. */ protected def generateAttributeFixture: String = { modelClass.getStructuralFeatures.map(attr => { - val finalS: String = if(attr.isFinal) "val" else "var" - val instanziationS: String = if(attr.getTypeElement.isInstanceOf[GenericSequence]) "= Set.empty" else s"= ${HelperFunctions.classEmptyType(attr.getTypeElement.getName)}" + val finalS: String = if (attr.isFinal) "val" else "var" + val instanziationS: String = if (attr.getTypeElement.isInstanceOf[GenericSequence]) "= Set.empty" else s"= ${HelperFunctions.classEmptyType(attr.getTypeElement.getName)}" s"${getVisibilityString(attr.getVisibility)}${finalS} ${attr.getName}: ${attr.getTypeElement.getDeepName} ${instanziationS} \n" }).mkString(" \n") } - /** 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. - */ + /** + * 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 = { //println("**************************************************") var params: Seq[String] = modelClass.getStructuralFeatures.map(attr => { - val finalS: String = if(attr.isFinal) "val" else "var" + val finalS: String = if (attr.isFinal) "val" else "var" s"${getVisibilityString(attr.getVisibility)}${finalS} ${attr.getName}: ${attr.getTypeElement.getDeepName}" }).toList var baseFixture: String = "" var parentConstructorParams: String = "" var parentConstructor: String = "" var allInterfaces: String = "" - val parent: STypedElement = modelClass.getClassParent - + val parent: STypedElement = modelClass.getClassParent + if (!modelClass.isRootClass) { parentConstructorParams = parent.getAllConstructorParameters.map(param => s"${constructorParamForParentConstructor(parent, param)}: ${param.getDeepTypeName}").mkString(", ") parentConstructor = s"(${parent.getAllConstructorParameters.map(constructorParamForParentConstructor(parent, _)).mkString(", ")})" - } - + } + if (!modelClass.getInterfaceParents.isEmpty) { allInterfaces = s"${modelClass.getInterfaceParents.map(namesOfInterfaces(_)).mkString(" with ")}" } if (parentConstructorParams != "") { params = params :+ parentConstructorParams - } - + } + val constructor: String = if (params.isEmpty) "" else s"(${params.mkString(", ")})" - + if (modelClass.isInterface) { baseFixture = s"trait ${modelClass.getName}" } else if (isObject) { baseFixture = s"object ${modelClass.getName}" - } else { + } else { baseFixture = s"class ${modelClass.getName}" if (!PackageNames.multiInhertitanceWithTraits) { baseFixture = s"$baseFixture$constructor" @@ -168,14 +193,13 @@ class SClassWriter(val modelClass: SClass, val isView: Boolean, val isObject: Bo baseFixture = s"abstract $baseFixture" } } - + if (modelClass.isRootClass) { if (allInterfaces != "") { if (isView) { - baseFixture = s"$baseFixture private extends $allInterfaces" - } else { - baseFixture = s"$baseFixture extends $allInterfaces" + baseFixture = s"$baseFixture private" } + baseFixture = s"$baseFixture extends $allInterfaces" } } else { baseFixture = s"$baseFixture extends ${parent.getName}" @@ -186,21 +210,15 @@ class SClassWriter(val modelClass: SClass, val isView: Boolean, val isObject: Bo baseFixture = s"$baseFixture with $allInterfaces" } } - - /*if (isView) { - return s"class CompleteView private extends IViewCompartment" - }*/ - - //println("baseFixture: " + baseFixture) - //println("**************************************************") baseFixture } - /** Generates a ''class parameter's'' name that will be used to initialize a field of the super class. - * - * This is purely to prevent naming conflicts and shadowing when trying to access a parent's field - * from the subclass. - */ + /** + * Generates a ''class parameter's'' name that will be used to initialize a field of the super class. + * + * This is purely to prevent naming conflicts and shadowing when trying to access a parent's field + * from the subclass. + */ private def constructorParamForParentConstructor(parent: STypedElement, param: SMethodParameter): String = { var parName = s"${parent.getName.head.toLower}_${param.getName.firstLetterToUpperCase}" @@ -220,9 +238,10 @@ class SClassWriter(val modelClass: SClass, val isView: Boolean, val isObject: Bo parName } - - /** Returns the name of the interface. - */ + + /** + * Returns the name of the interface. + */ private def namesOfInterfaces(interface: STypedElement): String = { interface.getName } 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 index f742da806ab55f4f56ec36b66e0623ce00d42896..17d26a0a5d21f05e592a59088f412cd401ff9cc7 100644 --- 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 @@ -59,12 +59,12 @@ class SModelFSWriter( override def visit(sModel: SModel): Unit = { sModel.getModelEnums.foreach(writeEnum(_)) - sModel.getModelClasses.foreach(writeClass(_, false, false)) - sModel.getJoinClasses.foreach(writeClass(_, false, false)) - sModel.getJoinObjects.foreach(writeClass(_, false, true)) //TODO - sModel.getRelationalCompartments.foreach(writeClass(_, false, false)) - sModel.getViewCompartments.foreach(writeClass(_, true, false)) - sModel.getProviderClasses.foreach(writeClass(_, false, false)) + sModel.getModelClasses.foreach(writeClass(_, false)) + sModel.getJoinClasses.foreach(writeClass(_, false)) + sModel.getJoinObjects.foreach(writeClass(_, true)) //TODO + sModel.getRelationalCompartments.foreach(writeClass(_, false)) + sModel.getViewCompartments.foreach(writeClass(_, false)) + sModel.getProviderClasses.foreach(writeClass(_, false)) //println(s"... Wrote files (sources) $sFilesToCompile") println("... Starting compilation") @@ -102,11 +102,11 @@ class SModelFSWriter( } } - private def writeClass(sClass: SClass, isView: Boolean, isObject: Boolean): Unit = { + private def writeClass(sClass: SClass, isObject: Boolean): Unit = { try { println(s"Writing class $sClass") val classNameWithPath = workingDir.toAbsolute.toString() + File.separator + pckg2Path(sClass.getPackage) + File.separator + s"${sClass.getName}.scala" - val writer = new SClassWriter(sClass, isView, isObject) + val writer = new SClassWriter(sClass, isObject) val classFile = File(classNameWithPath) diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/JoinGeneratingVisitor.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/JoinGeneratingVisitor.scala index b651db90edcacad765188a7a5b72574bac863a7e..d9c0e0d875ae5c41b5002712d32b68e7d21d7c95 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/sync/JoinGeneratingVisitor.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/JoinGeneratingVisitor.scala @@ -71,10 +71,10 @@ class JoinGeneratingVisitor(joinExpression: ModelJoinExpression) extends SModelV println("B: " + otherAttsBase) println("O: " + otherAttsOther) + val joinObject = new SClass(j.getTarget().getResourceName + "Object", j.getTarget().getResourcePath) val joinClass = new SJoinClass(j.getTarget().getResourceName, j.getTarget().getResourcePath, base = baseClass, other = otherClass, joinType = rsumJoinType, - joinAttributes = joinAtts, innerAttributes = joinAtts ++ otherAttsBase ++ otherAttsOther) - val joinObject = new SClass(j.getTarget().getResourceName + "Object", j.getTarget().getResourcePath) + joinAttributes = joinAtts, innerAttributes = joinAtts ++ otherAttsBase ++ otherAttsOther, joinObject) //add parents joinClass.addParent(PredefRsumTypes.IJOIN_COMPARTMENT_STYPE) diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/ModelJoinViewGeneratingVisitor.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/ModelJoinViewGeneratingVisitor.scala index 1a0d3a6c8babadae60bbd5cb7168344d0d7ecf1c..533d8d3dca2fc4aaf44770c4382689f64cfe2bea 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/sync/ModelJoinViewGeneratingVisitor.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/ModelJoinViewGeneratingVisitor.scala @@ -10,7 +10,7 @@ import org.rosi_project.model_sync.generator.PackageNames class ModelJoinViewGeneratingVisitor(joinExpression: ModelJoinExpression) extends SModelVisitor { - var viewCompartment: SClass = null + var viewCompartment: SViewClass = null var refClasses: Seq[SClass] = Seq.empty var refRelationalClasses: Seq[SClass] = Seq.empty var newInternalRoles: Seq[SInnerViewNaturalClass] = Seq.empty @@ -25,12 +25,13 @@ class ModelJoinViewGeneratingVisitor(joinExpression: ModelJoinExpression) extend newInternalRoles = Seq.empty newInternalRelationalRoles = Seq.empty allImportClasses = Set.empty - viewCompartment = new SClass(joinExpression.getName() + counter, PackageNames.viewPkgName) + viewCompartment = new SViewClass(joinExpression.getName() + counter, PackageNames.viewPkgName) counter += 1 viewCompartment.addParent(PredefRsumTypes.IVIEW_COMPARTMENT_STYPE) val joinType = STypeRegistry.query(j.getTarget.getResourceName, j.getTarget.getResourcePath) if (!joinType.isEmpty) { + viewCompartment.addJoinObject(joinType.get.asInstanceOf[SJoinClass]) createClasses(asScalaBuffer(j.getKeepsList).toSet, joinType.get.asInstanceOf[SClass], sModel) } diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/ToStringMethods.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/ToStringMethods.scala index 48abca24e3470dbfc3040adfc9f76e7987f17499..39d0ed6c7cf1e8297c341232a68239d17360d12c 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/sync/ToStringMethods.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/ToStringMethods.scala @@ -22,7 +22,7 @@ object ToStringMethods { name = "toString", result = PredefTypes.String, params = Seq.empty, - implementation = Seq(SMethodStatement(s""" "JOIN ${s}: " + base + " " + other """)), + implementation = Seq(SMethodStatement(s""" "JOIN ${s}: " + baseObj + " " + otherObj """)), overrides = true ) } diff --git a/src/main/scala/org/rosi_project/model_sync/generator/sync/ViewGeneratingVisitor.scala b/src/main/scala/org/rosi_project/model_sync/generator/sync/ViewGeneratingVisitor.scala index 1e8bc555f79e33e73393bd385316788347fcd60e..aef0d4ebd62c1a18f86b4f12402cea1528075d15 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/sync/ViewGeneratingVisitor.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/sync/ViewGeneratingVisitor.scala @@ -8,7 +8,7 @@ class ViewGeneratingVisitor extends SModelVisitor { override def visit(sModel: SModel): Unit = { - val viewCompartment = new SClass(sModel.getName.capitalize + PackageNames.viewPostName, PackageNames.viewPkgName) + val viewCompartment = new SViewClass(sModel.getName.capitalize + PackageNames.viewPostName, PackageNames.viewPkgName) var newInternalRoles: Seq[SInnerViewNaturalClass] = Seq.empty var newInternalRelationalRoles: Seq[SInnerViewRelationalClass] = Seq.empty viewCompartment.addParent(PredefRsumTypes.IVIEW_COMPARTMENT_STYPE) diff --git a/src/main/scala/org/rosi_project/model_sync/generator/test/ApplicationTest.scala b/src/main/scala/org/rosi_project/model_sync/generator/test/ApplicationTest.scala index 9bf0ba125e15a8b91a3cecc80bc1b66e1198b1ba..993cd52f9316dda9a3380f3d820bca62198589a4 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/test/ApplicationTest.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/test/ApplicationTest.scala @@ -61,7 +61,7 @@ object ApplicationTest extends App { } def runCombinedTest(cre: Creation.Value): Unit = { - var config: GeneratorConfig = GeneratorConfig(Seq("assets/models/IMDBDatabase.ecore", "assets/models/ModelJoinLibrary.ecore"), false, new File("assets/models"), modelJoin = "assets/model_join/complex.modeljoin"); + var config: GeneratorConfig = GeneratorConfig(Seq("assets/models/IMDBDatabase.ecore", "assets/models/ModelJoinLibrary.ecore"), false, new File("assets/models"), modelJoin = "assets/model_join/simple.modeljoin"); config.setCreate(cre) new Generator(config).run() } diff --git a/src/main/scala/org/rosi_project/model_sync/generator/test/SClassWriterTest.scala b/src/main/scala/org/rosi_project/model_sync/generator/test/SClassWriterTest.scala index 3c207c5f3ddf91031759898595bc21fae846d167..cd514fca498f49e1a7b031080b083af02f22ca3b 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/test/SClassWriterTest.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/test/SClassWriterTest.scala @@ -23,7 +23,7 @@ object SClassWriterTest/* extends App */{ val syncNotificationVisitor = new SyncEnhancingVisitor modelClass.accept(syncNotificationVisitor) - val writer = new SClassWriter(modelClass, false, false) + val writer = new SClassWriter(modelClass, false) println(writer.stringify)