<commit_before>package org.scalatra

import scala.collection.mutable.{Map => MMap, Set => MSet}
import util.MutableMapWithIndifferentAccess
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}

/**
 * A FlashMap is the data structure used by [[org.scalatra.FlashMapSupport]]
 * to allow passing temporary values between sequential actions.
 *
 * FlashMap behaves like [[org.scalatra.util.MapWithIndifferentAccess]].  By
 * default, anything placed in the map is available to the current request and
 * next request, and is then discarded.
 *
 * @see FlashMapSupport
 */
class FlashMap extends MutableMapWithIndifferentAccess[Any] with Serializable {
  private[this] val m = MMap[String, Any]()
  private[this] val flagged = MSet[String]()

  /**
   * Removes an entry from the flash map.  It is no longer available for this
   * request or the next.
   */
  def -=(key: String) = {
    m -= key
    this
  }

  /**
   * Adds an entry to the flash map.  Clears the sweep flag for the key.
   */
  def +=(kv: (String, Any)) = {
    flagged -= kv._1
    m += kv
    this
  }

  /**
   * Creates a new iterator over the values of the flash map.  These are the
   * values that were added during the last request.
   */
  def iterator = new Iterator[(String, Any)] {
    private[this] val it = m.iterator

    def hasNext = it.hasNext

    def next = {
      val kv = it.next
      flagged += kv._1
      kv
    }
  }

  /**
   * Returns the value associated with a key and flags it to be swept.
   */
  def get(key: String) = {
    flagged += key
    m.get(key)
  }

  /**
   * Removes all flagged entries.
   */
  def sweep() {
    flagged foreach { key => m -= key }
  }

  /**
   * Clears all flags so no entries are removed on the next sweep.
   */
  def keep() {
    flagged.clear()
  }

  /**
   * Clears the flag for the specified key so its entry is not removed on the next sweep.
   */
  def keep(key: String) {
    flagged -= key
  }

  /**
   * Flags all current keys so the entire map is cleared on the next sweep.
   */
  def flag() {
    flagged ++= m.keys
  }

  /**
   * Sets a value for the current request only.  It will be removed before the next request unless explicitly kept.
   * Data put in this object is availble as usual:
   * {{{
   * flash.now("notice") = "logged in succesfully"
   * flash("notice") // "logged in succesfully"
   * }}}
   */
  object now {
    def update(key: String, value: Any) =  {
      flagged += key
      m += key -> value
    }
  }
}

object FlashMapSupport {
  val sessionKey = FlashMapSupport.getClass.getName+".flashMap"
  val lockKey = FlashMapSupport.getClass.getName+".lock"
  val FlashMapKey = "org.scalatra.FlashMap"
}

/**
 * Allows an action to set key-value pairs in a transient state that is accessible only to the next action and is expired immediately after that.
 * This is especially useful when using the POST-REDIRECT-GET pattern to trace the result of an operation.
 * {{{
 * post("/article/create") {
 *   // create session
 *   flash("notice") = "article created succesfully"
 *   redirect("/home")
 * }
 * get("/home") {
 *   // this will access the value set in previous action
 *   stuff_with(flash("notice"))
 * }
 * }}}
 * @see FlashMap
 */
trait FlashMapSupport extends Handler {
  this: CoreDsl with DynamicScope with SessionSupport =>

  import FlashMapSupport._

  abstract override def handle(req: HttpServletRequest, res: HttpServletResponse) {
    withRequest(req) {
      val f = getFlash(req)
      val isOutermost = !req.contains(lockKey)
      if (isOutermost) {
        req(lockKey) = "locked"
        if (sweepUnusedFlashEntries(req)) {
          f.flag()
        }
      }
      session(sessionKey) = f
      super.handle(req, res)
      /*
       * http://github.com/scalatra/scalatra/issues/41
       * http://github.com/scalatra/scalatra/issues/57
       *
       * Only the outermost FlashMapSupport sweeps it at the end.  
       * This deals with both nested filters and redirects to other servlets.
       */
      if (isOutermost) {
        f.sweep()
      }
    }
  }

  private def getFlash(req: HttpServletRequest): FlashMap =
    session.get(sessionKey).map { _.asInstanceOf[FlashMap] }
      .getOrElse(new FlashMap)
<commit_msg>fixes #223 use a request variable to store the flashmap with the session as a backing for that var<commit_after>package org.scalatra

import scala.collection.mutable.{Map => MMap, Set => MSet}
import util.MutableMapWithIndifferentAccess
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}

/**
 * A FlashMap is the data structure used by [[org.scalatra.FlashMapSupport]]
 * to allow passing temporary values between sequential actions.
 *
 * FlashMap behaves like [[org.scalatra.util.MapWithIndifferentAccess]].  By
 * default, anything placed in the map is available to the current request and
 * next request, and is then discarded.
 *
 * @see FlashMapSupport
 */
