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 603 additions and 0 deletions
#### 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/
[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
assets/family.png

22.5 KiB

<?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>
{
"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
}
]
}
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)
Subproject commit ace8bdf5799dd89bd66d01782237123172bb0ec0
Subproject commit 623765816af6d27387efb82dfb890bc4318c58a2
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")
sbt.version = 1.2.1
\ No newline at end of file
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4")
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0")
Subproject commit 89e2af4e060ae7c39c261fde55ba9140caf85853
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 =>
}
}
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)
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")
}
}
}
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
}
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
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)
}
}
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))
}
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"
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment