<commit_before>package org.fusesource.scalate

import filter.FilterRequest
import java.util.{Locale, Date}
import java.text.{DateFormat, NumberFormat}
import introspector.Introspector
import collection.mutable.{ListBuffer, HashMap}
import util.{RenderHelper, XmlHelper, Lazy}
import xml.{Node, PCData, NodeSeq, NodeBuffer}

object RenderContext {
  val threadLocal = new ThreadLocal[RenderContext]

  def capture(body: => Unit) = apply().capture(body)

  def captureNodeSeq(body: => Unit) = apply().captureNodeSeq(body)

  def apply(): RenderContext = threadLocal.get

  @deprecated("Can leak thread local storage. Use the 'using' method instead.")
  def update(that: RenderContext) = threadLocal.set(that)
  
  def using[T](that: RenderContext)(func: =>T):T = {
    val previous = threadLocal.get
    try {
      threadLocal.set(that)
      func
    } finally {
      if( previous!=null ) {
        threadLocal.set(previous)
      } else {
        threadLocal.remove
      }
    }
  }

}

/**
 * Provides helper methods for rendering templates and values and for working with attributes.
 *
 * @see DefaultRenderContext
 * @see org.fusesource.scalate.servlet.ServletRenderContext
 */
trait RenderContext {
  /**
   * Default string used to output null values
   */
  var nullString = ""

  /**
   * Default string used to output None values
   */
  var noneString = ""

  /**
   * Whether or not markup sensitive characters for HTML/XML elements like &amp; &gt; &lt; are escaped or not
   */
  var escapeMarkup = true

  var currentTemplate: String = _

  var viewPrefixes = List("")
  var viewPostfixes = engine.codeGenerators.keysIterator.map(x => "." + x).toList

  def engine: TemplateEngine

  /**
   * Renders the provided value and inserts it into the final rendered document without sanitizing the value.
   */
  def <<(value: Any): Unit

  /**
   * Renders the provided value, sanitizes any XML special characters and inserts
   * it into the final rendered document.
   */
  def <<<(value: Any): Unit

  /**
   * Access the attributes available in this context
   */
  def attributes: AttributeMap[String, Any]


  /**
   * Sorted list of attribute keys
   */
  def attributeKeys = attributes.keySet.toList.sortWith(_ < _)

  /**
   * Returns the attribute of the given type or a  { @link NoValueSetException } exception is thrown
   */
  def attribute[T](name: String): T = {
    attributes.get(name)
            .getOrElse(throw new NoValueSetException(name))
            .asInstanceOf[T]
  }

  /**
   * Returns the attribute of the given name and type or the default value if it is not available
   */
  def attributeOrElse[T](name: String, defaultValue: T): T = {
    attributes.get(name)
            .getOrElse(defaultValue)
            .asInstanceOf[T]
  }

  def setAttribute(name: String, value: Option[Any]) {
    value match {
      case Some(v) => attributes(name) = v
      case None => attributes.remove(name)
    }
  }

  /////////////////////////////////////////////////////////////////////
  //
  // Rendering API
  //
  //////////////////////////////////x///////////////////////////////////
  def value(any: Any, shouldSanitize: Boolean = escapeMarkup): String = {
    def sanitize(text: String): String = if (shouldSanitize) {RenderHelper.sanitize(text)} else {text}

    any match {
      case u: Unit => ""
      case null => sanitize(nullString)
      case None => sanitize(noneString)
      case Unescaped(text) => text
      case f: Function0[_] => value(f(), shouldSanitize)
      case v: String => sanitize(v)
      case v: Date => sanitize(dateFormat.format(v))
      case n: Double if n.isNaN => "NaN"
      case n: Float if n.isNaN => "NaN"
      case v: Double => sanitize(numberFormat.format(v))
      case v: Float => sanitize(numberFormat.format(v))
      case v: Number => sanitize(numberFormat.format(v))
      case f: FilterRequest => {
        // NOTE assume a filter does the correct sanitizing
        var rc = filter(f.filter, f.content)
        rc
      }
      // No need to sanitize nodes as they are already sanitized
      case s: NodeBuffer =>
        // No need to sanitize nodes as they are already sanitized
        (s.foldLeft(new StringBuilder) {
          (rc, x) => x match {
            case cd: PCData => rc.append(cd.data)
            case _ => rc.append(x)
          }
        }).toString

      case n: Node => n.toString

      case x: Traversable[Any] =>
        x.map(value(_, shouldSanitize)).mkString("")

      // TODO for any should we use the renderView?
      case v: Any => sanitize(v.toString)
    }
  }

  def valueEscaped(any: Any) = value(any, true)

  def valueUnescaped(any: Any) = value(any, false)

  /**
   * Ensures that the string value of the parameter is not markup escaped
   */
  def unescape(v: Any): Unit = this << value(v, false)

  /**
   * Ensures that the string value of the parameter is always markup escaped
   */
  def escape(v: Any): Unit = this << value(v, true)

  def filter(name: String, content: String): String = {
    engine.filters.get(name) match {
      case None => throw new NoSuchFilterException(name)
      case Some(f) => f.filter(content)
    }
  }

  def include(path: String): Unit = include(path, false)

  /**
   * Includes the given template path
   *
   * @param layout if true then applying the layout the included template
   */
  def include(path: String, layout: Boolean): Unit = {
    val uri = resolveUri(path)

    withUri(uri) {
      val template = engine.load(uri)
      if (layout) {
        engine.layout(template, this);
      }
      else {
        template.render(this);
      }
    }
  }

  protected def blankString: String = ""

  /**
   * Renders a collection of model objects with an optional separator<commit_msg>you can now add custom bindings in the layout method.<commit_after>package org.fusesource.scalate

import filter.FilterRequest
import java.util.{Locale, Date}
import java.text.{DateFormat, NumberFormat}
import introspector.Introspector
import collection.mutable.{ListBuffer, HashMap}
import util.{RenderHelper, XmlHelper, Lazy}
import xml.{Node, PCData, NodeSeq, NodeBuffer}

object RenderContext {
  val threadLocal = new ThreadLocal[RenderContext]

  def capture(body: => Unit) = apply().capture(body)

  def captureNodeSeq(body: => Unit) = apply().captureNodeSeq(body)

  def apply(): RenderContext = threadLocal.get

  @deprecated("Can leak thread local storage. Use the 'using' method instead.")
  def update(that: RenderContext) = threadLocal.set(that)
  
  def using[T](that: RenderContext)(func: =>T):T = {
    val previous = threadLocal.get
    try {
      threadLocal.set(that)
      func
    } finally {
      if( previous!=null ) {
        threadLocal.set(previous)
      } else {
        threadLocal.remove
      }
    }
  }

}

/**
 * Provides helper methods for rendering templates and values and for working with attributes.
 *
 * @see DefaultRenderContext
 * @see org.fusesource.scalate.servlet.ServletRenderContext
 */
trait RenderContext {
  /**
   * Default string used to output null values
   */
  var nullString = ""

  /**
   * Default string used to output None values
   */
  var noneString = ""

  /**
   * Whether or not markup sensitive characters for HTML/XML elements like &amp; &gt; &lt; are escaped or not
   */
  var escapeMarkup = true

  var currentTemplate: String = _

  var viewPrefixes = List("")
  var viewPostfixes = engine.codeGenerators.keysIterator.map(x => "." + x).toList

  def engine: TemplateEngine

  /**
   * Renders the provided value and inserts it into the final rendered document without sanitizing the value.
   */
  def <<(value: Any): Unit

  /**
   * Renders the provided value, sanitizes any XML special characters and inserts
   * it into the final rendered document.
   */
  def <<<(value: Any): Unit

  /**
   * Access the attributes available in this context
   */
  def attributes: AttributeMap[String, Any]