class FlashMap extends MutableMapWithIndifferentAccess[Any] with Serializable {
  private[this] val m = MMap[String, Any]()
  private[this] val flagged = MSet[String]()

  /**
   * Removes an entry from the flash map.  It is no longer available for this
   * request or the next.
   */
  def -=(key: String) = {
    m -= key
    this
  }

  /**
   * Adds an entry to the flash map.  Clears the sweep flag for the key.
   */
  def +=(kv: (String, Any)) = {
    flagged -= kv._1
    m += kv
    this
  }

  /**
   * Creates a new iterator over the values of the flash map.  These are the
   * values that were added during the last request.
   */
  def iterator = new Iterator[(String, Any)] {
    private[this] val it = m.iterator

    def hasNext = it.hasNext

    def next = {
      val kv = it.next
      flagged += kv._1
      kv
    }
  }

  /**
   * Returns the value associated with a key and flags it to be swept.
   */
  def get(key: String) = {
    flagged += key
    m.get(key)
  }

  /**
   * Removes all flagged entries.
   */
  def sweep() {
    flagged foreach { key => m -= key }
  }

  /**
   * Clears all flags so no entries are removed on the next sweep.
   */
  def keep() {
    flagged.clear()
  }

  /**
   * Clears the flag for the specified key so its entry is not removed on the next sweep.
   */
  def keep(key: String) {
    flagged -= key
  }

  /**
   * Flags all current keys so the entire map is cleared on the next sweep.
   */
  def flag() {
    flagged ++= m.keys
  }

  /**
   * Sets a value for the current request only.  It will be removed before the next request unless explicitly kept.
   * Data put in this object is availble as usual:
   * {{{
   * flash.now("notice") = "logged in succesfully"
   * flash("notice") // "logged in succesfully"
   * }}}
   */
  object now {
    def update(key: String, value: Any) =  {
      flagged += key
      m += key -> value
    }
  }
}

object FlashMapSupport {
  val sessionKey = FlashMapSupport.getClass.getName+".flashMap"
  val lockKey = FlashMapSupport.getClass.getName+".lock"
  val FlashMapKey = "org.scalatra.FlashMap"
}

/**
 * Allows an action to set key-value pairs in a transient state that is accessible only to the next action and is expired immediately after that.
 * This is especially useful when using the POST-REDIRECT-GET pattern to trace the result of an operation.
 * {{{
 * post("/article/create") {
 *   // create session
 *   flash("notice") = "article created succesfully"
 *   redirect("/home")
 * }
 * get("/home") {
 *   // this will access the value set in previous action
 *   stuff_with(flash("notice"))
 * }
 * }}}
 * @see FlashMap
 */
trait FlashMapSupport extends Handler {
  this: CoreDsl with DynamicScope with SessionSupport =>

  import FlashMapSupport._

  abstract override def handle(req: HttpServletRequest, res: HttpServletResponse) {
    withRequest(req) {
      val f = getFlash(req)
      val isOutermost = !req.contains(lockKey)
      if (isOutermost) {
        req(lockKey) = "locked"
        if (sweepUnusedFlashEntries(req)) {
          f.flag()
        }
      }
      super.handle(req, res)
      /*
       * http://github.com/scalatra/scalatra/issues/41
       * http://github.com/scalatra/scalatra/issues/57
       *
       * Only the outermost FlashMapSupport sweeps it at the end.  
       * This deals with both nested filters and redirects to other servlets.
       */
      if (isOutermost) {
        f.sweep()
      }
      flashMapSetSession(f)
    }
  }
  
  /**
   * Override to implement custom session retriever, or sanity checks if session is still active
   * @param f
   */
  def flashMapSetSession(f: FlashMap) {
    try {
      // Save flashMap to Session after (a session could stop existing during a request, so catch exception)
      session(sessionKey) = f
    } catch {
      case e: Throwable =>
    }
  }

  private[this] def getFlash(req: HttpServletRequest): FlashMap =
    req.get(sessionKey).map(_.asInstanceOf[FlashMap]).getOrElse {
      val map = session.get(sessionKey).map {
        _.asInstanceOf[FlashMap]
      }.getOrElse(new FlashMap)

      req.setAttribute(sessionKey, map)
      map
    }

{'fraction_non_alphanumeric': 0.10755526228967338, 'fraction_numerical': 0.001649620587264929, 'mean_word_length': 2.6259968102073366, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '6263535', 'n_tokens_mistral': 2828, 'n_tokens_neox': 2757, 'n_words': 937}