diff --git a/src/main/scala/org/rosi_project/model_sync/generator/Generator.scala b/src/main/scala/org/rosi_project/model_sync/generator/Generator.scala index 9160439363db9d265363f96c6480494f94ac1ae7..acf4fd9eec7adfc4923adca5bd88bd402ecee93b 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/Generator.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/Generator.scala @@ -4,7 +4,7 @@ import net.liftweb.json._ 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.{ClassWritingException, ModelImagePreparationException, SModelFSWriter} import org.rosi_project.model_sync.generator.sync.{GetterSetterGeneratingVisitor, SyncEnhancingVisitor} import scopt.OptionParser import scroll.internal.ecore.ECoreImporter @@ -13,6 +13,7 @@ import scala.reflect.io.{Directory, File} import java.{io => jio} import org.rosi_project.model_sync.generator.acr_model.SModel +import org.rosi_project.model_sync.generator.env.{CompilationException, JarPackaginException} import scala.io.Source @@ -53,36 +54,56 @@ object Generator extends App { ).text("Remove the generated .scala files and only keep the compiled .class files") } - parser.parse(args, GeneratorConfig()) match { - case Some(config) => - - val modelJson = Source.fromFile(config.getModelFile).getLines.mkString - implicit val jsonFormats: Formats = DefaultFormats - val modelCfg = parse(modelJson).extract[ModelConfig] - - val ecoreModel = loadEcore(config.source) - val sModel = new SModelGenerator convert ecoreModel - 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)) - } - case None => + try { + parser.parse(args, GeneratorConfig()) match { + case Some(config) => + + 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 = loadEcore(config.source) + } catch { + case e: Exception => throw new EcoreLoadException(e) + } + + val sModel = new SModelGenerator convert ecoreModel + 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)) + } + case None => + } + } + 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") } /** Fetches an ecore model from XML. @@ -113,3 +134,5 @@ object Generator extends App { } } + +class EcoreLoadException(cause: Exception) extends RuntimeException(cause) diff --git a/src/main/scala/org/rosi_project/model_sync/generator/env/FilesCompiler.scala b/src/main/scala/org/rosi_project/model_sync/generator/env/FilesCompiler.scala index 29a00c40ccbc0f1f085f4248df695706bc876c62..516f2859e01e910e66f57eaeef2d17b0a28f814c 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/env/FilesCompiler.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/env/FilesCompiler.scala @@ -17,16 +17,26 @@ class FilesCompiler(outDir: File) { */ def run(filesToCompile: List[File]): Unit = { // see: https://stackoverflow.com/a/20323371/5161760 - 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)) + 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) diff --git a/src/main/scala/org/rosi_project/model_sync/generator/env/JarPackager.scala b/src/main/scala/org/rosi_project/model_sync/generator/env/JarPackager.scala index 86f7a64f1d3229c520fa4934872a044d2549c040..3d1478f542696ad245c92c73ba770637a9f0ad6b 100644 --- a/src/main/scala/org/rosi_project/model_sync/generator/env/JarPackager.scala +++ b/src/main/scala/org/rosi_project/model_sync/generator/env/JarPackager.scala @@ -21,49 +21,59 @@ class JarPackager(inputDir: File, outputDir: File, jarName: String = "model.jar" */ def run(): Unit = { // 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 + jarName), mf) - var filesToAdd: List[java.io.File] = inputDir.jfile.listFiles.toList + 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 + 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 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 + 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.write(buffer, 0, bytesRead) } + targetJar.closeEntry() + in.close() } - targetJar.closeEntry() - in.close() + + filesToAdd = filesToAdd.tail } - filesToAdd = filesToAdd.tail + targetJar.close() + } catch { + case e: Exception => throw new JarPackaginException(e) } - - targetJar.close() } } + +/** Exception to indicate that the packaging process failed. + * + * @param cause the causing exception + */ +class JarPackaginException(cause: Exception) extends RuntimeException(cause) 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 ec68ac7a365c0111475f6a3f937a35aa2b196c38..5a42da3e582afb901a5f5bcca1955b888d58f829 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 @@ -1,20 +1,13 @@ package org.rosi_project.model_sync.generator.io -import java.io.{BufferedInputStream, FileInputStream, FileOutputStream, PrintWriter} +import java.nio.file.{Files, StandardCopyOption} import java.{io => jio} -import java.net.URLClassLoader -import java.nio.file.{CopyOption, Files, StandardCopyOption} -import java.util.jar.{Attributes, JarEntry, JarOutputStream, Manifest} 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.{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 +import scala.reflect.io.{Directory, File, Path} /** The `FSWriter` writes a [[SModel]] as a single compiled ''JAR'' file to the File System. * @@ -40,12 +33,19 @@ class SModelFSWriter( println(s"Output dir is $outputDir") println("... Copying model images") - private val images = collectAllModelImages(modelCfg, currentDir) - private val 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) - }) + 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") @@ -69,15 +69,19 @@ class SModelFSWriter( } 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 + 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 = { @@ -125,3 +129,15 @@ class SModelFSWriter( } } + +/** 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)