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

Merge branch 'experimental' into 'develop'

Move away from experimental changes

See merge request !1
parents 3557b6af 516f567d
No related branches found
No related tags found
2 merge requests!2Publish v0.1,!1Move away from experimental changes
Showing
with 613 additions and 0 deletions
package org.rosi_project.model_sync.generator.acr
/**
* @author Rico Bergmann
*/
trait STypedElement extends SModelElement {
def getName: String
def getPackage: String
def getConstructorParameters: Seq[SMethodParameter] = Seq.empty
def getInheritanceHierarchy: Seq[STypedElement] = Seq.empty
}
package org.rosi_project.model_sync.generator
/** Abstract class representation of the Scala code to generate
* @author Rico Bergmann
*/
package object acr {
}
package org.rosi_project.model_sync.generator.conversion
/**
* @author Rico Bergmann
*/
trait Converter[S, T] {
def convert(source: S): T
}
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
}
package org.rosi_project.model_sync.generator.conversion
import org.eclipse.emf.ecore.{EClass, EGenericType, EPackage}
import org.rosi_project.model_sync.generator.acr.SType
import org.rosi_project.model_sync.generator.sync_model.{SModel, STypeRegistry, SimpleSModel}
import scala.collection.JavaConverters._
/**
* @author Rico Bergmann
*/
object 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(packageName)
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))
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 {
}
package org.rosi_project.model_sync.generator.io
import org.rosi_project.model_sync.generator.acr.{SAttribute, SMethod, SClass}
/**
* @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
def stringify: String = {
s"""$pckg
|
|${imports.map(i => s"import $i").mkString("\n")}
|
|class $clazzFixture {
|
| ${modelClass.getAdditionalConstructorStatements.map(_.content).mkString("\n")}
|
| ${modelClass.methods.map(stringifyMethod).mkString("\n")}
|
|}
""".stripMargin
}
protected def generateGetter(attr: SAttribute): String =
s"def get${firstLetterToUpperCase(attr.name)}(): ${attr.getType} = ${attr.name}"
protected def generateSetter(attr: SAttribute): String = {
s"""def set${firstLetterToUpperCase(attr.name)}(${attr.name.head}: ${attr.getType}): Unit = {
| this.${attr.name} = ${attr.name.head}
|}
""".stripMargin
}
protected def stringifyMethod(m: SMethod): String = {
s"""def ${m.name}(${m.params.map(param => s"${param.name}: ${param.getType}").mkString(", ")}): ${m.getResultType} = {
| ${m.implementation.map(_.content).mkString("\n")}
|}
""".stripMargin
}
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(", ")})"
var baseFixture = s"${modelClass.getName}$constructor"
if (modelClass.isRootClass) baseFixture else s"$baseFixture extends ${modelClass.parent.getName}$parentConstructor"
}
private def firstLetterToUpperCase(str: String): String = {
str.head.toUpper + str.tail
}
}
package org.rosi_project.model_sync.generator.io
import org.rosi_project.model_sync.generator.acr._
import org.rosi_project.model_sync.generator.sync_model.{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", attrs, methods = Seq(sayHelloMethod))
val getterSetterVisitor = new GetterSetterGeneratingVisitor
modelClass.accept(getterSetterVisitor)
val syncNotificationVisitor = new SyncEnhancingVisitor
modelClass.accept(syncNotificationVisitor)
val writer = new SClassWriter(modelClass)
println(writer.stringify)
}
package org.rosi_project.model_sync.generator.io
import java.io.{ BufferedInputStream, FileInputStream, FileOutputStream, PrintWriter }
import java.net.URLClassLoader
import java.nio.file.Files
import java.util.jar.{ Attributes, JarEntry, JarOutputStream, Manifest }
import org.rosi_project.model_sync.generator.acr.{ SAttribute, SClass, SMethod, SType }
import org.rosi_project.model_sync.generator.sync_model.{ SModel, SModelVisitor }
import scala.reflect.io.{ File, Path }
import scala.tools.nsc.reporters.ConsoleReporter
import scala.tools.nsc.{ GenericRunnerSettings, Global, Settings }
import scala.util.control.Breaks._
import scala.reflect.io.Directory
/** @author Rico Bergmann
*/
class SModelFSWriter(
workingDir: File = File(Files.createTempDirectory("model").toFile),
outputDir: Directory = File("output").toDirectory,
keepClassFiles: Boolean = false) extends SModelVisitor {
workingDir.jfile.mkdirs()
outputDir.jfile.mkdirs()
private var sFilesToCompile: List[File] = List.empty
println(s"... Temp dir (sources) is $workingDir")
override def visit(sModel: SModel): Unit = {
println(s"... Wrote files (sources) $sFilesToCompile")
println("... Starting compilation")
// see: https://stackoverflow.com/a/20323371/5161760
val out = new PrintWriter(System.out)
val compilationSettings: Settings = new GenericRunnerSettings(out.println)
// TODO do we want to reuse the whole classpath? Or just the parts we _actually_ need
compilationSettings.classpath.value = adaptClassPathToOSEnv(fetchCurrentClassPath).distinct.mkString(File.pathSeparator)
println(s"Using classpath ${compilationSettings.classpath.value}")
compilationSettings.outdir.value = workingDir.toAbsolute.toString
val reporter = new ConsoleReporter(compilationSettings)
val compiler = new Global(compilationSettings, reporter)
val runner = new compiler.Run
runner.compile(sFilesToCompile.map(_.toAbsolute.toString))
println("... Compilation done")
if (!keepClassFiles) {
println("... Cleaning up")
sFilesToCompile.foreach {
_.delete
}
} else {
println("... No clean-up requested")
}
println("... Generating JAR")
// see: https://stackoverflow.com/a/1281295/5161760
val mf = new Manifest
mf.getMainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0")
val targetJar = new JarOutputStream(new FileOutputStream(outputDir + File.separator + "model.jar"), mf)
var filesToAdd: List[java.io.File] = workingDir.jfile.listFiles.toList
while (filesToAdd.nonEmpty) {
val file = filesToAdd.head
var fname = file.getAbsolutePath.replace(workingDir.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()
println("... Done")
}
override def visit(sClass: SClass): Unit = {
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
}
override def visit(sAttr: SAttribute): Unit = {
// pass
}
override def visit(sMethod: SMethod): Unit = {
// pass
}
override def visit(sType: SType): Unit = {
// pass
}
private def pckg2Path(pckg: String): Path = Path(pckg.replace(".", File.separator))
private 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")
}
}
}
private 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(entry => {
entry match {
case UnixUrl(path) => path
case p => p
}
})
}
}
}
package org.rosi_project.model_sync.generator.io
import org.rosi_project.model_sync.generator.acr._
import org.rosi_project.model_sync.generator.sync_model.{ GetterSetterGeneratingVisitor, SimpleSModel, SyncEnhancingVisitor }
import scala.reflect.io.File
/** @author Rico Bergmann
*/
object SModelFSWriterTest/* extends App */{
val model = new SimpleSModel("org.foo")
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", personAttrs, methods = Seq(personSayHelloMethod), sPackage = "foo")
val getterSetterVisitor = new GetterSetterGeneratingVisitor
val syncNotificationVisitor = new SyncEnhancingVisitor
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.support
/**
* @author Rico Bergmann
*/
object Assert {
def notNull(obj: AnyRef, msg: String): Unit = isTrue(obj != null, msg)
def isTrue(cond: Boolean, msg: String): Unit = if (!cond) {
throw new AssertionError(msg)
}
def isFalse(cond: Boolean, msg: String): Unit = isTrue(!cond, msg)
}
package org.rosi_project.model_sync.generator.support
/**
* @author Rico Bergmann
*/
class GenericRegistry[T](val unique: Boolean = true) {
}
package org.rosi_project.model_sync.generator.support
/**
* @author Rico Bergmann
*/
object StringUtils {
def firstLetterToUpperCase(str: String): String =
str.headOption.map(h => h.toUpper + str.tail).getOrElse("")
}
package org.rosi_project.model_sync.generator.sync_model
import org.rosi_project.model_sync.generator.acr._
/**
* @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_model
import org.rosi_project.model_sync.generator.acr.{SClass, SModelElement}
/**
* @author Rico Bergmann
*/
trait SModel extends SModelElement {
def getPackageName: String
def getAllClasses: Set[SClass]
override def toString: String = s"SModel: pckg=$getPackageName, classes=$getAllClasses"
}
package org.rosi_project.model_sync.generator.sync_model
import org.rosi_project.model_sync.generator.acr.{SAttribute, SMethod, SClass, SType}
// TODO fix cyclic imports with classes from acr package
/**
* @author Rico Bergmann
*/
trait SModelVisitor {
def visit(sModel: SModel): Unit
def visit(sClass: SClass): Unit
def visit(sAttr: SAttribute): Unit
def visit(sMethod: SMethod): Unit
def visit(sType: SType): Unit
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment