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

Merge branch 'develop' into 'master'

Publish v0.1

Closes #6, #8, #7, #11, #10, and #5

See merge request !2
parents 3557b6af d859f286
No related branches found
No related tags found
1 merge request!2Publish v0.1
Showing
with 962 additions and 0 deletions
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
}
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
}
}
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]))
}
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)"}"
}
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
}
}
}
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)
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")
}
}
}
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)
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"
}
}
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)
}
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)
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))
}
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 {
}
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)
}
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)
}
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 {
}
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"
}
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
}
}
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)
}
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)
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment