package net.liftweb.util
/*
* Copyright 2007-2008 WorldWide Conferencing, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
*/
/**
* Companion object for FatLaxy.
*/
object FatLazy {
/**
* Create a new FatLazy.
*/
def apply[T](f: => T) = new FatLazy(f)
// implicit def fromLazy[T](in: Lazy[T]): T = in.get
}
/**
* A class that does lazy evaluation
*
* @param f -- a function that evaluates to the default value of the instance
*/
class FatLazy[T](f: => T) {
private var value: Box[T] = Empty
/**
* Get the value of the instance. If it's not yet been set, call f to calculate it
*
* @return the value of the instance
*/
def get: T = synchronized {
value match {
case Full(v) => v
case _ => value = Full(f)
value.open_!
}
}
/**
* Test whether the value of this class has been set or initialized from the default.
*/
def defined_? = synchronized {
value != None
}
/**
* Set the instance to a new value and return that value
*
* @param v - the new value of the instance
*
* @return v
*/
def set(v: T): T = synchronized {
value = Full(v)
v
}
/**
* Copy the value of the specified FatLazy into this FatLazy
*/
def setFrom(other: FatLazy[T]): Unit = synchronized {
value = other.value
}
/**
* and the lazy() = foo style of assignment
*/
def update(v: T): Unit = set(v)
/**
* Reset the value of this FatLazy to the default (which will be lazily determined
* on retrieval.)
*/
def reset = synchronized {value = Empty}
/**
* Determine whether the value of this FatLazy has been determined.
*/
def calculated_? = synchronized {value.isDefined}
// implicit def fromLazy[T](in: Lazy[T]): T = in.get
}
/**
* Sometimes, you want to do pattern matching against a lazy value. Why?
* Because, there may be parts of the pattern that must be evaluated first
* and if they evaluate successfully, you then want to test another part of
* the pattern. Thus, the LZ pattern match.
*/
object LZ {
def apply[T](f: => T): LZ[T] = new LZ(f)
def unapply[T](in: LZ[T]): Option[T] = Some(in.get)
// implicit def lazyToT[T](in: LazyMatcher[T]): T = in.get
}
/**
* LZ encapsulates a lazy value.
*
* @param f - a value to be evaluated lazily
*/
class LZ[T](f: => T) {
lazy val get = f
override def toString = "LZ("+get+")"
}
object ThreadLazy {
def apply[T](f: => T) = new ThreadLazy(f)
implicit def what[T](in: ThreadLazy[T]): T = in.get
}
/**
* A thread-local lazy value that provides a means to evaluate
* a function in a lazily-evaluated scope.
*
* @param theFunc the lazily-evaluated expression for which to
* cache the result in thread-local scope.
*/
class ThreadLazy[TheType](theFunc: => TheType) extends LoanWrapper {
private val calced = new ThreadGlobal[Boolean]
private val value = new ThreadGlobal[TheType]
/**
* Save the current cached lazy value, if any, evaluate the specified
* function and then restore the previous value to the cache. The effect
* of this function is to essentially perform a reset of this lazy value
* to being unevaluated prior to function evaluation.
*/
def apply[T](f: => T): T = {
val old = value.value
calced.set(false)
try {
f
} finally {
calced.set(false)
value.set(old)
}
}
/**
* Reset the lazy value so that it will be recalculated from the default expression
* on the next retrieval.
*/
def reset(): Unit = calced.set(false)
/**
* Return the value, evaluating the default expression if necessary.
*/
def get: TheType = {
if (calced.value) value.value
else {
value.set(theFunc)
calced.set(true)
value.value
}
}
}