PageRenderTime 28ms CodeModel.GetById 9ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/scalate-core/src/main/scala/org/fusesource/scalate/mustache/Scope.scala

http://github.com/scalate/scalate
Scala | 279 lines | 184 code | 44 blank | 51 comment | 15 complexity | 81786d45e63b42c266be82fe7dacab60 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.mustache
 19
 20import org.fusesource.scalate.RenderContext
 21
 22import scala.collection.JavaConverters._
 23
 24import java.{ lang => jl, util => ju }
 25import xml.NodeSeq
 26import org.fusesource.scalate.util.Log
 27
 28object Scope extends Log {
 29  def apply(context: RenderContext) = {
 30    context.attributeOrElse[Scope]("scope", RenderContextScope(context))
 31  }
 32}
 33
 34/**
 35 * Represents a variable scope
 36 *
 37 * @version $Revision : 1.1 $
 38 */
 39trait Scope {
 40  import Scope._
 41  def parent: Option[Scope]
 42
 43  def context: RenderContext
 44
 45  var implicitIterator: Option[String] = Some(".")
 46
 47  /**
 48   * Renders the given variable name to the context
 49   */
 50  def renderVariable(name: String, unescape: Boolean): Unit = {
 51    val v = apply(name) match {
 52      case Some(a) => a
 53      case None =>
 54        parent match {
 55          case Some(p) => p.apply(name)
 56          case _ => null
 57        }
 58    }
 59    debug("renderVariable %s = %s on %s", name, v, this)
 60    renderValue(v, unescape)
 61  }
 62
 63  def renderValue(v: Any, unescape: Boolean = false): Unit = if (unescape) {
 64    context.unescape(format(v))
 65  } else {
 66    context.escape(format(v))
 67  }
 68
 69  /**
 70   * Returns the variable of the given name looking in this scope or parent scopes to resolve the variable
 71   */
 72  def apply(name: String): Option[Any] = {
 73    val value = localVariable(name)
 74    value match {
 75      case Some(v) => value
 76      case _ =>
 77        if (implicitIterator.isDefined && implicitIterator.get == name) {
 78          iteratorObject
 79        } else {
 80          parent match {
 81            case Some(p) => p.apply(name)
 82            case _ => None
 83          }
 84        }
 85    }
 86  }
 87
 88  /**
 89   * Returns the current implicit iterator object
 90   */
 91  def iteratorObject: Option[Any] = None
 92
 93  /**
 94   * Returns the variable in the local scope if it is defined
 95   */
 96  def localVariable(name: String): Option[Any]
 97
 98  def section(name: String)(block: Scope => Unit): Unit = {
 99    apply(name) match {
100      case Some(t) =>
101        val v = toIterable(t, block)
102        debug("section value " + name + " = " + v + " in " + this)
103        v match {
104
105          // TODO we have to be really careful to distinguish between collections of things
106          // such as Seq from objects / products / Maps / partial functions which act as something to lookup names
107
108          case FunctionResult(r) => renderValue(r)
109
110          case s: NodeSeq => childScope(name, s)(block)
111          case s: Seq[Any] => foreachScope(name, s)(block)
112          case Some(a) => childScope(name, a)(block)
113          case None =>
114
115          // lets treat empty maps as being empty collections
116          // due to bug in JSON parser returning Map() for JSON expression []
117          case s: collection.Map[_, _] => if (!s.isEmpty) childScope(name, s)(block)
118
119          // maps and so forth, treat as child scopes
120          case a: PartialFunction[_, _] => childScope(name, a)(block)
121
122          // any other traversable treat as a collection
123          case s: Traversable[Any] => foreachScope(name, s.toIterable)(block)
124
125          case true => block(this)
126          case false =>
127          case null =>
128
129          // lets treat anything as an an object rather than a collection
130          case a => childScope(name, a)(block)
131        }
132      case None => parent match {
133        case Some(ps) => ps.section(name)(block)
134        case None => // do nothing, no value
135          debug("No value for " + name + " in " + this)
136
137      }
138    }
139  }
140
141  def invertedSection(name: String)(block: Scope => Unit): Unit = {
142    apply(name) match {
143      case Some(t) =>
144        val v = toIterable(t, block)
145        debug("invertedSection value " + name + " = " + v + " in " + this)
146        v match {
147
148          // TODO we have to be really careful to distinguish between collections of things
149          // such as Seq from objects / products / Maps / partial functions which act as something to lookup names
150
151          case FunctionResult(r) =>
152
153          case s: NodeSeq => if (s.isEmpty) block(this)
154          case s: Seq[_] => if (s.isEmpty) block(this)
155          case Some(a) =>
156          case None => block(this)
157
158          case s: collection.Map[_, _] => if (s.isEmpty) block(this)
159
160          // maps and so forth, treat as child scopes
161          case a: PartialFunction[_, _] =>
162
163          // any other traversible treat as a collection
164          case s: Traversable[Any] => if (s.isEmpty) block(this)
165
166          case true =>
167          case false => block(this)
168          case null => block(this)
169
170          // lets treat anything as an an object rather than a collection
171          case a =>
172        }
173      case None => block(this)
174    }
175  }
176
177  def partial(name: String): Unit = {
178    context.withAttributes(Map("scope" -> this)) {
179      // TODO allow the extension to be overloaded
180      context.include(name + ".mustache")
181    }
182  }
183
184  def childScope(name: String, v: Any)(block: Scope => Unit): Unit = {
185    debug("Creating scope for: " + v)
186    val scope = createScope(name, v)
187    block(scope)
188  }
189
190  def foreachScope[T](name: String, s: Iterable[T])(block: Scope => Unit): Unit = {
191    for (i <- s) {
192      debug("Creating traversable scope for: " + i)
193      val scope = createScope(name, i)
194      block(scope)
195    }
196  }
197
198  def createScope(name: String, value: Any): Scope = {
199    value match {
200      case n: NodeSeq => new NodeScope(this, name, n)
201      case v: scala.collection.Map[_, _] =>
202        new MapScope(
203          this,
204          name,
205          v.asInstanceOf[scala.collection.Map[String, _]])
206      case null => new EmptyScope(this)
207      case None => new EmptyScope(this)
208      case v: AnyRef => new ObjectScope(this, v)
209      case v =>
210        warn("Unable to process value: %s", v)
211        new EmptyScope(this)
212    }
213  }
214
215  @deprecated(message = "use toIterable instead", since = "")
216  def toTraversable(v: Any, block: Scope => Unit): Any = toIterable(v, block)
217
218  def toIterable(v: Any, block: Scope => Unit): Any = v match {
219    case t: Seq[_] => t
220    case t: Array[_] => t.toSeq
221    case t: ju.Map[_, _] => t.asScala
222
223    case f: Function0[_] => toIterable(f(), block)
224    case f: Function1[_, _] =>
225      if (isParam1(f, classOf[Object])) {
226        // Java lambda support since 1.8
227        try {
228          val f2 = f.asInstanceOf[Function1[Scope, _]]
229          toIterable(f2(this), block)
230        } catch {
231          case e: Exception =>
232            try {
233              val f2 = f.asInstanceOf[Function1[String, _]]
234              FunctionResult(f2(capture(block)))
235            } catch {
236              case e: Exception =>
237                f
238            }
239        }
240      } else {
241        f
242      }
243
244    case c: ju.Collection[_] => c.asScala
245    case i: ju.Iterator[_] => i.asScala
246    case i: jl.Iterable[_] => i.asScala
247
248    case _ => v
249  }
250
251  def format(v: Any): Any = v match {
252    case f: Function0[_] => format(f())
253    case f: Function1[_, _] if isParam1(f, classOf[Scope]) => format(f.asInstanceOf[Function1[Scope, _]](this))
254    case f: Function1[_, _] =>
255      try {
256        format(f.asInstanceOf[Function1[Object, _]](this))
257      } catch {
258        case e: ClassCastException =>
259          v
260      }
261    case _ => v
262  }
263
264  /**
265   * Captures the output of the given block
266   */
267  def capture(block: Scope => Unit): String = {
268    def body(): Unit = block(this)
269    context.capture(body)
270  }
271
272  def isParam1[T](f: Function1[_, _], clazz: Class[T]): Boolean = {
273    try {
274      f.getClass.getMethod("apply", clazz)
275      true
276    } catch { case e: NoSuchMethodException => false }
277  }
278
279}