  /**
   * Sorted list of attribute keys
   */
  def attributeKeys = attributes.keySet.toList.sortWith(_ < _)

  /**
   * Returns the attribute of the given type or a  { @link NoValueSetException } exception is thrown
   */
  def attribute[T](name: String): T = {
    attributes.get(name)
            .getOrElse(throw new NoValueSetException(name))
            .asInstanceOf[T]
  }

  /**
   * Returns the attribute of the given name and type or the default value if it is not available
   */
  def attributeOrElse[T](name: String, defaultValue: T): T = {
    attributes.get(name)
            .getOrElse(defaultValue)
            .asInstanceOf[T]
  }

  def setAttribute(name: String, value: Option[Any]) {
    value match {
      case Some(v) => attributes(name) = v
      case None => attributes.remove(name)
    }
  }

  /////////////////////////////////////////////////////////////////////
  //
  // Rendering API
  //
  //////////////////////////////////x///////////////////////////////////
  def value(any: Any, shouldSanitize: Boolean = escapeMarkup): String = {
    def sanitize(text: String): String = if (shouldSanitize) {RenderHelper.sanitize(text)} else {text}

    any match {
      case u: Unit => ""
      case null => sanitize(nullString)
      case None => sanitize(noneString)
      case Unescaped(text) => text
      case f: Function0[_] => value(f(), shouldSanitize)
      case v: String => sanitize(v)
      case v: Date => sanitize(dateFormat.format(v))
      case n: Double if n.isNaN => "NaN"
      case n: Float if n.isNaN => "NaN"
      case v: Double => sanitize(numberFormat.format(v))
      case v: Float => sanitize(numberFormat.format(v))
      case v: Number => sanitize(numberFormat.format(v))
      case f: FilterRequest => {
        // NOTE assume a filter does the correct sanitizing
        var rc = filter(f.filter, f.content)
        rc
      }
      // No need to sanitize nodes as they are already sanitized
      case s: NodeBuffer =>
        // No need to sanitize nodes as they are already sanitized
        (s.foldLeft(new StringBuilder) {
          (rc, x) => x match {
            case cd: PCData => rc.append(cd.data)
            case _ => rc.append(x)
          }
        }).toString

      case n: Node => n.toString

      case x: Traversable[Any] =>
        x.map(value(_, shouldSanitize)).mkString("")

      // TODO for any should we use the renderView?
      case v: Any => sanitize(v.toString)
    }
  }

  def valueEscaped(any: Any) = value(any, true)

  def valueUnescaped(any: Any) = value(any, false)

  /**
   * Ensures that the string value of the parameter is not markup escaped
   */
  def unescape(v: Any): Unit = this << value(v, false)

  /**
   * Ensures that the string value of the parameter is always markup escaped
   */
  def escape(v: Any): Unit = this << value(v, true)

  def filter(name: String, content: String): String = {
    engine.filters.get(name) match {
      case None => throw new NoSuchFilterException(name)
      case Some(f) => f.filter(content)
    }
  }

  def include(path: String): Unit = include(path, false)
  def include(path: String, layout: Boolean): Unit = include(path, layout, Nil)

  /**
   * Includes the given template path
   *
   * @param layout if true then applying the layout the included template
   */
  def include(path: String, layout: Boolean, extraBindings: List[Binding]): Unit = {
    val uri = resolveUri(path)

    withUri(uri) {
      val template = engine.load(uri, extraBindings)
      if (layout) {
        engine.layout(template, this);
      }
      else {
        template.render(this);
      }
    }
  }

  protected def blankString: String = ""

  /**
   * Renders a collection of model objects with an optional separator
{'fraction_non_alphanumeric': 0.13322495080003421, 'fraction_numerical': 0.00017113031573543253, 'mean_word_length': 2.55258358662614, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4231451', 'n_tokens_mistral': 3460, 'n_tokens_neox': 3370, 'n_words': 1076}