![JAR search and dependency download from the Maven repository](/logo.png)
fr.assoba.open.template.compiler.ScalaTemplateCompiler.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of template-maven-plugin Show documentation
Show all versions of template-maven-plugin Show documentation
Fork of Play 2 template engine, as a maven plugin, for Scala 2.10 and without any SBT dependency
The newest version!
/*
* Copyright 2013 Josselin Pujo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.assoba.open.template.compiler
import java.security.MessageDigest
import java.io.File
import scalax.file.Path
import java.nio.charset.Charset
import scala.annotation.tailrec
import scala.util.parsing.input.OffsetPosition
import scala.reflect.runtime.universe.Flag._
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
object Hash {
def apply(bytes: Array[Byte]): String = {
val digest = MessageDigest.getInstance("SHA-1")
digest.reset()
digest.update(bytes)
digest.digest().map(0xFF & _).map {
"%02x".format(_)
}.foldLeft("") {
_ + _
}
}
}
case class TemplateCompilationError(source: File, message: String, line: Int, column: Int) extends RuntimeException(message)
object MaybeGeneratedSource {
def unapply(source: File): Option[GeneratedSource] = {
val generated = GeneratedSource(source)
if (generated.meta.isDefinedAt("SOURCE")) {
Some(generated)
} else {
None
}
}
}
sealed trait AbstractGeneratedSource {
def content: String
lazy val meta: Map[String, String] = {
val Meta = """([A-Z]+): (.*)""".r
val UndefinedMeta = """([A-Z]+):""".r
Map.empty[String, String] ++ {
try {
content.split("-- GENERATED --")(1).trim.split('\n').map {
m =>
m.trim match {
case Meta(key, value) => (key -> value)
case UndefinedMeta(key) => (key -> "")
case _ => ("UNDEFINED", "")
}
}.toMap
} catch {
case _: Throwable => Map.empty[String, String]
}
}
}
lazy val matrix: Seq[(Int, Int)] = {
for (pos <- meta("MATRIX").split('|'); val c = pos.split("->"))
yield try {
Integer.parseInt(c(0)) -> Integer.parseInt(c(1))
} catch {
case _: Throwable => (0, 0) // Skip if MATRIX meta is corrupted
}
}
lazy val lines: Seq[(Int, Int)] = {
for (pos <- meta("LINES").split('|'); val c = pos.split("->"))
yield try {
Integer.parseInt(c(0)) -> Integer.parseInt(c(1))
} catch {
case _: Throwable => (0, 0) // Skip if LINES meta is corrupted
}
}
def mapPosition(generatedPosition: Int): Int = {
matrix.indexWhere(p => p._1 > generatedPosition) match {
case 0 => 0
case i if i > 0 => {
val pos = matrix(i - 1)
pos._2 + (generatedPosition - pos._1)
}
case _ => {
val pos = matrix.takeRight(1)(0)
pos._2 + (generatedPosition - pos._1)
}
}
}
def mapLine(generatedLine: Int): Int = {
lines.indexWhere(p => p._1 > generatedLine) match {
case 0 => 0
case i if i > 0 => {
val line = lines(i - 1)
line._2 + (generatedLine - line._1)
}
case _ => {
val line = lines.takeRight(1)(0)
line._2 + (generatedLine - line._1)
}
}
}
}
case class GeneratedSource(file: File) extends AbstractGeneratedSource {
def content = Path(file).string
def needRecompilation: Boolean = (!file.exists ||
// A generated source already exist but
source.isDefined && ((source.get.lastModified > file.lastModified) || // the source has been modified
(meta("HASH") != Hash(Path(source.get).byteArray))) // or the hash don't match
)
def toSourcePosition(marker: Int): (Int, Int) = {
try {
val targetMarker = mapPosition(marker)
val line = Path(source.get).string.substring(0, targetMarker).split('\n').size
(line, targetMarker)
} catch {
case _: Throwable => (0, 0)
}
}
def source: Option[File] = {
val s = new File(meta("SOURCE"))
if (s == null || !s.exists) {
None
} else {
Some(s)
}
}
def sync() {
if (file.exists && !source.isDefined) {
file.delete()
}
}
}
case class GeneratedSourceVirtual(path: String) extends AbstractGeneratedSource {
var _content = ""
def setContent(newContent: String) {
this._content = newContent
}
def content = _content
}
object ScalaTemplateCompiler {
import scala.util.parsing.input.Positional
import scala.util.parsing.input.CharSequenceReader
import scala.util.parsing.combinator.JavaTokenParsers
abstract class TemplateTree
abstract class ScalaExpPart
case class Params(code: String) extends Positional
case class Template(name: PosString, comment: Option[Comment], params: PosString, imports: Seq[Simple], defs: Seq[Def], sub: Seq[Template], content: Seq[TemplateTree]) extends Positional
case class PosString(str: String) extends Positional {
override def toString = str
}
case class Def(name: PosString, params: PosString, code: Simple) extends Positional
case class Plain(text: String) extends TemplateTree with Positional
case class Display(exp: ScalaExp) extends TemplateTree with Positional
case class Comment(msg: String) extends TemplateTree with Positional
case class ScalaExp(parts: Seq[ScalaExpPart]) extends TemplateTree with Positional
case class Simple(code: String) extends ScalaExpPart with Positional
case class Block(whitespace: String, args: Option[PosString], content: Seq[TemplateTree]) extends ScalaExpPart with Positional
case class Value(ident: PosString, block: Block) extends Positional
def compile(source: File, sourceDirectory: File, generatedDirectory: File, formatterType: String, additionalImports: String = "") = {
val resultType = formatterType + ".Appendable"
val (templateName, generatedSource) = generatedFile(source, sourceDirectory, generatedDirectory)
if (generatedSource.needRecompilation) {
val generated = parseAndGenerateCode(templateName, Path(source).byteArray, source.getAbsolutePath, resultType, formatterType, additionalImports)
Path(generatedSource.file).write(generated.toString)
Some(generatedSource.file)
} else {
None
}
}
def compileVirtual(content: String, source: File, sourceDirectory: File, resultType: String, formatterType: String, additionalImports: String = "") = {
val (templateName, generatedSource) = generatedFileVirtual(source, sourceDirectory)
val generated = parseAndGenerateCode(templateName, content.getBytes(Charset forName "UTF-8"), source.getAbsolutePath, resultType, formatterType, additionalImports)
generatedSource.setContent(generated)
generatedSource
}
def parseAndGenerateCode(templateName: Array[String], content: Array[Byte], absolutePath: String, resultType: String, formatterType: String, additionalImports: String): String = {
templateParser.parser(new CharSequenceReader(new String(content, Charset forName "UTF-8"))) match {
case templateParser.Success(parsed: Template, rest) if rest.atEnd => {
generateFinalTemplate(absolutePath,
content,
templateName.dropRight(1).mkString("."),
templateName.takeRight(1).mkString,
parsed,
resultType,
formatterType,
additionalImports)
}
case templateParser.Success(_, rest) => {
throw new TemplateCompilationError(new File(absolutePath), "Not parsed?", rest.pos.line, rest.pos.column)
}
case templateParser.NoSuccess(message, input) => {
throw new TemplateCompilationError(new File(absolutePath), message, input.pos.line, input.pos.column)
}
}
}
def generatedFile(template: File, sourceDirectory: File, generatedDirectory: File) = {
val templateName = source2TemplateName(template, sourceDirectory, template.getName.split('.').takeRight(1).head).split('.')
templateName -> GeneratedSource(new File(generatedDirectory, templateName.mkString("/") + ".template.scala"))
}
def generatedFileVirtual(template: File, sourceDirectory: File) = {
val templateName = source2TemplateName(template, sourceDirectory, template.getName.split('.').takeRight(1).head).split('.')
templateName -> GeneratedSourceVirtual(templateName.mkString("/") + ".template.scala")
}
def source2TemplateName(f: File, sourceDirectory: File, ext: String): String = {
var filePackage = f.getCanonicalPath()
filePackage = filePackage.substring(sourceDirectory.getCanonicalPath.length + 1, filePackage.length)
filePackage = filePackage.split("[\\\\/]").reverse.tail.reverse.mkString(".")
filePackage + "." + f.getName.split('.').head
}
val templateParser = new TemplateParser
class TemplateParser extends JavaTokenParsers {
def as[T](parser: Parser[T], error: String) = {
Parser(in => parser(in) match {
case s@Success(_, _) => s
case Failure(_, next) => Failure("`" + error + "' expected but `" + next.first + "' found", next)
case Error(_, next) => Error(error, next)
})
}
def several[T](p: => Parser[T]): Parser[List[T]] = Parser {
in =>
import scala.collection.mutable.ListBuffer
val elems = new ListBuffer[T]
def continue(in: Input): ParseResult[List[T]] = {
val p0 = p // avoid repeatedly re-evaluating by-name parser
@tailrec
def applyp(in0: Input): ParseResult[List[T]] = p0(in0) match {
case Success(x, rest) => elems += x; applyp(rest)
case Failure(_, _) => Success(elems.toList, in0)
case err: Error => err
}
applyp(in)
}
continue(in)
}
def at = "@"
def eof = """\Z""".r
def newLine = (("\r" ?) ~> "\n")
def identifier = as(ident, "identifier")
def whiteSpaceNoBreak = """[ \t]+""".r
def escapedAt = at ~> at
def any = {
Parser(in => if (in.atEnd) {
Failure("end of file", in)
} else {
Success(in.first, in.rest)
})
}
def plain: Parser[Plain] = {
positioned(
((escapedAt | (not(at) ~> (not("{" | "}") ~> any))) +) ^^ {
case charList => Plain(charList.mkString)
})
}
def squareBrackets: Parser[String] = {
"[" ~ (several((squareBrackets | not("]") ~> any))) ~ commit("]") ^^ {
case p1 ~ charList ~ p2 => p1 + charList.mkString + p2
}
}
def parentheses: Parser[String] = {
"(" ~ (several((parentheses | not(")") ~> any))) ~ commit(")") ^^ {
case p1 ~ charList ~ p2 => p1 + charList.mkString + p2
}
}
def comment: Parser[Comment] = {
positioned((at ~ "*") ~> ((not("*@") ~> any *) ^^ {
case chars => Comment(chars.mkString)
}) <~ ("*" ~ at))
}
def brackets: Parser[String] = {
ensureMatchedBrackets((several((brackets | not("}") ~> any)))) ^^ {
case charList => "{" + charList.mkString + "}"
}
}
def ensureMatchedBrackets[T](p: Parser[T]): Parser[T] = Parser {
in =>
val pWithBrackets = "{" ~> p <~ ("}" | eof ~ err("EOF"))
pWithBrackets(in) match {
case s@Success(_, _) => s
case f@Failure(_, _) => f
case Error("EOF", _) => Error("Unmatched bracket", in)
case e: Error => e
}
}
def block: Parser[Block] = {
positioned(
(whiteSpaceNoBreak ?) ~ ensureMatchedBrackets((blockArgs ?) ~ several(mixed)) ^^ {
case w ~ (args ~ content) => Block(w.getOrElse(""), args, content.flatten)
})
}
def blockArgs: Parser[PosString] = positioned((not("=>" | newLine) ~> any *) ~ "=>" ^^ {
case args ~ arrow => PosString(args.mkString + arrow)
})
def methodCall: Parser[String] = identifier ~ (squareBrackets ?) ~ (parentheses ?) ^^ {
case methodName ~ types ~ args => methodName + types.getOrElse("") + args.getOrElse("")
}
def expression: Parser[Display] = {
at ~> commit(positioned(methodCall ^^ {
case code => Simple(code)
})) ~ several(expressionPart) ^^ {
case first ~ parts => Display(ScalaExp(first :: parts))
}
}
def expressionPart: Parser[ScalaExpPart] = {
chainedMethods | block | (whiteSpaceNoBreak ~> scalaBlockChained) | elseCall | positioned[Simple]((parentheses ^^ {
case code => Simple(code)
}))
}
def chainedMethods: Parser[Simple] = {
positioned(
"." ~> rep1sep(methodCall, ".") ^^ {
case calls => Simple("." + calls.mkString("."))
})
}
def elseCall: Parser[Simple] = {
(whiteSpaceNoBreak ?) ~> positioned("else" ^^ {
case e => Simple(e)
}) <~ (whiteSpaceNoBreak ?)
}
def safeExpression: Parser[Display] = {
at ~> positioned(parentheses ^^ {
case code => Simple(code)
}) ^^ {
case code => Display(ScalaExp(code :: Nil))
}
}
def matchExpression: Parser[Display] = {
val simpleExpr: Parser[List[ScalaExpPart]] = positioned(methodCall ^^ {
Simple(_)
}) ~ several(expressionPart) ^^ {
case first ~ parts => first :: parts
}
val complexExpr = positioned(parentheses ^^ {
expr => (Simple(expr))
}) ^^ {
List(_)
}
at ~> ((simpleExpr | complexExpr) ~ positioned((whiteSpaceNoBreak ~ "match" ^^ {
case w ~ m => Simple(w + m)
})) ^^ {
case e ~ m => e ++ Seq(m)
}) ~ block ^^ {
case expr ~ block => Display(ScalaExp(expr ++ Seq(block)))
}
}
def forExpression: Parser[Display] = {
at ~> positioned("for" ~ parentheses ^^ {
case f ~ p => Simple(f + p + " yield ")
}) ~ block ^^ {
case expr ~ block => {
Display(ScalaExp(List(expr, block)))
}
}
}
def caseExpression: Parser[ScalaExp] = {
(whiteSpace ?) ~> positioned( """case (.+)=>""".r ^^ {
case c => Simple(c)
}) ~ block <~ (whiteSpace ?) ^^ {
case pattern ~ block => ScalaExp(List(pattern, block))
}
}
def importExpression: Parser[Simple] = {
at ~> positioned( """import .*(\r)?\n""".r ^^ {
case stmt => Simple(stmt)
})
}
def scalaBlock: Parser[Simple] = {
at ~> positioned(
brackets ^^ {
case code => Simple(code)
})
}
def scalaBlockChained: Parser[Block] = {
scalaBlock ^^ {
case code => Block("", None, ScalaExp(code :: Nil) :: Nil)
}
}
def scalaBlockDisplayed: Parser[Display] = {
scalaBlock ^^ {
case code => Display(ScalaExp(code :: Nil))
}
}
def positionalLiteral(s: String): Parser[Plain] = new Parser[Plain] {
def apply(in: Input) = {
val offset = in.offset
val result = literal(s)(in)
result match {
case Success(s, r) => {
val plainString = Plain(s)
plainString.pos = new OffsetPosition(in.source, offset)
Success(plainString, r)
}
case Failure(s, t) => Failure(s, t)
case Error(s, t) => Error(s, t)
}
}
}
def mixed: Parser[Seq[TemplateTree]] = {
((comment | scalaBlockDisplayed | caseExpression | matchExpression | forExpression | safeExpression | plain | expression) ^^ {
case t => List(t)
}) |
(positionalLiteral("{") ~ several(mixed) ~ positionalLiteral("}")) ^^ {
case p1 ~ content ~ p2 => {
p1 +: content.flatten :+ p2
}
}
}
def template: Parser[Template] = {
templateDeclaration ~ """[ \t]*=[ \t]*[{]""".r ~ templateContent <~ "}" ^^ {
case declaration ~ assign ~ content => {
Template(declaration._1, None, declaration._2, content._1, content._2, content._3, content._4)
}
}
}
def localDef: Parser[Def] = {
templateDeclaration ~ """[ \t]*=[ \t]*""".r ~ scalaBlock ^^ {
case declaration ~ w ~ code => {
Def(declaration._1, declaration._2, code)
}
}
}
def templateDeclaration: Parser[(PosString, PosString)] = {
at ~> positioned(identifier ^^ {
case s => PosString(s)
}) ~ positioned(opt(squareBrackets) ~ several(parentheses) ^^ {
case t ~ p => PosString(t.getOrElse("") + p.mkString)
}) ^^ {
case name ~ params => name -> params
}
}
def templateContent: Parser[(List[Simple], List[Def], List[Template], List[TemplateTree])] = {
(several(importExpression | localDef | template | mixed)) ^^ {
case elems => {
elems.foldLeft((List[Simple](), List[Def](), List[Template](), List[TemplateTree]())) {
(s, e) =>
e match {
case i: Simple => (s._1 :+ i, s._2, s._3, s._4)
case d: Def => (s._1, s._2 :+ d, s._3, s._4)
case v: Template => (s._1, s._2, s._3 :+ v, s._4)
case c: Seq[_] => (s._1, s._2, s._3, s._4 ++ c.asInstanceOf[Seq[TemplateTree]])
}
}
}
}
}
def parser: Parser[Template] = {
opt(comment) ~ opt(whiteSpace) ~ opt(at ~> positioned((parentheses +) ^^ {
case s => PosString(s.mkString)
})) ~ templateContent ^^ {
case comment ~ _ ~ args ~ content => {
Template(PosString(""), comment, args.getOrElse(PosString("()")), content._1, content._2, content._3, content._4)
}
}
}
override def skipWhitespace = false
}
def visit(elem: Seq[TemplateTree], previous: Seq[Any]): Seq[Any] = {
elem match {
case head :: tail =>
val tripleQuote = "\"\"\""
visit(tail, head match {
case p@Plain(text) => (if (previous.isEmpty) Nil else previous :+ ",") :+ "format.raw" :+ Source("(", p.pos) :+ tripleQuote :+ text :+ tripleQuote :+ ")"
case Comment(msg) => previous
case Display(exp) => (if (previous.isEmpty) Nil else previous :+ ",") :+ "_display_(Seq[Any](" :+ visit(Seq(exp), Nil) :+ "))"
case ScalaExp(parts) => previous :+ parts.map {
case s@Simple(code) => Source(code, s.pos)
case b@Block(whitespace, args, content) if (content.forall(_.isInstanceOf[ScalaExp])) => Nil :+ Source(whitespace + "{" + args.getOrElse(""), b.pos) :+ visit(content, Nil) :+ "}"
case b@Block(whitespace, args, content) => Nil :+ Source(whitespace + "{" + args.getOrElse(""), b.pos) :+ "_display_(Seq[Any](" :+ visit(content, Nil) :+ "))}"
}
})
case Nil => previous
}
}
def templateCode(template: Template, resultType: String): Seq[Any] = {
val defs = (template.sub ++ template.defs).map {
i =>
i match {
case t: Template if t.name == "" => templateCode(t, resultType)
case t: Template => {
Nil :+ (if (t.name.str.startsWith("implicit")) "implicit def " else "def ") :+ Source(t.name.str, t.name.pos) :+ Source(t.params.str, t.params.pos) :+ ":" :+ resultType :+ " = {_display_(" :+ templateCode(t, resultType) :+ ")};"
}
case Def(name, params, block) => {
Nil :+ (if (name.str.startsWith("implicit")) "implicit def " else "def ") :+ Source(name.str, name.pos) :+ Source(params.str, params.pos) :+ " = {" :+ block.code :+ "};"
}
}
}
val imports = template.imports.map(_.code).mkString("\n")
Nil :+ imports :+ "\n" :+ defs :+ "\n" :+ "Seq[Any](" :+ visit(template.content, Nil) :+ ")"
}
def generateCode(packageName: String, name: String, root: Template, resultType: String, formatterType: String, additionalImports: String) = {
val extra = TemplateAsFunctionCompiler.getFunctionMapping(
root.params.str,
resultType)
val generated = {
Nil :+ """
package """ :+ packageName :+ """
import fr.assoba.open.template.api._
import fr.assoba.open.template.api.TemplateMagic._
""" :+ additionalImports :+ """
/*""" :+ root.comment.map(_.msg).getOrElse("") :+ """*/
object """ :+ name :+ """ extends BaseScalaTemplate[""" :+ resultType :+ """,Format[""" :+ resultType :+ """]](""" :+ formatterType :+ """) with """ :+ extra._3 :+ """ {
/*""" :+ root.comment.map(_.msg).getOrElse("") :+ """*/
def apply""" :+ Source(root.params.str, root.params.pos) :+ """:""" :+ resultType :+ """ = {
_display_ {""" :+ templateCode(root, resultType) :+ """}
}
""" :+ extra._1 :+ """
""" :+ extra._2 :+ """
def ref: this.type = this
}"""
}
generated
}
def generateFinalTemplate(absolutePath: String, contents: Array[Byte], packageName: String, name: String, root: Template, resultType: String, formatterType: String, additionalImports: String): String = {
val generated = generateCode(packageName, name, root, resultType, formatterType, additionalImports)
Source.finalSource(absolutePath, contents, generated)
}
object TemplateAsFunctionCompiler {
// Note, the presentation compiler is not thread safe, all access to it must be synchronized. If access to it
// is not synchronized, then weird things happen like FreshRunReq exceptions are thrown when multiple sub projects
// are compiled (done in parallel by default by SBT). So if adding any new methods to this object, make sure you
// make them synchronized.
import java.io.File
import scala.tools.nsc.interactive.Global
import scala.tools.nsc.util.{SourceFile, Position, BatchSourceFile}
import scala.tools.nsc.Settings
import scala.tools.nsc.reporters.ConsoleReporter
def getFunctionMapping(signature: String, returnType: String): (String, String, String) = synchronized {
def filterType(t: String) = t match {
case vararg if vararg.startsWith("_root_.scala.") => vararg.replace("_root_.scala.", "Array")
case synthetic if synthetic.contains("") => synthetic.replace("", "")
case t => t
}
def findSignature(tree: Tree): Option[DefDef] = {
tree match {
case t: DefDef if t.name.toString == "signature" => Some(t)
case t: Tree => t.children.flatMap(findSignature).headOption
}
}
val sigTree = PresentationCompiler.toolBox.parse("object FT { def signature" + signature + " }")
val params = findSignature(sigTree).get.vparamss
val functionType = "(" + params.map(group => "(" + group.map {
case a if a.mods.hasFlag(BYNAMEPARAM) => " => " + a.tpt.children(1).toString
case a => filterType(a.tpt.toString)
}.mkString(",") + ")").mkString(" => ") + " => " + returnType + ")"
val renderCall = "def render%s: %s = apply%s".format(
"(" + params.flatten.map {
case a if a.mods.hasFlag(BYNAMEPARAM) => a.name.toString + ":" + a.tpt.children(1).toString
case a => a.name.toString + ":" + filterType(a.tpt.toString)
}.mkString(",") + ")",
returnType,
params.map(group => "(" + group.map {
p =>
p.name.toString + Option(p.tpt.toString).filter(_.startsWith("_root_.scala.")).map(_ => ":_*").getOrElse("")
}.mkString(",") + ")").mkString)
var templateType = "fr.assoba.open.template.api.Template%s[%s%s]".format(
params.flatten.size,
params.flatten.map {
case a if a.mods.hasFlag(BYNAMEPARAM) => a.tpt.children(1).toString
case a => filterType(a.tpt.toString)
}.mkString(","),
(if (params.flatten.isEmpty) "" else ",") + returnType)
val f = "def f:%s = %s => apply%s".format(
functionType,
params.map(group => "(" + group.map(_.name.toString).mkString(",") + ")").mkString(" => "),
params.map(group => "(" + group.map {
p =>
p.name.toString + Option(p.tpt.toString).filter(_.startsWith("_root_.scala.")).map(_ => ":_*").getOrElse("")
}.mkString(",") + ")").mkString)
(renderCall, f, templateType)
}
class CompilerInstance {
def additionalClassPathEntry: Option[String] = None
lazy val compiler = {
val settings = new Settings
val scalaObjectSource = Class.forName("scala.ScalaObject").getProtectionDomain.getCodeSource
// is null in Eclipse/OSGI but luckily we don't need it there
if (scalaObjectSource != null) {
import java.security.CodeSource
def toAbsolutePath(cs: CodeSource) = new File(cs.getLocation.toURI).getAbsolutePath
val compilerPath = toAbsolutePath(Class.forName("scala.tools.nsc.Interpreter").getProtectionDomain.getCodeSource)
val libPath = toAbsolutePath(scalaObjectSource)
val pathList = List(compilerPath, libPath)
val origBootclasspath = settings.bootclasspath.value
settings.bootclasspath.value = ((origBootclasspath :: pathList) ::: additionalClassPathEntry.toList) mkString File.pathSeparator
}
val compiler = new Global(settings, new ConsoleReporter(settings) {
override def printMessage(pos: Position, msg: String) = ()
})
compiler.ask(() => new compiler.Run)
compiler
}
}
trait TreeCreationMethods {
val global: scala.tools.nsc.interactive.Global
val randomFileName = {
val r = new java.util.Random
() => "file" + r.nextInt
}
def treeFrom(src: String): global.Tree = {
val file = new BatchSourceFile(randomFileName(), src)
treeFrom(file)
}
def treeFrom(file: SourceFile): global.Tree = {
import tools.nsc.interactive.Response
type Scala29Compiler = {
def askParsedEntered(file: SourceFile, keepLoaded: Boolean, response: Response[global.Tree]): Unit
def askType(file: SourceFile, forceReload: Boolean, respone: Response[global.Tree]): Unit
}
val newCompiler = global.asInstanceOf[Scala29Compiler]
val r1 = new Response[global.Tree]
newCompiler.askParsedEntered(file, true, r1)
r1.get.left.toOption.getOrElse(throw r1.get.right.get)
}
}
object CompilerInstance extends CompilerInstance
object PresentationCompiler extends TreeCreationMethods {
val global = CompilerInstance.compiler
val toolBox = runtimeMirror(getClass.getClassLoader).mkToolBox()
def shutdown() {
global.askShutdown()
}
}
}
}
/* ------- */
import scala.util.parsing.input.{Position, OffsetPosition, NoPosition}
case class Source(code: String, pos: Position = NoPosition)
object Source {
import scala.collection.mutable.ListBuffer
def finalSource(absolutePath: String, contents: Array[Byte], generatedTokens: Seq[Any]): String = {
val scalaCode = new StringBuilder
val positions = ListBuffer.empty[(Int, Int)]
val lines = ListBuffer.empty[(Int, Int)]
serialize(generatedTokens, scalaCode, positions, lines)
scalaCode + """
/*
-- GENERATED --
DATE: """ + new java.util.Date + """
SOURCE: """ + absolutePath.replace(File.separator, "/") + """
HASH: """ + Hash(contents) + """
MATRIX: """ + positions.map {
pos =>
pos._1 + "->" + pos._2
}.mkString("|") + """
LINES: """ + lines.map {
line =>
line._1 + "->" + line._2
}.mkString("|") + """
-- GENERATED --
*/
"""
}
@deprecated("use finalSource with 3 parameters instead", "Play 2.1")
def finalSource(template: File, generatedTokens: Seq[Any]): String = {
finalSource(template.getAbsolutePath, Path(template).byteArray, generatedTokens)
}
private def serialize(parts: Seq[Any], source: StringBuilder, positions: ListBuffer[(Int, Int)], lines: ListBuffer[(Int, Int)]) {
parts.foreach {
case s: String => source.append(s)
case Source(code, pos@OffsetPosition(_, offset)) => {
source.append("/*" + pos + "*/")
positions += (source.length -> offset)
lines += (source.toString.split('\n').size -> pos.line)
source.append(code)
}
case Source(code, NoPosition) => source.append(code)
case s: Seq[Any] => serialize(s, source, positions, lines)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy