PageRenderTime 33ms CodeModel.GetById 10ms app.highlight 20ms RepoModel.GetById 1ms 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 */
 18package org.fusesource.scalate.support
 19
 20import org.fusesource.scalate.util.Log
 21import org.fusesource.scalate.{ Binding, TemplateEngine, TemplateSource }
 22
 23import scala.collection.immutable.TreeMap
 24import scala.language.postfixOps
 25import scala.util.parsing.input.{ OffsetPosition, Position, Positional }
 26
 27object AbstractCodeGenerator extends Log
 28
 29/**
 30 * Provides a common base class for CodeGenerator implementations.
 31 *
 32 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
 33 */
 34abstract class AbstractCodeGenerator[T] extends CodeGenerator {
 35
 36  abstract class AbstractSourceBuilder[T] {
 37    var indentLevel = 0
 38    var code = ""
 39    var generatedPositions = Map[OffsetPosition, Int]()
 40
 41    def <<(): this.type = <<("")
 42
 43    def <<(line: String): this.type = {
 44      for (i <- 0 until indentLevel) {
 45        code += "  "
 46      }
 47      code += line + "\n"
 48      this
 49    }
 50
 51    def <<[T](list: List[T]): this.type = {
 52      for (i <- 0 until indentLevel) {
 53        code += "  "
 54      }
 55      for (value <- list) value match {
 56        case text: Positional => this << text.pos << text.toString
 57        case pos: Position => this << pos
 58        case _ => code += value.toString
 59      }
 60      code += "\n"
 61      this
 62    }
 63
 64    def <<(pos: Position): this.type = {
 65      if (pos != null) {
 66        pos match {
 67          case p: OffsetPosition =>
 68            generatedPositions = generatedPositions + (p -> current_position)
 69          case _ =>
 70        }
 71      }
 72      this
 73    }
 74
 75    def current_position = {
 76      code.length + (indentLevel * 2)
 77    }
 78
 79    def positions() = {
 80      var rc = new TreeMap[OffsetPosition, OffsetPosition]()(new Ordering[OffsetPosition] {
 81        def compare(p1: OffsetPosition, p2: OffsetPosition): Int = {
 82          val rc = p1.line - p2.line
 83          if (rc == 0) {
 84            p1.column - p2.column
 85          } else {
 86            rc
 87          }
 88        }
 89      })
 90      generatedPositions.foreach {
 91        entry =>
 92          rc = rc + (OffsetPosition(code, entry._2) -> entry._1)
 93      }
 94      rc
 95    }
 96
 97    def indent[T](op: => T): T = {
 98      indentLevel += 1
 99      val rc = op
100      indentLevel -= 1
101      rc
102    }
103
104    def generate(
105      engine: TemplateEngine,
106      source: TemplateSource,
107      bindings: Iterable[Binding],
108      statements: List[T]): Unit = {
109
110      val packageName = source.packageName
111      val className = source.simpleClassName
112
113      this << "/* NOTE this file is autogenerated by Scalate : see http://scalate.fusesource.org/ */"
114      if (packageName != "") {
115        this << "package " + packageName
116      }
117
118      this << ""
119
120      val engineImports = engine.importStatements
121      for (i <- engineImports) {
122        this << i
123      }
124      if (!engineImports.isEmpty) {
125        this << ""
126      }
127
128      this << "object " + className + " {"
129      indent {
130        // We prefix the function an variables with $_scalate_$ to avoid namespace pollution which could
131        // conflict with definitions declared in the template
132        this << "def $_scalate_$render($_scalate_$_context: _root_.org.fusesource.scalate.RenderContext): Unit = {"
133        indent {
134          generateInitialImports
135          generateBindings(bindings) {
136            generateTemplatePackage(source, bindings)
137
138            generate(statements)
139            if (statements.isEmpty) {
140              // lets generate a dummy statement to avoid a compile error
141              this << "$_scalate_$_context << \"\""
142            }
143          }
144        }
145        this << "}"
146      }
147      this << "}"
148      this <<
149
150      this <<;
151      this << "class " + className + " extends _root_.org.fusesource.scalate.Template {"
152      indent {
153        this << "def render(context: _root_.org.fusesource.scalate.RenderContext): Unit = " + className + ".$_scalate_$render(context)"
154      }
155      this << "}"
156
157    }
158
159    def generateInitialImports(): Unit = {}
160
161    def generate(statements: List[T]): Unit
162
163    def generateBindings(bindings: Iterable[Binding])(body: => Unit): Unit = {
164      bindings.foreach(arg => {
165        this << ";{"
166        indentLevel += 1
167        generateBinding(arg)
168      })
169
170      body
171
172      bindings.foreach(arg => {
173        indentLevel -= 1
174        this << "}"
175      })
176    }
177
178    def generateBinding(binding: Binding): Unit = {
179      def generateImplicit = if (binding.isImplicit) "implicit " else ""
180
181      def attributeMethodCall: List[_] = if (binding.defaultValue.isEmpty) {
182        "attribute(" :: asString(binding.name) :: ")" :: Nil
183      } else {
184        "attributeOrElse(" :: asString(binding.name) :: ", " :: binding.defaultValuePositionalOrText :: ")" :: Nil
185      }
186
187      this << generateImplicit :: binding.kind :: " " :: binding.name :: ": " + binding.classNamePositionalOrText :: " = $_scalate_$_context." :: attributeMethodCall
188
189      /*
190            this << generateImplicit + binding.kind + " " + binding.name + ":" + binding.className + " = ($_scalate_$_context.attributes.get(" + asString(binding.name) + ") match {"
191            indent {
192              //this << "case Some(value: "+binding.className+") => value"
193              this << "case Some(value) => value.asInstanceOf[" + binding.className + "]"
194              if (binding.defaultValue.isEmpty) {
195                this << "case None => throw new _root_.org.fusesource.scalate.NoValueSetException(" + asString(binding.name) + ")"
196              } else {
197                this << "case None => " + binding.defaultValue.get
198              }
199            }
200            this << "});"
201      */
202
203      if (binding.importMembers) {
204        this << "import " + binding.name + "._"
205      }
206    }
207
208    protected def generateTemplatePackage(source: TemplateSource, bindings: Iterable[Binding]): Unit = {
209      val templatePackage = TemplatePackage.findTemplatePackage(source).getOrElse(new DefaultTemplatePackage())
210      this << templatePackage.header(source, bindings.toList)
211      this <<
212    }
213
214    def asString(text: String): String = {
215      val buffer = new StringBuffer
216      buffer.append("\"")
217      text.foreach(c => {
218        if (c == '"')
219          buffer.append("\\\"")
220        else if (c == '\\')
221          buffer.append("\\\\")
222        else if (c == '\n')
223          buffer.append("\\n")
224        else if (c == '\r')
225          buffer.append("\\r")
226        else if (c == '\b')
227          buffer.append("\\b")
228        else if (c == '\t')
229          buffer.append("\\t")
230        else if ((c >= '#' && c <= '~') || c == ' ' || c == '!')
231          buffer.append(c)
232        else {
233          buffer.append("\\u")
234          buffer.append("%04x".format(c.asInstanceOf[Int]))
235        }
236      })
237      buffer.append("\"")
238      buffer.toString
239    }
240  }
241
242}