Skip to content
Browse files

first commit.

  • Loading branch information...
0 parents commit 68bcb70d3d0c5569232e51c3635231331da3031f Sam Ritchie committed Jan 1, 2013
45 .gitignore
@@ -0,0 +1,45 @@
+# see also test/files/.gitignore
+/test/files/.gitignore
+
+*.jar
+*~
+
+#sbt
+/project/target/
+/project/project/target
+
+/target/
+/src/jline/target/
+
+# target directories for ant build
+/build/
+/dists/
+
+# other
+/out/
+/bin/
+/sandbox/
+
+# eclipse, intellij
+/.classpath
+/.project
+/src/intellij/*.iml
+/src/intellij/*.ipr
+/src/intellij/*.iws
+/.cache
+/.idea
+/.settings
+
+# bak files produced by ./cleanup-commit
+*.bak
+*.swp
+
+# from Scalding
+BUILD
+target/
+lib_managed/
+project/boot/
+project/build/target/
+project/plugins/target/
+project/plugins/lib_managed/
+project/plugins/src_managed/
5 CHANGES.md
@@ -0,0 +1,5 @@
+# Bijection #
+
+### Version 0.1.0 ###
+
+* Add Bijection trait with many implicits.
177 LICENSE
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
4 NOTICE
@@ -0,0 +1,4 @@
+MurmurHash implementation provided by software developed by The Apache Software
+Foundation (http://www.apache.org/).
+
+
23 README.md
@@ -0,0 +1,23 @@
+## Bijection [![Build Status](https://secure.travis-ci.org/twitter/bijection.png)](http://travis-ci.org/twitter/bijection)
+
+Bijections for Scala.
+
+## What can you do with this code?
+
+TODO: Fill in some examples here.
+
+## Maven
+
+Current version is `0.1.0`. groupid=`"com.twitter"` artifact=`"bijection_2.9.2"`.
+
+## Authors
+
+* Oscar Boykin <http://twitter.com/posco>
+* Marius Eriksen <http://twitter.com/marius>
+* Sam Ritchie <http://twitter.com/sritchie>
+
+## License
+
+Copyright 2012 Twitter, Inc.
+
+Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
69 build.sbt
@@ -0,0 +1,69 @@
+name := "bijection"
+
+version := "0.1.0-SNAPSHOT"
+
+organization := "com.twitter"
+
+scalaVersion := "2.9.2"
+
+// Use ScalaCheck
+resolvers ++= Seq(
+ "snapshots" at "http://oss.sonatype.org/content/repositories/snapshots",
+ "releases" at "http://oss.sonatype.org/content/repositories/releases"
+)
+
+libraryDependencies ++= Seq(
+ "commons-codec" % "commons-codec" % "1.7",
+ "org.scalacheck" %% "scalacheck" % "1.10.0" % "test",
+ "org.scala-tools.testing" % "specs_2.9.0-1" % "1.6.8" % "test"
+)
+
+parallelExecution in Test := true
+
+// Publishing options:
+
+publishMavenStyle := true
+
+publishTo <<= version { (v: String) =>
+ val nexus = "https://oss.sonatype.org/"
+ if (v.trim.endsWith("SNAPSHOT"))
+ Some("sonatype-snapshots" at nexus + "content/repositories/snapshots")
+ else
+ Some("sonatype-releases" at nexus + "service/local/staging/deploy/maven2")
+}
+
+publishArtifact in Test := false
+
+pomIncludeRepository := { x => false }
+
+pomExtra := (
+ <url>https://github.com/twitter/bijection</url>
+ <licenses>
+ <license>
+ <name>Apache 2</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ <comments>A business-friendly OSS license</comments>
+ </license>
+ </licenses>
+ <scm>
+ <url>git@github.com:twitter/scalding.git</url>
+ <connection>scm:git:git@github.com:twitter/bijection.git</connection>
+ </scm>
+ <developers>
+ <developer>
+ <id>oscar</id>
+ <name>Oscar Boykin</name>
+ <url>http://twitter.com/posco</url>
+ </developer>
+ <developer>
+ <id>oscar</id>
+ <name>Marius Eriksen</name>
+ <url>http://twitter.com/marius</url>
+ </developer>
+ <developer>
+ <id>sritchie</id>
+ <name>Sam Ritchie</name>
+ <url>http://twitter.com/sritchie</url>
+ </developer>
+ </developers>)
49 codegen/Generator.scala
@@ -0,0 +1,49 @@
+// Run this generator like a script:
+// scala Generator.scala > ../src/main/scala/com/twitter/bijection/GeneratedTupleBijections.scala
+val pkg = "package com.twitter.bijection"
+
+/* Example of the code generated:
+ implicit def tuple2[A1,B1,A2,B2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2]):
+ Bijection[(A1,B1),(A2,B2)] = Bijection[(A1,B1),(A2,B2)] { in =>
+ (ba(in._1), bb(in._2))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2))
+ }
+*/
+
+val lowerLetters = ('a' to 'z').toIndexedSeq
+val upperLetters = ('A' to 'Z').toIndexedSeq
+
+def bijectionParameter(i: Int): String = {
+ val l = lowerLetters(i)
+ val U = upperLetters(i)
+ "b" + l + ": Bijection[" + U + "1," + U + "2]"
+}
+
+def typeList(cnt: Int, suffix: String) =
+ upperLetters.slice(0, cnt) map { l => l.toString + suffix } mkString(",")
+
+def tupleBijectionType(cnt: Int): String =
+ "Bijection[(" + typeList(cnt, "1") + "), (" + typeList(cnt, "2") + ")]"
+
+def applyPart(i: Int): String = "b" + lowerLetters(i) + "(in._" + (i+1) + ")"
+def invertPart(i: Int): String = "b" + lowerLetters(i) + ".invert(out._" + (i+1) + ")"
+
+def expressionTuple(cnt: Int)(part: (Int) => String) =
+ (0 until cnt) map { part(_) } mkString("(", ", ", ")")
+
+// Here we put it all together:
+def implicitTuple(cnt: Int): String =
+ " implicit def tuple" + cnt + "[" + typeList(cnt,"1") + "," + typeList(cnt,"2") +
+ "](implicit " + ((0 until cnt) map { bijectionParameter(_) } mkString(", ") ) + "):\n " +
+ tupleBijectionType(cnt) + " = " + tupleBijectionType(cnt) + " { in =>\n " +
+ expressionTuple(cnt) { applyPart _ } + "\n " +
+ "} { out =>\n " +
+ expressionTuple(cnt) { invertPart _ } + "\n " +
+ "}"
+
+println("// Autogenerated code DO NOT EDIT BY HAND")
+println(pkg)
+println("\ntrait GeneratedTupleBijections {")
+(2 to 22).foreach { cnt => println(implicitTuple(cnt)) }
+println("}")
1 codegen/README
@@ -0,0 +1 @@
+This code may be removed when we move to scala 2.10 (and macros)
128 src/main/scala/com/twitter/bijection/Bijection.scala
@@ -0,0 +1,128 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import scala.annotation.implicitNotFound
+
+/**
+ * A Bijection[A, B] is a pair of functions that transform an element between
+ * types A and B isomorphically; that is, for all items,
+ *
+ * item == someBijection.inverse(someBijection.apply(item))
+ */
+
+@implicitNotFound(msg = "Cannot find Bijection type class between ${A} and ${B}")
+trait Bijection[A, B] extends (A => B) {
+ def apply(a: A): B
+ def invert(b: B): A = inverse(b)
+
+ def inverse: Bijection[B, A]
+
+ /**
+ * Composes two instances of Bijection in a new Bijection,
+ * with this one applied first.
+ */
+ def andThen[C](g: Bijection[B, C]): Bijection[A, C] =
+ Bijection[A, C] { a => g(this(a)) } { c => this.invert(g.invert(c)) }
+
+ /**
+ * Composes two instances of Bijection in a new Bijection,
+ * with this one applied last.
+ */
+ def compose[T](g: Bijection[T, A]): Bijection[T, B] = g andThen this
+}
+
+/**
+ * Biject allows the user to convert an instance of type A to type B given an implicit bijection
+ * that goes either way between the two.
+ *
+ * For example, with an implicit Bijection[String,Array[Byte]], the following works:
+ * Array(1.toByte, 2.toByte).as[String]
+ *
+ * Thanks to
+ * [hylotech](https://github.com/hylotech/suits/blob/master/src/main/scala/hylotech/util/Bijection.scala)
+ * for the following "as" pattern.
+ */
+sealed class Biject[A](a: A) {
+ def as[B](implicit f: Either[Bijection[A, B], Bijection[B, A]]): B = f.fold(_.apply(a), _.invert(a))
+}
+
+object Bijection extends NumericBijections with CollectionBijections
+ with BinaryBijections with GeneratedTupleBijections {
+
+ def apply[A, B](to: A => B)(from: B => A): Bijection[A, B] =
+ new Bijection[A, B] { self =>
+ override def apply(a: A) = to(a)
+ override val inverse = new Bijection[B, A] {
+ override def apply(b: B) = from(b)
+ override val inverse = self
+ }
+ }
+
+ /*
+ * Implicit conversion to Biject. This allows the user to use bijections as implicit
+ * conversions between types.
+ *
+ * For example, with an implicit Bijection[String, Array[Byte]], the following works:
+ * Array(1.toByte, 2.toByte).as[String]
+ */
+ implicit def biject[A](a: A): Biject[A] = new Biject(a)
+ implicit def forwardEither[A, B](implicit a: Bijection[A, B]): Either[Bijection[A, B], Bijection[B, A]] = Left(a)
+ implicit def reverseEither[A, B](implicit b: Bijection[B, A]): Either[Bijection[A, B], Bijection[B, A]] = Right(b)
+
+ implicit def identity[A]: Bijection[A, A] = new IdentityBijection[A]
+ implicit def class2String[T]: Bijection[Class[T], String] =
+ CastBijection.of[Class[T], Class[_]] andThen ClassBijection
+
+ /**
+ * Converts a function that transforms type A into a function that
+ * transforms type B.
+ */
+ implicit def fnBijection[A, B](implicit bij: Bijection[A, B]): Bijection[A => A, B => B] =
+ Bijection[A => A, B => B] { fn =>
+ { b => bij.apply(fn(bij.invert(b))) }
+ } { fn =>
+ { a => bij.invert(fn(bij.apply(a))) }
+ }
+
+ /**
+ * Converts a function that combines two arguments of type A into a function that
+ * combines two arguments of type B into a single B. Useful for converting
+ * input functions to "reduce".
+ */
+ implicit def fn2Bijection[A, B](implicit bij: Bijection[A, B]): Bijection[(A, A) => A, (B, B) => B] =
+ Bijection[(A, A) => A, (B, B) => B] { fn =>
+ { (acc, b) => bij.apply(fn(bij.invert(acc), bij.invert(b))) }
+ } { fn =>
+ { (acc, a) => bij.invert(fn(bij.apply(acc), bij.apply(a))) }
+ }
+}
+
+class IdentityBijection[A] extends Bijection[A, A] {
+ override def apply(a: A) = a
+ override val inverse = this
+
+ override def andThen[T](g: Bijection[A, T]) = g
+ override def compose[T](g: Bijection[T, A]) = g
+}
+
+/**
+ * Bijection that flips the order of items in a Tuple2.
+ */
+object SwapBijection {
+ def apply[T, U] = Bijection[(T, U), (U, T)] { _.swap } { _.swap }
+}
95 src/main/scala/com/twitter/bijection/BinaryBijections.scala
@@ -0,0 +1,95 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import java.util.zip.{ GZIPInputStream, GZIPOutputStream }
+import java.nio.ByteBuffer
+import org.apache.commons.codec.binary.Base64
+import java.io.{OutputStream, InputStream, ByteArrayInputStream, ByteArrayOutputStream}
+import annotation.tailrec
+
+// TODO: Convert to value classes on Scala 2.10 upgrade.
+case class GZippedBytes(bytes: Array[Byte])
+case class GZippedBase64String(str: String)
+case class Base64String(str: String)
+
+/**
+ * A collection of utilities for encoding strings and byte arrays to
+ * and decoding from strings compressed from with gzip.
+ *
+ * This object is thread-safe because there are no streams shared
+ * outside of method scope, and therefore no contention for shared byte arrays.
+ */
+trait BinaryBijections {
+ /**
+ * Bijection between byte array and java.nio.ByteBuffer.
+ */
+ implicit val bytes2Buffer: Bijection[Array[Byte], ByteBuffer] =
+ Bijection[Array[Byte], ByteBuffer] { ByteBuffer.wrap(_) } { byteBuffer =>
+ val buf = byteBuffer.duplicate()
+ val ret = Array.ofDim[Byte](buf.remaining())
+ buf.get(ret)
+ ret
+ }
+
+ @tailrec
+ private def copy(inputStream: InputStream, outputStream: OutputStream, bufferSize: Int = 1024) {
+ val buf = new Array[Byte](bufferSize)
+ inputStream.read(buf, 0, buf.size) match {
+ case -1 => ()
+ case n =>
+ outputStream.write(buf, 0, n)
+ copy(inputStream, outputStream, bufferSize)
+ }
+ }
+ /**
+ * Bijection between byte array and GZippedBytes.
+ */
+ implicit val bytes2GzippedBytes: Bijection[Array[Byte], GZippedBytes] =
+ Bijection[Array[Byte], GZippedBytes] { bytes =>
+ val baos = new ByteArrayOutputStream
+ val gos = new GZIPOutputStream(baos)
+ gos.write(bytes)
+ gos.finish()
+ GZippedBytes(baos.toByteArray)
+ } { case GZippedBytes(bytes) =>
+ val baos = new ByteArrayOutputStream
+ copy(new GZIPInputStream(new ByteArrayInputStream(bytes)), baos)
+ baos.toByteArray
+ }
+
+ /**
+ * Bijection between byte array and Base64 encoded string.
+ *
+ * The "trim" here is important, as encodeBase64String sometimes
+ * tags a newline on the end of its encoding. DON'T REMOVE THIS
+ * CALL TO TRIM.
+ */
+ implicit val bytes2Base64: Bijection[Array[Byte], Base64String] =
+ Bijection[Array[Byte], Base64String] { bytes =>
+ Base64String(Base64.encodeBase64String(bytes).trim)
+ } { case Base64String(str) => Base64.decodeBase64(str) }
+
+ implicit val bytes2GZippedBase64: Bijection[Array[Byte], GZippedBase64String] =
+ bytes2GzippedBytes
+ .andThen(Bijection[GZippedBytes, Array[Byte]] { _.bytes } { GZippedBytes(_) })
+ .andThen(bytes2Base64)
+ .andThen(Bijection[Base64String, GZippedBase64String] { case Base64String(s) =>
+ GZippedBase64String(s) } { case GZippedBase64String(s) =>
+ Base64String(s)
+ })
+}
33 src/main/scala/com/twitter/bijection/ClassBijection.scala
@@ -0,0 +1,33 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+/**
+ * Bijection between Class objects and string.
+ */
+object ClassBijection extends Bijection[Class[_], String] {
+ override def apply(k: Class[_]) = k.getName
+ override val inverse = Bijection[String, Class[_]] { Class.forName(_) } { this.apply(_) }
+}
+
+/**
+ * Bijection to cast back and forth between two types.
+ * Note that this uses casting and can fail at runtime.
+ */
+object CastBijection {
+ def of[A, B] = Bijection[A, B] { _.asInstanceOf[B] } { _.asInstanceOf[A] }
+}
76 src/main/scala/com/twitter/bijection/CollectionBijections.scala
@@ -0,0 +1,76 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import java.lang.{ Iterable => JIterable }
+import java.util.{
+ Collection => JCollection,
+ Dictionary => JDictionary,
+ Enumeration => JEnumeration,
+ Iterator => JIterator,
+ List => JList,
+ Map => JMap,
+ Set => JSet
+}
+import java.util.concurrent.{ ConcurrentMap => JConcurrentMap }
+import scala.collection.JavaConverters._
+import scala.collection.mutable
+import collection.generic.CanBuildFrom
+
+trait CollectionBijections {
+ /**
+ * Bijections between collection types defined in scala.collection.JavaConverters.
+ */
+ implicit def iterable2java[T]: Bijection[Iterable[T], JIterable[T]] =
+ Bijection[Iterable[T], JIterable[T]] { _.asJava } { _.asScala }
+ implicit def iterator2java[T]: Bijection[Iterator[T], JIterator[T]] =
+ Bijection[Iterator[T], JIterator[T]] { _.asJava } { _.asScala }
+ implicit def buffer2java[T]: Bijection[mutable.Buffer[T], JList[T]] =
+ Bijection[mutable.Buffer[T], JList[T]] { _.asJava } { _.asScala }
+ implicit def set2java[T]: Bijection[mutable.Set[T], JSet[T]] =
+ Bijection[mutable.Set[T], JSet[T]] { _.asJava } { _.asScala }
+ implicit def map2java[K, V]: Bijection[mutable.Map[K,V], JMap[K,V]] =
+ Bijection[mutable.Map[K,V], JMap[K,V]] { _.asJava } { _.asScala }
+ implicit def concurrentMap2java[K, V]: Bijection[mutable.ConcurrentMap[K,V], JConcurrentMap[K,V]] =
+ Bijection[mutable.ConcurrentMap[K,V], JConcurrentMap[K,V]] { _.asJava } { _.asScala }
+ implicit def iterable2jcollection[T]: Bijection[Iterable[T], JCollection[T]] =
+ Bijection[Iterable[T], JCollection[T]] { _.asJavaCollection } { _.asScala }
+ implicit def iterator2jenumeration[T]: Bijection[Iterator[T], JEnumeration[T]] =
+ Bijection[Iterator[T], JEnumeration[T]] { _.asJavaEnumeration } { _.asScala }
+ implicit def map2jdictionary[K, V]: Bijection[mutable.Map[K, V], JDictionary[K, V]] =
+ Bijection[mutable.Map[K, V], JDictionary[K, V]] { _.asJavaDictionary } { _.asScala }
+
+ /**
+ * Accepts a Bijection[A, B] and returns a bijection that can
+ * transform traversable containers of A into traversable containers of B.
+ *
+ * Be careful going from ordered to unordered containers;
+ * Bijection[Iterable[A], Set[B]] is inaccurate, and really makes
+ * no sense.
+ */
+ def toContainer[A, B, C <: TraversableOnce[A], D <: TraversableOnce[B]]
+ (implicit bij: Bijection[A, B], cd: CanBuildFrom[C, B, D], dc: CanBuildFrom[D, A, C]) =
+ Bijection[C, D] { c: C =>
+ val builder = cd(c)
+ c foreach { builder += bij(_) }
+ builder.result()
+ } { d: D =>
+ val builder = dc(d)
+ d foreach { builder += bij.invert(_) }
+ builder.result()
+ }
+}
131 src/main/scala/com/twitter/bijection/GeneratedTupleBijections.scala
@@ -0,0 +1,131 @@
+// Autogenerated code DO NOT EDIT BY HAND
+package com.twitter.bijection
+
+trait GeneratedTupleBijections {
+ implicit def tuple2[A1,B1,A2,B2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2]):
+ Bijection[(A1,B1), (A2,B2)] = Bijection[(A1,B1), (A2,B2)] { in =>
+ (ba(in._1), bb(in._2))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2))
+ }
+ implicit def tuple3[A1,B1,C1,A2,B2,C2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2]):
+ Bijection[(A1,B1,C1), (A2,B2,C2)] = Bijection[(A1,B1,C1), (A2,B2,C2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3))
+ }
+ implicit def tuple4[A1,B1,C1,D1,A2,B2,C2,D2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2]):
+ Bijection[(A1,B1,C1,D1), (A2,B2,C2,D2)] = Bijection[(A1,B1,C1,D1), (A2,B2,C2,D2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4))
+ }
+ implicit def tuple5[A1,B1,C1,D1,E1,A2,B2,C2,D2,E2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2]):
+ Bijection[(A1,B1,C1,D1,E1), (A2,B2,C2,D2,E2)] = Bijection[(A1,B1,C1,D1,E1), (A2,B2,C2,D2,E2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5))
+ }
+ implicit def tuple6[A1,B1,C1,D1,E1,F1,A2,B2,C2,D2,E2,F2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2]):
+ Bijection[(A1,B1,C1,D1,E1,F1), (A2,B2,C2,D2,E2,F2)] = Bijection[(A1,B1,C1,D1,E1,F1), (A2,B2,C2,D2,E2,F2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6))
+ }
+ implicit def tuple7[A1,B1,C1,D1,E1,F1,G1,A2,B2,C2,D2,E2,F2,G2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1), (A2,B2,C2,D2,E2,F2,G2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1), (A2,B2,C2,D2,E2,F2,G2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7))
+ }
+ implicit def tuple8[A1,B1,C1,D1,E1,F1,G1,H1,A2,B2,C2,D2,E2,F2,G2,H2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1), (A2,B2,C2,D2,E2,F2,G2,H2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1), (A2,B2,C2,D2,E2,F2,G2,H2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8))
+ }
+ implicit def tuple9[A1,B1,C1,D1,E1,F1,G1,H1,I1,A2,B2,C2,D2,E2,F2,G2,H2,I2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1), (A2,B2,C2,D2,E2,F2,G2,H2,I2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1), (A2,B2,C2,D2,E2,F2,G2,H2,I2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9))
+ }
+ implicit def tuple10[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10))
+ }
+ implicit def tuple11[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11))
+ }
+ implicit def tuple12[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12))
+ }
+ implicit def tuple13[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13))
+ }
+ implicit def tuple14[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14))
+ }
+ implicit def tuple15[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2], bo: Bijection[O1,O2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14), bo(in._15))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14), bo.invert(out._15))
+ }
+ implicit def tuple16[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2], bo: Bijection[O1,O2], bp: Bijection[P1,P2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14), bo(in._15), bp(in._16))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14), bo.invert(out._15), bp.invert(out._16))
+ }
+ implicit def tuple17[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2], bo: Bijection[O1,O2], bp: Bijection[P1,P2], bq: Bijection[Q1,Q2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14), bo(in._15), bp(in._16), bq(in._17))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14), bo.invert(out._15), bp.invert(out._16), bq.invert(out._17))
+ }
+ implicit def tuple18[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2], bo: Bijection[O1,O2], bp: Bijection[P1,P2], bq: Bijection[Q1,Q2], br: Bijection[R1,R2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14), bo(in._15), bp(in._16), bq(in._17), br(in._18))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14), bo.invert(out._15), bp.invert(out._16), bq.invert(out._17), br.invert(out._18))
+ }
+ implicit def tuple19[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2], bo: Bijection[O1,O2], bp: Bijection[P1,P2], bq: Bijection[Q1,Q2], br: Bijection[R1,R2], bs: Bijection[S1,S2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14), bo(in._15), bp(in._16), bq(in._17), br(in._18), bs(in._19))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14), bo.invert(out._15), bp.invert(out._16), bq.invert(out._17), br.invert(out._18), bs.invert(out._19))
+ }
+ implicit def tuple20[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2], bo: Bijection[O1,O2], bp: Bijection[P1,P2], bq: Bijection[Q1,Q2], br: Bijection[R1,R2], bs: Bijection[S1,S2], bt: Bijection[T1,T2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14), bo(in._15), bp(in._16), bq(in._17), br(in._18), bs(in._19), bt(in._20))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14), bo.invert(out._15), bp.invert(out._16), bq.invert(out._17), br.invert(out._18), bs.invert(out._19), bt.invert(out._20))
+ }
+ implicit def tuple21[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2,U2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2], bo: Bijection[O1,O2], bp: Bijection[P1,P2], bq: Bijection[Q1,Q2], br: Bijection[R1,R2], bs: Bijection[S1,S2], bt: Bijection[T1,T2], bu: Bijection[U1,U2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2,U2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2,U2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14), bo(in._15), bp(in._16), bq(in._17), br(in._18), bs(in._19), bt(in._20), bu(in._21))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14), bo.invert(out._15), bp.invert(out._16), bq.invert(out._17), br.invert(out._18), bs.invert(out._19), bt.invert(out._20), bu.invert(out._21))
+ }
+ implicit def tuple22[A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2,U2,V2](implicit ba: Bijection[A1,A2], bb: Bijection[B1,B2], bc: Bijection[C1,C2], bd: Bijection[D1,D2], be: Bijection[E1,E2], bf: Bijection[F1,F2], bg: Bijection[G1,G2], bh: Bijection[H1,H2], bi: Bijection[I1,I2], bj: Bijection[J1,J2], bk: Bijection[K1,K2], bl: Bijection[L1,L2], bm: Bijection[M1,M2], bn: Bijection[N1,N2], bo: Bijection[O1,O2], bp: Bijection[P1,P2], bq: Bijection[Q1,Q2], br: Bijection[R1,R2], bs: Bijection[S1,S2], bt: Bijection[T1,T2], bu: Bijection[U1,U2], bv: Bijection[V1,V2]):
+ Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2,U2,V2)] = Bijection[(A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1), (A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2,N2,O2,P2,Q2,R2,S2,T2,U2,V2)] { in =>
+ (ba(in._1), bb(in._2), bc(in._3), bd(in._4), be(in._5), bf(in._6), bg(in._7), bh(in._8), bi(in._9), bj(in._10), bk(in._11), bl(in._12), bm(in._13), bn(in._14), bo(in._15), bp(in._16), bq(in._17), br(in._18), bs(in._19), bt(in._20), bu(in._21), bv(in._22))
+ } { out =>
+ (ba.invert(out._1), bb.invert(out._2), bc.invert(out._3), bd.invert(out._4), be.invert(out._5), bf.invert(out._6), bg.invert(out._7), bh.invert(out._8), bi.invert(out._9), bj.invert(out._10), bk.invert(out._11), bl.invert(out._12), bm.invert(out._13), bn.invert(out._14), bo.invert(out._15), bp.invert(out._16), bq.invert(out._17), br.invert(out._18), bs.invert(out._19), bt.invert(out._20), bu.invert(out._21), bv.invert(out._22))
+ }
+}
92 src/main/scala/com/twitter/bijection/NumericBijections.scala
@@ -0,0 +1,92 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import java.lang.{
+ Short => JShort,
+ Integer => JInt,
+ Long => JLong,
+ Float => JFloat,
+ Double => JDouble,
+ Byte => JByte
+}
+import java.nio.ByteBuffer
+
+trait NumericBijections {
+ /**
+ * Bijections between the numeric types and their java versions.
+ */
+ implicit val byte2Boxed: Bijection[Byte, JByte] =
+ Bijection[Byte, JByte] { JByte.valueOf(_) } { _.byteValue }
+ implicit val short2Boxed: Bijection[Short, JShort] =
+ Bijection[Short, JShort] { JShort.valueOf(_) } { _.shortValue }
+ implicit val int2Boxed: Bijection[Int, JInt] =
+ Bijection[Int, JInt] { JInt.valueOf(_) } { _.intValue }
+ implicit val long2Boxed: Bijection[Long, JLong] =
+ Bijection[Long, JLong] { JLong.valueOf(_) } { _.longValue }
+ implicit val float2Boxed: Bijection[Float, JFloat] =
+ Bijection[Float, JFloat] { JFloat.valueOf(_) } { _.floatValue }
+ implicit val double2Boxed: Bijection[Double, JDouble] =
+ Bijection[Double, JDouble] { JDouble.valueOf(_) } { _.doubleValue }
+
+ /**
+ * Bijections between the numeric types and string.
+ */
+ implicit val byte2String: Bijection[Byte, String] =
+ Bijection[Byte, String] { _.toString } { _.toByte }
+ implicit val short2String: Bijection[Short, String] =
+ Bijection[Short, String] { _.toString } { _.toShort }
+ implicit val int2String: Bijection[Int, String] =
+ Bijection[Int, String] { _.toString } { _.toInt }
+ implicit val long2String: Bijection[Long, String] =
+ Bijection[Long, String] { _.toString } { _.toLong }
+ implicit val float2String: Bijection[Float, String] =
+ Bijection[Float, String] { _.toString } { _.toFloat }
+ implicit val double2String: Bijection[Double, String] =
+ Bijection[Double, String] { _.toString } { _.toDouble }
+
+ /**
+ * Bijections between the numeric types and Array[Byte].
+ */
+ val float2IntIEEE754: Bijection[Float, Int] =
+ Bijection[Float, Int] { JFloat.floatToIntBits(_) } { JFloat.intBitsToFloat(_) }
+ val double2LongIEEE754: Bijection[Double, Long] =
+ Bijection[Double, Long] { JDouble.doubleToLongBits(_) } { JDouble.longBitsToDouble(_) }
+
+ implicit val short2BigEndian: Bijection[Short, Array[Byte]] =
+ Bijection[Short, Array[Byte]] { value =>
+ val buf = ByteBuffer.allocate(2)
+ buf.putShort(value)
+ buf.array
+ } { ByteBuffer.wrap(_).getShort }
+ implicit val int2BigEndian: Bijection[Int, Array[Byte]] =
+ Bijection[Int, Array[Byte]] { value =>
+ val buf = ByteBuffer.allocate(4)
+ buf.putInt(value)
+ buf.array
+ } { ByteBuffer.wrap(_).getInt }
+ implicit val long2BigEndian: Bijection[Long, Array[Byte]] =
+ Bijection[Long, Array[Byte]] { value =>
+ val buf = ByteBuffer.allocate(8)
+ buf.putLong(value)
+ buf.array
+ } { ByteBuffer.wrap(_).getLong }
+ implicit val float2BigEndian: Bijection[Float, Array[Byte]] =
+ float2IntIEEE754 andThen int2BigEndian
+ implicit val double2BigEndian: Bijection[Double, Array[Byte]] =
+ double2LongIEEE754 andThen long2BigEndian
+}
159 src/main/scala/com/twitter/bijection/Pivot.scala
@@ -0,0 +1,159 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+object Pivot {
+ /**
+ * Returns a new Pivot[K, K1, K2] using the supplied bijection
+ * to split each input key.
+ */
+ def apply[K, K1, K2](bijection: Bijection[K, (K1, K2)]): Pivot[K, K1, K2] =
+ new Pivot[K, K1, K2] {
+ override val pivot = bijection
+ }
+
+ /**
+ * Returns a new Pivot[(K, V), V, K] -- this Pivot can be used to
+ * transform a Map[K,V] -> Map[V,Iterable[K]].
+ */
+ def swap[K, V]: Pivot[(K, V), V, K] = apply(SwapBijection[K, V])
+
+ def encoder[K, K1, K2](fn: K => (K1, K2)): PivotEncoder[K, K1, K2] =
+ new PivotEncoder[K, K1, K2] {
+ override val enc = fn
+ }
+ def decoder[K, K1, K2](fn: ((K1, K2)) => K): PivotDecoder[K, K1, K2] =
+ new PivotDecoder[K, K1, K2] {
+ override val dec = fn
+ }
+}
+
+trait PivotEncoder[K, K1, K2] extends (Iterable[K] => Map[K1, Iterable[K2]]) {
+ def enc: (K) => (K1, K2)
+ /**
+ * Pivots an Iterable[K] into Map[K1, Iterable[K2]] by
+ * pivoting each K into (K1, K2) and grouping all
+ * instances of K2 for each K1 into an iterable.
+ */
+ def apply(pairs: Iterable[K]): Map[K1, Iterable[K2]] =
+ pairs.map { k =>
+ val (k1, k2) = enc(k)
+ (k1 -> List(k2))
+ }
+ .groupBy { _._1 }
+ .mapValues { _.map { case (_,k2s) => k2s }.flatten }
+
+ /**
+ * "Uncurries" the supplied nested fn of K1 and K2 into a function
+ * that accepts a single K.
+ */
+ def unsplit[V](fn: K1 => K2 => V): K => V = { k =>
+ val (k1,k2) = enc(k)
+ fn(k1)(k2)
+ }
+}
+
+trait PivotDecoder[K, K1, K2] extends (Map[K1, Iterable[K2]] => Iterable[K]) {
+ def dec: ((K1, K2)) => K
+
+ /**
+ * Expands a Map[K1, Iterable[K2]] into the original
+ * Iterable[K] by joining every instance of K2 in each
+ * Iterable[K2] with its corresponding instance of K1 and
+ * calling `dec` on each pair.
+ */
+ def apply(m: Map[K1,Iterable[K2]]) = for ((k1, k2s) <- m; k2 <- k2s) yield dec((k1,k2))
+
+ /**
+ * Curries the supplied fn of K into a nested function
+ * of K1 then K2 using the inversion of `pivot`.
+ */
+ def split[V](fn: K => V): K1 => K2 => V =
+ { k1 => { k2 => fn(dec((k1,k2))) } }
+}
+
+/**
+ * Pivot is useful in moving from a 1D space of K to a 2D mapping
+ * space of K1 x K2. If the elements within the K space have many repeated
+ * elements -- imagine the "time" component of a Key in a timeseries key-value
+ * store -- pivoting the changing component into an inner K2 while leaving
+ * the repeated component in an outer K1 can assist in compressing
+ * a datastore's space requirements.
+ *
+ * Type Parameters:
+ *
+ * K: Original Key
+ * K1: Outer Key
+ * K2: Inner Key
+ *
+ * Trivial: Pivot[(Event, Timestamp), Event, Timestamp] would
+ * pivot the timestamp component out of a compound key.
+ *
+ * @author Oscar Boykin
+ * @author Sam Ritchie
+ */
+
+trait Pivot[K, K1, K2] extends Bijection[Iterable[K], Map[K1, Iterable[K2]]] {
+ def pivot: Bijection[K, (K1, K2)]
+
+ def andThenPivot[K3, K4](after: Bijection[(K1, K2), (K3, K4)]): Pivot[K, K3, K4] =
+ Pivot(pivot andThen after)
+
+ def composePivot[T](before: Bijection[T, K]): Pivot[T, K1, K2] =
+ Pivot(pivot compose before)
+
+ lazy val encoder = Pivot.encoder[K, K1, K2](pivot.apply _)
+ lazy val decoder = Pivot.decoder[K, K1, K2](pivot.invert _)
+
+ override def apply(pairs: Iterable[K]): Map[K1, Iterable[K2]] = encoder(pairs)
+ override val inverse =
+ Bijection[Map[K1, Iterable[K2]], Iterable[K]] { decoder(_) } { this.apply(_) }
+
+ def split[V](fn: K => V): K1 => K2 => V = decoder.split(fn)
+ def unsplit[V](fn: K1 => K2 => V): K => V = encoder.unsplit(fn)
+
+ /**
+ * Returns a new Pivot that converts an Iterable of (K, T) to an Iterable of
+ * ((K1, T), Iterable[K2]). This is useful for applying a new pivoting scheme
+ * on top of this one while maintaining some outer key component.
+ */
+
+ def wrapOuter[T]: Pivot[(K, T), (K1, T), K2] =
+ withValue[T] andThenPivot(
+ Bijection[(K1, (K2, T)), ((K1, T), K2)] { pair: (K1, (K2, T)) =>
+ val (k1, (k2, t)) = pair
+ ((k1, t), k2)
+ } { pair: ((K1, T), K2) =>
+ val ((k1, t), k2) = pair
+ (k1, (k2, t))
+ })
+
+ /**
+ * Returns a new pivot that converts an Iterable of (K, V) to an Iterable of
+ * (K1, Iterable[(K2, V)]). This is useful for pivoting multiple (K, V) pairs into
+ * a single key in some KV store.
+ */
+ def withValue[V]: Pivot[(K, V), K1, (K2, V)] =
+ Pivot(Bijection[(K, V), (K1, (K2, V))] { case pair@(k, v) =>
+ val (k1, k2) = pivot(k)
+ (k1, (k2, v))
+ } { pair: (K1, (K2, V)) =>
+ val (k1, (k2, v)) = pair
+ val k = pivot.invert((k1, k2))
+ (k, v)
+ })
+}
37 src/main/scala/com/twitter/bijection/StringBijections.scala
@@ -0,0 +1,37 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+object StringCodec {
+ def utf8: Bijection[String, Array[Byte]] = withEncoding("UTF-8")
+ def withEncoding(encoding: String): Bijection[String, Array[Byte]] =
+ Bijection[String, Array[Byte]] { _.getBytes(encoding) } { new String(_, encoding) }
+}
+
+/**
+ * Bijection for joining together iterables of strings into a single string
+ * and splitting them back out. Useful for storing sequences of strings
+ * in Config maps.
+ */
+object StringJoinBijection {
+ def apply(separator: String = ":") =
+ Bijection[Iterable[String], String] { xs =>
+ // TODO: Instead of throwing, escape the separator in the encoded string.
+ assert(!xs.exists(_.contains(separator)), "Can't encode strings that include the separator.")
+ xs.mkString(separator)
+ } { _.split(separator) }
+}
28 src/main/scala/com/twitter/package.scala
@@ -0,0 +1,28 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter
+
+/**
+ * Bijection trait with numerous implementations.
+ *
+ * A Bijection[A, B] is an invertible function from A -> B.
+ * Knowing that two types have this relationship can be very helpful
+ * for serialization (Bijection[T, Array[Byte]]]), communication between
+ * libraries (Bijection[MyTrait, YourTrait]) and many other purposes.
+ */
+package object bijection {
+}
28 src/test/scala/com/twitter/bijection/BaseProperties.scala
@@ -0,0 +1,28 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import org.scalacheck.{ Arbitrary, Properties }
+import org.scalacheck.Prop.forAll
+
+trait BaseProperties {
+ def rt[A, B](a: A)(implicit bij: Bijection[A, B]): A = bij.invert(bij(a))
+ def defaultEq[A](a1: A, a2: A) = a1 == a2
+ def roundTrips[A, B](eqFn: (A, A) => Boolean = defaultEq _)
+ (implicit a: Arbitrary[A], bij: Bijection[A, B]) =
+ forAll { a: A => eqFn(a, rt(a)) }
+}
36 src/test/scala/com/twitter/bijection/BinaryBijectionLaws.scala
@@ -0,0 +1,36 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import java.nio.ByteBuffer
+import java.util.Arrays
+import org.scalacheck.Properties
+import org.scalacheck.Prop._
+
+object BinaryBijectionLaws extends Properties("BinaryBijections")
+with BaseProperties {
+ def barrEq(a1: Array[Byte], a2: Array[Byte]) = Arrays.equals(a1, a2)
+
+ property("rts Array[Byte] -> ByteBuffer") =
+ roundTrips[Array[Byte], ByteBuffer](barrEq)
+ property("rts Array[Byte] -> GZippedBytes") =
+ roundTrips[Array[Byte], GZippedBytes](barrEq)
+ property("rts Array[Byte] -> Base64String") =
+ roundTrips[Array[Byte], Base64String](barrEq)
+ property("rts Array[Byte] -> GZippedBase64String") =
+ roundTrips[Array[Byte], GZippedBase64String](barrEq)
+}
52 src/test/scala/com/twitter/bijection/NumericBijectionLaws.scala
@@ -0,0 +1,52 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import java.lang.{
+ Short => JShort,
+ Integer => JInt,
+ Long => JLong,
+ Float => JFloat,
+ Double => JDouble,
+ Byte => JByte
+}
+
+import org.scalacheck.Properties
+import org.scalacheck.Prop.forAll
+
+object NumericBijectionLaws extends Properties("NumericBijections")
+with BaseProperties {
+ property("round trips byte -> jbyte") = roundTrips[Byte, JByte]()
+ property("round trips short -> jshort") = roundTrips[Short, JShort]()
+ property("round trips int -> jint") = roundTrips[Int, JInt]()
+ property("round trips long -> jlong") = roundTrips[Long, JLong]()
+ property("round trips float -> jfloat") = roundTrips[Float, JFloat]()
+ property("round trips double -> jdouble") = roundTrips[Double, JDouble]()
+
+ property("round trips byte -> string") = roundTrips[Byte, String]()
+ property("round trips short -> string") = roundTrips[Short, String]()
+ property("round trips int -> string") = roundTrips[Int, String]()
+ property("round trips long -> string") = roundTrips[Long, String]()
+ property("round trips float -> string") = roundTrips[Float, String]()
+ property("round trips double -> string") = roundTrips[Double, String]()
+
+ property("round trips short -> Array[Byte]") = roundTrips[Short, Array[Byte]]()
+ property("round trips int -> Array[Byte]") = roundTrips[Int, Array[Byte]]()
+ property("round trips long -> Array[Byte]") = roundTrips[Long, Array[Byte]]()
+ property("round trips float -> Array[Byte]") = roundTrips[Float, Array[Byte]]()
+ property("round trips double -> Array[Byte]") = roundTrips[Double, Array[Byte]]()
+}
33 src/test/scala/com/twitter/bijection/StringBijectionLaws.scala
@@ -0,0 +1,33 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import org.scalacheck.Properties
+import org.scalacheck.Prop._
+
+object StringBijectionLaws extends Properties("StringBijections")
+with BaseProperties {
+ implicit val bij: Bijection[String, Array[Byte]] = StringCodec.utf8
+
+ property("round trips string -> Array[Byte]") = roundTrips[String, Array[Byte]]()
+ property("rts through StringJoinBijection") =
+ forAll { (sep: String, xs: List[String]) =>
+ val sjBij = StringJoinBijection(sep)
+ val iter = xs.toIterable
+ (!iter.exists(_.contains(sep))) ==> (iter == rt(iter)(sjBij))
+ }
+}
38 src/test/scala/com/twitter/bijection/TupleBijectionLaws.scala
@@ -0,0 +1,38 @@
+/*
+Copyright 2012 Twitter, Inc.
+
+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.
+*/
+
+package com.twitter.bijection
+
+import java.lang.{
+ Short => JShort,
+ Integer => JInt,
+ Long => JLong,
+ Float => JFloat,
+ Double => JDouble,
+ Byte => JByte
+}
+
+import org.scalacheck.Properties
+import org.scalacheck.Prop.forAll
+
+object TupleBijectionLaws extends Properties("TupleBijections")
+with BaseProperties {
+ property("round trips (Int,Long) -> (String,String)") = roundTrips[(Int,Long), (String,String)]()
+ property("round trips (Int,Long,String) -> (String,String,String)") =
+ roundTrips[(Int,Long,String), (String,String,String)]()
+ property("round trips (Int,Long,String,Long) -> (String,String,String,Array[Byte])") =
+ roundTrips[(Int,Long,String,Long), (String,String,String,Array[Byte])]()
+}

0 comments on commit 68bcb70

Please sign in to comment.
Something went wrong with that request. Please try again.