PageRenderTime 15ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/scalate-core/src/main/scala/org/fusesource/scalate/support/AbstractCodeGenerator.scala

http://github.com/scalate/scalate
Scala | 242 lines | 170 code | 34 blank | 38 comment | 40 complexity | 07f611858eba290ad9b6061f570eabdd MD5 | raw file
  1. /**
  2. * Copyright (C) 2009-2011 the original author or authors.
  3. * See the notice.md file distributed with this work for additional
  4. * information regarding copyright ownership.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.fusesource.scalate.support
  19. import org.fusesource.scalate.util.Log
  20. import org.fusesource.scalate.{ Binding, TemplateEngine, TemplateSource }
  21. import scala.collection.immutable.TreeMap
  22. import scala.language.postfixOps
  23. import scala.util.parsing.input.{ OffsetPosition, Position, Positional }
  24. object AbstractCodeGenerator extends Log
  25. /**
  26. * Provides a common base class for CodeGenerator implementations.
  27. *
  28. * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
  29. */
  30. abstract class AbstractCodeGenerator[T] extends CodeGenerator {
  31. abstract class AbstractSourceBuilder[T] {
  32. var indentLevel = 0
  33. var code = ""
  34. var generatedPositions = Map[OffsetPosition, Int]()
  35. def <<(): this.type = <<("")
  36. def <<(line: String): this.type = {
  37. for (i <- 0 until indentLevel) {
  38. code += " "
  39. }
  40. code += line + "\n"
  41. this
  42. }
  43. def <<[T](list: List[T]): this.type = {
  44. for (i <- 0 until indentLevel) {
  45. code += " "
  46. }
  47. for (value <- list) value match {
  48. case text: Positional => this << text.pos << text.toString
  49. case pos: Position => this << pos
  50. case _ => code += value.toString
  51. }
  52. code += "\n"
  53. this
  54. }
  55. def <<(pos: Position): this.type = {
  56. if (pos != null) {
  57. pos match {
  58. case p: OffsetPosition =>
  59. generatedPositions = generatedPositions + (p -> current_position)
  60. case _ =>
  61. }
  62. }
  63. this
  64. }
  65. def current_position = {
  66. code.length + (indentLevel * 2)
  67. }
  68. def positions() = {
  69. var rc = new TreeMap[OffsetPosition, OffsetPosition]()(new Ordering[OffsetPosition] {
  70. def compare(p1: OffsetPosition, p2: OffsetPosition): Int = {
  71. val rc = p1.line - p2.line
  72. if (rc == 0) {
  73. p1.column - p2.column
  74. } else {
  75. rc
  76. }
  77. }
  78. })
  79. generatedPositions.foreach {
  80. entry =>
  81. rc = rc + (OffsetPosition(code, entry._2) -> entry._1)
  82. }
  83. rc
  84. }
  85. def indent[T](op: => T): T = {
  86. indentLevel += 1
  87. val rc = op
  88. indentLevel -= 1
  89. rc
  90. }
  91. def generate(
  92. engine: TemplateEngine,
  93. source: TemplateSource,
  94. bindings: Iterable[Binding],
  95. statements: List[T]): Unit = {
  96. val packageName = source.packageName
  97. val className = source.simpleClassName
  98. this << "/* NOTE this file is autogenerated by Scalate : see http://scalate.fusesource.org/ */"
  99. if (packageName != "") {
  100. this << "package " + packageName
  101. }
  102. this << ""
  103. val engineImports = engine.importStatements
  104. for (i <- engineImports) {
  105. this << i
  106. }
  107. if (!engineImports.isEmpty) {
  108. this << ""
  109. }
  110. this << "object " + className + " {"
  111. indent {
  112. // We prefix the function an variables with $_scalate_$ to avoid namespace pollution which could
  113. // conflict with definitions declared in the template
  114. this << "def $_scalate_$render($_scalate_$_context: _root_.org.fusesource.scalate.RenderContext): Unit = {"
  115. indent {
  116. generateInitialImports
  117. generateBindings(bindings) {
  118. generateTemplatePackage(source, bindings)
  119. generate(statements)
  120. if (statements.isEmpty) {
  121. // lets generate a dummy statement to avoid a compile error
  122. this << "$_scalate_$_context << \"\""
  123. }
  124. }
  125. }
  126. this << "}"
  127. }
  128. this << "}"
  129. this <<
  130. this <<;
  131. this << "class " + className + " extends _root_.org.fusesource.scalate.Template {"
  132. indent {
  133. this << "def render(context: _root_.org.fusesource.scalate.RenderContext): Unit = " + className + ".$_scalate_$render(context)"
  134. }
  135. this << "}"
  136. }
  137. def generateInitialImports(): Unit = {}
  138. def generate(statements: List[T]): Unit
  139. def generateBindings(bindings: Iterable[Binding])(body: => Unit): Unit = {
  140. bindings.foreach(arg => {
  141. this << ";{"
  142. indentLevel += 1
  143. generateBinding(arg)
  144. })
  145. body
  146. bindings.foreach(arg => {
  147. indentLevel -= 1
  148. this << "}"
  149. })
  150. }
  151. def generateBinding(binding: Binding): Unit = {
  152. def generateImplicit = if (binding.isImplicit) "implicit " else ""
  153. def attributeMethodCall: List[_] = if (binding.defaultValue.isEmpty) {
  154. "attribute(" :: asString(binding.name) :: ")" :: Nil
  155. } else {
  156. "attributeOrElse(" :: asString(binding.name) :: ", " :: binding.defaultValuePositionalOrText :: ")" :: Nil
  157. }
  158. this << generateImplicit :: binding.kind :: " " :: binding.name :: ": " + binding.classNamePositionalOrText :: " = $_scalate_$_context." :: attributeMethodCall
  159. /*
  160. this << generateImplicit + binding.kind + " " + binding.name + ":" + binding.className + " = ($_scalate_$_context.attributes.get(" + asString(binding.name) + ") match {"
  161. indent {
  162. //this << "case Some(value: "+binding.className+") => value"
  163. this << "case Some(value) => value.asInstanceOf[" + binding.className + "]"
  164. if (binding.defaultValue.isEmpty) {
  165. this << "case None => throw new _root_.org.fusesource.scalate.NoValueSetException(" + asString(binding.name) + ")"
  166. } else {
  167. this << "case None => " + binding.defaultValue.get
  168. }
  169. }
  170. this << "});"
  171. */
  172. if (binding.importMembers) {
  173. this << "import " + binding.name + "._"
  174. }
  175. }
  176. protected def generateTemplatePackage(source: TemplateSource, bindings: Iterable[Binding]): Unit = {
  177. val templatePackage = TemplatePackage.findTemplatePackage(source).getOrElse(new DefaultTemplatePackage())
  178. this << templatePackage.header(source, bindings.toList)
  179. this <<
  180. }
  181. def asString(text: String): String = {
  182. val buffer = new StringBuffer
  183. buffer.append("\"")
  184. text.foreach(c => {
  185. if (c == '"')
  186. buffer.append("\\\"")
  187. else if (c == '\\')
  188. buffer.append("\\\\")
  189. else if (c == '\n')
  190. buffer.append("\\n")
  191. else if (c == '\r')
  192. buffer.append("\\r")
  193. else if (c == '\b')
  194. buffer.append("\\b")
  195. else if (c == '\t')
  196. buffer.append("\\t")
  197. else if ((c >= '#' && c <= '~') || c == ' ' || c == '!')
  198. buffer.append(c)
  199. else {
  200. buffer.append("\\u")
  201. buffer.append("%04x".format(c.asInstanceOf[Int]))
  202. }
  203. })
  204. buffer.append("\"")
  205. buffer.toString
  206. }
  207. }
  208. }