2010-06-14 04:59:29 +02:00
|
|
|
/* sbt -- Simple Build Tool
|
|
|
|
|
* Copyright 2009 Mark Harrah
|
|
|
|
|
*/
|
|
|
|
|
package sbt
|
2009-08-16 20:29:08 +02:00
|
|
|
|
2010-10-27 00:02:27 +02:00
|
|
|
import sbinary.{CollectionTypes, DefaultProtocol, Format, Input, JavaFormats, Output}
|
2009-08-16 20:29:08 +02:00
|
|
|
import java.io.File
|
2010-10-27 00:02:27 +02:00
|
|
|
import java.net.{URI, URL}
|
2010-06-14 04:59:29 +02:00
|
|
|
import Types.:+:
|
2010-10-27 00:02:27 +02:00
|
|
|
import DefaultProtocol.{asProduct2, asSingleton, BooleanFormat, ByteFormat, IntFormat, wrap}
|
|
|
|
|
import scala.xml.NodeSeq
|
2009-08-16 20:29:08 +02:00
|
|
|
|
|
|
|
|
trait Cache[I,O]
|
|
|
|
|
{
|
|
|
|
|
def apply(file: File)(i: I): Either[O, O => Unit]
|
|
|
|
|
}
|
2010-10-27 00:02:27 +02:00
|
|
|
trait SBinaryFormats extends CollectionTypes with JavaFormats
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2010-10-27 00:02:27 +02:00
|
|
|
implicit def urlFormat: Format[URL] = DefaultProtocol.UrlFormat
|
|
|
|
|
implicit def uriFormat: Format[URI] = DefaultProtocol.UriFormat
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
2010-10-27 00:02:27 +02:00
|
|
|
object Cache extends CacheImplicits
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
def cache[I,O](implicit c: Cache[I,O]): Cache[I,O] = c
|
|
|
|
|
|
2009-12-12 00:56:09 +01:00
|
|
|
def cached[I,O](file: File)(f: I => O)(implicit cache: Cache[I,O]): I => O =
|
|
|
|
|
in =>
|
|
|
|
|
cache(file)(in) match
|
|
|
|
|
{
|
|
|
|
|
case Left(value) => value
|
|
|
|
|
case Right(store) =>
|
|
|
|
|
val out = f(in)
|
|
|
|
|
store(out)
|
|
|
|
|
out
|
|
|
|
|
}
|
2010-10-27 00:02:27 +02:00
|
|
|
|
|
|
|
|
def debug[I](label: String, c: InputCache[I]): InputCache[I] =
|
|
|
|
|
new InputCache[I]
|
|
|
|
|
{
|
|
|
|
|
type Internal = c.Internal
|
|
|
|
|
def convert(i: I) = c.convert(i)
|
|
|
|
|
def read(from: Input) =
|
|
|
|
|
{
|
|
|
|
|
val v = c.read(from)
|
|
|
|
|
println(label + ".read: " + v)
|
|
|
|
|
v
|
|
|
|
|
}
|
|
|
|
|
def write(to: Output, v: Internal)
|
|
|
|
|
{
|
|
|
|
|
println(label + ".write: " + v)
|
|
|
|
|
c.write(to, v)
|
|
|
|
|
}
|
|
|
|
|
def equiv: Equiv[Internal] = new Equiv[Internal] {
|
|
|
|
|
def equiv(a: Internal, b: Internal)=
|
|
|
|
|
{
|
|
|
|
|
val equ = c.equiv.equiv(a,b)
|
|
|
|
|
println(label + ".equiv(" + a + ", " + b +"): " + equ)
|
|
|
|
|
equ
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
2010-10-27 00:02:27 +02:00
|
|
|
trait CacheImplicits extends BasicCacheImplicits with SBinaryFormats with HListCacheImplicits with UnionImplicits
|
|
|
|
|
trait BasicCacheImplicits
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2010-10-27 00:02:27 +02:00
|
|
|
implicit def basicCache[I, O](implicit in: InputCache[I], outFormat: Format[O]): Cache[I,O] =
|
|
|
|
|
new BasicCache()(in, outFormat)
|
|
|
|
|
def basicInput[I](implicit eq: Equiv[I], fmt: Format[I]): InputCache[I] = InputCache.basicInputCache(fmt, eq)
|
|
|
|
|
|
|
|
|
|
def defaultEquiv[T]: Equiv[T] = new Equiv[T] { def equiv(a: T, b: T) = a == b }
|
|
|
|
|
|
|
|
|
|
implicit def optInputCache[T](implicit t: InputCache[T]): InputCache[Option[T]] =
|
|
|
|
|
new InputCache[Option[T]]
|
|
|
|
|
{
|
|
|
|
|
type Internal = Option[t.Internal]
|
|
|
|
|
def convert(v: Option[T]): Internal = v.map(x => t.convert(x))
|
|
|
|
|
def read(from: Input) =
|
|
|
|
|
{
|
|
|
|
|
val isDefined = BooleanFormat.reads(from)
|
|
|
|
|
if(isDefined) Some(t.read(from)) else None
|
|
|
|
|
}
|
|
|
|
|
def write(to: Output, j: Internal): Unit =
|
|
|
|
|
{
|
|
|
|
|
BooleanFormat.writes(to, j.isDefined)
|
|
|
|
|
j foreach { x => t.write(to, x) }
|
|
|
|
|
}
|
|
|
|
|
def equiv = optEquiv(t.equiv)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def wrapEquiv[S,T](f: S => T)(implicit eqT: Equiv[T]): Equiv[S] =
|
|
|
|
|
new Equiv[S] {
|
|
|
|
|
def equiv(a: S, b: S) =
|
|
|
|
|
eqT.equiv( f(a), f(b) )
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
implicit def optEquiv[T](implicit t: Equiv[T]): Equiv[Option[T]] =
|
|
|
|
|
new Equiv[Option[T]] {
|
|
|
|
|
def equiv(a: Option[T], b: Option[T]) =
|
|
|
|
|
(a,b) match
|
|
|
|
|
{
|
|
|
|
|
case (None, None) => true
|
|
|
|
|
case (Some(va), Some(vb)) => t.equiv(va, vb)
|
|
|
|
|
case _ => false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
implicit def urlEquiv(implicit uriEq: Equiv[URI]): Equiv[URL] = wrapEquiv[URL, URI](_.toURI)(uriEq)
|
|
|
|
|
implicit def uriEquiv: Equiv[URI] = defaultEquiv
|
|
|
|
|
implicit def stringSetEquiv: Equiv[Set[String]] = defaultEquiv
|
|
|
|
|
implicit def stringMapEquiv: Equiv[Map[String, String]] = defaultEquiv
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
implicit def xmlInputCache(implicit strEq: InputCache[String]): InputCache[NodeSeq] = wrapIn[NodeSeq, String](_.toString, strEq)
|
|
|
|
|
|
|
|
|
|
implicit def seqCache[T](implicit t: InputCache[T]): InputCache[Seq[T]] =
|
|
|
|
|
new InputCache[Seq[T]]
|
|
|
|
|
{
|
|
|
|
|
type Internal = Seq[t.Internal]
|
|
|
|
|
def convert(v: Seq[T]) = v.map(x => t.convert(x))
|
|
|
|
|
def read(from: Input) =
|
|
|
|
|
{
|
|
|
|
|
val size = IntFormat.reads(from)
|
|
|
|
|
def next(left: Int, acc: List[t.Internal]): Internal =
|
|
|
|
|
if(left <= 0) acc.reverse else next(left - 1, t.read(from) :: acc)
|
|
|
|
|
next(size, Nil)
|
|
|
|
|
}
|
|
|
|
|
def write(to: Output, vs: Internal)
|
|
|
|
|
{
|
|
|
|
|
val size = vs.length
|
|
|
|
|
IntFormat.writes(to, size)
|
|
|
|
|
for(v <- vs) t.write(to, v)
|
|
|
|
|
}
|
|
|
|
|
def equiv: Equiv[Internal] = seqEquiv(t.equiv)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
implicit def arrEquiv[T](implicit t: Equiv[T]): Equiv[Array[T]] =
|
|
|
|
|
wrapEquiv( (x: Array[T]) => x :Seq[T] )(seqEquiv[T](t))
|
|
|
|
|
|
|
|
|
|
implicit def seqEquiv[T](implicit t: Equiv[T]): Equiv[Seq[T]] =
|
|
|
|
|
new Equiv[Seq[T]]
|
|
|
|
|
{
|
|
|
|
|
def equiv(a: Seq[T], b: Seq[T]) =
|
|
|
|
|
a.length == b.length &&
|
|
|
|
|
((a,b).zipped forall t.equiv)
|
|
|
|
|
}
|
|
|
|
|
implicit def seqFormat[T](implicit t: Format[T]): Format[Seq[T]] =
|
|
|
|
|
wrap[Seq[T], List[T]](_.toList, _.toSeq)(DefaultProtocol.listFormat)
|
|
|
|
|
|
|
|
|
|
def wrapIn[I,J](implicit f: I => J, jCache: InputCache[J]): InputCache[I] =
|
|
|
|
|
new InputCache[I]
|
|
|
|
|
{
|
|
|
|
|
type Internal = jCache.Internal
|
|
|
|
|
def convert(i: I) = jCache.convert(f(i))
|
|
|
|
|
def read(from: Input) = jCache.read(from)
|
|
|
|
|
def write(to: Output, j: Internal) = jCache.write(to, j)
|
|
|
|
|
def equiv = jCache.equiv
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def singleton[T](t: T): InputCache[T] =
|
|
|
|
|
basicInput(trueEquiv, asSingleton(t))
|
|
|
|
|
|
|
|
|
|
def trueEquiv[T] = new Equiv[T] { def equiv(a: T, b: T) = true }
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
2010-10-27 00:02:27 +02:00
|
|
|
|
2010-06-14 04:59:29 +02:00
|
|
|
trait HListCacheImplicits
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2010-10-27 00:02:27 +02:00
|
|
|
implicit def hConsCache[H, T <: HList](implicit head: InputCache[H], tail: InputCache[T]): InputCache[H :+: T] =
|
|
|
|
|
new InputCache[H :+: T]
|
|
|
|
|
{
|
|
|
|
|
type Internal = (head.Internal, tail.Internal)
|
|
|
|
|
def convert(in: H :+: T) = (head.convert(in.head), tail.convert(in.tail))
|
|
|
|
|
def read(from: Input) =
|
|
|
|
|
{
|
|
|
|
|
val h = head.read(from)
|
|
|
|
|
val t = tail.read(from)
|
|
|
|
|
(h, t)
|
|
|
|
|
}
|
|
|
|
|
def write(to: Output, j: Internal)
|
|
|
|
|
{
|
|
|
|
|
head.write(to, j._1)
|
|
|
|
|
tail.write(to, j._2)
|
|
|
|
|
}
|
|
|
|
|
def equiv = new Equiv[Internal]
|
|
|
|
|
{
|
|
|
|
|
def equiv(a: Internal, b: Internal) =
|
|
|
|
|
head.equiv.equiv(a._1, b._1) &&
|
|
|
|
|
tail.equiv.equiv(a._2, b._2)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
implicit def hNilCache: InputCache[HNil] = Cache.singleton(HNil : HNil)
|
2010-11-10 02:43:58 +01:00
|
|
|
|
|
|
|
|
implicit def hConsFormat[H, T <: HList](implicit head: Format[H], tail: Format[T]): Format[H :+: T] = new Format[H :+: T] {
|
|
|
|
|
def reads(from: Input) =
|
|
|
|
|
{
|
|
|
|
|
val h = head.reads(from)
|
|
|
|
|
val t = tail.reads(from)
|
|
|
|
|
HCons(h, t)
|
|
|
|
|
}
|
|
|
|
|
def writes(to: Output, hc: H :+: T)
|
|
|
|
|
{
|
|
|
|
|
head.writes(to, hc.head)
|
|
|
|
|
tail.writes(to, hc.tail)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
implicit def hNilFormat: Format[HNil] = asSingleton(HNil)
|
2010-06-07 16:50:51 +02:00
|
|
|
}
|
2010-10-27 00:02:27 +02:00
|
|
|
trait UnionImplicits
|
|
|
|
|
{
|
|
|
|
|
def unionInputCache[UB, HL <: HList](implicit uc: UnionCache[HL, UB]): InputCache[UB] =
|
|
|
|
|
new InputCache[UB]
|
|
|
|
|
{
|
|
|
|
|
type Internal = Found[_]
|
|
|
|
|
def convert(in: UB) = uc.find(in)
|
|
|
|
|
def read(in: Input) =
|
|
|
|
|
{
|
|
|
|
|
val index = ByteFormat.reads(in)
|
|
|
|
|
val (cache, clazz) = uc.at(index)
|
|
|
|
|
val value = cache.read(in)
|
|
|
|
|
new Found[cache.Internal](cache, clazz, value, index)
|
|
|
|
|
}
|
|
|
|
|
def write(to: Output, i: Internal)
|
|
|
|
|
{
|
|
|
|
|
def write0[I](f: Found[I])
|
|
|
|
|
{
|
|
|
|
|
ByteFormat.writes(to, f.index.toByte)
|
|
|
|
|
f.cache.write(to, f.value)
|
|
|
|
|
}
|
|
|
|
|
write0(i)
|
|
|
|
|
}
|
|
|
|
|
def equiv: Equiv[Internal] = new Equiv[Internal]
|
|
|
|
|
{
|
|
|
|
|
def equiv(a: Internal, b: Internal) =
|
|
|
|
|
{
|
|
|
|
|
if(a.clazz == b.clazz)
|
|
|
|
|
force(a.cache.equiv, a.value, b.value)
|
|
|
|
|
else
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
def force[T <: UB, UB](e: Equiv[T], a: UB, b: UB) = e.equiv(a.asInstanceOf[T], b.asInstanceOf[T])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
implicit def unionCons[H <: UB, UB, T <: HList](implicit head: InputCache[H], mf: Manifest[H], t: UnionCache[T, UB]): UnionCache[H :+: T, UB] =
|
|
|
|
|
new UnionCache[H :+: T, UB]
|
|
|
|
|
{
|
|
|
|
|
val size = 1 + t.size
|
|
|
|
|
def c = mf.erasure
|
|
|
|
|
def find(value: UB): Found[_] =
|
|
|
|
|
if(c.isInstance(value)) new Found[head.Internal](head, c, head.convert(value.asInstanceOf[H]), size - 1) else t.find(value)
|
|
|
|
|
def at(i: Int): (InputCache[_ <: UB], Class[_]) = if(size == i + 1) (head, c) else t.at(i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
implicit def unionNil[UB]: UnionCache[HNil, UB] = new UnionCache[HNil, UB] {
|
|
|
|
|
def size = 0
|
|
|
|
|
def find(value: UB) = error("No valid sum type for " + value)
|
|
|
|
|
def at(i: Int) = error("Invalid union index " + i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final class Found[I](val cache: InputCache[_] { type Internal = I }, val clazz: Class[_], val value: I, val index: Int)
|
|
|
|
|
sealed trait UnionCache[HL <: HList, UB]
|
|
|
|
|
{
|
|
|
|
|
def size: Int
|
|
|
|
|
def at(i: Int): (InputCache[_ <: UB], Class[_])
|
|
|
|
|
def find(forValue: UB): Found[_]
|
|
|
|
|
}
|
|
|
|
|
}
|