pax_global_header00006660000000000000000000000064125321742430014515gustar00rootroot0000000000000052 comment=b86c887384de0906754a7971286e0c1e294badce serialization-0.1.2/000077500000000000000000000000001253217424300143725ustar00rootroot00000000000000serialization-0.1.2/.gitignore000066400000000000000000000004001253217424300163540ustar00rootroot00000000000000*.iml *.lock *.komodoproject .DS_Store .history .idea .classpath .cache .project/ .project .idea/ .idea_modules/ .settings/ .target/ project/boot/ workspace/ repository/ target/ logs/ .settings .classpath .project .cache bin/ .sbtserver project/.sbtserver serialization-0.1.2/.travis.yml000066400000000000000000000016441253217424300165100ustar00rootroot00000000000000# Use Docker-based container (instead of OpenVZ) sudo: false cache: directories: - $HOME/.ivy2/cache # At the moment, sbt 0.13.5 is preinstalled in Travis VM image, # which fortunately corresponds to current scalaz settings. # The line below can be used to cache a given sbt version. # - $HOME/.sbt/launchers/0.13.x # The line below is used to cache the scala version used by the build # job, as these versions might be replaced after a Travis CI build # environment upgrade (e.g. scala 2.11.2 could be replaced by scala 2.11.4). - $HOME/.sbt/boot/scala-$TRAVIS_SCALA_VERSION language: scala scala: - 2.10.3 jdk: - openjdk6 - openjdk7 - oraclejdk7 notifications: email: - qbranch@typesafe.com script: - sbt clean test # Tricks to avoid unnecessary cache updates - find $HOME/.sbt -name "*.lock" | xargs rm - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm serialization-0.1.2/LICENSE000066400000000000000000000012201253217424300153720ustar00rootroot00000000000000This software is licensed under the Apache 2 license, quoted below. Copyright 2012-2013 Typesafe Inc. [http://www.typesafe.com] 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. serialization-0.1.2/README.markdown000066400000000000000000000004161253217424300170740ustar00rootroot00000000000000sbt/serialization ================= sbt serialization is an opinionated wrapper around [Scala pickling][pickling] focused on sbt's usage. In particular it provides: - JSON format that's nice - static-only core picklers [pickling]: https://github.com/scala/pickling serialization-0.1.2/build.sbt000066400000000000000000000034361253217424300162110ustar00rootroot00000000000000import Dependencies._ import com.typesafe.sbt.SbtGit._ lazy val commonSettings = Seq( git.baseVersion := "0.1.2", scalaVersion := scala210Version, crossScalaVersions := Seq(scala210Version, scala211Version), libraryDependencies ++= Seq(junitInterface % Test, scalaCheck % Test), bintrayOrganization := Some("sbt"), bintrayRepository := "maven-releases", scalacOptions <<= (scalaVersion) map { sv => Seq("-unchecked", "-deprecation", "-Xmax-classfile-name", "72") ++ { if (sv.startsWith("2.9")) Seq.empty else Seq("-feature") } }, javacOptions in Compile := Seq("-target", "1.6", "-source", "1.6"), javacOptions in (Compile, doc) := Seq("-source", "1.6") ) lazy val root = (project in file(".")). aggregate(serialization). settings( inThisBuild(Seq( organization := "org.scala-sbt", homepage := Some(url("https://github.com/sbt/serialization")), licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")), scmInfo := Some(ScmInfo(url("https://github.com/sbt/serialization"), "git@github.com:sbt/serialization.git")), developers := List( Developer("havocp", "Havoc Pennington", "@havocp", url("https://github.com/havocp")), Developer("eed3si9n", "Eugene Yokota", "@eed3si9n", url("https://github.com/eed3si9n")), Developer("jsuereth", "Josh Suereth", "@jsuereth", url("https://github.com/jsuereth")) ), bintrayReleaseOnPublish := false )), commonSettings, publishArtifact := false, publish := {}, publishLocal := {} ) lazy val serialization = (project in file("serialization")). settings(commonSettings: _*). settings( parallelExecution in Test := false, libraryDependencies ++= Seq( pickling, junitInterface % Test ) ++ jsonDependencies ) serialization-0.1.2/project/000077500000000000000000000000001253217424300160405ustar00rootroot00000000000000serialization-0.1.2/project/Dependencies.scala000066400000000000000000000030031253217424300214270ustar00rootroot00000000000000import sbt._ import Keys._ import com.typesafe.sbt.SbtScalariform.ScalariformKeys object Dependencies { // Here are the versions used for the core project val scala210Version = "2.10.4" val scala211Version = "2.11.5" val picklingVersion = "0.10.1" val pickling210 = "org.scala-lang.modules" % "scala-pickling_2.10" % picklingVersion val pickling211 = "org.scala-lang.modules" % "scala-pickling_2.11" % picklingVersion val pickling = "org.scala-lang.modules" %% "scala-pickling" % picklingVersion private val jsonTuples = Seq( ("org.json4s", "json4s-core", "3.2.10"), ("org.spire-math", "jawn-parser", "0.6.0"), ("org.spire-math", "json4s-support", "0.6.0") ) val jsonDependencies = jsonTuples map { case (group, mod, version) => (group %% mod % version).exclude("org.scala-lang", "scalap") } val jsonDependencies210 = jsonTuples map { case (group, mod, version) => group % s"${mod}_2.10" % version } val jsonDependencies211 = jsonTuples map { case (group, mod, version) => group % s"${mod}_2.11" % version } val mimeUtil = "eu.medsea.mimeutil" % "mime-util" % "2.1.1" // need to manually set this to override an incompatible old version val slf4jLog4j = "org.slf4j" % "slf4j-log4j12" % "1.6.6" val scalaCheckVersion = "1.11.5" val junitInterface = "com.novocode" % "junit-interface" % "0.11" val scalaCheck = "org.scalacheck" %% "scalacheck" % scalaCheckVersion val specs2 = "org.specs2" %% "specs2" % "2.3.11" } serialization-0.1.2/project/bintray.sbt000066400000000000000000000000641253217424300202220ustar00rootroot00000000000000addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") serialization-0.1.2/project/build.properties000066400000000000000000000000431253217424300212520ustar00rootroot00000000000000sbt.version=0.13.9-20150529-062038 serialization-0.1.2/project/houserules.sbt000066400000000000000000000000731253217424300207500ustar00rootroot00000000000000addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.1.0") serialization-0.1.2/project/nightlies.sbt000066400000000000000000000002021253217424300205320ustar00rootroot00000000000000resolvers += Resolver.url("bintray-sbt-ivy-releases", url("https://dl.bintray.com/sbt/ivy-snapshots"))(Resolver.ivyStylePatterns) serialization-0.1.2/serialization/000077500000000000000000000000001253217424300172475ustar00rootroot00000000000000serialization-0.1.2/serialization/src/000077500000000000000000000000001253217424300200365ustar00rootroot00000000000000serialization-0.1.2/serialization/src/main/000077500000000000000000000000001253217424300207625ustar00rootroot00000000000000serialization-0.1.2/serialization/src/main/scala/000077500000000000000000000000001253217424300220455ustar00rootroot00000000000000serialization-0.1.2/serialization/src/main/scala/sbt/000077500000000000000000000000001253217424300226355ustar00rootroot00000000000000serialization-0.1.2/serialization/src/main/scala/sbt/serialization/000077500000000000000000000000001253217424300255125ustar00rootroot00000000000000serialization-0.1.2/serialization/src/main/scala/sbt/serialization/CanToString.scala000066400000000000000000000026171253217424300307200ustar00rootroot00000000000000package sbt.serialization import java.io.File import java.net.URI // import xsbti.Severity // import xsbti.Severity.{ Info, Warn, Error } /** * This trait represents a type that can be converted to/from a human readable string. The conversion MUST be * bi-directional. * * Note: There are Pickler helper methods to construct picklers which use this human-readable string when pickling * rather than a more-binary-encoding of these types. Ideal for messages which are sent/used in JSON. * * @tparam A The type that can be human readable. */ trait CanToString[A] { def toString(a: A): String def fromString(s: String): A } /** * This contains default mechanisms to create CanToString and implciits for supported types: File, URI, TypeExpression. */ object CanToString { /** * Construct a new CanToString instance using the given conversion operations. * * NOTE: The following must hold: fs(ts(x)) == x * * @param ts A function which can turn the type into a human readable string. * @param fs A function which can take the human readable string and turn it back into an instance of the same type. * @tparam A The type we can conver. * @return A new CanToString bidriectional conversion. */ def apply[A](ts: A => String, fs: String => A): CanToString[A] = new CanToString[A] { def toString(a: A): String = ts(a) def fromString(s: String): A = fs(s) } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/CustomPicklerUnpickler.scala000066400000000000000000000016271253217424300331660ustar00rootroot00000000000000package sbt.serialization // TODO - Why are type aliases not workign? import scala.pickling.pickler.{ PrimitivePicklers, PrimitiveArrayPicklers, RefPicklers, DatePicklers } import sbt.serialization.pickler.{ OptionPicklers, ThrowablePicklers, VectorPicklers, ListPicklers, ArrayPicklers, SeqPicklers, MapPicklers, StringMapPicklers, Tuple2Picklers, JavaExtraPicklers, TypeExpressionPicklers, SerializationPicklers } trait CustomPicklers extends PrimitivePicklers with DatePicklers with PrimitiveArrayPicklers with OptionPicklers with ThrowablePicklers with JavaExtraPicklers with TypeExpressionPicklers with Tuple2Picklers with RefPicklers with LowPriorityCustomPicklers with SerializationPicklers {} trait LowPriorityCustomPicklers extends VectorPicklers with ListPicklers with ArrayPicklers with SeqPicklers with MapPicklers with StringMapPicklers {} serialization-0.1.2/serialization/src/main/scala/sbt/serialization/DebugPickleFormat.scala000066400000000000000000000052031253217424300320460ustar00rootroot00000000000000package sbt.serialization import scala.pickling.{ Pickle, PickleFormat, FastTypeTag, PBuilder, PReader, PicklingException, Output } import scala.reflect.runtime.universe.Mirror import scala.util.{ Failure, Success } // Note: This debug format should move into scala pickling. private[serialization] object EmptyPickle extends Pickle { type ValueType = Unit val value: ValueType = () } private[serialization] class DebugPickleFormat extends PickleFormat { type PickleType = EmptyPickle.type type OutputType = Output[String] def createBuilder() = new DebugPickleBuilder() def createBuilder(out: Output[String]): PBuilder = new DebugPickleBuilder() override def createReader(pickle: PickleType) = ??? } private[serialization] class DebugPickleBuilder(indent: Int = 0) extends PBuilder { private val indentString = (0 to indent) map (_ => " ") mkString "" private def println(s: String): Unit = System.err.println(s"$indentString$s") private def nextLevelBuilder = new DebugPickleBuilder(indent + 1) override def beginEntry(picklee: Any): PBuilder = { println(s"beginEntry($picklee)") this } override def result(): Pickle = { println("result()") EmptyPickle } override def putElement(pickler: (PBuilder) => Unit): PBuilder = { println(s"putElement($pickler)") pickler(nextLevelBuilder) this } override def beginCollection(length: Int): PBuilder = { println(s"beginCollection($length)") this } override def endEntry(): Unit = { println("endEntry()") } override def endCollection(): Unit = { println(s"endCollection()") } override def putField(name: String, pickler: (PBuilder) => Unit): PBuilder = { println(s"putField($name, $pickler)") pickler(nextLevelBuilder) this } override def hintKnownSize(knownSize: Int): this.type = { println(s"hintKnownSize($knownSize") this } override def popHints(): this.type = { println(s"popHints()") this } override def pushHints(): this.type = { println(s"pushHints()") this } override def hintStaticallyElidedType(): this.type = { println(s"hintStaticallyElidedType()") this } override def hintOid(id: Int): this.type = { println(s"hintOid($id)") this } override def pinHints(): this.type = { println(s"pinHints()") this } override def hintTag(tag: FastTypeTag[_]): this.type = { println(s"hintTag($tag)") this } override def hintDynamicallyElidedType(): this.type = { System.err.println(s"hintDynamicallyElidedType()") this } override def unpinHints(): this.type = { System.err.println(s"unpinHints()") this } }serialization-0.1.2/serialization/src/main/scala/sbt/serialization/ManifestUtil.scala000066400000000000000000000055041253217424300311270ustar00rootroot00000000000000package sbt.serialization import sbt.serialization.ScalaShims.ManifestFactory import scala.pickling.FastTypeTag /** * Utilities used in PrimitivePicklers/SeqPicklers. * * TODO - Remove this once we clean up scala/pickling default picklers (we hope). * Additionally, check to see if BuildValue is still using this. */ private[serialization] object ManifestUtil { def isApproxIterable(tag: FastTypeTag[_], cl: ClassLoader = ManifestUtil.getClass.getClassLoader): Boolean = tag match { case x if x.key startsWith "scala.Array[" => true case x if x.key startsWith "scala.Option[" => true case x if x.key startsWith "scala.collection.immutable.Nil.type" => true case x if x.key startsWith "scala.collection.immutable.Vector[" => true case x if x.key startsWith "scala.collection.immutable.$colon$colon[" => true case x if x.key startsWith "scala.collection.immutable.List[" => true case x if x.key startsWith "scala.collection.Seq[" => true case x if x.key startsWith "scala.collection.immutable.Seq[" => true case x if x.key startsWith "scala.collection.mutable.ArrayBuffer[" => true case x => val mitr = implicitly[Manifest[Iterable[Any]]] toManifest(tag, cl) map { _ <:< mitr } getOrElse false } def isApproxSubType(lhs: FastTypeTag[_], rhs: FastTypeTag[_], cl: ClassLoader = ManifestUtil.getClass.getClassLoader): Boolean = (lhs, rhs) match { case (_, x) if x.key == "scala.Any" => true case _ => (toManifest(lhs, cl), toManifest(rhs, cl)) match { case (Some(lhsm), Some(rhsm)) => lhsm <:< rhsm case _ => false } } def toManifest(tag: FastTypeTag[_], cl: ClassLoader = ManifestUtil.getClass.getClassLoader): Option[Manifest[_]] = toManifest(tag.key, cl) def toManifest(key: String, cl: ClassLoader): Option[Manifest[_]] = { val typeExpression = TypeExpression.parse(key)._1 toManifest(typeExpression, cl) } def toManifest(typeExpression: TypeExpression, cl: ClassLoader): Option[Manifest[_]] = { val args = typeExpression.typeArgs map { toManifest(_, cl) } if (args forall { _.isDefined }) { val realArgs = args.flatten typeExpression.typeName match { case "scala.Unit" => Some(ManifestFactory.Unit) case default => try { val ourClass = cl.loadClass(default) val mf = if (realArgs.isEmpty) ManifestFactory.classType(ourClass) else ManifestFactory.classType(ourClass, realArgs.head, realArgs.tail: _*) Some(mf.asInstanceOf[Manifest[_]]) } catch { case _: ClassNotFoundException => None } } } else None } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/RichTypes.scala000066400000000000000000000011311253217424300304250ustar00rootroot00000000000000package sbt.serialization /** Utilities used in PrimitivePicklers. Note: We can remove this once we clean up scala/pickling. */ private[serialization] trait RichTypes { import scala.reflect.runtime.universe._ implicit class RichType(tpe: scala.reflect.api.Universe#Type) { import definitions._ def isEffectivelyPrimitive: Boolean = tpe match { case TypeRef(_, sym: ClassSymbol, _) if sym.isPrimitive => true case TypeRef(_, sym, eltpe :: Nil) if sym == ArrayClass && eltpe.typeSymbol.isClass && eltpe.typeSymbol.asClass.isPrimitive => true case _ => false } } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/ScalaShim.scala000066400000000000000000000003261253217424300303640ustar00rootroot00000000000000package sbt.serialization // Note this is only used by ManifestUtil.scala, and can be removed if that is removed. private[serialization] object ScalaShims { val ManifestFactory = scala.reflect.ManifestFactory } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/SerializationFunctions.scala000066400000000000000000000016221253217424300332260ustar00rootroot00000000000000package sbt.serialization import scala.pickling.Generated import scala.util.Try import java.io.File /** A layer of serialization cake which provides the gen* macros for auto-constructing new picklers. */ trait SerializationFunctions { import scala.language.experimental.macros // non-implicit aliases of pickling's gen macros def genPickler[T]: Pickler[T] = macro scala.pickling.Compat.PicklerMacros_impl[T] def genUnpickler[T]: Unpickler[T] with scala.pickling.Generated = macro scala.pickling.Compat.UnpicklerMacros_impl[T] def toJsonString[A: Pickler](a: A): String = SerializedValue(a).toJsonString def toJsonFile[A: Pickler](a: A, file: File): Unit = SerializedValue(a).toJsonFile(file) def fromJsonString[A: Unpickler](json: String): Try[A] = SerializedValue.fromJsonString(json).parse[A] def fromJsonFile[A: Unpickler](file: File): Try[A] = SerializedValue.fromJsonFile(file).parse[A] } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/SerializedValue.scala000066400000000000000000000152751253217424300316210ustar00rootroot00000000000000package sbt.serialization import java.io.File import org.json4s.{ JString, JValue } import org.json4s.JsonAST._ import scala.pickling.PicklingException import scala.util.control.NonFatal import scala.util.{ Try, Success } import scala.pickling.functions._ import sbt.serialization.json.{ JSONPickle } import sbt.serialization.json.JsonMethods._ import sbt.serialization.json.JSONPickleFormat /** * We serialize to and from this opaque type. The idea is to * hide exactly what we can serialize from/to and hide * which library we use to do it. * * What this will expose is the mechanism of using Pickler/Unpickler to * handle unknown serialized values. */ sealed trait SerializedValue { def parse[T](implicit unpickler: Unpickler[T]): Try[T] /** * Returns true if the supplied unpickler matches the * type tag in the SerializedValue. This can return * false in some cases where parse[T] could succeed (parse * is allowed to use duck typing). So this should only * be used when the type to be unpickled is not known * for sure and a discriminator is required. The advantages * of hasTag over simply attempting to parse are that it * doesn't throw an expensive exception on match failure, * and it can't accidentally match a structurally-compatible * but distinct type. */ def hasTag[T](implicit unpickler: Unpickler[T]): Boolean def toJsonString: String def toJsonFile(file: File): Unit // TODO toBinary if/when we add binary pickling // private because we don't have our chosen // json AST library in the public API protected def toJValue: JValue final override def equals(other: Any): Boolean = other match { case null => false case sv: SerializedValue => json.JsonMethods.jvalueEquals(toJValue, sv.toJValue) case _ => false } final override def hashCode(): Int = json.JsonMethods.jvalueHashCode(toJValue) } object SerializedValue { def apply[V](value: V)(implicit pickler: Pickler[V]): SerializedValue = LazyValue[V](value, pickler) /** Reconstitutes a SerializedValue from a json string. */ def fromJsonString(value: String): SerializedValue = JsonValue(JSONPickle(value)) /** Reconstitutes a SerializedValue from a json string. */ def fromJsonFile(file: File): SerializedValue = JsonValue(JSONPickle.fromFile(file)) // TODO fromBinary if/when we add binary pickling // this is in this file so it can use private JsonValue, // but the public implicit version is in a trait elsewhere. // NOTE: this pickler ONLY works with our JSONPickleFormat because // it assumes JValue is a "primitive" known to the format. // we can adjust this if we add a binary format. private[sbt] object pickler extends Pickler[SerializedValue] with Unpickler[SerializedValue] { val cheaterTag = implicitly[FastTypeTag[JValue]] // TODO - This is super hacky mechanism to avoid issues w/ pinned types. override val tag = cheaterTag.asInstanceOf[FastTypeTag[SerializedValue]] def pickle(a: SerializedValue, builder: PBuilder): Unit = { val json = a.toJValue builder.hintTag(cheaterTag) builder.hintStaticallyElidedType() builder.beginEntry(json) builder.endEntry() //jsonPickler.pickle(spsv.toJson, builder) } def unpickle(tag: String, preader: PReader): Any = { preader.hintTag(cheaterTag) preader.hintStaticallyElidedType() preader.beginEntry() // TODO - Check beginEntry returns cheaterTag val value = preader.readPrimitive().asInstanceOf[JValue] preader.endEntry() JsonValue(JSONPickle.fromJValue(value)) } } } /** A value we have serialized as JSON */ private final case class JsonValue(pickledValue: JSONPickle) extends SerializedValue { require(pickledValue ne null) import sbt.serialization.json.pickleFormat override def parse[T](implicit unpicklerForT: Unpickler[T]): Try[T] = Try { unpickle[T](pickledValue) } def hasTag[T](implicit unpickler: Unpickler[T]): Boolean = pickledValue.readTypeTag.map(tag => tag == unpickler.tag.key).getOrElse(false) override def toJsonString: String = pickledValue.value override def toJsonFile(file: File): Unit = Using.fileWriter(file) { _.write(toJsonString) } // this deliberately doesn't simply toJsonString because it would // be broken to use toString to get parseable json (since the SerializedValue // may not be a JsonValue) override def toString = s"JsonValue($toJsonString)" override def toJValue: JValue = pickledValue.parsedValue } /** * A value we have the info available to serialize, but we haven't * picked a format yet. Allows us to defer format selection, or * for in-process uses we can even try to skip serialization. */ private final case class LazyValue[V](value: V, pickler: Pickler[V]) extends SerializedValue { // we use this to optimize a common case private def unpicklerMatchesExactly[T](unpickler: Unpickler[T]): Boolean = { // We compare tag.key because FastTypeTag.equals uses Mirror // and Type and we don't want to use the reflection API. pickler.tag.key == unpickler.tag.key } // this could theoretically avoid the round-trip through JSON // in some cases, but pretty annoying to figure out what those // cases are so forget it. // Not expecting to actually call this really anyway because // we use LazyValue on the "send" side. override def parse[T](implicit unpickler: Unpickler[T]): Try[T] = if (unpicklerMatchesExactly(unpickler)) Success(value.asInstanceOf[T]) // this allows duck typing to succeed and also handles // V=Fruit, T=Apple case. else toJson.parse[T] def hasTag[T](implicit unpickler: Unpickler[T]): Boolean = // the toJson is needed if you have a Fruit pickler // and an Apple unpickler, so $type is Apple but pickler.tag // is Fruit unpicklerMatchesExactly(unpickler) || toJson.hasTag[T] override def toJsonString = toJson.toJsonString override def toJsonFile(file: File): Unit = { val output = new UFT8FileOutput(file) var success = false try { val builder = json.pickleFormat.createBuilder(output) pickleInto(value, builder)(pickler) success = true } finally { // don't forget to close output.close() // UTF8FileOutput truncates the file if it exists, so there's no reason why we wouldn't delete it in case an error occured // Otherwise we end up with partially serialized data that will break when trying to deserialize it if (!success) { file.delete() } } } override def toJValue = toJson.toJValue private lazy val jsonValue = JsonValue(pickle(value)(sbt.serialization.json.pickleFormat, pickler)) def toJson: JsonValue = jsonValue } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/TypeExpression.scala000066400000000000000000000033241253217424300315220ustar00rootroot00000000000000package sbt.serialization // Copied from scala/pickling // https://github.com/scala/pickling/blob/c0fc5df7784188cf470debb3f9d41adaf37df5a6/core/src/main/scala/pickling/internal/AppliedType.scala object TypeExpression { // the delimiters in an applied type private val delims = List(',', '[', ']') /* Parse an applied type. * * @param s the string that is parsed * @return a pair with the parsed applied type and the remaining string. */ def parse(s: String): (TypeExpression, String) = { // shape of `s`: fqn[at_1, ..., at_n] val (typeName, rem) = s.span(!delims.contains(_)) if (rem.isEmpty || rem.startsWith(",") || rem.startsWith("]")) { (TypeExpression(typeName, List()), rem) } else { // parse type arguments var typeArgs = List[TypeExpression]() var remaining = rem while (remaining.startsWith("[") || remaining.startsWith(",")) { remaining = remaining.substring(1) val (next, rem) = parse(remaining) typeArgs = typeArgs :+ next remaining = rem } (TypeExpression(typeName, typeArgs), if (remaining.startsWith("]")) remaining.substring(1) else remaining) } } } /** * Simple representation of an applied type. Used for reading pickled types. * * Example, ``List[String]`` would be represented as: * * {{{ * TypeExpression("scala.collection.immutable.List", * Seq(TypeExpression("java.lang.String", Nil) * ) * }}} * * As you can see, simple types like "String" are represented as applied types with no arguments. */ case class TypeExpression(typeName: String, typeArgs: List[TypeExpression]) { override def toString = typeName + (if (typeArgs.isEmpty) "" else typeArgs.mkString("[", ",", "]")) } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/UTF8FileOutput.scala000066400000000000000000000013351253217424300312700ustar00rootroot00000000000000package sbt.serialization import java.io.{ File, FileOutputStream, BufferedWriter, OutputStreamWriter } import scala.pickling.Output import java.nio.charset.Charset private[serialization] class UFT8FileOutput(file: File) extends Output[String] { private[this] val writer = { if (!file.getParentFile.exists) { file.getParentFile.mkdirs() } new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, false), UFT8FileOutput.utf8)) } def result(): String = throw new UnsupportedOperationException() def put(obj: String): this.type = { writer.write(obj) this } def close(): Unit = writer.close() } private[serialization] object UFT8FileOutput { val utf8 = Charset.forName("UTF-8") } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/Using.scala000066400000000000000000000011301253217424300275770ustar00rootroot00000000000000package sbt.serialization import java.io.{ File, FileOutputStream, BufferedWriter, OutputStreamWriter } import java.nio.charset.Charset private[serialization] object Using { def fileWriter(file: File, charset: Charset = Charset.forName("UTF-8"), append: Boolean = false)(f: BufferedWriter => Unit): Unit = { if (!file.getParentFile.exists) { file.getParentFile.mkdirs() } val resource = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset)) try { f(resource) } finally { resource.close() } } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/json/000077500000000000000000000000001253217424300264635ustar00rootroot00000000000000serialization-0.1.2/serialization/src/main/scala/sbt/serialization/json/JSONPickleFormat.scala000066400000000000000000000644221253217424300325520ustar00rootroot00000000000000package sbt.serialization import java.io.File import scala.pickling.{ FastTypeTag, Output, Pickle, PickleFormat, PickleTools, PicklingException, StringOutput, UnpickleOps } import scala.pickling.internal.lookupUnpicklee // FIXME this isn't threadsafe right? we need to get rid of its use. import org.json4s._ import scala.util.parsing.json.JSONFormat.quoteString import scala.collection.mutable.{ StringBuilder, Stack } import scala.util.{ Success, Failure } import jawn.support.json4s.{ Parser => JawnParser } package json { import scala.pickling.Hints private[serialization] object `package` { implicit val pickleFormat: JSONPickleFormat = new JSONPickleFormat } private[serialization] sealed abstract class JSONPickle extends Pickle { type ValueType = String type PickleFormatType = JSONPickleFormat //abstract val value: String /** The value in the pickled parsed into a JValue AST. note this may throw. */ def parsedValue: JValue private[serialization] def readTypeTag: Option[String] = parsedValue match { case obj: JObject => (obj \ JSONPickleFormat.TYPE_TAG_FIELD) match { case JString(s) => Some(s) case _ => None } case _ => None } override final def equals(other: Any): Boolean = other match { case null => false case o: JSONPickle => JsonMethods.jvalueEquals(parsedValue, o.parsedValue) case _ => false } override def hashCode = parsedValue.hashCode } private[json] class RawStringPickle(override val value: String) extends JSONPickle { require(value ne null) def parsedValue: JValue = JawnParser.parseFromString(value) match { case Success(json: JValue) => json case Failure(e) => throw new PicklingException(s"""failed to parse "${value}" as JSON: ${e.getMessage}""") } } private[json] class JValuePickle(override val parsedValue: JValue) extends JSONPickle { require(parsedValue ne null) // This HAS to be val based on the pickling API. However, we may never call it for a given pickle, // so we'd like to not pay the string rendering tax unless we must. override lazy val value: String = JsonMethods.compact(parsedValue) } private[serialization] object JSONPickle { def apply(in: String): JSONPickle = new RawStringPickle(in) def fromFile(file: File): JSONPickle = fromJValue(JawnParser.parseFromFile(file) match { case Success(json: JValue) => json case Failure(e) => throw new PicklingException(s"""failed to parse "${file}" as JSON: ${e.getMessage}""") }) def fromJValue(in: JValue): JSONPickle = in match { // this null check is because when we read primitive (I think with no type tag), // we get null instead of JNull. case null => new JValuePickle(JNull) case other => new JValuePickle(other) } } private[serialization] class JSONPickleFormat extends PickleFormat { type PickleType = JSONPickle type OutputType = Output[String] def createBuilder() = new VerifyingJSONPickleBuilder(this, new StringOutput) def createBuilder(out: Output[String]): PBuilder = new VerifyingJSONPickleBuilder(this, out) def createReader(pickle: JSONPickle) = new VerifyingJSONPickleReader(this, IniitalReaderState(pickle.parsedValue)) } private[serialization] object JSONPickleFormat { private[json] val TYPE_TAG_FIELD = "$type" private[json] val DYNAMIC_KEY_FIELD = "$keys" private[json] val REF_ID_FIELD = "$ref" private[json] def isSpecialField(name: String): Boolean = (TYPE_TAG_FIELD == name) || (DYNAMIC_KEY_FIELD == name) || (REF_ID_FIELD == name) private[json] def isElidedField(name: String): Boolean = (DYNAMIC_KEY_FIELD == name) } private[json] sealed trait BuilderState { def previous: BuilderState } private[json] case class CollectionState(val previous: BuilderState, numElements: Int, hasInput: Boolean) extends BuilderState private[json] case class RawEntryState(previous: BuilderState, picklee: Any, hints: Hints, var wasCollectionOrMap: Boolean = false) extends BuilderState private[json] case class MapEntryState(val previous: BuilderState, picklee: Any, hints: Hints) extends BuilderState private[json] case class RefEntryState(val previous: BuilderState) extends BuilderState private[json] case class WriteOptionState(val previous: BuilderState) extends BuilderState private[json] object EmptyState extends BuilderState { def previous = this } // A slow implementation of of a pickle builder // This uses a TON of branch statements to ensure the builder is in the correct state for any call // and to programatically enforce constraints of Pickler implementations. // We use this just to verify our own picklers. private[json] class VerifyingJSONPickleBuilder(format: JSONPickleFormat, buf: Output[String]) extends PBuilder with PickleTools { import JSONPickleFormat._ var state: BuilderState = EmptyState //(tag.key startsWith "scala.Option[") private def isJValue(tag: FastTypeTag[_]): Boolean = (tag.key startsWith "org.json4s.JsonAST.") // Hackery so we elide option collection types. private def isOption(tag: FastTypeTag[_]): Boolean = (tag.key startsWith "scala.Option") // Here we get notified of object/value-like things. override def beginEntry(picklee: Any): PBuilder = withHints { hints => // Here we check to see if we need to serialize a reference. These are used to avoid circular object // dependencies for picklers which have circluarly-references objects. if (hints.oid != -1) { buf.put("{\"" + REF_ID_FIELD + "\":" + hints.oid + "}") state = RefEntryState(state) } else if (isOption(hints.tag)) { // We expect to be writing a collection, we just ignore the collection aspect. state = WriteOptionState(RawEntryState(state, picklee, hints, true)) } else { state = new RawEntryState(state, picklee, hints) } this } override def putField(name: String, pickler: (PBuilder) => Unit): PBuilder = if (!isElidedField(name)) { state match { case x: RawEntryState => x.wasCollectionOrMap = true // Now we know we're in a map state, so we swap into map state. state = MapEntryState(x.previous, x.picklee, x.hints) buf.put("{") case _: MapEntryState => // here we just need another , buf.put(",") case _ => sys.error("Cannot put a field when not in entry state!") } // Here we must append all the stringy things around the field. buf.put('"' + name + "\":") pickler(this) this } else this override def endEntry(): Unit = { state match { case RawEntryState(prev, _, _, true) => // Here we do nothing because it was a collection. state = prev case RawEntryState(prev, picklee, hints, false) => // Here we have to actually serialize the thing, as we're not a collection or a map. if (primitives.contains(hints.tag.key)) primitives(hints.tag.key)(picklee) else if (primitiveArrays.contains(hints.tag.key)) { primitiveArrays(hints.tag.key)(picklee) } else if (isJValue(hints.tag)) { import JsonMethods._ buf.put(compact(render(picklee.asInstanceOf[JValue]))) } else { // Note: It's possible the object is empty, so we just put an empty object here, // as the type we're serializing may not have any contents. // we also serialize the "$type" here if needed. buf.put("{") if (!hints.isStaticallyElidedType) appendTagString(picklee, hints) buf.put("}") } state = prev case MapEntryState(prev, picklee, hints) => // Add the type tag if we don't know it statically. if (!hints.isStaticallyElidedType) { buf.put(",") appendTagString(picklee, hints) } buf.put("}") state = prev case RefEntryState(prev) => state = prev case _ => sys.error("Unable to endEntry() when not in entry state!") } } private def appendTagString(picklee: Any, hints: Hints): Unit = buf.put("\"" + TYPE_TAG_FIELD + "\":\"" + makeTagString(picklee, hints) + "\"") private def makeTagString(picklee: Any, hints: Hints): String = if (hints.tag.key.contains("anonfun$")) picklee.getClass.getName else hints.tag.key // We cover ararys of primitives separately here. // NOTE: these are special cased in the core pickler design (probably for binary encoding efficiency) private val primitiveArrays = Map[String, Any => Unit]( FastTypeTag.ArrayByte.key -> ((picklee: Any) => pickleArray(picklee.asInstanceOf[Array[Byte]], FastTypeTag.Byte)), FastTypeTag.ArrayShort.key -> ((picklee: Any) => pickleArray(picklee.asInstanceOf[Array[Short]], FastTypeTag.Short)), FastTypeTag.ArrayChar.key -> ((picklee: Any) => pickleArray(picklee.asInstanceOf[Array[Char]], FastTypeTag.Char)), FastTypeTag.ArrayInt.key -> ((picklee: Any) => pickleArray(picklee.asInstanceOf[Array[Int]], FastTypeTag.Int)), FastTypeTag.ArrayLong.key -> ((picklee: Any) => pickleArray(picklee.asInstanceOf[Array[Long]], FastTypeTag.Long)), FastTypeTag.ArrayBoolean.key -> ((picklee: Any) => pickleArray(picklee.asInstanceOf[Array[Boolean]], FastTypeTag.Boolean)), FastTypeTag.ArrayFloat.key -> ((picklee: Any) => pickleArray(picklee.asInstanceOf[Array[Float]], FastTypeTag.Float)), FastTypeTag.ArrayDouble.key -> ((picklee: Any) => pickleArray(picklee.asInstanceOf[Array[Double]], FastTypeTag.Double))) private def pickleArray(arr: Array[_], tag: FastTypeTag[_]) = { beginCollection(arr.length) pushHints() hintStaticallyElidedType() hintTag(tag) pinHints() var i = 0 while (i < arr.length) { putElement(b => b.beginEntry(arr(i)).endEntry()) i += 1 } popHints() endCollection() } private val primitives = Map[String, Any => Unit]( FastTypeTag.Unit.key -> ((picklee: Any) => buf.put("\"()\"")), FastTypeTag.Null.key -> ((picklee: Any) => buf.put("null")), FastTypeTag.Ref.key -> ((picklee: Any) => throw new Error("fatal error: shouldn't be invoked explicitly")), FastTypeTag.Int.key -> ((picklee: Any) => buf.put(picklee.toString)), FastTypeTag.Long.key -> ((picklee: Any) => buf.put(picklee.toString)), FastTypeTag.Short.key -> ((picklee: Any) => buf.put(picklee.toString)), FastTypeTag.Double.key -> ((picklee: Any) => buf.put(picklee.toString)), FastTypeTag.Float.key -> ((picklee: Any) => buf.put(picklee.toString)), FastTypeTag.Boolean.key -> ((picklee: Any) => buf.put(picklee.toString)), FastTypeTag.Byte.key -> ((picklee: Any) => buf.put(picklee.toString)), FastTypeTag.Char.key -> ((picklee: Any) => buf.put("\"" + quoteString(picklee.toString) + "\"")), FastTypeTag.String.key -> ((picklee: Any) => buf.put("\"" + quoteString(picklee.toString) + "\"")) // Note we've removed all Array knowledge in favor of traeting this NOT as primitive types, but instead // provide a collection pickler for them. ) override def beginCollection(length: Int): PBuilder = { state match { case x: RawEntryState => x.wasCollectionOrMap = true state = CollectionState(x, length, false) buf.put("[") this case x: WriteOptionState => // We need to serialize None if (length == 0) buf.put("null") this case _ => sys.error(s"Unable to begin collection when in unknown state: $state") } } override def putElement(pickler: (PBuilder) => Unit): PBuilder = state match { case s: CollectionState => // TODO - Verify if (s.hasInput) { buf.put(",") } else { state = s.copy(hasInput = true) } pickler(this) this case s: WriteOptionState => // Cheater methods to serialize options as raw values. pickler(this) this case _ => sys.error("Cannot put an element without first specifying a collection.") } override def endCollection(): Unit = state match { case s: CollectionState => buf.put("]") state = s.previous case s: WriteOptionState => state = s.previous case _ => sys.error("cannot end a collection when not in collection state!") } override def result(): JSONPickle = { // TODO - verify everything is done, and we have no state stack... if (state != EmptyState) sys.error("Failed to close/end all entries and collections!") JSONPickle(buf.toString) } } private[json] sealed trait ReaderState { def previous: ReaderState def current: JValue } // The state where we're looking at a value, but the reader hasn't told us to do anything yet. private[json] case class RawJsValue(current: JValue, previous: ReaderState) extends ReaderState // The state in which we've attempted to read a type tag. // i.e. this means beginEntry has been called. private[json] case class JsValueWithTag(current: JValue, tagKey: String, previous: ReaderState) extends ReaderState // The initial state where we pass parsed JSON and begin parsing. private[json] case class IniitalReaderState(current: JValue) extends ReaderState { def previous: ReaderState = this } // The state where we are reading elements from a collection. private[json] case class CollectionReadingState(current: JValue, idx: Int, previous: ReaderState) extends ReaderState private[json] class VerifyingJSONPickleReader(format: JSONPickleFormat, var state: ReaderState) extends PReader with PickleTools { import JSONPickleFormat._ // Debugging hints override def hintTag(tag: FastTypeTag[_]): this.type = { //System.err.println(s"hintTag($tag)") super.hintTag(tag) } override def hintStaticallyElidedType(): this.type = { //System.err.println(s"hintStaticallyElidedType()") super.hintStaticallyElidedType() } override def pinHints(): this.type = { //System.err.println(s"pinHints()") super.pinHints() } override def unpinHints(): this.type = { //System.err.println(s"unpinHints()") super.pinHints() } override def beginEntry(): String = withHints { hints => // This should be the default for static picklers. We don't need runtime reflection, // so we just grab tag strings and use that to match known/sealed class hierarchies. val tag = currentTag(state.current, hints) state = JsValueWithTag(state.current, tag, state.previous) tag } override def endEntry(): Unit = { //System.err.println(s"endEntry()") // TODO - validate state is correct before we pop the stack. state = state.previous } // Check for primitive at current state. override def atPrimitive: Boolean = state match { case JsValueWithTag(_, tag, _) => primitives.contains(tag) case _ => false } // Check if the user is aksing for a raw "JValue" so we don't deserialize it. private def atJValue: Boolean = state match { case JsValueWithTag(_, tag, _) => (tag startsWith "org.json4s.JsonAST.") case _ => false } override def readPrimitive(): Any = { //System.err.println(s"readPrimitive()") def unpickleHelper(value: JValue, tag: String): Any = { if (tag startsWith "org.json4s.JsonAST.") value else if (primitives.contains(tag)) primitives(tag)(value) // NOTE - This is a dirty, rotten hack when the tag.key does not lineup with the data. // We need to figure out hwat's wrong with our SPickles that would case this. else value match { case x: JString => x.values //case x: JDouble => x.values case x: JBool => x.value // TODO - We need to understand why the tag doesn't say JsonAST here... case x: JObject => x case JNull => null case _ => // TODO - check to see if we need the old primitiveSeqKeys handling // to read a primtiive out of a JArray val e = new PicklingException(s"Not a primitive: $tag, found $value") e.printStackTrace() throw e } } state match { case JsValueWithTag(value, tag, _) => unpickleHelper(value, tag) // Here we've gotten to a readPrimtive without a beginEntry which reads the tag. We'll // assume the statically hinted type is the right one case _: IniitalReaderState | _: RawJsValue => withHints { hints => unpickleHelper(state.current, hints.tag.key) } // TODO - Do we need a state where we can read a value if we're in a collection reading state? case state => throw new PicklingException(s"Cannot deserialize primitive in state: $state") } } // Check for object at current state, and read fields. override def atObject: Boolean = // TODO - Check for legit state state.current.isInstanceOf[JObject] override def readField(name: String): PReader = { //System.err.println(s"readField($name)") // TODO - assert(atObject) && we're in legit state to read fields... val nextState = if (name == DYNAMIC_KEY_FIELD) { // TODO - Should we sort here? val keys = state.current.asInstanceOf[JObject].values.keys.toList.sorted.map(k => JString(k)) RawJsValue(JArray(keys), state) // TODO - what do we do if we're at a JNothing here... } else RawJsValue(state.current.asInstanceOf[JObject] \ name, state) val nested = new VerifyingJSONPickleReader(format, nextState) if (this.areHintsPinned) { nested.pinHints() nested.hints = hints // TODO - maybe we modify the state to remember the type tag... } else { nested.hints = hints } nested } // Methods around reading collections. override def beginCollection(): PReader = { //System.err.println(s"beginCollection()") // For now we just migrate into collection reading state. state = CollectionReadingState(state.current, 0, state) this } override def readLength(): Int = state match { case CollectionReadingState(value, 0, _) => //System.err.println(s"readLength()") value match { case JNothing => 0 case JNull => 0 // Hackery for Option handling case x: JArray => x.arr.size case x => 1 // Hackery for Option handling } case x => throw new PicklingException(s"Cannot read length when not in collection reading state.") } override def readElement(): PReader = state match { case cs @ CollectionReadingState(value, idx, _) => //System.err.println(s"readElement()") // First advance internal state. state = cs.copy(idx = idx + 1) val subState = value match { case x: JArray => RawJsValue(x.apply(idx), state) case _ if idx == 0 => RawJsValue(value, state) } val tmp = new VerifyingJSONPickleReader(format, subState) tmp.hints = this.hints // TODO - is this correct? tmp case x => throw new PicklingException(s"Cannot read an element when not in collection reading state.") } override def endCollection(): Unit = state match { case CollectionReadingState(value, idx, prev) => //System.err.println(s"endCollection()") // TODO - Warn if we haven't read all value, maybe state = prev case _ => throw new PicklingException(s"Cannot end reading a collection when we never started, state: $state") } // IMPLEMENTATION DETAILS // NOTE - most of this can be moved into static helper method // especially the state so we don't create it over and over on every pickle call. private val primitives = Map[String, JValue => Any]( FastTypeTag.Unit.key -> (datum => ()), FastTypeTag.Null.key -> (datum => null), FastTypeTag.Ref.key -> (datum => lookupUnpicklee(datum match { case obj: JObject => (obj \ REF_ID_FIELD) match { case JDouble(num) => num.toInt case x => unexpectedValue(x, FastTypeTag.Ref) } case x => unexpectedValue(x, FastTypeTag.Ref) })), FastTypeTag.Int.key -> (datum => datum match { case JDouble(num) => num.toInt case x => unexpectedValue(x, FastTypeTag.Int) }), FastTypeTag.Short.key -> (datum => datum match { case JDouble(num) => num.toShort case x => unexpectedValue(x, FastTypeTag.Short) }), FastTypeTag.Double.key -> (datum => datum match { case JDouble(num) => num case x => unexpectedValue(x, FastTypeTag.Double) }), FastTypeTag.Float.key -> (datum => datum match { case JDouble(num) => num.toFloat case x => unexpectedValue(x, FastTypeTag.Float) }), FastTypeTag.Long.key -> (datum => datum match { case JDouble(num) => num.toLong case JString(s) => s.toLong case x => unexpectedValue(x, FastTypeTag.Long) }), FastTypeTag.Byte.key -> (datum => datum match { case JDouble(num) => num.toByte case x => unexpectedValue(x, FastTypeTag.Byte) }), FastTypeTag.Boolean.key -> (datum => datum match { case JBool(b) => b case x => unexpectedValue(x, FastTypeTag.Boolean) }), FastTypeTag.Char.key -> (datum => datum match { case JString(s) => s.head case x => unexpectedValue(x, FastTypeTag.Char) }), FastTypeTag.String.key -> (datum => datum match { // TODO - where is this coming from... appears to be `Option[String]`, when option is `None` // More importantly, why is Jawn returning null instead of JNull? case null => null case JString(s) => s case x => unexpectedValue(x, FastTypeTag.String) }), FastTypeTag.ArrayByte.key -> (datum => (datum match { case JArray(arr) => arr map { case JDouble(num) => num.toByte case x => unexpectedValue(x, FastTypeTag.Byte) } case x => unexpectedValue(x, FastTypeTag.ArrayByte) }).toArray), FastTypeTag.ArrayShort.key -> (datum => (datum match { case JArray(arr) => arr map { case JDouble(num) => num.toShort case x => unexpectedValue(x, FastTypeTag.Short) } case x => unexpectedValue(x, FastTypeTag.ArrayShort) }).toArray), FastTypeTag.ArrayChar.key -> (datum => (datum match { case JArray(arr) => arr map { case JString(s) => s.head case x: JValue => unexpectedValue(x, FastTypeTag.Char) } case x => unexpectedValue(x, FastTypeTag.ArrayChar) }).toArray), FastTypeTag.ArrayInt.key -> { datum => (datum match { case JArray(arr) => arr map { case JDouble(num) => num.toInt case x => unexpectedValue(x, FastTypeTag.Int) } case x => unexpectedValue(x, FastTypeTag.ArrayInt) }).toArray }, FastTypeTag.ArrayLong.key -> (datum => (datum match { case JArray(arr) => arr map { case JDouble(num) => num.toLong case JString(s) => s.toLong case x => unexpectedValue(x, FastTypeTag.Long) } case x => unexpectedValue(x, FastTypeTag.ArrayLong) }).toArray), FastTypeTag.ArrayBoolean.key -> (datum => (datum match { case JArray(arr) => arr map { case JBool(b) => b case x => unexpectedValue(x, FastTypeTag.Boolean) } case x => unexpectedValue(x, FastTypeTag.ArrayBoolean) }).toArray), FastTypeTag.ArrayFloat.key -> (datum => (datum match { case JArray(arr) => arr map { case JDouble(num) => num.toFloat case x => unexpectedValue(x, FastTypeTag.Float) } case x => unexpectedValue(x, FastTypeTag.ArrayFloat) }).toArray), FastTypeTag.ArrayDouble.key -> (datum => (datum match { case JArray(arr) => arr map { case JDouble(num) => num case x => unexpectedValue(x, FastTypeTag.Double) } case x => unexpectedValue(x, FastTypeTag.ArrayDouble) }).toArray)) private def unexpectedValue(value: JValue, tag: FastTypeTag[_]): Nothing = throw new PicklingException("unexpected value: " + value.toString + ", looking for: " + tag) /** * Reads the pickled "$type" fields from a JObject. * Throws an exception if the "$type" fired doesn't exist. * * Note: This will use some runtime reflection to check if the pickled type still exists. If it does not, * this will use the type hint provided if we're deserializing a known subclass (not an abstract/trait) */ private def readTypeTagKey(obj: JObject, hints: Hints): String = { (obj \ TYPE_TAG_FIELD) match { case JString(s) => s case found => hints.tag.key } } /** Helper to read (or return elided) type tag for the given entry. */ private def currentTag(current: JValue, hints: Hints): String = { current match { case JNull => FastTypeTag.Null.key case JNothing => FastTypeTag.Nothing.key case obj: JObject => (obj \ REF_ID_FIELD) match { case JDouble(num) => FastTypeTag.Ref.key // Not a reference type. case _ => if (hints.isElidedType || hints.isStaticallyElidedType || hints.isDynamicallyElidedType) hints.tag.key else readTypeTagKey(obj, hints) } case _ if (hints.tag != null) => hints.tag.key case _ => // TODO - This should be an error. We need a tag and we have NO IDEA what we are. throw new PicklingException(s"Attempting to find tag in $current, but hints has ${hints.tag}") } } } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/json/JsonMethods.scala000066400000000000000000000113011253217424300317210ustar00rootroot00000000000000package sbt.serialization.json import org.json4s.{ JsonInput, StringInput, StreamInput, ReaderInput, FileInput, JValue, JField, JNothing, JBool, JString, JInt, JDecimal, JArray, JObject, JNull, JDouble } import java.io.File import scala.pickling.PicklingException import scala.util.Try /** An implementation of JsonMethods for json4s that uses Jawn and our own toStrings. */ private[serialization] object JsonMethods extends org.json4s.JsonMethods[JValue] { // Redner doesn't do anything, as we aren't translating to an intermediate format before rendering. override def render(value: JValue): JValue = value // TODO - Write this. override def pretty(d: JValue): String = compact(d) // Compact rendering. override def compact(d: JValue): String = { val buf = new StringBuilder("") import org.json4s._ def trimArr(xs: List[JValue]) = xs.filter(_ != JNothing) def trimObj(xs: List[JField]) = xs.filter(_._2 != JNothing) def append(d: JValue): Unit = { d match { case null => buf.append("null") case JBool(true) => buf.append("true") case JBool(false) => buf.append("false") case JDouble(n) => buf.append(n.toString) case JDecimal(n) => buf.append(n.toString) case JInt(n) => buf.append(n.toString) case JNull => buf.append("null") // TODO - better error message case JNothing => sys.error("can't render 'nothing'") // TODO - does this even make sense? case JString(null) => buf.append("null") case JString(s) => buf.append("\"") buf.append(ParserUtil.quote(s)) buf.append("\"") case JArray(arr) => buf.append("[") val trimmed = trimArr(arr) var l = trimmed while (!l.isEmpty) { val el = l.head if (l ne trimmed) buf.append(",") append(el) l = l.tail } buf.append("]") case JObject(obj) => buf.append("{") val trimmed = trimObj(obj) var l = trimmed while (!l.isEmpty) { val (k, v) = l.head if (l ne trimmed) buf.append(",") buf.append("\"").append(ParserUtil.quote(k)).append("\":") append(v) l = l.tail } buf.append("}") } } append(d) buf.toString } override def parse(in: JsonInput, useBigDecimalForDouble: Boolean): JValue = parseTry(in, useBigDecimalForDouble).get override def parseOpt(in: JsonInput, useBigDecimalForDouble: Boolean): Option[JValue] = parseTry(in, useBigDecimalForDouble).toOption def parseTry(in: JsonInput, useBigDecimalForDouble: Boolean): Try[JValue] = { val result: Try[JValue] = in match { case StringInput(string) => jawn.support.json4s.Parser.parseFromString(string) // TODO - We should support the reader case too. case ReaderInput(reader) => util.Try(???) case StreamInput(stream) => val in = java.nio.channels.Channels.newChannel(stream) try jawn.support.json4s.Parser.parseFromChannel(in) finally in.close() case FileInput(file: File) => val in = (new java.io.FileInputStream(file)).getChannel try jawn.support.json4s.Parser.parseFromChannel(in) finally in.close() } result recover { case e @ jawn.ParseException(msg, _, line, col) => throw PicklingException(s"Parse error line $line column $col '$msg' in $in", Some(e)) case e @ jawn.IncompleteParseException(msg) => throw PicklingException(s"Incomplete json '$msg' in $in", Some(e)) } } private final def jvalueSorted(jvalue: JValue): JValue = jvalue match { case null => null case JObject(el) => JObject(el.sortBy(_._1).map(kv => kv._1 -> jvalueSorted(kv._2))) case JArray(el) => JArray(el.map(jvalueSorted(_))) case other => other } def jvalueEquals(jvalue: JValue, jvalue2: JValue): Boolean = (jvalue, jvalue2) match { // deal with null case (null, null) => true case (JNull, JNull) => true case (JNull, null) | (null, JNull) => false // optimize by avoiding the jvalueSorted if sizes don't match anyhow case (JArray(el), JArray(el2)) if (el.size != el2.size) => false case (JObject(el), JObject(el2)) if (el.size != el2.size) => false case (left, right) => // use the order-sensitive json4s implementation after sorting object fields jvalueSorted(left).equals(jvalueSorted(right)) } def jvalueHashCode(jvalue: JValue): Int = jvalueSorted(jvalue).hashCode } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/package.scala000066400000000000000000000024041253217424300301120ustar00rootroot00000000000000package sbt /** * A package object which can be used to create new serializers. * * This package supports creating Pickler/Unpickler functions which can serialize arbitrary types. See the * SerializedValue type for what formats this library supports serializing into. */ package object serialization extends SerializationFunctions with CustomPicklers { type Pickler[A] = scala.pickling.Pickler[A] val Pickler = scala.pickling.Pickler type Unpickler[A] = scala.pickling.Unpickler[A] val Unpickler = scala.pickling.Unpickler val PicklerUnpickler = scala.pickling.PicklerUnpickler // These are exposed for custom implementations of picklers. type FastTypeTag[A] = scala.pickling.FastTypeTag[A] type PReader = scala.pickling.PReader type PBuilder = scala.pickling.PBuilder // pickling macros need FastTypeTag$ to have been initialized; // if things ever compile with this removed, it can be removed. private val __forceInitializeFastTypeTagCompanion = scala.pickling.FastTypeTag // All generated picklers are required to be static-only in this library. implicit val StaticOnly = scala.pickling.static.StaticOnly implicit val ShareNothing = scala.pickling.shareNothing.ShareNothing type directSubclasses = _root_.scala.pickling.directSubclasses } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/000077500000000000000000000000001253217424300271435ustar00rootroot00000000000000serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/JavaExtraPicklers.scala000066400000000000000000000042671253217424300335430ustar00rootroot00000000000000package sbt.serialization package pickler import java.io.File import java.net.URI import scala.pickling.{ FastTypeTag, PBuilder, PReader, PicklingException } // TODO - Why is alias not working. import scala.pickling.pickler.{ PrimitivePicklers, RefPicklers } /** Contains implementation-details of "can to strings" for Java/sbt 'raw' types. */ object JavaExtraPicklers { private val fileCanToString: CanToString[File] = CanToString( _.toURI.toASCIIString, { s: String => new File(new URI(s)) }) private val uriCanToString: CanToString[URI] = CanToString( _.toASCIIString, { s: String => new URI(s) }) } /** * Picklers relating to additional Java types we'd like to support. * * THis includes java.io.File, java.net.URI and the sbt "TypeExpression". */ trait JavaExtraPicklers extends PrimitivePicklers { // TODO - Maybe this shouldn't be implicitly available. implicit def canToStringPickler[A: FastTypeTag](implicit canToString: CanToString[A]): Pickler[A] with Unpickler[A] = new Pickler[A] with Unpickler[A] { val tag = implicitly[FastTypeTag[A]] def pickle(a: A, builder: PBuilder): Unit = { builder.pushHints() builder.hintTag(FastTypeTag.String) builder.hintStaticallyElidedType() stringPickler.pickle(canToString.toString(a), builder) builder.popHints() } def unpickle(tag: String, preader: PReader): Any = { preader.pushHints() preader.hintTag(FastTypeTag.String) preader.hintStaticallyElidedType() preader.pinHints() val s = stringPickler.unpickle(FastTypeTag.String.key, preader).asInstanceOf[String] preader.unpinHints() preader.popHints() try { val result = canToString.fromString(s) result } catch { case e: PicklingException => throw e case e: Throwable => throw PicklingException(s""""$s" is not valid ${tag}""", Some(e)) } } } implicit val filePickler: Pickler[File] with Unpickler[File] = canToStringPickler[File](FastTypeTag[File], JavaExtraPicklers.fileCanToString) implicit val uriPickler: Pickler[URI] with Unpickler[URI] = canToStringPickler[URI](FastTypeTag[URI], JavaExtraPicklers.uriCanToString) }serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/Option.scala000066400000000000000000000046511253217424300314260ustar00rootroot00000000000000package sbt.serialization package pickler import scala.pickling.{ FastTypeTag, PBuilder, PReader, PicklingException } // TODO - Why is alias not working. import scala.pickling.pickler.PrimitivePicklers trait OptionPicklers extends PrimitivePicklers with RichTypes { implicit def optionPickler[A: FastTypeTag](implicit elemPickler: Pickler[A], elemUnpickler: Unpickler[A], collTag: FastTypeTag[Option[A]]): Pickler[Option[A]] with Unpickler[Option[A]] = new Pickler[Option[A]] with Unpickler[Option[A]] { private implicit val elemTag = implicitly[FastTypeTag[A]] val tag = implicitly[FastTypeTag[Option[A]]] private val isPrimitive = elemTag.tpe.isEffectivelyPrimitive private val nullTag = implicitly[FastTypeTag[Null]] def pickle(coll: Option[A], builder: PBuilder): Unit = { // Here we cheat the "entry" so that the notion of option // is erased for "null" coll match { case Some(elem) => builder.hintTag(tag) builder.beginEntry(coll) builder.beginCollection(1) builder.putElement { b => b.hintTag(elemTag) b.hintStaticallyElidedType() elemPickler.pickle(elem, b) } builder.endCollection() builder.endEntry() case None => // TODO - Json Format shoudl special case this. builder.hintTag(tag) builder.beginEntry(None) builder.beginCollection(0) builder.endCollection() builder.endEntry() } } def unpickle(tag: String, preader: PReader): Any = { // Note - if we call beginEntry we should see JNothing or JNull show up if the option is empty. val reader = preader.beginCollection() preader.pushHints() // TODO - we may be ALWAYS eliding the type, so we shouldn't use an isPrimitive hack here. if (isPrimitive) { reader.hintStaticallyElidedType() reader.hintTag(elemTag) reader.pinHints() } else reader.hintTag(elemTag) val length = reader.readLength val result: Option[A] = if (length == 0) None else { val elem = elemUnpickler.unpickleEntry(reader.readElement()) Some(elem.asInstanceOf[A]) } if (isPrimitive) preader.unpinHints() preader.popHints() reader.endCollection() result } } }serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/SerializedValue.scala000066400000000000000000000003051253217424300332360ustar00rootroot00000000000000package sbt.serialization package pickler trait SerializationPicklers { implicit val serializedValuePickler: Pickler[SerializedValue] with Unpickler[SerializedValue] = SerializedValue.pickler } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/StringMap.scala000066400000000000000000000036701253217424300320620ustar00rootroot00000000000000package sbt.serialization package pickler import scala.collection.generic.CanBuildFrom import scala.pickling.{ FastTypeTag, PBuilder, PReader, PicklingException } trait StringMapPicklers { // FIXME this could theoretically work for M<:Map[String,A] and use a CanBuildFrom for M? implicit def stringMapPickler[A](implicit valuePickler: Pickler[A], valueUnpickler: Unpickler[A], valueTag: FastTypeTag[A], mapTag: FastTypeTag[Map[String, A]], keysPickler: Pickler[List[String]], keysUnpickler: Unpickler[List[String]]): Pickler[Map[String, A]] with Unpickler[Map[String, A]] = new Pickler[Map[String, A]] with Unpickler[Map[String, A]] { override val tag = mapTag def pickle(m: Map[String, A], builder: PBuilder): Unit = { builder.pushHints() builder.hintTag(mapTag) builder.hintStaticallyElidedType() builder.beginEntry(m) // This is a pseudo-field that the JSON format will ignore reading, but // the binary format WILL write. // TODO - We should have this be a "hintDynamicKeys" instead. builder.putField("$keys", { b => keysPickler.pickle(m.keys.toList.sorted, b) }) m foreach { kv => builder.putField(kv._1, { b => b.hintTag(valueTag) valuePickler.pickle(kv._2, b) }) } builder.endEntry() builder.popHints() } def unpickle(tpe: String, reader: PReader): Any = { reader.pushHints() reader.hintStaticallyElidedType() reader.hintTag(mapTag) reader.hintStaticallyElidedType() reader.beginEntry() val keys = keysUnpickler.unpickleEntry(reader.readField("$keys")).asInstanceOf[List[String]] val results = for (key <- keys) yield { val value = valueUnpickler.unpickleEntry(reader.readField(key)) key -> value.asInstanceOf[A] } reader.endEntry() reader.popHints() results.toMap } override def toString = "StringMapPicklerUnpickler" } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/Throwable.scala000066400000000000000000000076601253217424300321100ustar00rootroot00000000000000package sbt.serialization package pickler // TODO - Why is alias not working. import scala.pickling.pickler.{ PrimitivePicklers, RefPicklers } import scala.pickling.PicklingException trait ThrowablePicklers extends PrimitivePicklers with OptionPicklers with VectorPicklers with RefPicklers { private implicit object stackTracePickler extends Pickler[StackTraceElement] with Unpickler[StackTraceElement] { override val tag: FastTypeTag[StackTraceElement] = implicitly[FastTypeTag[StackTraceElement]] private val intTag = implicitly[FastTypeTag[Int]] private val stringOptTag = implicitly[FastTypeTag[Option[String]]] private val stringOptPickler = implicitly[Pickler[Option[String]]] private val stringOptUnpickler = implicitly[Unpickler[Option[String]]] override def pickle(a: StackTraceElement, builder: PBuilder): Unit = { builder.beginEntry(a) def pickleString(field: String, value: String): Unit = { builder.putField(field, { b => b.hintTag(stringOptTag) stringOptPickler.pickle(Option(value), b) }) } pickleString("className", a.getClassName) pickleString("methodName", a.getMethodName) pickleString("fileName", a.getFileName) builder.putField("lineNumber", { b => b.hintTag(intTag) intPickler.pickle(a.getLineNumber, b) }) builder.endEntry() } override def unpickle(tag: String, preader: PReader): StackTraceElement = { def unpickleString(field: String): Option[String] = { stringOptUnpickler.unpickleEntry(preader.readField(field)).asInstanceOf[Option[String]] } val className = unpickleString("className") val methodName = unpickleString("methodName") val fileName = unpickleString("fileName") val lineNumber = intPickler.unpickleEntry(preader.readField("lineNumber")).asInstanceOf[Int] new StackTraceElement(className.orNull, methodName.orNull, fileName.orNull, lineNumber) } } // TODO why isn't this in LowPriority / what goes in Low and what goes here? implicit object throwablePicklerUnpickler extends Pickler[Throwable] with Unpickler[Throwable] { val tag: FastTypeTag[Throwable] = implicitly[FastTypeTag[Throwable]] private val stringTag = implicitly[FastTypeTag[String]] private val stringOptTag = implicitly[FastTypeTag[Option[String]]] private val throwableOptTag = implicitly[FastTypeTag[Option[Throwable]]] private val stringOptPickler = implicitly[Pickler[Option[String]]] private val stringOptUnpickler = implicitly[Unpickler[Option[String]]] private val throwableOptPicklerUnpickler = optionPickler[Throwable](tag, this, this, throwableOptTag) private val vsteTag = implicitly[FastTypeTag[Vector[StackTraceElement]]] private val vstePickler = vectorPickler[StackTraceElement] private val vsteUnpickler = vstePickler def pickle(a: Throwable, builder: PBuilder): Unit = { builder.beginEntry(a) builder.putField("message", { b => b.hintTag(stringOptTag) stringOptPickler.pickle(Option(a.getMessage), b) }) builder.putField("cause", { b => b.hintTag(throwableOptTag) throwableOptPicklerUnpickler.pickle(Option(a.getCause), b) }) builder.putField("stackTrace", { b => b.hintTag(vsteTag) vstePickler.pickle(a.getStackTrace.toVector, b) }) builder.endEntry() } def unpickle(tag: String, preader: PReader): Any = { val message = stringOptUnpickler.unpickleEntry(preader.readField("message")).asInstanceOf[Option[String]] val cause = throwableOptPicklerUnpickler.unpickleEntry(preader.readField("cause")).asInstanceOf[Option[Throwable]] preader.hintStaticallyElidedType() val stackTrace = vsteUnpickler.unpickleEntry(preader.readField("stackTrace")).asInstanceOf[Vector[StackTraceElement]] val result = new Exception(message.orNull, cause.orNull) result.setStackTrace(stackTrace.toArray) result } } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/Traversable.scala000066400000000000000000000072711253217424300324310ustar00rootroot00000000000000package sbt.serialization package pickler import scala.collection.generic.CanBuildFrom import scala.pickling.{ FastTypeTag, PBuilder, PReader, PicklingException } trait VectorPicklers { implicit def vectorPickler[T: FastTypeTag](implicit elemPickler: Pickler[T], elemUnpickler: Unpickler[T], collTag: FastTypeTag[Vector[T]], cbf: CanBuildFrom[Vector[T], T, Vector[T]]): Pickler[Vector[T]] with Unpickler[Vector[T]] = TravPickler[T, Vector[T]] } trait ArrayPicklers { implicit def arrayPickler[A >: Null: FastTypeTag](implicit elemPickler: Pickler[A], elemUnpickler: Unpickler[A], collTag: FastTypeTag[Array[A]], cbf: CanBuildFrom[Array[A], A, Array[A]]): Pickler[Array[A]] with Unpickler[Array[A]] = TravPickler[A, Array[A]] } trait ListPicklers { implicit def listPickler[A: FastTypeTag](implicit elemPickler: Pickler[A], elemUnpickler: Unpickler[A], collTag: FastTypeTag[List[A]]): Pickler[List[A]] with Unpickler[List[A]] = TravPickler[A, List[A]] } trait SeqPicklers { // Ideally we wouldn't have this one, but it some sbt tasks return Seq implicit def seqPickler[A: FastTypeTag](implicit elemPickler: Pickler[A], elemUnpickler: Unpickler[A], collTag: FastTypeTag[Seq[A]], cbf: CanBuildFrom[Seq[A], A, Seq[A]]): Pickler[Seq[A]] with Unpickler[Seq[A]] = TravPickler[A, Seq[A]] } trait MapPicklers { implicit def mapPickler[A: FastTypeTag, B: FastTypeTag, C >: (A, B)](implicit keyPickler: Pickler[A], keyUnpickler: Unpickler[A], valuePickler: Pickler[B], valueUnpickler: Unpickler[B], collTag: FastTypeTag[Map[A, B]], cbf: CanBuildFrom[Map[A, B], C, Map[A, B]]): Pickler[Map[A, B]] with Unpickler[Map[A, B]] = TravPickler[(A, B), Map[A, B]] } // Custom pickler for Traversable is needed to emit $type hints for each element. object TravPickler { def apply[A: FastTypeTag, C <% Traversable[_]](implicit elemPickler: Pickler[A], elemUnpickler: Unpickler[A], cbf: CanBuildFrom[C, A, C], collTag: FastTypeTag[C]): Pickler[C] with Unpickler[C] = new Pickler[C] with Unpickler[C] with RichTypes { private implicit val elemTag = implicitly[FastTypeTag[A]] private val isPrimitive = elemTag.isEffectivelyPrimitive val tag = collTag def pickle(coll: C, builder: PBuilder): Unit = { if (elemTag == FastTypeTag.Int) builder.hintKnownSize(coll.size * 4 + 100) builder.beginEntry(coll) builder.beginCollection(coll.size) builder.pushHints() if (isPrimitive) { builder.hintStaticallyElidedType() builder.hintTag(elemTag) builder.pinHints() } (coll: Traversable[_]).asInstanceOf[Traversable[A]].foreach { (elem: A) => builder putElement { b => if (!isPrimitive) b.hintTag(elemTag) elemPickler.pickle(elem, b) } } if (isPrimitive) builder.unpinHints() builder.popHints() builder.endCollection() builder.endEntry() } def unpickle(tpe: String, preader: PReader): Any = { val reader = preader.beginCollection() preader.pushHints() if (isPrimitive) { reader.hintStaticallyElidedType() reader.hintTag(elemTag) reader.pinHints() } else { reader.hintTag(elemTag) // custom code here reader.pinHints() // custom code here } val length = reader.readLength() val builder = cbf.apply() var i = 0 while (i < length) { val elem = elemUnpickler.unpickleEntry(reader.readElement()) builder += elem.asInstanceOf[A] i = i + 1 } reader.unpinHints() preader.popHints() preader.endCollection() builder.result } } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/Tuple2.scala000066400000000000000000000032411253217424300313230ustar00rootroot00000000000000package sbt.serialization package pickler import scala.pickling.{ FastTypeTag, PBuilder, PReader } import scala.pickling.pickler.PrimitivePicklers trait Tuple2Picklers extends PrimitivePicklers with RichTypes { implicit def tuple2Pickler[T1: FastTypeTag, T2: FastTypeTag](implicit elem1Pickler: Pickler[T1], elem1Unpickler: Unpickler[T1], elem2Pickler: Pickler[T2], elem2Unpickler: Unpickler[T2], collTag: FastTypeTag[(T1, T2)]): Pickler[(T1, T2)] with Unpickler[(T1, T2)] = new Pickler[(T1, T2)] with Unpickler[(T1, T2)] { val tag = collTag private implicit val elem1Tag = implicitly[FastTypeTag[T1]] private implicit val elem2Tag = implicitly[FastTypeTag[T2]] def pickle(coll: (T1, T2), builder: PBuilder): Unit = { // Our type should already be hinted before this method, however we additionally mark our type as // statically elided. builder.hintStaticallyElidedType() builder.beginEntry(coll) builder.beginCollection(2) builder.putElement { b => b.hintTag(elem1Tag) elem1Pickler.pickle(coll._1, b) } builder.putElement { b => b.hintTag(elem2Tag) elem2Pickler.pickle(coll._2, b) } builder.endCollection() builder.endEntry() } def unpickle(tag: String, preader: PReader): Any = { preader.beginCollection() // TODO - better warning here. assert(preader.readLength() == 2) val fst = elem1Unpickler.unpickleEntry(preader.readElement()) val snd = elem2Unpickler.unpickleEntry(preader.readElement()) preader.endCollection() (fst, snd) } } } serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/TypeExpression.scala000066400000000000000000000011301253217424300331440ustar00rootroot00000000000000package sbt.serialization package pickler import scala.pickling.FastTypeTag object TypeExpressionPicklers { private val typeExpressionCanToString: CanToString[TypeExpression] = CanToString( _.toString, { s: String => TypeExpression.parse(s)._1 }) } /** Provides a layer of pickler cake for type expressoins. */ trait TypeExpressionPicklers extends JavaExtraPicklers { implicit val typeExpressionPickler: Pickler[TypeExpression] with Unpickler[TypeExpression] = canToStringPickler[TypeExpression](FastTypeTag[TypeExpression], TypeExpressionPicklers.typeExpressionCanToString) }serialization-0.1.2/serialization/src/main/scala/sbt/serialization/pickler/package.scala000066400000000000000000000004001253217424300315350ustar00rootroot00000000000000package sbt.serialization package object pickler { type PrimitivePicklers = scala.pickling.pickler.PrimitivePicklers type PrimitiveArrayPicklers = scala.pickling.pickler.PrimitiveArrayPicklers type RefPicklers = scala.pickling.pickler.RefPicklers } serialization-0.1.2/serialization/src/test/000077500000000000000000000000001253217424300210155ustar00rootroot00000000000000serialization-0.1.2/serialization/src/test/scala/000077500000000000000000000000001253217424300221005ustar00rootroot00000000000000serialization-0.1.2/serialization/src/test/scala/sbt/000077500000000000000000000000001253217424300226705ustar00rootroot00000000000000serialization-0.1.2/serialization/src/test/scala/sbt/serialization/000077500000000000000000000000001253217424300255455ustar00rootroot00000000000000serialization-0.1.2/serialization/src/test/scala/sbt/serialization/ArrayPicklerSpec.scala000066400000000000000000000131641253217424300317620ustar00rootroot00000000000000package sbt.serialization.spec import org.junit.Assert._ import org.junit._ import java.io.File import java.net.URI import JUnitUtil._ import sbt.serialization._ import scala.language.higherKinds class ArrayPicklerTest { @Test def testArrays: Unit = { // Array(1: Byte) should pickle as [1] pointed1[Array, Byte] // pointed2[Array, Byte] // Array(1: Short) should pickle as [1] pointed1[Array, Short] // pointed2[Array, Short] // Array('a') should pickle as ["a"]. pointed1[Array, Char] // pointed2[Array, Char] // Array("a") should pickle as ["a"] pointed1[Array, String] // pointed2[Array, String] // Array(1) should pickle as [1] pointed1[Array, Int] // pointed2[Array, Int] // Array(1L) should pickle as [1] pointed1[Array, Long] // pointed2[Array, Long] // Array(false) should pickle as [false] pointed1[Array, Boolean] // pointed2[Array, Boolean] // Array(1.0F) should pickle as [1.0] pointed1[Array, Float] // pointed2[Array, Float] // Array(1.0) should pickle as [1.0] pointed1[Array, Double] // pointed2[Array, Double] } @Test def testLists: Unit = { // List(1: Byte) should pickle as [1] pointed1[List, Byte] pointed2[List, Byte] // List(1: Short) should pickle as [1] pointed1[List, Short] pointed2[List, Short] // List('a') should pickle as ["a"] pointed1[List, Char] pointed2[List, Char] // List("a") should pickle as ["a"] pointed1[List, String] pointed2[List, String] // List(1) should pickle as [1] pointed1[List, Int] pointed2[List, Int] // List(1L) should pickle as [1] pointed1[List, Long] pointed2[List, Long] // List(false) should pickle as [false] pointed1[List, Boolean] pointed2[List, Boolean] // List(1.0F) should pickle as [1.0] pointed1[List, Float] pointed2[List, Float] // List(1.0) should pickle as [1.0] pointed1[List, Double] pointed2[List, Double] } @Test def testVectors: Unit = { // Vector(1: Byte) should pickle as [1] pointed1[Vector, Byte] pointed2[Vector, Byte] // Vector(1: Short) should pickle as [1] pointed1[Vector, Short] pointed2[Vector, Short] // Vector('a') should pickle as ["a"] pointed1[Vector, Char] pointed2[Vector, Char] // Vector("a") should pickle as ["a"] pointed1[Vector, String] pointed2[Vector, String] // Vector(1) should pickle as [1] pointed1[Vector, Int] pointed2[Vector, Int] // Vector(1L) should pickle as [1] pointed1[Vector, Long] pointed2[Vector, Long] // Vector(false) should pickle as [false] pointed1[Vector, Boolean] pointed2[Vector, Boolean] // Vector(1.0F) should pickle as [1.0] pointed1[Vector, Float] pointed2[Vector, Float] // Vector(1.0) should pickle as [1.0] pointed1[Vector, Double] pointed2[Vector, Double] } @Test def testOptions: Unit = { SerializedValue(Some(1): Option[Int]).toJsonString must_== "1" SerializedValue.fromJsonString("1").parse[Option[Int]].get must_== Some(1) SerializedValue(Some("a"): Option[String]).toJsonString must_== "\"a\"" SerializedValue.fromJsonString("\"a\"").parse[Option[String]].get must_== Some("a") SerializedValue(None: Option[Int]).toJsonString must_== "null" SerializedValue.fromJsonString("null").parse[Option[Int]].get must_== None SerializedValue(None: Option[String]).toJsonString must_== "null" SerializedValue.fromJsonString("null").parse[Option[String]].get must_== None } @Test def testRoundtrip: Unit = { // TODO it would be nice to pickle Nil.type so this works //roundTrip(Nil) // custom format to support both Nil and List[A] roundTrip(Nil: List[String]) roundTrip(Vector(): Vector[String]) roundTripArray(Array(1, 2, 3)) roundTripArray(Array("Bar", "Baz")) roundTrip(Vector("Bar", "Baz")) roundTrip(List("Bar", "Baz")) roundTrip(Vector(1, 2, 3)) } def trimLine(s: String): String = (s.lines map { _.trim }).mkString("\n") def pointed1[F[_], A: ClassManifest](implicit m: Pointed[F], ae: ArrayExample[A], ev0: Pickler[F[A]], ev1: FastTypeTag[F[A]]) = assertEquals(s"With type $ev1", ae.arrayJson, (trimLine(SerializedValue(m.pointed(ae.one)).toJsonString))) def pointed2[F[_], A: ClassManifest](implicit m: Pointed[F], ae: ArrayExample[A], ev0: Unpickler[F[A]], ev1: FastTypeTag[F[A]]) = SerializedValue.fromJsonString(ae.arrayJson).parse[F[A]].get must_== m.pointed(ae.one) } trait ArrayExample[A] { def one: A def arrayJson: String } object ArrayExample { def apply[A](one0: A, arrayJson0: String) = new ArrayExample[A] { def one = one0 def arrayJson: String = arrayJson0 } val arrayIntExample = """[1]""" val arrayDoubleExample = """[1.0]""" val arrayStringExample = """["a"]""" implicit val byteArrayExample: ArrayExample[Byte] = ArrayExample(1: Byte, arrayIntExample) implicit val shortArrayExample: ArrayExample[Short] = ArrayExample(1: Short, arrayIntExample) implicit val intArrayExample: ArrayExample[Int] = ArrayExample(1, arrayIntExample) implicit val charArrayExample: ArrayExample[Char] = ArrayExample('a', arrayStringExample) implicit val stringArrayExample: ArrayExample[String] = ArrayExample("a", arrayStringExample) implicit val longArrayExample: ArrayExample[Long] = ArrayExample(1L, arrayIntExample) implicit val booleanArrayExample: ArrayExample[Boolean] = ArrayExample(false, """[false]""".stripMargin) implicit val floatArrayExample: ArrayExample[Float] = ArrayExample(1.0F, arrayDoubleExample) implicit val doubleArrayExample: ArrayExample[Double] = ArrayExample(1.0, arrayDoubleExample) } serialization-0.1.2/serialization/src/test/scala/sbt/serialization/BasicPicklerSpec.scala000066400000000000000000000046701253217424300317270ustar00rootroot00000000000000package sbt.serialization.spec import org.junit.Assert._ import org.junit._ import java.io.File import java.net.URI import scala.pickling.{ PickleOps, UnpickleOps } import sbt.serialization._, sbt.serialization.json._ import JUnitUtil._ import sbt.serialization.pickler.{ PrimitivePicklers, PrimitiveArrayPicklers, JavaExtraPicklers, OptionPicklers, ThrowablePicklers } import scala.pickling.Defaults.pickleOps import scala.pickling.static._ class BasicPicklerTest { val basicProtocol = new PrimitivePicklers with PrimitiveArrayPicklers with JavaExtraPicklers with OptionPicklers with ThrowablePicklers { implicit val staticOnly = scala.pickling.static.StaticOnly } import basicProtocol._ @Test def testInt: Unit = { 1.pickle.value must_== "1" "1".unpickle[Int] must_== 1 } @Test def testLong: Unit = { 1L.pickle.value must_== "1" "1".unpickle[Long] must_== 1L } @Test def testString: Unit = { "a".pickle.value must_== "\"a\"" "\"a\"".unpickle[String] must_== "a" } @Test def testBoolean: Unit = { false.pickle.value must_== "false" "false".unpickle[Boolean] must_== false } @Test def testDouble: Unit = { 1.0.pickle.value must_== "1.0" "1.0".unpickle[Double] must_== 1.0 } @Test def testRoundtrip: Unit = { roundTrip("Foo") roundTrip(new File("/tmp")) roundTrip(new URI("/tmp")) roundTrip(true) roundTrip(false) roundTrip(10: Short) roundTrip(11) roundTrip(12L) roundTrip(13.0f) roundTrip(14.0) roundTrip(None: Option[String]) // roundTrip(None) must fail to compile roundTrip(Some("Foo"): Option[String]) // roundTrip(Some("Foo")) must fail to compile roundTrip(Some(true): Option[Boolean]) // roundTrip(Some(true)) must fail to compile roundTrip(Some(10): Option[Int]) // roundTrip(Some(10)) must fail to compile roundTrip((10, "Foo")) roundTrip(((10, false), None: Option[String])) roundTrip(Map("a" -> 10, "b" -> 20)) roundTrip(Map(10 -> "a", 20 -> "b")) roundTrip(Map.empty[Int, Long]) } @Test def testThrowable: Unit = { roundTrip(new Exception(): Throwable) roundTrip(new Exception("foo"): Throwable) val nested: Throwable = new Exception("foo", new Exception("bar")) val recovered = nested.pickle.value.unpickle[Throwable] recovered.getCause.getMessage must_== "bar" roundTrip(nested) recovered.getStackTrace()(0).getFileName must_== "BasicPicklerSpec.scala" } } serialization-0.1.2/serialization/src/test/scala/sbt/serialization/JUnitUtil.scala000066400000000000000000000033611253217424300304440ustar00rootroot00000000000000package sbt.serialization.spec import org.junit.Assert._ import org.junit._ import sbt.serialization._ object JUnitUtil { private def addWhatWeWerePickling[T, U](t: T)(body: => U): U = try body catch { case e: Throwable => e.printStackTrace() throw new AssertionError(s"Crash round-tripping ${t.getClass.getName}: value was: ${t}", e) } def roundTripArray[A](x: Array[A])(implicit ev0: Pickler[Array[A]], ev1: Unpickler[Array[A]]): Unit = roundTripBase[Array[A]](x)((a, b) => assertEquals(a.toList, b.toList)) { (a, b) => assertEquals(s"Failed to round trip $x via ${implicitly[Pickler[Array[A]]]} and ${implicitly[Unpickler[Array[A]]]}", a.getMessage, b.getMessage) } def roundTrip[A: Pickler: Unpickler](x: A): Unit = roundTripBase[A](x)((a, b) => assertEquals(a, b)) { (a, b) => assertEquals(s"Failed to round trip $x via ${implicitly[Pickler[A]]} and ${implicitly[Unpickler[A]]}", a.getMessage, b.getMessage) } def roundTripBase[A: Pickler: Unpickler](a: A)(f: (A, A) => Unit)(e: (Throwable, Throwable) => Unit): Unit = addWhatWeWerePickling(a) { val json = toJsonString(a) //System.err.println(s"json: $json") val parsed = fromJsonString[A](json).get (a, parsed) match { case (a: Throwable, parsed: Throwable) => e(a, parsed) case _ => f(a, parsed) } } implicit class AnyOp[A](a: A) { def must_==(b: A): Unit = assertEquals(b, a) } import scala.language.implicitConversions import sbt.serialization.json.JSONPickle import scala.pickling.UnpickleOps implicit def toJSONPickle(value: String): JSONPickle = JSONPickle(value) implicit def toUnpickleOps(value: String): UnpickleOps = new UnpickleOps(JSONPickle(value)) } serialization-0.1.2/serialization/src/test/scala/sbt/serialization/PicklerGrowableSpec.scala000066400000000000000000000015241253217424300324430ustar00rootroot00000000000000package sbt.serialization.spec import org.junit.Assert._ import org.junit._ import sbt.serialization._ import JUnitUtil._ case class Foo(x: Int, y: Option[Int]) object Foo { implicit val pickler = genPickler[Foo] implicit val unpickler = genUnpickler[Foo] } class PicklerGrowableTest { @Test def testUnpickleWithExtra: Unit = { SerializedValue.fromJsonString(extraFieldExample).parse[Foo].get must_== Foo(1, Some(1)) } @Test def testUnpickleWithMissing: Unit = { SerializedValue.fromJsonString(missingFieldExample).parse[Foo].get must_== Foo(1, None) } lazy val extraFieldExample = """{ | "$type": "sbt.serialization.spec.Foo", | "x": 1, | "y": 1, | "z": 1 |}""".stripMargin lazy val missingFieldExample = """{ | "$type": "sbt.serialization.spec.Foo", | "x": 1 |}""".stripMargin } serialization-0.1.2/serialization/src/test/scala/sbt/serialization/PicklerTypeSpec.scala000066400000000000000000000033641253217424300316260ustar00rootroot00000000000000package sbt.serialization.spec import org.junit.Assert._ import org.junit._ import scala.pickling.{ PicklingException } import sbt.serialization._ import JUnitUtil._ object Fruits { sealed trait Fruit case class Apple(x: Int) extends Fruit object Apple { implicit val pickler = genPickler[Apple] implicit val unpickler = genUnpickler[Apple] } case class Orange(x: Int) extends Fruit object Orange { implicit val pickler = genPickler[Orange] implicit val unpickler = genUnpickler[Orange] } object Fruit { implicit val pickler = genPickler[Fruit] implicit val unpickler = genUnpickler[Fruit] } } class PicklerTypeTest { import Fruits._ @Test def testPickleApple: Unit = { assertEquals("Apple(1)", appleExample, SerializedValue(Apple(1)).toJsonString) } @Test def testUnpickleApple: Unit = { SerializedValue.fromJsonString(appleExample).parse[Apple].get must_== Apple(1) } @Test def testUnpickleFruit: Unit = { SerializedValue.fromJsonString(appleExample).parse[Fruit].get must_== Apple(1) } @Test def testUnpickleOrange: Unit = { SerializedValue.fromJsonString(appleExample).parse[Orange].get must_== Orange(1) } @Test def testUnpickleOrangeFromUnknown: Unit = { SerializedValue.fromJsonString(unknownTypeExample).parse[Orange].get must_== Orange(1) } @Test def testUnpickleFruitFromUnknown: Unit = { try { SerializedValue(unknownTypeExample).parse[Fruit].get sys.error("didn't fail") } catch { case _: PicklingException => () } } lazy val appleExample = """{"x":1,"$type":"sbt.serialization.spec.Fruits.Apple"}""".stripMargin lazy val unknownTypeExample = """{ | "$type": "something_unknown", | "x": 1 |}""".stripMargin } serialization-0.1.2/serialization/src/test/scala/sbt/serialization/Pointed.scala000066400000000000000000000010041253217424300301470ustar00rootroot00000000000000package sbt.serialization.spec trait Pointed[F[_]] { def pointed[A: ClassManifest](a: A): F[A] } object Pointed { implicit def arrayPointed: Pointed[Array] = new Pointed[Array] { def pointed[A: ClassManifest](a: A): Array[A] = Array(a) } implicit def listPointed: Pointed[List] = new Pointed[List] { def pointed[A: ClassManifest](a: A): List[A] = List(a) } implicit def vectorPointed: Pointed[Vector] = new Pointed[Vector] { def pointed[A: ClassManifest](a: A): Vector[A] = Vector(a) } } serialization-0.1.2/serialization/src/test/scala/sbt/serialization/SerializedValueSpec.scala000066400000000000000000000024721253217424300324620ustar00rootroot00000000000000package sbt.serialization.spec import org.junit.Assert._ import org.junit._ import sbt.serialization._ import JUnitUtil._ private final case class Apple(foo: Int) extends Fruit private object Apple { implicit val pickler = Pickler.generate[Apple] implicit val unpickler = Unpickler.generate[Apple] } private final case class Orange(bar: String) extends Fruit private object Orange { implicit val pickler = Pickler.generate[Orange] implicit val unpickler = Unpickler.generate[Orange] } private sealed trait Fruit private object Fruit { implicit val pickler = Pickler.generate[Fruit] implicit val unpickler = Unpickler.generate[Fruit] } class SerializedValueTest { @Test def serializedValueParses(): Unit = { assertEquals(Apple(42), SerializedValue(Apple(42)).parse[Apple].get) } @Test def serializedValueHasTag(): Unit = { val serialized = SerializedValue(Apple(42)) assertTrue("apple has the apple tag", serialized.hasTag[Apple]) assertFalse("apple does not have the orange tag", serialized.hasTag[Orange]) val serializedFruit = SerializedValue[Fruit](Apple(42)) assertTrue("as-fruit apple has the apple tag", serializedFruit.hasTag[Apple]) val serializedOrange = SerializedValue(Orange("hello")) assertFalse("orange is not tagged as apple", serializedOrange.hasTag[Apple]) } }