From fe1be363d83b3e4242479756c85bdd93c523bb43 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sat, 6 Jul 2013 21:05:52 -0700 Subject: [PATCH 01/32] added Date classes date classes to support joda time and other date conversions. --- .../twitter/bijection/DateBijections.scala | 44 ++++++++++++++++++ .../twitter/bijection/DateInjections.scala | 46 +++++++++++++++++++ project/Build.scala | 5 +- 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala create mode 100644 bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala new file mode 100644 index 000000000..737c3109e --- /dev/null +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala @@ -0,0 +1,44 @@ +package com.twitter.bijection + +import java.util._ + +trait DateBijections extends GeneratedTupleBijections { + + implicit val date2Long: Bijection[java.util.Date, Long] = + new AbstractBijection[java.util.Date, Long] { + def apply(d: java.util.Date) = d.getTime + override def invert(l: Long) = new java.util.Date(l) + } + + implicit val date2String: Bijection[java.util.Date, String] = + new AbstractBijection[java.util.Date, String] { + def apply(d: java.util.Date) = d.toString() + override def invert(s: String) = new DateTime(s).toDate() + + } + + implicit val date2joda: Bijection[java.util.Date, DateTime] = + new AbstractBijection[java.util.Date, DateTime] { + def apply(d: java.util.Date) = new DateTime(d) + override def invert(joda: DateTime) = joda.toDate() + } + + implicit val joda2Long: Bijection[DateTime, Long] = + new AbstractBijection[DateTime, Long] { + def apply(d: DateTime) = d.getMillis() + override def invert(l: Long) = new DateTime(l) + } + + implicit val joda2String: Bijection[DateTime, String] = + new AbstractBijection[DateTime, String] { + def apply(d: DateTime) = d.toString() + override def invert(s: String) = new DateTime(s) + } + + implicit val joda2Date: Bijection[DateTime, java.util.Date] = + new AbstractBijection[DateTime, java.util.Date] { + def apply(d: DateTime) = d.toDate() + override def invert(dt: java.util.Date) = new DateTime(dt) + } + +} \ No newline at end of file diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala new file mode 100644 index 000000000..8edd9f666 --- /dev/null +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala @@ -0,0 +1,46 @@ +package com.twitter.bijection + +import java.util._ +import scala.util.control.Exception.allCatch + +trait DateInjections extends GeneratedTupleInjections { + + implicit val date2String: Injection[Date, String] = + new AbstractInjection[Date, String] { + def apply(d: Date) = d.toString + override def invert(s: String) = allCatch.opt(new DateTime(s).toDate()) + } + + implicit val date2Long: Injection[Date, Long] = + new AbstractInjection[Date, Long] { + def apply(d: Date) = new DateTime(d).getMillis() + override def invert(l: Long) = allCatch.opt(new DateTime(l).toDate()) + } + + implicit val date2joda: Injection[Date, DateTime] = + new AbstractInjection[Date, DateTime] { + def apply(d: Date) = new DateTime(d) + override def invert(dt: DateTime) = allCatch.opt(dt.toDate()) + } + + + + implicit val joda2String: Injection[DateTime, String] = + new AbstractInjection[DateTime, String] { + def apply(d: DateTime) = d.toString + override def invert(s: String) = allCatch.opt(new DateTime(s)) + } + + + implicit val joda2Long: Injection[DateTime, Long] = + new AbstractInjection[DateTime, Long] { + def apply(d: DateTime) = d.getMillis() + override def invert(l: Long) = allCatch.opt(new DateTime(l)) + } + + implicit val joda2Date: Injection[DateTime, Date] = + new AbstractInjection[DateTime, Date] { + def apply(d: DateTime) = d.toDate() + override def invert(d: Date) = allCatch.opt(new DateTime(d)) + } +} \ No newline at end of file diff --git a/project/Build.scala b/project/Build.scala index 40aef39f9..481ac8969 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -17,9 +17,12 @@ object BijectionBuild extends Build { javacOptions in doc := Seq("-source", "1.6"), + //nscala-time to support joda time libraryDependencies ++= Seq( "org.scalacheck" %% "scalacheck" % "1.10.0" % "test" withSources(), - "org.scala-tools.testing" %% "specs" % "1.6.9" % "test" withSources() + "org.scala-tools.testing" %% "specs" % "1.6.9" % "test" withSources(), + "com.github.nscala-time" %% "nscala-time" % "0.4.2" + ), resolvers ++= Seq( From 73482121091e5354a0df18698d72d74788e98a71 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sun, 7 Jul 2013 09:49:57 -0700 Subject: [PATCH 02/32] added correct import statement for jodatime --- .../src/main/scala/com/twitter/bijection/DateBijections.scala | 1 + .../src/main/scala/com/twitter/bijection/DateInjections.scala | 2 ++ 2 files changed, 3 insertions(+) diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala index 737c3109e..8c90cb591 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala @@ -1,6 +1,7 @@ package com.twitter.bijection import java.util._ +import com.github.nscala_time.time.Imports._ trait DateBijections extends GeneratedTupleBijections { diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala index 8edd9f666..d590dc3cf 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala @@ -2,6 +2,8 @@ package com.twitter.bijection import java.util._ import scala.util.control.Exception.allCatch +import com.github.nscala_time.time.Imports._ + trait DateInjections extends GeneratedTupleInjections { From 63f2c0eb2581628aba3add9482fb7a35861f63f4 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Tue, 9 Jul 2013 06:01:55 -0700 Subject: [PATCH 03/32] updated code to use Attempt --- .../twitter/bijection/DateBijections.scala | 1 + .../twitter/bijection/DateInjections.scala | 30 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala index 8c90cb591..bea41c496 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala @@ -1,6 +1,7 @@ package com.twitter.bijection import java.util._ +import com.github.nscala_time.time._ import com.github.nscala_time.time.Imports._ trait DateBijections extends GeneratedTupleBijections { diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala index d590dc3cf..04cf984f0 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala @@ -1,48 +1,48 @@ package com.twitter.bijection import java.util._ -import scala.util.control.Exception.allCatch +import com.github.nscala_time.time._ import com.github.nscala_time.time.Imports._ - +import scala.util.control.Exception.allCatch +import Bijection.build +import Inversion.{ attempt, attemptWhen } trait DateInjections extends GeneratedTupleInjections { implicit val date2String: Injection[Date, String] = new AbstractInjection[Date, String] { def apply(d: Date) = d.toString - override def invert(s: String) = allCatch.opt(new DateTime(s).toDate()) + override def invert(s: String) = attempt(s)(new DateTime(_).toDate()) + } implicit val date2Long: Injection[Date, Long] = new AbstractInjection[Date, Long] { def apply(d: Date) = new DateTime(d).getMillis() - override def invert(l: Long) = allCatch.opt(new DateTime(l).toDate()) + override def invert(l: Long) = attempt(l)(new DateTime(_).toDate()) } implicit val date2joda: Injection[Date, DateTime] = new AbstractInjection[Date, DateTime] { def apply(d: Date) = new DateTime(d) - override def invert(dt: DateTime) = allCatch.opt(dt.toDate()) - } - + override def invert(dt: DateTime) = attempt(dt)(_.toDate()) + } - - implicit val joda2String: Injection[DateTime, String] = + implicit val joda2String: Injection[DateTime, String] = new AbstractInjection[DateTime, String] { def apply(d: DateTime) = d.toString - override def invert(s: String) = allCatch.opt(new DateTime(s)) + override def invert(s: String) = attempt(s)(new DateTime(_)) } - - implicit val joda2Long: Injection[DateTime, Long] = + implicit val joda2Long: Injection[DateTime, Long] = new AbstractInjection[DateTime, Long] { def apply(d: DateTime) = d.getMillis() - override def invert(l: Long) = allCatch.opt(new DateTime(l)) + override def invert(l: Long) = attempt(l)(new DateTime(_)) } - + implicit val joda2Date: Injection[DateTime, Date] = new AbstractInjection[DateTime, Date] { def apply(d: DateTime) = d.toDate() - override def invert(d: Date) = allCatch.opt(new DateTime(d)) + override def invert(d: Date) = attempt(d)(new DateTime(_)) } } \ No newline at end of file From f3426825bcf5d44ef01395f742ff2f66c6f0202a Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Tue, 9 Jul 2013 06:20:45 -0700 Subject: [PATCH 04/32] removed some bijections --- .../twitter/bijection/DateBijections.scala | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala index bea41c496..7cf43a72f 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala @@ -6,41 +6,12 @@ import com.github.nscala_time.time.Imports._ trait DateBijections extends GeneratedTupleBijections { - implicit val date2Long: Bijection[java.util.Date, Long] = - new AbstractBijection[java.util.Date, Long] { - def apply(d: java.util.Date) = d.getTime - override def invert(l: Long) = new java.util.Date(l) - } - - implicit val date2String: Bijection[java.util.Date, String] = - new AbstractBijection[java.util.Date, String] { - def apply(d: java.util.Date) = d.toString() - override def invert(s: String) = new DateTime(s).toDate() - - } - + implicit val date2joda: Bijection[java.util.Date, DateTime] = new AbstractBijection[java.util.Date, DateTime] { def apply(d: java.util.Date) = new DateTime(d) override def invert(joda: DateTime) = joda.toDate() } - implicit val joda2Long: Bijection[DateTime, Long] = - new AbstractBijection[DateTime, Long] { - def apply(d: DateTime) = d.getMillis() - override def invert(l: Long) = new DateTime(l) - } - - implicit val joda2String: Bijection[DateTime, String] = - new AbstractBijection[DateTime, String] { - def apply(d: DateTime) = d.toString() - override def invert(s: String) = new DateTime(s) - } - - implicit val joda2Date: Bijection[DateTime, java.util.Date] = - new AbstractBijection[DateTime, java.util.Date] { - def apply(d: DateTime) = d.toDate() - override def invert(dt: java.util.Date) = new DateTime(dt) - } - + } \ No newline at end of file From 4fefd06af150a7d156d419f6582809f42267a3e8 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Tue, 9 Jul 2013 19:57:47 -0700 Subject: [PATCH 05/32] created new package called bijections-jodatime --- .../bijection/jodatime/DateBijections.scala | 20 ++++++++ .../bijection/jodatime/DateInjections.scala | 47 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala create mode 100644 bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala new file mode 100644 index 000000000..56b5f6d40 --- /dev/null +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala @@ -0,0 +1,20 @@ +package com.twitter.bijection.jodatime + +import com.twitter.bijection.{ Bijection, GeneratedTupleBijections, AbstractBijection} + +import com.twitter.bijection.Inversion.{ attempt, attemptWhen } +import java.util._ +import com.github.nscala_time.time._ +import com.github.nscala_time.time.Imports._ + +trait DateBijections extends GeneratedTupleBijections { + + + implicit val date2joda: Bijection[java.util.Date, DateTime] = + new AbstractBijection[java.util.Date, DateTime] { + def apply(d: java.util.Date) = new DateTime(d) + override def invert(joda: DateTime) = joda.toDate() + } + + +} \ No newline at end of file diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala new file mode 100644 index 000000000..999876c4f --- /dev/null +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala @@ -0,0 +1,47 @@ +package com.twitter.bijection.jodatime + +import java.util._ +import com.github.nscala_time.time._ +import com.github.nscala_time.time.Imports._ +import com.twitter.bijection.Inversion.attempt +import com.twitter.bijection.{ Injection, InversionFailure, GeneratedTupleInjections, AbstractInjection } + +trait DateInjections extends GeneratedTupleInjections { + + implicit val date2String: Injection[Date, String] = + new AbstractInjection[Date, String] { + def apply(d: Date) = d.toString + override def invert(s: String) = attempt(s)(new DateTime(_).toDate()) + + } + + implicit val date2Long: Injection[Date, Long] = + new AbstractInjection[Date, Long] { + def apply(d: Date) = new DateTime(d).getMillis() + override def invert(l: Long) = attempt(l)(new DateTime(_).toDate()) + } + + implicit val date2joda: Injection[Date, DateTime] = + new AbstractInjection[Date, DateTime] { + def apply(d: Date) = new DateTime(d) + override def invert(dt: DateTime) = attempt(dt)(_.toDate()) + } + + implicit val joda2String: Injection[DateTime, String] = + new AbstractInjection[DateTime, String] { + def apply(d: DateTime) = d.toString + override def invert(s: String) = attempt(s)(new DateTime(_)) + } + + implicit val joda2Long: Injection[DateTime, Long] = + new AbstractInjection[DateTime, Long] { + def apply(d: DateTime) = d.getMillis() + override def invert(l: Long) = attempt(l)(new DateTime(_)) + } + + implicit val joda2Date: Injection[DateTime, Date] = + new AbstractInjection[DateTime, Date] { + def apply(d: DateTime) = d.toDate() + override def invert(d: Date) = attempt(d)(new DateTime(_)) + } +} \ No newline at end of file From b0e84dd2c86ad238d78cc32e53e057bf506231f0 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Tue, 9 Jul 2013 20:00:45 -0700 Subject: [PATCH 06/32] Added dependency in build.scala lazy val for bijectionJodaTime --- project/Build.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/project/Build.scala b/project/Build.scala index a4003af81..6231a46c5 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -251,4 +251,17 @@ object BijectionBuild extends Build { osgiExportAll("com.twitter.bijection.netty"), libraryDependencies += "io.netty" % "netty" % "3.5.5.Final" ).dependsOn(bijectionCore % "test->test;compile->compile") + + + lazy val bijectionJodaTime = Project( + id = "bijection-jodatime", + base = file("bijection-jodatime"), + settings = sharedSettings + ).settings( + name := "bijection-jodatime", + previousArtifact := youngestForwardCompatible("jodatime"), + osgiExportAll("com.twitter.bijection.jodatime"), + libraryDependencies += "com.github.nscala-time" % "nscala-time" % "0.4.2 + ).dependsOn(bijectionCore % "test->test;compile->compile") + } From 5cb56c641113559dd6fe2415c6502cd5350b0b80 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sat, 13 Jul 2013 04:26:15 -0700 Subject: [PATCH 07/32] moved to a new package --- .../com/twitter/bijection/DateBijections.scala | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala deleted file mode 100644 index 7cf43a72f..000000000 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala +++ /dev/null @@ -1,17 +0,0 @@ -package com.twitter.bijection - -import java.util._ -import com.github.nscala_time.time._ -import com.github.nscala_time.time.Imports._ - -trait DateBijections extends GeneratedTupleBijections { - - - implicit val date2joda: Bijection[java.util.Date, DateTime] = - new AbstractBijection[java.util.Date, DateTime] { - def apply(d: java.util.Date) = new DateTime(d) - override def invert(joda: DateTime) = joda.toDate() - } - - -} \ No newline at end of file From f516cfdd3fe22ac2cc5071a7014148b6c65b1b5f Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sat, 13 Jul 2013 04:26:30 -0700 Subject: [PATCH 08/32] moved to a new package --- .../twitter/bijection/DateInjections.scala | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala deleted file mode 100644 index 04cf984f0..000000000 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala +++ /dev/null @@ -1,48 +0,0 @@ -package com.twitter.bijection - -import java.util._ -import com.github.nscala_time.time._ -import com.github.nscala_time.time.Imports._ -import scala.util.control.Exception.allCatch -import Bijection.build -import Inversion.{ attempt, attemptWhen } - -trait DateInjections extends GeneratedTupleInjections { - - implicit val date2String: Injection[Date, String] = - new AbstractInjection[Date, String] { - def apply(d: Date) = d.toString - override def invert(s: String) = attempt(s)(new DateTime(_).toDate()) - - } - - implicit val date2Long: Injection[Date, Long] = - new AbstractInjection[Date, Long] { - def apply(d: Date) = new DateTime(d).getMillis() - override def invert(l: Long) = attempt(l)(new DateTime(_).toDate()) - } - - implicit val date2joda: Injection[Date, DateTime] = - new AbstractInjection[Date, DateTime] { - def apply(d: Date) = new DateTime(d) - override def invert(dt: DateTime) = attempt(dt)(_.toDate()) - } - - implicit val joda2String: Injection[DateTime, String] = - new AbstractInjection[DateTime, String] { - def apply(d: DateTime) = d.toString - override def invert(s: String) = attempt(s)(new DateTime(_)) - } - - implicit val joda2Long: Injection[DateTime, Long] = - new AbstractInjection[DateTime, Long] { - def apply(d: DateTime) = d.getMillis() - override def invert(l: Long) = attempt(l)(new DateTime(_)) - } - - implicit val joda2Date: Injection[DateTime, Date] = - new AbstractInjection[DateTime, Date] { - def apply(d: DateTime) = d.toDate() - override def invert(d: Date) = attempt(d)(new DateTime(_)) - } -} \ No newline at end of file From 727f02fb243c4b84e9871b9285dbb39eb81e3a16 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sat, 13 Jul 2013 11:47:11 -0700 Subject: [PATCH 09/32] removed unnecessary bijections --- .../com/twitter/bijection/DateInjections.scala | 12 ------------ .../bijection/jodatime/DateBijections.scala | 4 ++-- .../bijection/jodatime/DateInjections.scala | 16 ++-------------- 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala index 04cf984f0..dc74e0844 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala @@ -16,18 +16,6 @@ trait DateInjections extends GeneratedTupleInjections { } - implicit val date2Long: Injection[Date, Long] = - new AbstractInjection[Date, Long] { - def apply(d: Date) = new DateTime(d).getMillis() - override def invert(l: Long) = attempt(l)(new DateTime(_).toDate()) - } - - implicit val date2joda: Injection[Date, DateTime] = - new AbstractInjection[Date, DateTime] { - def apply(d: Date) = new DateTime(d) - override def invert(dt: DateTime) = attempt(dt)(_.toDate()) - } - implicit val joda2String: Injection[DateTime, String] = new AbstractInjection[DateTime, String] { def apply(d: DateTime) = d.toString diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala index 56b5f6d40..16eddf006 100644 --- a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala @@ -1,13 +1,13 @@ package com.twitter.bijection.jodatime -import com.twitter.bijection.{ Bijection, GeneratedTupleBijections, AbstractBijection} +import com.twitter.bijection.{ Bijection, AbstractBijection} import com.twitter.bijection.Inversion.{ attempt, attemptWhen } import java.util._ import com.github.nscala_time.time._ import com.github.nscala_time.time.Imports._ -trait DateBijections extends GeneratedTupleBijections { +trait DateBijections { implicit val date2joda: Bijection[java.util.Date, DateTime] = diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala index 999876c4f..1868bf4f8 100644 --- a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala @@ -4,9 +4,9 @@ import java.util._ import com.github.nscala_time.time._ import com.github.nscala_time.time.Imports._ import com.twitter.bijection.Inversion.attempt -import com.twitter.bijection.{ Injection, InversionFailure, GeneratedTupleInjections, AbstractInjection } +import com.twitter.bijection.{ Injection, InversionFailure, AbstractInjection } -trait DateInjections extends GeneratedTupleInjections { +trait DateInjections { implicit val date2String: Injection[Date, String] = new AbstractInjection[Date, String] { @@ -15,18 +15,6 @@ trait DateInjections extends GeneratedTupleInjections { } - implicit val date2Long: Injection[Date, Long] = - new AbstractInjection[Date, Long] { - def apply(d: Date) = new DateTime(d).getMillis() - override def invert(l: Long) = attempt(l)(new DateTime(_).toDate()) - } - - implicit val date2joda: Injection[Date, DateTime] = - new AbstractInjection[Date, DateTime] { - def apply(d: Date) = new DateTime(d) - override def invert(dt: DateTime) = attempt(dt)(_.toDate()) - } - implicit val joda2String: Injection[DateTime, String] = new AbstractInjection[DateTime, String] { def apply(d: DateTime) = d.toString From 1ae28e8111176d85ade0658d0dcc22ebda83fd96 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sat, 13 Jul 2013 11:54:43 -0700 Subject: [PATCH 10/32] I will delete soon --- .../bijection/DateInjections.scala.orig | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala.orig diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala.orig b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala.orig new file mode 100644 index 000000000..dc74e0844 --- /dev/null +++ b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala.orig @@ -0,0 +1,36 @@ +package com.twitter.bijection + +import java.util._ +import com.github.nscala_time.time._ +import com.github.nscala_time.time.Imports._ +import scala.util.control.Exception.allCatch +import Bijection.build +import Inversion.{ attempt, attemptWhen } + +trait DateInjections extends GeneratedTupleInjections { + + implicit val date2String: Injection[Date, String] = + new AbstractInjection[Date, String] { + def apply(d: Date) = d.toString + override def invert(s: String) = attempt(s)(new DateTime(_).toDate()) + + } + + implicit val joda2String: Injection[DateTime, String] = + new AbstractInjection[DateTime, String] { + def apply(d: DateTime) = d.toString + override def invert(s: String) = attempt(s)(new DateTime(_)) + } + + implicit val joda2Long: Injection[DateTime, Long] = + new AbstractInjection[DateTime, Long] { + def apply(d: DateTime) = d.getMillis() + override def invert(l: Long) = attempt(l)(new DateTime(_)) + } + + implicit val joda2Date: Injection[DateTime, Date] = + new AbstractInjection[DateTime, Date] { + def apply(d: DateTime) = d.toDate() + override def invert(d: Date) = attempt(d)(new DateTime(_)) + } +} \ No newline at end of file From 560c7d42592fd6f67fd909cd2efd05724eaf5516 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sat, 13 Jul 2013 11:55:50 -0700 Subject: [PATCH 11/32] moved to new location --- .../twitter/bijection/DateBijections.scala | 17 --------- .../twitter/bijection/DateInjections.scala | 36 ------------------- 2 files changed, 53 deletions(-) delete mode 100644 bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala delete mode 100644 bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala deleted file mode 100644 index 7cf43a72f..000000000 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateBijections.scala +++ /dev/null @@ -1,17 +0,0 @@ -package com.twitter.bijection - -import java.util._ -import com.github.nscala_time.time._ -import com.github.nscala_time.time.Imports._ - -trait DateBijections extends GeneratedTupleBijections { - - - implicit val date2joda: Bijection[java.util.Date, DateTime] = - new AbstractBijection[java.util.Date, DateTime] { - def apply(d: java.util.Date) = new DateTime(d) - override def invert(joda: DateTime) = joda.toDate() - } - - -} \ No newline at end of file diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala deleted file mode 100644 index dc74e0844..000000000 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala +++ /dev/null @@ -1,36 +0,0 @@ -package com.twitter.bijection - -import java.util._ -import com.github.nscala_time.time._ -import com.github.nscala_time.time.Imports._ -import scala.util.control.Exception.allCatch -import Bijection.build -import Inversion.{ attempt, attemptWhen } - -trait DateInjections extends GeneratedTupleInjections { - - implicit val date2String: Injection[Date, String] = - new AbstractInjection[Date, String] { - def apply(d: Date) = d.toString - override def invert(s: String) = attempt(s)(new DateTime(_).toDate()) - - } - - implicit val joda2String: Injection[DateTime, String] = - new AbstractInjection[DateTime, String] { - def apply(d: DateTime) = d.toString - override def invert(s: String) = attempt(s)(new DateTime(_)) - } - - implicit val joda2Long: Injection[DateTime, Long] = - new AbstractInjection[DateTime, Long] { - def apply(d: DateTime) = d.getMillis() - override def invert(l: Long) = attempt(l)(new DateTime(_)) - } - - implicit val joda2Date: Injection[DateTime, Date] = - new AbstractInjection[DateTime, Date] { - def apply(d: DateTime) = d.toDate() - override def invert(d: Date) = attempt(d)(new DateTime(_)) - } -} \ No newline at end of file From 3f54c74559d0897d8db91c8a4eecfbad2ddc9eaf Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sat, 13 Jul 2013 11:59:50 -0700 Subject: [PATCH 12/32] Delete DateInjections.scala.orig --- .../bijection/DateInjections.scala.orig | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala.orig diff --git a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala.orig b/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala.orig deleted file mode 100644 index dc74e0844..000000000 --- a/bijection-core/src/main/scala/com/twitter/bijection/DateInjections.scala.orig +++ /dev/null @@ -1,36 +0,0 @@ -package com.twitter.bijection - -import java.util._ -import com.github.nscala_time.time._ -import com.github.nscala_time.time.Imports._ -import scala.util.control.Exception.allCatch -import Bijection.build -import Inversion.{ attempt, attemptWhen } - -trait DateInjections extends GeneratedTupleInjections { - - implicit val date2String: Injection[Date, String] = - new AbstractInjection[Date, String] { - def apply(d: Date) = d.toString - override def invert(s: String) = attempt(s)(new DateTime(_).toDate()) - - } - - implicit val joda2String: Injection[DateTime, String] = - new AbstractInjection[DateTime, String] { - def apply(d: DateTime) = d.toString - override def invert(s: String) = attempt(s)(new DateTime(_)) - } - - implicit val joda2Long: Injection[DateTime, Long] = - new AbstractInjection[DateTime, Long] { - def apply(d: DateTime) = d.getMillis() - override def invert(l: Long) = attempt(l)(new DateTime(_)) - } - - implicit val joda2Date: Injection[DateTime, Date] = - new AbstractInjection[DateTime, Date] { - def apply(d: DateTime) = d.toDate() - override def invert(d: Date) = attempt(d)(new DateTime(_)) - } -} \ No newline at end of file From ba4c9c971d5e42a6720478176cc25b0b841080d7 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sat, 13 Jul 2013 15:50:43 -0700 Subject: [PATCH 13/32] removed wrapper --- .../bijection/jodatime/DateBijections.scala | 13 +++++++++---- .../bijection/jodatime/DateInjections.scala | 14 +++++--------- project/Build.scala | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala index 16eddf006..cffb2b9db 100644 --- a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala @@ -2,10 +2,10 @@ package com.twitter.bijection.jodatime import com.twitter.bijection.{ Bijection, AbstractBijection} -import com.twitter.bijection.Inversion.{ attempt, attemptWhen } -import java.util._ -import com.github.nscala_time.time._ -import com.github.nscala_time.time.Imports._ + +import java.util.Date + +import org.joda.time.DateTime trait DateBijections { @@ -16,5 +16,10 @@ trait DateBijections { override def invert(joda: DateTime) = joda.toDate() } + implicit val joda2Long: Bijection[DateTime, Long] = + new AbstractBijection[DateTime, Long] { + def apply(d: DateTime) = d.getMillis() + override def invert(l: Long) = new DateTime(l) + } } \ No newline at end of file diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala index 1868bf4f8..64ea15934 100644 --- a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala @@ -1,8 +1,9 @@ package com.twitter.bijection.jodatime -import java.util._ -import com.github.nscala_time.time._ -import com.github.nscala_time.time.Imports._ +import java.util.Date + +import org.joda.time.DateTime + import com.twitter.bijection.Inversion.attempt import com.twitter.bijection.{ Injection, InversionFailure, AbstractInjection } @@ -12,7 +13,7 @@ trait DateInjections { new AbstractInjection[Date, String] { def apply(d: Date) = d.toString override def invert(s: String) = attempt(s)(new DateTime(_).toDate()) - + } implicit val joda2String: Injection[DateTime, String] = @@ -21,11 +22,6 @@ trait DateInjections { override def invert(s: String) = attempt(s)(new DateTime(_)) } - implicit val joda2Long: Injection[DateTime, Long] = - new AbstractInjection[DateTime, Long] { - def apply(d: DateTime) = d.getMillis() - override def invert(l: Long) = attempt(l)(new DateTime(_)) - } implicit val joda2Date: Injection[DateTime, Date] = new AbstractInjection[DateTime, Date] { diff --git a/project/Build.scala b/project/Build.scala index 6231a46c5..028b74852 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -261,7 +261,7 @@ object BijectionBuild extends Build { name := "bijection-jodatime", previousArtifact := youngestForwardCompatible("jodatime"), osgiExportAll("com.twitter.bijection.jodatime"), - libraryDependencies += "com.github.nscala-time" % "nscala-time" % "0.4.2 + libraryDependencies ++= Seq("joda-time" % "joda-time" % "2.2" , "org.joda" % "joda-convert" % "1.2") ).dependsOn(bijectionCore % "test->test;compile->compile") } From 11f67c70853f4ed8f0c7124a6489ba02d3e4f416 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sat, 13 Jul 2013 19:28:54 -0700 Subject: [PATCH 14/32] adding test class for bijections --- .../jodatime/DateBijectionLaws.scala | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala diff --git a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala new file mode 100644 index 000000000..774d8f0e9 --- /dev/null +++ b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala @@ -0,0 +1,29 @@ +package com.twitter.bijection.jodatime + +import org.scalacheck.Properties +import org.scalacheck.Gen._ +import org.scalacheck.Arbitrary +import org.scalacheck.Prop._ + +import com.twitter.bijection.{ Bijection,BaseProperties , ImplicitBijection} +//import Conversion.asMethod +import java.util.Date +import org.joda.time.DateTime +import com.twitter.bijection._ + + + + + +object DateBijectionsLaws extends Properties("DateBijections") with BaseProperties { + + implicit val Long : Arbitrary [Long] = arbitraryViaFn { (s: DateTime) => (s.getMillis()) } + property("Joda <=> Long") = isBijection[DateTime, Long] + + + implicit val DateTime : Arbitrary [DateTime] = arbitraryViaFn { (s: Date) => ( new DateTime (s) ) } + property("Date <=> Joda") = isBijection[Date, DateTime] + + + +} \ No newline at end of file From 4b416a0c8b3cd0c709a5d778f059cf46e79d4437 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sun, 14 Jul 2013 13:58:42 -0700 Subject: [PATCH 15/32] fixed implicit not find issue --- .../com/twitter/bijection/jodatime/DateBijectionLaws.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala index 774d8f0e9..37b8c3046 100644 --- a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala +++ b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala @@ -15,7 +15,10 @@ import com.twitter.bijection._ -object DateBijectionsLaws extends Properties("DateBijections") with BaseProperties { +object DateBijectionsLaws extends Properties("DateBijections") with BaseProperties with DateBijections { + + + implicit val Long : Arbitrary [Long] = arbitraryViaFn { (s: DateTime) => (s.getMillis()) } property("Joda <=> Long") = isBijection[DateTime, Long] From e71bf4b35c574b57b2934d7979220816b8350dd8 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Sun, 14 Jul 2013 14:03:31 -0700 Subject: [PATCH 16/32] added injections tests --- .../jodatime/DateBijectionLaws.scala | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala index 37b8c3046..f3a801914 100644 --- a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala +++ b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala @@ -5,28 +5,24 @@ import org.scalacheck.Gen._ import org.scalacheck.Arbitrary import org.scalacheck.Prop._ -import com.twitter.bijection.{ Bijection,BaseProperties , ImplicitBijection} +import com.twitter.bijection.{ Bijection, BaseProperties, ImplicitBijection } //import Conversion.asMethod import java.util.Date import org.joda.time.DateTime import com.twitter.bijection._ +object DateBijectionsLaws extends Properties("DateBijections") with BaseProperties with DateBijections with DateInjections { - - - -object DateBijectionsLaws extends Properties("DateBijections") with BaseProperties with DateBijections { - - - - - implicit val Long : Arbitrary [Long] = arbitraryViaFn { (s: DateTime) => (s.getMillis()) } + //bijections test + implicit val Long: Arbitrary[Long] = arbitraryViaFn { (s: DateTime) => (s.getMillis()) } property("Joda <=> Long") = isBijection[DateTime, Long] - - - implicit val DateTime : Arbitrary [DateTime] = arbitraryViaFn { (s: Date) => ( new DateTime (s) ) } + + implicit val DateTime: Arbitrary[DateTime] = arbitraryViaFn { (s: Date) => (new DateTime(s)) } property("Date <=> Joda") = isBijection[Date, DateTime] - - + + //injection test + property("round trips Date -> String") = isLooseInjection[Date, String] + property("round trips Joda -> String") = isLooseInjection[DateTime, String] + property("round trips Joda -> Date") = isLooseInjection[DateTime, Date] } \ No newline at end of file From 2f5c399e3430de8d6d40cceee73cd7ac34291218 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Fri, 19 Jul 2013 17:05:54 -0700 Subject: [PATCH 17/32] created an update --- CHANGES.md | 12 + README.md | 34 +- .../twitter/bijection/avro/AvroCodecs.scala | 219 +++++++++++++ .../src/test/java/avro/FiscalRecord.java | 293 ++++++++++++++++++ .../bijection/avro/AvroCodecLaws.scala | 116 +++++++ .../avro/AvroCodecsSpecification.scala | 122 ++++++++ .../clojure/ClojureBijectionLaws.scala | 24 ++ .../com/twitter/bijection/Bijection.scala | 2 +- .../com/twitter/bijection/Bufferable.scala | 10 +- .../bijection/CollectionInjections.scala | 4 +- .../com/twitter/bijection/Conversion.scala | 7 +- .../com/twitter/bijection/Injection.scala | 18 +- .../com/twitter/bijection/Inversion.scala | 4 +- .../twitter/bijection/InversionFailure.scala | 8 +- .../src/main/scala/com/twitter/package.scala | 2 + .../guava/GuavaBinaryBijections.scala | 82 +++++ .../guava/GuavaBinaryBijectionsLaws.scala | 36 +++ .../bijection/hbase/HBaseBijections.scala | 129 ++++++++ .../bijection/hbase/HBaseBijectionsLaws.scala | 76 +++++ .../hbase/HBaseBijectionsSpecifications.scala | 135 ++++++++ .../bijection/json/JsonInjection.scala | 12 +- .../bijection/json/JsonInjectionLaws.scala | 5 +- version.sbt | 2 +- 23 files changed, 1313 insertions(+), 39 deletions(-) create mode 100644 bijection-avro/src/main/scala/com/twitter/bijection/avro/AvroCodecs.scala create mode 100644 bijection-avro/src/test/java/avro/FiscalRecord.java create mode 100644 bijection-avro/src/test/scala/com/twitter/bijection/avro/AvroCodecLaws.scala create mode 100644 bijection-avro/src/test/scala/com/twitter/bijection/avro/AvroCodecsSpecification.scala create mode 100644 bijection-clojure/src/test/scala/com/twitter/bijection/clojure/ClojureBijectionLaws.scala create mode 100644 bijection-guava/src/main/scala/com/twitter/bijection/guava/GuavaBinaryBijections.scala create mode 100644 bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBinaryBijectionsLaws.scala create mode 100644 bijection-hbase/src/main/scala/com/twitter/bijection/hbase/HBaseBijections.scala create mode 100644 bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsLaws.scala create mode 100644 bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsSpecifications.scala diff --git a/CHANGES.md b/CHANGES.md index 1a029260d..0e8f9eac9 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,17 @@ # Bijection # +### + +* Move bijection-algebird to the Algebird project as "algebird-bijection" + +### 0.5.2 + +* Remove withSources + +### 0.5.1 + +* Lets bijection-util be distributed +* Correct README ### 0.5.0 diff --git a/README.md b/README.md index e8b929ad8..1043a1af9 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,6 @@ the inverse has the same property. See the [current API documentation](http://twitter.github.com/bijection) for more information. -## Maven - -Current version is `0.5.0`. groupid=`"com.twitter"` artifact=`"bijection-core_2.9.3"`. - ## Examples: ```scala @@ -19,7 +15,7 @@ res0: java.lang.Integer = 42 In addition to Bijection, we have Injection. An Injection embeds a type A in a larger space of type B. Every item from A can be round-tripped through B, but not every B can be mapped to A. So -Injection is like a pair of function: `A => B, B => Option[A]`. +Injection is like a pair of function: `A => B, B => Try[A]`. ```scala import com.twitter.bijection._ @@ -28,7 +24,7 @@ scala> Injection[Int, String](100) res0: String = 100 scala> Injection.invert[Int, String](res0) -res1: Option[Int] = Some(100) +res1: Try[Int] = Success(100) ``` If we want to treat an Injection like a Bijection (over a restricted subspace of the larger set), we use the `B @@ Rep[A]` syntax, for instance: `String @@ Rep[Int]` @@ -67,7 +63,7 @@ scala> injection(123456789L) res1: com.twitter.bijection.GZippedBase64String = GZippedBase64String(H4sIAAAAAAAAAGNgYGBgjz4rCgBpa5WLCAAAAA==) scala> injection.invert(res1) -res2: Option[Long] = Some(123456789) +res2: Try[Long] = Success(123456789) ``` When you have bijections between a path of items you can `Bijection.connect` or `Injection.connect` them: @@ -89,7 +85,7 @@ scala> 243L.as[Base64String] res0: com.twitter.bijection.Base64String = Base64String(MjQz) scala> long2String2Bytes2B64.invert(res5) -res1: Option[Long] = Some(243) +res1: Try[Long] = Success(243) ``` ## Supported Bijections/Injections @@ -124,6 +120,28 @@ of Java or Scala, please contribute! `Bufferable[T]` instances for all primitives/tuples/containers are provided. Bijections and Injections to any of these types give you binary serialization via Bufferable. +## Maven + +Bijection modules are available on maven central. The current groupid and version for all modules is, respectively, `"com.twitter"` and `0.5.2`. + +Current published artifacts are + +* `bijection-core` +* `bijection-protobuf` +* `bijection-thrift` +* `bijection-guava` +* `bijection-scrooge` +* `bijection-json` +* `bijection-util` +* `bijection-clojure` +* `bijection-netty` +* `bijection-avro` +* `bijection-hbase` + +Every artifact is published against Scala `"2.9.3"` and `"2.10"`. To pull in the jars, make sure to add your desired scala version as a suffix, ie: + +`bijection-core_2.9.2` or `bijection-core_2.10` + ## Authors * Oscar Boykin diff --git a/bijection-avro/src/main/scala/com/twitter/bijection/avro/AvroCodecs.scala b/bijection-avro/src/main/scala/com/twitter/bijection/avro/AvroCodecs.scala new file mode 100644 index 000000000..a4641196f --- /dev/null +++ b/bijection-avro/src/main/scala/com/twitter/bijection/avro/AvroCodecs.scala @@ -0,0 +1,219 @@ +/* + +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.avro + +import com.twitter.bijection.Injection +import org.apache.avro.specific.{SpecificDatumReader, SpecificDatumWriter, SpecificRecordBase} +import org.apache.avro.file.{DataFileStream, DataFileWriter} +import java.io.{UnsupportedEncodingException, ByteArrayInputStream, ByteArrayOutputStream} +import com.twitter.bijection.Inversion.attempt +import com.twitter.bijection.Attempt +import org.apache.avro.generic.{GenericDatumReader, GenericDatumWriter, GenericRecord} +import org.apache.avro.Schema +import org.apache.avro.io.{DecoderFactory, DatumReader, EncoderFactory, DatumWriter} + +/** + * Factory providing various avro injections. + * @author Muhammad Ashraf + * @since 7/4/13 + */ +object AvroCodecs { + /** + * Returns Injection capable of serializing and deserializing a compiled Avro record using SpecificDatumWriter and + * SpecificDatumReader + * @tparam T compiled Avro record + * @return Injection + */ + def apply[T <: SpecificRecordBase : Manifest]: Injection[T, Array[Byte]] = { + val klass = manifest[T].erasure.asInstanceOf[Class[T]] + new SpecificAvroCodec[T](klass) + } + + + /** + * Returns Injection capable of serializing and deserializing a generic record using GenericDatumReader and + * GenericDatumReader + * @tparam T generic record + * @return Injection + */ + def apply[T <: GenericRecord](schema: Schema): Injection[T, Array[Byte]] = { + new GenericAvroCodec[T](schema) + } + + /** + * Returns Injection capable of serializing and deserializing a compiled avro record using org.apache.avro.io.BinaryEncoder + * @tparam T compiled Avro record + * @return Injection + */ + def toBinary[T <: SpecificRecordBase : Manifest]: Injection[T, Array[Byte]] = { + val klass = manifest[T].erasure.asInstanceOf[Class[T]] + val writer = new SpecificDatumWriter[T](klass) + val reader = new SpecificDatumReader[T](klass) + new BinaryAvroCodec[T](writer, reader) + } + + /** + * Returns Injection capable of serializing and deserializing a generic avro record using org.apache.avro.io.BinaryEncoder + * @tparam T GenericRecord + * @return Injection + */ + def toBinary[T <: GenericRecord](schema: Schema): Injection[T, Array[Byte]] = { + val writer = new GenericDatumWriter[T](schema) + val reader = new GenericDatumReader[T](schema) + new BinaryAvroCodec[T](writer, reader) + } + + /** + * Returns Injection capable of serializing and deserializing a compiled avro record using org.apache.avro.io.JsonEncoder + * @tparam T compiled Avro record + * @return Injection + */ + def toJson[T <: GenericRecord](schema: Schema): Injection[T, String] = { + val writer = new GenericDatumWriter[T](schema) + val reader = new GenericDatumReader[T](schema) + new JsonAvroCodec[T](schema, writer, reader) + } + + /** + * Returns Injection capable of serializing and deserializing a generic avro record using org.apache.avro.io.JsonEncoder + * @tparam T compiled Avro record + * @return Injection + */ + def toJson[T <: SpecificRecordBase : Manifest](schema: Schema): Injection[T, String] = { + val klass = manifest[T].erasure.asInstanceOf[Class[T]] + val writer = new SpecificDatumWriter[T](klass) + val reader = new SpecificDatumReader[T](klass) + new JsonAvroCodec[T](schema, writer, reader) + } +} + +/** + * Provides methods to serialize and deserialize complied avro record. + * @param klass class of complied record + * @tparam T compiled record + */ +class SpecificAvroCodec[T <: SpecificRecordBase](klass: Class[T]) extends Injection[T, Array[Byte]] { + def apply(a: T): Array[Byte] = { + val writer = new SpecificDatumWriter[T](a.getSchema) + val fileWriter = new DataFileWriter[T](writer) + val stream = new ByteArrayOutputStream() + fileWriter.create(a.getSchema, stream) + fileWriter.append(a) + fileWriter.flush() + stream.toByteArray + } + + def invert(bytes: Array[Byte]): Attempt[T] = attempt(bytes) { + bytes => + val reader = new SpecificDatumReader[T](klass) + val stream = new DataFileStream[T](new ByteArrayInputStream(bytes), reader) + val result = stream.next() + stream.close() + result + } +} + +/** + * Provides methods to serialize and deserialize generic avro record. + * @param schema avro schema + * @tparam T generic record + */ +class GenericAvroCodec[T <: GenericRecord](schema: Schema) extends Injection[T, Array[Byte]] { + def apply(a: T): Array[Byte] = { + val writer = new GenericDatumWriter[T](a.getSchema) + val fileWriter = new DataFileWriter[T](writer) + val stream = new ByteArrayOutputStream() + fileWriter.create(a.getSchema, stream) + fileWriter.append(a) + fileWriter.flush() + stream.toByteArray + } + + def invert(bytes: Array[Byte]): Attempt[T] = attempt(bytes) { + bytes => + val reader = new GenericDatumReader[T](schema) + val stream = new DataFileStream[T](new ByteArrayInputStream(bytes), reader) + val result = stream.next() + stream.close() + result + } +} + +/** + * Provides methods to serializing and deserializing a generic and compiled avro record using org.apache.avro.io.BinaryEncoder + * @param writer Datum writer + * @param reader Datum reader + * @tparam T avro record + */ +class BinaryAvroCodec[T](writer: DatumWriter[T], reader: DatumReader[T]) extends Injection[T, Array[Byte]] { + def apply(a: T): Array[Byte] = { + val stream = new ByteArrayOutputStream() + val binaryEncoder = EncoderFactory.get().binaryEncoder(stream, null) + writer.write(a, binaryEncoder) + binaryEncoder.flush() + stream.toByteArray + } + + def invert(bytes: Array[Byte]): Attempt[T] = attempt(bytes) { + bytes => + val binaryDecoder = DecoderFactory.get().binaryDecoder(bytes, null) + reader.read(null.asInstanceOf[T], binaryDecoder) + } +} + +/** + * Provides methods to serializing and deserializing a generic and compiled avro record using org.apache.avro.io.JsonEncoder + * @param writer Datum writer + * @param reader Datum reader + * @tparam T avro record + */ +class JsonAvroCodec[T](schema: Schema, writer: DatumWriter[T], reader: DatumReader[T]) extends Injection[T, String] { + def apply(a: T): String = { + val stream = new ByteArrayOutputStream() + val encoder = EncoderFactory.get().jsonEncoder(schema, stream) + writer.write(a, encoder) + encoder.flush() + newUtf8(stream.toByteArray) + } + + def invert(str: String): Attempt[T] = attempt(str) { + str => + val decoder = DecoderFactory.get().jsonDecoder(schema, new ByteArrayInputStream(getBytesUtf8(str))) + reader.read(null.asInstanceOf[T], decoder) + } + + private def newUtf8(bytes: Array[Byte]): String = { + try { + if (bytes == null) null else new String(bytes, "UTF-8") + } + catch { + case uee: UnsupportedEncodingException => { + throw new RuntimeException("UTF-8 Not supported on this platform") + } + } + } + + private def getBytesUtf8(string: String): Array[Byte] = { + try { + if (string == null) null else string.getBytes("UTF-8") + } + catch { + case uee: UnsupportedEncodingException => { + throw new RuntimeException("UTF-8 Not supported on this platform") + } + } + } + +} diff --git a/bijection-avro/src/test/java/avro/FiscalRecord.java b/bijection-avro/src/test/java/avro/FiscalRecord.java new file mode 100644 index 000000000..79d57ba2a --- /dev/null +++ b/bijection-avro/src/test/java/avro/FiscalRecord.java @@ -0,0 +1,293 @@ +/** + * Autogenerated by Avro + * + * DO NOT EDIT DIRECTLY + */ +package avro; + +@SuppressWarnings("all") +public class FiscalRecord extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord { + public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"FiscalRecord\",\"namespace\":\"avro\",\"fields\":[{\"name\":\"calendarDate\",\"type\":\"string\"},{\"name\":\"fiscalWeek\",\"type\":[\"int\",\"null\"]},{\"name\":\"fiscalYear\",\"type\":[\"int\",\"null\"]}]}"); + @Deprecated + public java.lang.CharSequence calendarDate; + @Deprecated + public java.lang.Integer fiscalWeek; + @Deprecated + public java.lang.Integer fiscalYear; + + /** + * Default constructor. + */ + public FiscalRecord() { + } + + /** + * All-args constructor. + */ + public FiscalRecord(java.lang.CharSequence calendarDate, java.lang.Integer fiscalWeek, java.lang.Integer fiscalYear) { + this.calendarDate = calendarDate; + this.fiscalWeek = fiscalWeek; + this.fiscalYear = fiscalYear; + } + + public org.apache.avro.Schema getSchema() { + return SCHEMA$; + } + + // Used by DatumWriter. Applications should not call. + public java.lang.Object get(int field$) { + switch (field$) { + case 0: + return calendarDate; + case 1: + return fiscalWeek; + case 2: + return fiscalYear; + default: + throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + // Used by DatumReader. Applications should not call. + @SuppressWarnings(value = "unchecked") + public void put(int field$, java.lang.Object value$) { + switch (field$) { + case 0: + calendarDate = (java.lang.CharSequence) value$; + break; + case 1: + fiscalWeek = (java.lang.Integer) value$; + break; + case 2: + fiscalYear = (java.lang.Integer) value$; + break; + default: + throw new org.apache.avro.AvroRuntimeException("Bad index"); + } + } + + /** + * Gets the value of the 'calendarDate' field. + */ + public java.lang.CharSequence getCalendarDate() { + return calendarDate; + } + + /** + * Sets the value of the 'calendarDate' field. + * + * @param value the value to set. + */ + public void setCalendarDate(java.lang.CharSequence value) { + this.calendarDate = value; + } + + /** + * Gets the value of the 'fiscalWeek' field. + */ + public java.lang.Integer getFiscalWeek() { + return fiscalWeek; + } + + /** + * Sets the value of the 'fiscalWeek' field. + * + * @param value the value to set. + */ + public void setFiscalWeek(java.lang.Integer value) { + this.fiscalWeek = value; + } + + /** + * Gets the value of the 'fiscalYear' field. + */ + public java.lang.Integer getFiscalYear() { + return fiscalYear; + } + + /** + * Sets the value of the 'fiscalYear' field. + * + * @param value the value to set. + */ + public void setFiscalYear(java.lang.Integer value) { + this.fiscalYear = value; + } + + /** + * Creates a new FiscalRecord RecordBuilder + */ + public static avro.FiscalRecord.Builder newBuilder() { + return new avro.FiscalRecord.Builder(); + } + + /** + * Creates a new FiscalRecord RecordBuilder by copying an existing Builder + */ + public static avro.FiscalRecord.Builder newBuilder(avro.FiscalRecord.Builder other) { + return new avro.FiscalRecord.Builder(other); + } + + /** + * Creates a new FiscalRecord RecordBuilder by copying an existing FiscalRecord instance + */ + public static avro.FiscalRecord.Builder newBuilder(avro.FiscalRecord other) { + return new avro.FiscalRecord.Builder(other); + } + + /** + * RecordBuilder for FiscalRecord instances. + */ + public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase + implements org.apache.avro.data.RecordBuilder { + + private java.lang.CharSequence calendarDate; + private java.lang.Integer fiscalWeek; + private java.lang.Integer fiscalYear; + + /** + * Creates a new Builder + */ + private Builder() { + super(avro.FiscalRecord.SCHEMA$); + } + + /** + * Creates a Builder by copying an existing Builder + */ + private Builder(avro.FiscalRecord.Builder other) { + super(other); + } + + /** + * Creates a Builder by copying an existing FiscalRecord instance + */ + private Builder(avro.FiscalRecord other) { + super(avro.FiscalRecord.SCHEMA$); + if (isValidValue(fields()[0], other.calendarDate)) { + this.calendarDate = (java.lang.CharSequence) data().deepCopy(fields()[0].schema(), other.calendarDate); + fieldSetFlags()[0] = true; + } + if (isValidValue(fields()[1], other.fiscalWeek)) { + this.fiscalWeek = (java.lang.Integer) data().deepCopy(fields()[1].schema(), other.fiscalWeek); + fieldSetFlags()[1] = true; + } + if (isValidValue(fields()[2], other.fiscalYear)) { + this.fiscalYear = (java.lang.Integer) data().deepCopy(fields()[2].schema(), other.fiscalYear); + fieldSetFlags()[2] = true; + } + } + + /** + * Gets the value of the 'calendarDate' field + */ + public java.lang.CharSequence getCalendarDate() { + return calendarDate; + } + + /** + * Sets the value of the 'calendarDate' field + */ + public avro.FiscalRecord.Builder setCalendarDate(java.lang.CharSequence value) { + validate(fields()[0], value); + this.calendarDate = value; + fieldSetFlags()[0] = true; + return this; + } + + /** + * Checks whether the 'calendarDate' field has been set + */ + public boolean hasCalendarDate() { + return fieldSetFlags()[0]; + } + + /** + * Clears the value of the 'calendarDate' field + */ + public avro.FiscalRecord.Builder clearCalendarDate() { + calendarDate = null; + fieldSetFlags()[0] = false; + return this; + } + + /** + * Gets the value of the 'fiscalWeek' field + */ + public java.lang.Integer getFiscalWeek() { + return fiscalWeek; + } + + /** + * Sets the value of the 'fiscalWeek' field + */ + public avro.FiscalRecord.Builder setFiscalWeek(java.lang.Integer value) { + validate(fields()[1], value); + this.fiscalWeek = value; + fieldSetFlags()[1] = true; + return this; + } + + /** + * Checks whether the 'fiscalWeek' field has been set + */ + public boolean hasFiscalWeek() { + return fieldSetFlags()[1]; + } + + /** + * Clears the value of the 'fiscalWeek' field + */ + public avro.FiscalRecord.Builder clearFiscalWeek() { + fiscalWeek = null; + fieldSetFlags()[1] = false; + return this; + } + + /** + * Gets the value of the 'fiscalYear' field + */ + public java.lang.Integer getFiscalYear() { + return fiscalYear; + } + + /** + * Sets the value of the 'fiscalYear' field + */ + public avro.FiscalRecord.Builder setFiscalYear(java.lang.Integer value) { + validate(fields()[2], value); + this.fiscalYear = value; + fieldSetFlags()[2] = true; + return this; + } + + /** + * Checks whether the 'fiscalYear' field has been set + */ + public boolean hasFiscalYear() { + return fieldSetFlags()[2]; + } + + /** + * Clears the value of the 'fiscalYear' field + */ + public avro.FiscalRecord.Builder clearFiscalYear() { + fiscalYear = null; + fieldSetFlags()[2] = false; + return this; + } + + @Override + public FiscalRecord build() { + try { + FiscalRecord record = new FiscalRecord(); + record.calendarDate = fieldSetFlags()[0] ? this.calendarDate : (java.lang.CharSequence) defaultValue(fields()[0]); + record.fiscalWeek = fieldSetFlags()[1] ? this.fiscalWeek : (java.lang.Integer) defaultValue(fields()[1]); + record.fiscalYear = fieldSetFlags()[2] ? this.fiscalYear : (java.lang.Integer) defaultValue(fields()[2]); + return record; + } catch (Exception e) { + throw new org.apache.avro.AvroRuntimeException(e); + } + } + } +} diff --git a/bijection-avro/src/test/scala/com/twitter/bijection/avro/AvroCodecLaws.scala b/bijection-avro/src/test/scala/com/twitter/bijection/avro/AvroCodecLaws.scala new file mode 100644 index 000000000..40c6214c0 --- /dev/null +++ b/bijection-avro/src/test/scala/com/twitter/bijection/avro/AvroCodecLaws.scala @@ -0,0 +1,116 @@ +/* +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.avro + +import com.twitter.bijection.{BaseProperties, Injection} + +import org.scalacheck.Properties +import org.apache.avro.generic.{GenericRecord, GenericData} +import org.apache.avro.Schema +import avro.FiscalRecord + +/** + * @author Muhammad Ashraf + * @since 7/5/13 + */ +object AvroCodecLaws extends Properties("AvroCodecs") with BaseProperties { + val testSchema = new Schema.Parser().parse( """{ + "type":"record", + "name":"FiscalRecord", + "namespace":"avro", + "fields":[ + { + "name":"calendarDate", + "type":"string" + }, + { + "name":"fiscalWeek", + "type":[ + "int", + "null" + ] + }, + { + "name":"fiscalYear", + "type":[ + "int", + "null" + ] + } + ] + }""") + + + def buildSpecificAvroRecord(i: (String, Int, Int)): FiscalRecord = { + FiscalRecord.newBuilder() + .setCalendarDate(i._1) + .setFiscalWeek(i._2) + .setFiscalYear(i._3) + .build() + } + + def buildGenericAvroRecord(i: (String, Int, Int)): GenericRecord = { + + val fiscalRecord = new GenericData.Record(testSchema) + fiscalRecord.put("calendarDate", i._1) + fiscalRecord.put("fiscalWeek", i._2) + fiscalRecord.put("fiscalYear", i._3) + fiscalRecord + } + + implicit val testSpecificRecord = arbitraryViaFn { + is: (String, Int, Int) => buildSpecificAvroRecord(is) + } + + implicit val testGenericRecord = arbitraryViaFn { + is: (String, Int, Int) => buildGenericAvroRecord(is) + } + + def roundTripsSpecificRecord(implicit injection: Injection[FiscalRecord, Array[Byte]]) = { + isLooseInjection[FiscalRecord, Array[Byte]] + } + + def roundTripsGenericRecord(implicit injection: Injection[GenericRecord, Array[Byte]]) = { + isLooseInjection[GenericRecord, Array[Byte]] + } + + def roundTripsSpecificRecordToJson(implicit injection: Injection[FiscalRecord, String]) = { + isLooseInjection[FiscalRecord, String] + } + + def roundTripsGenericRecordToJson(implicit injection: Injection[GenericRecord, String]) = { + isLooseInjection[GenericRecord, String] + } + + property("round trips Specific Record -> Array[Byte]") = + roundTripsSpecificRecord(AvroCodecs[FiscalRecord]) + + property("round trips Generic Record -> Array[Byte]") = + roundTripsGenericRecord(AvroCodecs[GenericRecord](testSchema)) + + property("round trips Specific Record -> Array[Byte] using Binary Encoder/Decoder") = + roundTripsSpecificRecord(AvroCodecs.toBinary[FiscalRecord]) + + property("round trips Generic Record -> Array[Byte] using Binary Encoder/Decoder") = + roundTripsGenericRecord(AvroCodecs.toBinary[GenericRecord](testSchema)) + + property("round trips Generic Record -> String using Json Encoder/Decoder") = + roundTripsGenericRecordToJson(AvroCodecs.toJson[GenericRecord](testSchema)) + + property("round trips Specific Record -> String using Json Encoder/Decoder") = + roundTripsSpecificRecordToJson(AvroCodecs.toJson[FiscalRecord](testSchema)) + +} + + diff --git a/bijection-avro/src/test/scala/com/twitter/bijection/avro/AvroCodecsSpecification.scala b/bijection-avro/src/test/scala/com/twitter/bijection/avro/AvroCodecsSpecification.scala new file mode 100644 index 000000000..de0c41a07 --- /dev/null +++ b/bijection-avro/src/test/scala/com/twitter/bijection/avro/AvroCodecsSpecification.scala @@ -0,0 +1,122 @@ +/* + +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.avro + +import org.specs.Specification +import com.twitter.bijection.{Injection, BaseProperties} +import org.apache.avro.Schema +import avro.FiscalRecord +import org.apache.avro.generic.{GenericData, GenericRecord} + +/** + * @author Muhammad Ashraf + * @since 7/6/13 + */ +object AvroCodecsSpecification extends Specification with BaseProperties { + val testSchema = new Schema.Parser().parse( """{ + "type":"record", + "name":"FiscalRecord", + "namespace":"avro", + "fields":[ + { + "name":"calendarDate", + "type":"string" + }, + { + "name":"fiscalWeek", + "type":[ + "int", + "null" + ] + }, + { + "name":"fiscalYear", + "type":[ + "int", + "null" + ] + } + ] + }""") + + + "Avro codec" should { + + "Round trip specific record using Specific Injection" in { + implicit val specificInjection = AvroCodecs[FiscalRecord] + val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) + val bytes = Injection[FiscalRecord, Array[Byte]](testRecord) + val attempt = Injection.invert[FiscalRecord, Array[Byte]](bytes) + attempt.get must_== testRecord + } + + "Round trip specific record using Binary Injection" in { + implicit val specificBinaryInjection = AvroCodecs.toBinary[FiscalRecord] + val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) + val bytes = Injection[FiscalRecord, Array[Byte]](testRecord) + val attempt = Injection.invert[FiscalRecord, Array[Byte]](bytes) + attempt.get must_== testRecord + } + + "Round trip specific record using Json Injection" in { + implicit val specificJsonInjection = AvroCodecs.toJson[FiscalRecord](testSchema) + val testRecord = buildSpecificAvroRecord(("2012-01-01", 1, 12)) + val jsonString = Injection[FiscalRecord, String](testRecord) + val attempt = Injection.invert[FiscalRecord, String](jsonString) + attempt.get must_== testRecord + } + + "Round trip generic record using Generic Injection" in { + implicit val genericInjection = AvroCodecs[GenericRecord](testSchema) + val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) + val bytes = Injection[GenericRecord, Array[Byte]](testRecord) + val attempt = Injection.invert[GenericRecord, Array[Byte]](bytes) + attempt.get must_== testRecord + } + + "Round trip specific record using Binary Injection" in { + implicit val genericBinaryInjection = AvroCodecs.toBinary[GenericRecord](testSchema) + val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) + val bytes = Injection[GenericRecord, Array[Byte]](testRecord) + val attempt = Injection.invert[GenericRecord, Array[Byte]](bytes) + attempt.get must_== testRecord + } + + "Round trip specific record using Json Injection" in { + implicit val genericJsonInjection = AvroCodecs.toJson[GenericRecord](testSchema) + val testRecord = buildGenericAvroRecord(("2012-01-01", 1, 12)) + val jsonString = Injection[GenericRecord, String](testRecord) + val attempt = Injection.invert[GenericRecord, String](jsonString) + attempt.get must_== testRecord + } + } + + def buildSpecificAvroRecord(i: (String, Int, Int)): FiscalRecord = { + FiscalRecord.newBuilder() + .setCalendarDate(i._1) + .setFiscalWeek(i._2) + .setFiscalYear(i._3) + .build() + } + + def buildGenericAvroRecord(i: (String, Int, Int)): GenericRecord = { + + val fiscalRecord = new GenericData.Record(testSchema) + fiscalRecord.put("calendarDate", i._1) + fiscalRecord.put("fiscalWeek", i._2) + fiscalRecord.put("fiscalYear", i._3) + fiscalRecord + } +} diff --git a/bijection-clojure/src/test/scala/com/twitter/bijection/clojure/ClojureBijectionLaws.scala b/bijection-clojure/src/test/scala/com/twitter/bijection/clojure/ClojureBijectionLaws.scala new file mode 100644 index 000000000..74a82ddd9 --- /dev/null +++ b/bijection-clojure/src/test/scala/com/twitter/bijection/clojure/ClojureBijectionLaws.scala @@ -0,0 +1,24 @@ +/* + * Copyright 2010 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.clojure + +import com.twitter.bijection.{ BaseProperties, Bijection } +import org.scalacheck.{ Arbitrary, Properties } + +object ClojureBijectionLaws extends Properties("ClojureBijections") with BaseProperties { + // TODO: Fill in tests. +} diff --git a/bijection-core/src/main/scala/com/twitter/bijection/Bijection.scala b/bijection-core/src/main/scala/com/twitter/bijection/Bijection.scala index cbb41c06e..4a8de2fe0 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/Bijection.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/Bijection.scala @@ -76,7 +76,7 @@ trait LowPriorityBijections { implicit def fromInjection[A, B](implicit inj: Injection[A, B]): Bijection[A, B @@ Rep[A]] = new AbstractBijection[A, B @@ Rep[A]] { override def apply(a: A): B @@ Rep[A] = Tag(inj.apply(a)) - // This tag promises the Attempt will return something: + // This tag promises the Try will return something: override def invert(b: B @@ Rep[A]): A = inj.invert(b).get } } diff --git a/bijection-core/src/main/scala/com/twitter/bijection/Bufferable.scala b/bijection-core/src/main/scala/com/twitter/bijection/Bufferable.scala index 62bf428e1..725c80bc3 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/Bufferable.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/Bufferable.scala @@ -34,7 +34,7 @@ import com.twitter.bijection.Inversion.attempt @implicitNotFound(msg = "Cannot find Bufferable type class for ${T}") trait Bufferable[T] extends Serializable { def put(into: ByteBuffer, t: T): ByteBuffer - def get(from: ByteBuffer): Attempt[(ByteBuffer, T)] + def get(from: ByteBuffer): Try[(ByteBuffer, T)] /** Retrieve the value of get or throw an exception if the operation fails */ def unsafeGet(from: ByteBuffer): (ByteBuffer, T) = get(from) match { @@ -64,7 +64,7 @@ object Bufferable extends GeneratedTupleBufferable with Serializable { inj.invert(inj(t)).get } def put[T](into: ByteBuffer, t: T)(implicit buf: Bufferable[T]): ByteBuffer = buf.put(into, t) - def get[T](from: ByteBuffer)(implicit buf: Bufferable[T]): Attempt[(ByteBuffer, T)] = buf.get(from) + def get[T](from: ByteBuffer)(implicit buf: Bufferable[T]): Try[(ByteBuffer, T)] = buf.get(from) // get the bytes from a given position to the current position def getBytes(from: ByteBuffer, start: Int = 0): Array[Byte] = { val fromd = from.duplicate @@ -133,7 +133,7 @@ object Bufferable extends GeneratedTupleBufferable with Serializable { /** remember: putfn and getfn must call duplicate and not change the input ByteBuffer * We are duplicating the ByteBuffer state, not the backing array (which IS mutated) */ - def build[T](putfn: (ByteBuffer,T) => ByteBuffer)(getfn: (ByteBuffer) => Attempt[(ByteBuffer, T)]): + def build[T](putfn: (ByteBuffer,T) => ByteBuffer)(getfn: (ByteBuffer) => Try[(ByteBuffer, T)]): Bufferable[T] = new AbstractBufferable[T] { override def put(into: ByteBuffer, t: T) = putfn(into,t) override def get(from: ByteBuffer) = getfn(from) @@ -223,9 +223,9 @@ object Bufferable extends GeneratedTupleBufferable with Serializable { l.foldLeft(nextBb) { (oldbb, t) => reallocatingPut(oldbb) { buf.put(_, t) } } } def getCollection[T,C](initbb: ByteBuffer, builder: Builder[T,C])(implicit buf: Bufferable[T]): - Attempt[(ByteBuffer, C)] = { + Try[(ByteBuffer, C)] = { - val bbOpt: Attempt[ByteBuffer] = Try(initbb.duplicate) + val bbOpt: Try[ByteBuffer] = Try(initbb.duplicate) val size = bbOpt.get.getInt // We can't mutate the builder while calling other functions (not safe) // so we write into this array: diff --git a/bijection-core/src/main/scala/com/twitter/bijection/CollectionInjections.scala b/bijection-core/src/main/scala/com/twitter/bijection/CollectionInjections.scala index 1a48bc2f2..f83828cc4 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/CollectionInjections.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/CollectionInjections.scala @@ -30,7 +30,7 @@ import java.util.concurrent.{ ConcurrentMap => JConcurrentMap } import scala.collection.JavaConverters._ import scala.collection.mutable import scala.collection.generic.CanBuildFrom -import scala.util.Success +import scala.util.{ Success, Try } trait CollectionInjections extends StringInjections { @@ -86,7 +86,7 @@ trait CollectionInjections extends StringInjections { c foreach { builder += inj(_) } builder.result() } - override def invert(d: D): Attempt[C] = { + override def invert(d: D): Try[C] = { val builder = dc() d foreach { b => val thisB = inj.invert(b) diff --git a/bijection-core/src/main/scala/com/twitter/bijection/Conversion.scala b/bijection-core/src/main/scala/com/twitter/bijection/Conversion.scala index bf11ce720..46f06aba7 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/Conversion.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/Conversion.scala @@ -17,7 +17,7 @@ limitations under the License. package com.twitter.bijection import java.io.Serializable -import scala.util.Success +import scala.util.{ Success, Try } /** * Convert allows the user to convert an instance of type A to type B given an implicit Conversion @@ -52,8 +52,8 @@ trait CrazyLowPriorityConversion extends Serializable { case _ => None } } - implicit def fromInjectionInverse[A,B](implicit inj: Injection[B,A]): Conversion[A,Attempt[B]] = - new Conversion[A,Attempt[B]] { + implicit def fromInjectionInverse[A,B](implicit inj: Injection[B,A]): Conversion[A,Try[B]] = + new Conversion[A,Try[B]] { def apply(a: A) = inj.invert(a) } implicit def fromBijectionInv[A,B](implicit fn: ImplicitBijection[B,A]) = new Conversion[A,B] { @@ -80,4 +80,3 @@ object Conversion extends LowPriorityConversion { def apply(a: A) = fn(a) } } - diff --git a/bijection-core/src/main/scala/com/twitter/bijection/Injection.scala b/bijection-core/src/main/scala/com/twitter/bijection/Injection.scala index acc204ce8..a24a29e63 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/Injection.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/Injection.scala @@ -29,7 +29,7 @@ import com.twitter.bijection.Inversion.attempt @implicitNotFound(msg = "Cannot find Injection type class from ${A} to ${B}") trait Injection[A, B] extends Serializable { self => def apply(a: A): B - def invert(b: B): Attempt[A] + def invert(b: B): Try[A] /** * Composes two instances of Injection in a new Injection, @@ -75,7 +75,7 @@ private [bijection] class InjectionFn[A, B](inj: Injection[A, B]) extends (A => */ abstract class AbstractInjection[A, B] extends Injection[A, B] { override def apply(a: A): B - override def invert(b: B): Attempt[A] + override def invert(b: B): Try[A] } trait LowPriorityInjections { @@ -93,9 +93,9 @@ object Injection extends CollectionInjections implicit def toFunction[A,B](inj: Injection[A, B]): (A => B) = inj.toFunction def apply[A, B](a: A)(implicit inj: Injection[A, B]): B = inj(a) - def invert[A, B](b: B)(implicit inj: Injection[A, B]): Attempt[A] = inj.invert(b) + def invert[A, B](b: B)(implicit inj: Injection[A, B]): Try[A] = inj.invert(b) - def build[A, B](to: A => B)(from: B => Attempt[A]): Injection[A, B] = + def build[A, B](to: A => B)(from: B => Try[A]): Injection[A, B] = new AbstractInjection[A, B] { override def apply(a: A) = to(a) override def invert(b: B) = from(b) @@ -132,6 +132,16 @@ object Injection extends CollectionInjections case _ => InversionFailure.failedAttempt(e) } } + /** + * It might be nice for this to be implicit, but this seems to make ambiguous implicit Injections + */ + def fromBijectionRep[A,B](implicit bij: ImplicitBijection[A, B @@ Rep[A]]): Injection[A, B] = + new AbstractInjection[A, B] { + override def apply(a: A) = bij(a) + override def invert(b: B) = attempt(b) { bin => + bij.invert(bin.asInstanceOf[B @@ Rep[A]]) + } + } implicit def option[A]: Injection[A, Option[A]] = new AbstractInjection[A, Option[A]] { override def apply(a: A) = Some(a) diff --git a/bijection-core/src/main/scala/com/twitter/bijection/Inversion.scala b/bijection-core/src/main/scala/com/twitter/bijection/Inversion.scala index 7d9568581..890390686 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/Inversion.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/Inversion.scala @@ -26,13 +26,13 @@ object Inversion { * The analog of Exception.allCatch either where exceptions * are wrapped by the InversionFailure type */ - def attempt[A, B](b: B)(inv: B => A): Attempt[A] = + def attempt[A, B](b: B)(inv: B => A): Try[A] = Try(inv(b)).recoverWith(partialFailure(b)) /** * Applies tests for known inversion failure before returning * a success or failure */ - def attemptWhen[A, B](b: B)(test: B => Boolean)(inv: B => A): Attempt[A] = + def attemptWhen[A, B](b: B)(test: B => Boolean)(inv: B => A): Try[A] = if (test(b)) Success(inv(b)) else failedAttempt(b) } diff --git a/bijection-core/src/main/scala/com/twitter/bijection/InversionFailure.scala b/bijection-core/src/main/scala/com/twitter/bijection/InversionFailure.scala index 09d95a47c..9f4372c36 100644 --- a/bijection-core/src/main/scala/com/twitter/bijection/InversionFailure.scala +++ b/bijection-core/src/main/scala/com/twitter/bijection/InversionFailure.scala @@ -30,16 +30,16 @@ object InversionFailure { new InversionFailure(b, new UnsupportedOperationException) /** - * Produces a failed Attempt - */ - def failedAttempt[A, B](b: B): Attempt[A] = + * Produces a failed Try + */ + def failedAttempt[A, B](b: B): Try[A] = Failure(apply(b)) /** * Produces a failed attempt statisfying a partial function defined * for any non-fatal Throwable */ - def partialFailure[A, B](b: B): PartialFunction[Throwable, Attempt[A]] = { + def partialFailure[A, B](b: B): PartialFunction[Throwable, Try[A]] = { case NonFatal(t) => Failure(InversionFailure(b, t)) } } diff --git a/bijection-core/src/main/scala/com/twitter/package.scala b/bijection-core/src/main/scala/com/twitter/package.scala index 894899a64..5e4f26251 100644 --- a/bijection-core/src/main/scala/com/twitter/package.scala +++ b/bijection-core/src/main/scala/com/twitter/package.scala @@ -33,6 +33,8 @@ package object bijection { * This type represents the attempted conversion. A failure * will result in a scala.util.Failure containing the InversionFailure. A success * will result in a scala.util.Success containing the inverted value. + * + * TODO: Remove in 0.6.0. */ type Attempt[T] = Try[T] diff --git a/bijection-guava/src/main/scala/com/twitter/bijection/guava/GuavaBinaryBijections.scala b/bijection-guava/src/main/scala/com/twitter/bijection/guava/GuavaBinaryBijections.scala new file mode 100644 index 000000000..81378da9f --- /dev/null +++ b/bijection-guava/src/main/scala/com/twitter/bijection/guava/GuavaBinaryBijections.scala @@ -0,0 +1,82 @@ +/* + * 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.guava + +import com.twitter.bijection.{AbstractBijection, Bijection} +import com.google.common.io.BaseEncoding + +/** + * @author Muhammad Ashraf + * @since 7/7/13 + */ +object GuavaBinaryBijections { + + trait BaseEncoding { + val str: String + } + + case class Base16String(str: String) extends BaseEncoding + + case class Base32String(str: String) extends BaseEncoding + + case class Base32HEXString(str: String) extends BaseEncoding + + case class Base64String(str: String) extends BaseEncoding + + case class Base64URLString(str: String) extends BaseEncoding + + implicit def unwrap(encodedString: BaseEncoding): String = Option(encodedString) match { + case Some(x) => x.str + case None => null + } + + implicit lazy val bytes2Base64: Bijection[Array[Byte], Base64String] = + new AbstractBijection[Array[Byte], Base64String] { + def apply(bytes: Array[Byte]) = Base64String(BaseEncoding.base64().encode(bytes)) + + override def invert(b64: Base64String) = BaseEncoding.base64().decode(b64.str) + } + + + implicit lazy val bytes2Base64Url: Bijection[Array[Byte], Base64URLString] = + new AbstractBijection[Array[Byte], Base64URLString] { + def apply(bytes: Array[Byte]) = Base64URLString(BaseEncoding.base64Url().encode(bytes)) + + override def invert(b64: Base64URLString) = BaseEncoding.base64Url().decode(b64.str) + } + + + implicit lazy val bytes2Base32: Bijection[Array[Byte], Base32String] = + new AbstractBijection[Array[Byte], Base32String] { + def apply(bytes: Array[Byte]) = Base32String(BaseEncoding.base32().encode(bytes)) + + override def invert(b32: Base32String) = BaseEncoding.base32().decode(b32.str) + } + + + implicit lazy val bytes2Base32HEX: Bijection[Array[Byte], Base32HEXString] = + new AbstractBijection[Array[Byte], Base32HEXString] { + def apply(bytes: Array[Byte]) = Base32HEXString(BaseEncoding.base32Hex().encode(bytes)) + + override def invert(b32HEX: Base32HEXString) = BaseEncoding.base32Hex().decode(b32HEX.str) + } + + implicit lazy val bytes2Base16: Bijection[Array[Byte], Base16String] = + new AbstractBijection[Array[Byte], Base16String] { + def apply(bytes: Array[Byte]) = Base16String(BaseEncoding.base16().encode(bytes)) + + override def invert(b16: Base16String) = BaseEncoding.base16().decode(b16.str) + } +} diff --git a/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBinaryBijectionsLaws.scala b/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBinaryBijectionsLaws.scala new file mode 100644 index 000000000..4f36cf2e1 --- /dev/null +++ b/bijection-guava/src/test/scala/com/twitter/bijection/guava/GuavaBinaryBijectionsLaws.scala @@ -0,0 +1,36 @@ +/* + * 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.guava + +import com.twitter.bijection.BaseProperties +import org.scalacheck.Properties +import com.twitter.bijection.guava.GuavaBinaryBijections._ +import com.twitter.bijection.guava.GuavaBinaryBijections.Base64String +import com.twitter.bijection.guava.GuavaBinaryBijections.Base64URLString + + +/** + * @author Muhammad Ashraf + * @since 7/7/13 + */ +object GuavaBinaryBijectionsLaws extends Properties("GuavaBinaryBijections") +with BaseProperties { + + property("rts Array[Byte] -> Base64String") = isInjective[Array[Byte], Base64String] + property("rts Array[Byte] -> Base64URLString") = isInjective[Array[Byte], Base64URLString] + property("rts Array[Byte] -> Base32String") = isInjective[Array[Byte], Base32String] + property("rts Array[Byte] -> Base32HEXString") = isInjective[Array[Byte], Base32HEXString] + property("rts Array[Byte] -> Base16String") = isInjective[Array[Byte], Base16String] +} diff --git a/bijection-hbase/src/main/scala/com/twitter/bijection/hbase/HBaseBijections.scala b/bijection-hbase/src/main/scala/com/twitter/bijection/hbase/HBaseBijections.scala new file mode 100644 index 000000000..f8678d68a --- /dev/null +++ b/bijection-hbase/src/main/scala/com/twitter/bijection/hbase/HBaseBijections.scala @@ -0,0 +1,129 @@ +/* + * 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.hbase + +import com.twitter.bijection._ +import org.apache.hadoop.hbase.util.Bytes +import org.apache.hadoop.hbase.io.ImmutableBytesWritable +import scala.annotation.implicitNotFound + +/** + * Provides various HBase specific Bijections by wrapping org.apache.hadoop.hbase.util.Bytes + * @author Muhammad Ashraf + * @since 7/9/13 + */ +object HBaseBijections { + /** Byte Representation of a String value */ + type StringBytes = Array[Byte] @@ Rep[String] + + /** Byte Representation of a Long value */ + type LongBytes = Array[Byte] @@ Rep[Long] + + /** Byte Representation of a Boolean value */ + type BooleanBytes = Array[Byte] @@ Rep[Boolean] + + /** Byte Representation of a Int value */ + type IntBytes = Array[Byte] @@ Rep[Int] + + /** Byte Representation of a Double value */ + type DoubleBytes = Array[Byte] @@ Rep[Double] + + /** Byte Representation of a Float value */ + type FloatBytes = Array[Byte] @@ Rep[Float] + + /** Byte Representation of a Short value */ + type ShortBytes = Array[Byte] @@ Rep[Short] + + /** Byte Representation of a BigDecimal value */ + type BigDecimalBytes = Array[Byte] @@ Rep[BigDecimal] + + + implicit lazy val string2Bytes: Bijection[String, StringBytes] = + new AbstractBijection[String, StringBytes] { + def apply(str: String) = Tag[Array[Byte], Rep[String]](Bytes.toBytes(str)) + + override def invert(bytes: StringBytes) = Bytes.toString(bytes) + } + + implicit lazy val long2Bytes: Bijection[Long, LongBytes] = + new AbstractBijection[Long, LongBytes] { + def apply(input: Long) = Tag[Array[Byte], Rep[Long]](Bytes.toBytes(input)) + + override def invert(bytes: LongBytes) = Bytes.toLong(bytes) + } + + implicit lazy val boolean2Bytes: Bijection[Boolean, BooleanBytes] = + new AbstractBijection[Boolean, BooleanBytes] { + def apply(input: Boolean) = Tag[Array[Byte], Rep[Boolean]](Bytes.toBytes(input)) + + override def invert(bytes: BooleanBytes) = Bytes.toBoolean(bytes) + } + + implicit lazy val int2Bytes: Bijection[Int, IntBytes] = + new AbstractBijection[Int, IntBytes] { + def apply(input: Int) = Tag[Array[Byte], Rep[Int]](Bytes.toBytes(input)) + + override def invert(bytes: IntBytes) = Bytes.toInt(bytes) + } + + implicit lazy val float2Bytes: Bijection[Float, FloatBytes] = + new AbstractBijection[Float, FloatBytes] { + def apply(input: Float) = Tag[Array[Byte], Rep[Float]](Bytes.toBytes(input)) + + override def invert(bytes: FloatBytes) = Bytes.toFloat(bytes) + } + + implicit lazy val short2Bytes: Bijection[Short, ShortBytes] = + new AbstractBijection[Short, ShortBytes] { + def apply(input: Short) = Tag[Array[Byte], Rep[Short]](Bytes.toBytes(input)) + + override def invert(bytes: ShortBytes) = Bytes.toShort(bytes) + } + + implicit lazy val double2Bytes: Bijection[Double, DoubleBytes] = + new AbstractBijection[Double, DoubleBytes] { + def apply(input: Double) = Tag[Array[Byte], Rep[Double]](Bytes.toBytes(input)) + + override def invert(bytes: DoubleBytes) = Bytes.toDouble(bytes) + } + + implicit lazy val bigdecimal2Bytes: Bijection[BigDecimal, BigDecimalBytes] = + new AbstractBijection[BigDecimal, BigDecimalBytes] { + def apply(input: BigDecimal) = Tag[Array[Byte], Rep[BigDecimal]](Bytes.toBytes(input.underlying())) + + override def invert(bytes: BigDecimalBytes) = Bytes.toBigDecimal(bytes) + } + + implicit lazy val string2BytesWritable = ImmutableBytesWritableBijection[String] + implicit lazy val int2BytesWritable = ImmutableBytesWritableBijection[Int] + implicit lazy val long2BytesWritable = ImmutableBytesWritableBijection[Long] + implicit lazy val double2BytesWritable = ImmutableBytesWritableBijection[Double] + implicit lazy val float2BytesWritable = ImmutableBytesWritableBijection[Float] + implicit lazy val short2BytesWritable = ImmutableBytesWritableBijection[Short] + implicit lazy val boolean2BytesWritable = ImmutableBytesWritableBijection[Boolean] + implicit lazy val bigDecimal2BytesWritable = ImmutableBytesWritableBijection[BigDecimal] + + @implicitNotFound(msg = "Cannot find Bijection type class between ${T} and [Array[Byte] @@ Rep[${T}]") + object ImmutableBytesWritableBijection { + def apply[T](implicit bijection: Bijection[T, Array[Byte] @@ Rep[T]]) = new ImmutableBytesWritableBijection[T](bijection) + } + + class ImmutableBytesWritableBijection[@specialized T](bijection: Bijection[T, Array[Byte] @@ Rep[T]]) extends Bijection[T, ImmutableBytesWritable] { + def apply(a: T): ImmutableBytesWritable = new ImmutableBytesWritable(bijection(a)) + + override def invert(b: ImmutableBytesWritable): T = bijection.invert(Tag[Array[Byte], Rep[T]](b.get())) + } + +} diff --git a/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsLaws.scala b/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsLaws.scala new file mode 100644 index 000000000..a060f8493 --- /dev/null +++ b/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsLaws.scala @@ -0,0 +1,76 @@ +package com.twitter.bijection.hbase + +import org.scalacheck.Properties +import com.twitter.bijection.BaseProperties +import HBaseBijections._ +import org.apache.hadoop.hbase.io.ImmutableBytesWritable +import org.apache.hadoop.hbase.util.Bytes + +/** + * @author Muhammad Ashraf + * @since 7/10/13 + */ +object HBaseBijectionsLaws extends Properties("HBaseBijections") +with BaseProperties { + + + property("String <=> StringBytes") = isInjective[String, StringBytes] + property("Int <=> IntBytes") = isInjective[Int, IntBytes] + property("Long <=> LongBytes") = isInjective[Long, LongBytes] + property("Double <=> DoubleBytes") = isInjective[Double, DoubleBytes] + property("Float <=> FloatBytes") = isInjective[Float, FloatBytes] + property("Boolean <=> BooleanBytes") = isInjective[Boolean, BooleanBytes] + property("Short <=> ShortBytes") = isInjective[Short, ShortBytes] + property("BigDecimal <=> BigDecimalBytes") = isInjective[BigDecimal, BigDecimalBytes] + + property("BigDecimal <=> ImmutableBytesWritable") = { + implicit val arbBD = arbitraryViaFn { + bd: BigDecimal => new ImmutableBytesWritable(Bytes.toBytes(bd.underlying())) + } + isBijection[BigDecimal, ImmutableBytesWritable] + } + + property("String <=> ImmutableBytesWritable") = { + implicit val arbString = arbitraryViaFn { + input: String => new ImmutableBytesWritable(Bytes.toBytes(input)) + } + isBijection[String, ImmutableBytesWritable] + } + property("Long <=> ImmutableBytesWritable") = { + implicit val arbLong = arbitraryViaFn { + input: Long => new ImmutableBytesWritable(Bytes.toBytes(input)) + } + isBijection[Long, ImmutableBytesWritable] + } + property("Int <=> ImmutableBytesWritable") = { + implicit val arbInt = arbitraryViaFn { + input: Int => new ImmutableBytesWritable(Bytes.toBytes(input)) + } + isBijection[Int, ImmutableBytesWritable] + } + property("Double <=> ImmutableBytesWritable") = { + implicit val arbDouble = arbitraryViaFn { + input: Double => new ImmutableBytesWritable(Bytes.toBytes(input)) + } + isBijection[Double, ImmutableBytesWritable] + } + property("Float <=> ImmutableBytesWritable") = { + implicit val arbFloat = arbitraryViaFn { + input: Float => new ImmutableBytesWritable(Bytes.toBytes(input)) + } + isBijection[Float, ImmutableBytesWritable] + } + property("Short <=> ImmutableBytesWritable") = { + implicit val arbShort = arbitraryViaFn { + input: Short => new ImmutableBytesWritable(Bytes.toBytes(input)) + } + isBijection[Short, ImmutableBytesWritable] + } + property("Boolean <=> ImmutableBytesWritable") = { + implicit val arbBoolean = arbitraryViaFn { + input: Boolean => new ImmutableBytesWritable(Bytes.toBytes(input)) + } + isBijection[Boolean, ImmutableBytesWritable] + } + +} diff --git a/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsSpecifications.scala b/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsSpecifications.scala new file mode 100644 index 000000000..493bcd3ce --- /dev/null +++ b/bijection-hbase/src/test/scala/com/twitter/bijection/hbase/HBaseBijectionsSpecifications.scala @@ -0,0 +1,135 @@ +/* + * 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.hbase + +import org.specs.Specification +import com.twitter.bijection.{Bijection, BaseProperties} +import HBaseBijections._ +import org.apache.hadoop.hbase.io.ImmutableBytesWritable + +/** + * @author Muhammad Ashraf + * @since 7/10/13 + */ +object HBaseBijectionsSpecifications extends Specification with BaseProperties { + + "HBaseBijections" should { + "round trip String -> Array[Byte]" in { + val expected = "Bonjour le monde" + val bytes = Bijection[String, StringBytes](expected) + val result = Bijection.invert[String, StringBytes](bytes) + result must_== expected + } + + "round trip Long -> Array[Byte]" in { + val expected = 42L + val bytes = Bijection[Long, LongBytes](expected) + val result = Bijection.invert[Long, LongBytes](bytes) + result must_== expected + } + + "round trip Int -> Array[Byte]" in { + val expected = 42 + val bytes = Bijection[Int, IntBytes](expected) + val result = Bijection.invert[Int, IntBytes](bytes) + result must_== expected + } + + "round trip Double -> Array[Byte]" in { + val expected = 42.0 + val bytes = Bijection[Double, DoubleBytes](expected) + val result = Bijection.invert[Double, DoubleBytes](bytes) + result must_== expected + } + + "round trip Float -> Array[Byte]" in { + val expected = 42.0F + val bytes = Bijection[Float, FloatBytes](expected) + val result = Bijection.invert[Float, FloatBytes](bytes) + result must_== expected + } + + "round trip Short -> Array[Byte]" in { + val expected = 1.toShort + val bytes = Bijection[Short, ShortBytes](expected) + val result = Bijection.invert[Short, ShortBytes](bytes) + result must_== expected + } + + "round trip BigDecimal -> Array[Byte]" in { + val expected = BigDecimal(1) + val bytes = Bijection[BigDecimal, BigDecimalBytes](expected) + val result = Bijection.invert[BigDecimal, BigDecimalBytes](bytes) + result must_== expected + } + + "round trip Boolean -> Array[Byte]" in { + val expected = true + val bytes = Bijection[Boolean, BooleanBytes](expected) + val result = Bijection.invert[Boolean, BooleanBytes](bytes) + result must_== expected + } + + "round trip String -> ImmutableBytesWritable" in { + val expected = "Bonjour le monde" + val bytes = Bijection[String, ImmutableBytesWritable](expected) + val result = Bijection.invert[String, ImmutableBytesWritable](bytes) + result must_== expected + } + + "round trip Long -> ImmutableBytesWritable" in { + val expected = 1L + val bytes = Bijection[Long, ImmutableBytesWritable](expected) + val result = Bijection.invert[Long, ImmutableBytesWritable](bytes) + result must_== expected + } + + "round trip Int -> ImmutableBytesWritable" in { + val expected = 42 + val bytes = Bijection[Int, ImmutableBytesWritable](expected) + val result = Bijection.invert[Int, ImmutableBytesWritable](bytes) + result must_== expected + } + + "round trip Double -> ImmutableBytesWritable" in { + val expected = 42.0 + val bytes = Bijection[Double, ImmutableBytesWritable](expected) + val result = Bijection.invert[Double, ImmutableBytesWritable](bytes) + result must_== expected + } + + "round trip Float -> ImmutableBytesWritable" in { + val expected = 42.0F + val bytes = Bijection[Float, ImmutableBytesWritable](expected) + val result = Bijection.invert[Float, ImmutableBytesWritable](bytes) + result must_== expected + } + + "round trip Short -> ImmutableBytesWritable" in { + val expected = 1.toShort + val bytes = Bijection[Short, ImmutableBytesWritable](expected) + val result = Bijection.invert[Short, ImmutableBytesWritable](bytes) + result must_== expected + } + + "round trip BigDecimal -> ImmutableBytesWritable" in { + val expected = BigDecimal(1) + val bytes = Bijection[BigDecimal, ImmutableBytesWritable](expected) + val result = Bijection.invert[BigDecimal, ImmutableBytesWritable](bytes) + result must_== expected + } + } + +} diff --git a/bijection-json/src/main/scala/com/twitter/bijection/json/JsonInjection.scala b/bijection-json/src/main/scala/com/twitter/bijection/json/JsonInjection.scala index bc4981776..c5dd88387 100644 --- a/bijection-json/src/main/scala/com/twitter/bijection/json/JsonInjection.scala +++ b/bijection-json/src/main/scala/com/twitter/bijection/json/JsonInjection.scala @@ -16,7 +16,7 @@ limitations under the License. package com.twitter.bijection.json -import com.twitter.bijection.{Attempt, Bijection, Injection, InversionFailure, ImplicitBijection} +import com.twitter.bijection.{Bijection, Injection, InversionFailure, ImplicitBijection} import com.twitter.bijection.Inversion.{ attempt, attemptWhen } import org.codehaus.jackson.{JsonParser, JsonNode, JsonFactory} import org.codehaus.jackson.map.ObjectMapper @@ -30,7 +30,7 @@ import org.codehaus.jackson.node.{ import scala.collection.generic.CanBuildFrom import scala.collection.mutable.Builder import scala.collection.JavaConverters._ -import scala.util.Success +import scala.util.{ Success, Try } import scala.util.control.NonFatal /** @@ -75,7 +75,7 @@ object JsonNodeInjection extends LowPriorityJson with java.io.Serializable { def toJsonNode[T](t: T)(implicit json: JsonNodeInjection[T]): JsonNode = json.apply(t) - def fromJsonNode[T](node: JsonNode)(implicit json: JsonNodeInjection[T]): Attempt[T] = + def fromJsonNode[T](node: JsonNode)(implicit json: JsonNodeInjection[T]): Try[T] = json.invert(node) implicit val identity = new AbstractJsonNodeInjection[JsonNode] { @@ -141,7 +141,7 @@ object JsonNodeInjection extends LowPriorityJson with java.io.Serializable { l foreach { t => ary.add(jbij(t)) } ary } - override def invert(n: JsonNode): Attempt[C] = { + override def invert(n: JsonNode): Try[C] = { builder.clear var inCount = 0 n.getElements.asScala.foreach { jn => @@ -181,7 +181,7 @@ object JsonNodeInjection extends LowPriorityJson with java.io.Serializable { } obj } - override def invert(n: JsonNode): Attempt[Map[String,V]] = { + override def invert(n: JsonNode): Try[Map[String,V]] = { val builder = Map.newBuilder[String, V] builder.clear var cnt = 0 @@ -221,6 +221,6 @@ object JsonInjection { def toString[T](implicit json: JsonNodeInjection[T]): Injection[T, String] = UnparsedJson.injection[T] andThen (UnparsedJson.unwrap) - def fromString[T](s: String)(implicit json: JsonNodeInjection[T]): Attempt[T] = + def fromString[T](s: String)(implicit json: JsonNodeInjection[T]): Try[T] = toString.invert(s) } diff --git a/bijection-json/src/test/scala/com/twitter/bijection/json/JsonInjectionLaws.scala b/bijection-json/src/test/scala/com/twitter/bijection/json/JsonInjectionLaws.scala index 22e625dbf..2c1f5257e 100644 --- a/bijection-json/src/test/scala/com/twitter/bijection/json/JsonInjectionLaws.scala +++ b/bijection-json/src/test/scala/com/twitter/bijection/json/JsonInjectionLaws.scala @@ -18,13 +18,14 @@ package com.twitter.bijection.json import com.twitter.bijection.Conversion.asMethod -import com.twitter.bijection.{ Attempt, BaseProperties, Bijection, Injection } +import com.twitter.bijection.{ BaseProperties, Bijection, Injection } import org.scalacheck.Properties import org.scalacheck.Prop.forAll import org.scalacheck.Arbitrary import org.codehaus.jackson.JsonNode import com.twitter.bijection.json.JsonNodeInjection.{fromJsonNode, toJsonNode} +import scala.util.Try object JsonInjectionLaws extends Properties("JsonInjection") with BaseProperties { // Needed from some recursive injections (like tuples) @@ -77,7 +78,7 @@ object JsonInjectionLaws extends Properties("JsonInjection") with BaseProperties val jsonMixed = mixedMap.as[UnparsedJson] - jsonMixed.as[Attempt[Map[String, JsonNode]]].get.map({ kup : (String, JsonNode) => + jsonMixed.as[Try[Map[String, JsonNode]]].get.map({ kup : (String, JsonNode) => val (k, up) = kup if (k.endsWith("i")) { fromJsonNode[Int](up).get == fromJsonNode[Int](mixedMap(k)).get diff --git a/version.sbt b/version.sbt index 297961dab..72f5deac6 100644 --- a/version.sbt +++ b/version.sbt @@ -1,2 +1,2 @@ -version in ThisBuild := "0.5.0" +version in ThisBuild := "0.5.2" From 22fa01accb33e323b0cf7edae4bcc51b926e6397 Mon Sep 17 00:00:00 2001 From: Haidar Hadi Date: Fri, 19 Jul 2013 17:06:16 -0700 Subject: [PATCH 18/32] upodate From fda1e7db4e935d7d61c16a26ef70cd7a2834c6fc Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sat, 20 Jul 2013 18:07:28 +0000 Subject: [PATCH 19/32] new updates --- project/Build.scala | 189 +++++++++++++++++++------------------------- 1 file changed, 80 insertions(+), 109 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 028b74852..e169e0277 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -8,6 +8,13 @@ import com.typesafe.tools.mima.plugin.MimaKeys.previousArtifact import com.typesafe.sbt.osgi.SbtOsgi._ object BijectionBuild extends Build { + def withCross(dep: ModuleID) = + dep cross CrossVersion.binaryMapped { + case "2.9.3" => "2.9.2" // TODO: hack because twitter hasn't built things against 2.9.3 + case version if version startsWith "2.10" => "2.10" // TODO: hack because sbt is broken + case x => x + } + val sharedSettings = Project.defaultSettings ++ releaseSettings ++ osgiSettings ++ Seq( organization := "com.twitter", @@ -19,12 +26,9 @@ object BijectionBuild extends Build { javacOptions in doc := Seq("-source", "1.6"), - //nscala-time to support joda time libraryDependencies ++= Seq( - "org.scalacheck" %% "scalacheck" % "1.10.0" % "test" withSources(), - "org.scala-tools.testing" %% "specs" % "1.6.9" % "test" withSources(), - "com.github.nscala-time" %% "nscala-time" % "0.4.2" - + "org.scalacheck" %% "scalacheck" % "1.10.0" % "test", + "org.scala-tools.testing" %% "specs" % "1.6.9" % "test" ), resolvers ++= Seq( @@ -93,10 +97,11 @@ object BijectionBuild extends Build { // This returns the youngest jar we released that is compatible with the current def youngestForwardCompatible(subProj: String) = { if(subProj == "netty") None // This is new. Update after next version - else Some("com.twitter" % ("bijection-" + subProj + "_2.9.2") % "0.4.0") + else Some("com.twitter" % ("bijection-" + subProj + "_2.9.3") % "0.5.1") } - def osgiExportAll(packs: String*) = OsgiKeys.exportPackage := packs.map(_ + ".*;version=${Bundle-Version}") + def osgiExportAll(packs: String*) = + OsgiKeys.exportPackage := packs.map(_ + ".*;version=${Bundle-Version}") lazy val bijection = Project( id = "bijection", @@ -106,52 +111,58 @@ object BijectionBuild extends Build { test := { }, publish := { }, // skip publishing for this root project. publishLocal := { } - ).aggregate(bijectionCore, - bijectionProtobuf, - bijectionThrift, - bijectionGuava, - bijectionScrooge, - bijectionJson, - bijectionAlgebird, - bijectionUtil, - bijectionClojure, - bijectionNetty) + ).aggregate( + bijectionCore, + bijectionProtobuf, + bijectionThrift, + bijectionGuava, + bijectionScrooge, + bijectionJson, + bijectionUtil, + bijectionClojure, + bijectionNetty, + bijectionAvro, + bijectionHbase, + bijectionJodaTime + ) + + def module(name: String) = { + val id = "bijection-%s".format(name) + Project(id = id, base = file(id), settings = sharedSettings ++ Seq( + Keys.name := id, + previousArtifact := youngestForwardCompatible(name)) + ) + } /** No dependencies in bijection other than java + scala */ - lazy val bijectionCore = Project( - id = "bijection-core", - base = file("bijection-core"), - settings = sharedSettings - ).settings( - name := "bijection-core", - previousArtifact := youngestForwardCompatible("core"), + lazy val bijectionCore = module("core").settings( osgiExportAll("com.twitter.bijection"), libraryDependencies ++= Seq( - "com.novocode" % "junit-interface" % "0.10-M1" % "test", - "org.scalatest" %% "scalatest" % "1.9.1" % "test" - ) + "com.novocode" % "junit-interface" % "0.10-M1" % "test", + "org.scalatest" %% "scalatest" % "1.9.1" % "test" + ), + sourceGenerators in Compile <+= (sourceManaged in Compile, streams) map { + (main, out) => + val pkg = main / "scala"/ "com" / "twitter" / "bijection" + def genSrc(name: String, gen: => String) = { + val srcFile = pkg / name + IO.write(srcFile, gen) + out.log.debug("generated %s" format srcFile) + srcFile + } + Seq(genSrc("GeneratedTupleBijections.scala", Generator.generate), + genSrc("GeneratedTupleBuffer.scala", BufferableGenerator.generate)) + } ) - lazy val bijectionProtobuf = Project( - id = "bijection-protobuf", - base = file("bijection-protobuf"), - settings = sharedSettings - ).settings( - name := "bijection-protobuf", - previousArtifact := youngestForwardCompatible("protobuf"), + lazy val bijectionProtobuf = module("protobuf").settings( osgiExportAll("com.twitter.bijection.protobuf"), libraryDependencies += "com.google.protobuf" % "protobuf-java" % "2.4.1" ).dependsOn(bijectionCore % "test->test;compile->compile") val jsonParser = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.8.1" - lazy val bijectionThrift = Project( - id = "bijection-thrift", - base = file("bijection-thrift"), - settings = sharedSettings - ).settings( - name := "bijection-thrift", - previousArtifact := youngestForwardCompatible("thrift"), + lazy val bijectionThrift = module("thrift").settings( osgiExportAll("com.twitter.bijection.thrift"), libraryDependencies ++= Seq( "org.apache.thrift" % "libthrift" % "0.6.1" exclude("junit", "junit"), @@ -159,29 +170,17 @@ object BijectionBuild extends Build { ) ).dependsOn(bijectionCore % "test->test;compile->compile") - lazy val bijectionGuava = Project( - id = "bijection-guava", - base = file("bijection-guava"), - settings = sharedSettings - ).settings( - name := "bijection-guava", - previousArtifact := youngestForwardCompatible("guava"), + lazy val bijectionGuava = module("guava").settings( osgiExportAll("com.twitter.bijection.guava"), libraryDependencies ++= Seq( // This dependency is required due to a bug with guava 13.0, detailed here: // http://code.google.com/p/guava-libraries/issues/detail?id=1095 "com.google.code.findbugs" % "jsr305" % "1.3.+", - "com.google.guava" % "guava" % "13.0" + "com.google.guava" % "guava" % "14.0" ) ).dependsOn(bijectionCore % "test->test;compile->compile") - lazy val bijectionScrooge = Project( - id = "bijection-scrooge", - base = file("bijection-scrooge"), - settings = sharedSettings - ).settings( - name := "bijection-scrooge", - previousArtifact := youngestForwardCompatible("scrooge"), + lazy val bijectionScrooge = module("scrooge").settings( osgiExportAll("com.twitter.bijection.scrooge"), libraryDependencies ++= Seq( "org.apache.thrift" % "libthrift" % "0.6.1" exclude("junit", "junit"), @@ -189,70 +188,41 @@ object BijectionBuild extends Build { ) ).dependsOn(bijectionCore % "test->test;compile->compile") - lazy val bijectionJson = Project( - id = "bijection-json", - base = file("bijection-json"), - settings = sharedSettings - ).settings( - name := "bijection-json", - previousArtifact := youngestForwardCompatible("json"), + lazy val bijectionJson = module("json").settings( osgiExportAll("com.twitter.bijection.json"), libraryDependencies += jsonParser ).dependsOn(bijectionCore % "test->test;compile->compile") - lazy val bijectionAlgebird = Project( - id = "bijection-algebird", - base = file("bijection-algebird"), - settings = sharedSettings - ).settings( - name := "bijection-algebird", - previousArtifact := youngestForwardCompatible("algebird"), - osgiExportAll("com.twitter.bijection.algebird"), - libraryDependencies += "com.twitter" %% "algebird-core" % "0.1.9" cross CrossVersion.binaryMapped { - case "2.9.3" => "2.9.2" // TODO: hack because twitter hasn't built things agaisnt 2.9.3 - case version if version startsWith "2.10" => "2.10" // TODO: hack because sbt is broken - case x => x - } - ).dependsOn(bijectionCore % "test->test;compile->compile") - - lazy val bijectionUtil = Project( - id = "bijection-util", - base = file("bijection-util"), - settings = sharedSettings - ).settings( - name := "bijection-util", - previousArtifact := youngestForwardCompatible("util"), - osgiExportAll("com.twitter.bijection.util"), - libraryDependencies += "com.twitter" %% "util-core" % "6.2.0" cross CrossVersion.binaryMapped { - case "2.9.3" => "2.9.2" // TODO: hack because twitter hasn't built things agaisnt 2.9.3 - case version if version startsWith "2.10" => "2.10" // TODO: hack because sbt is broken - case x => x - } + lazy val bijectionUtil = module("util").settings( + osgiExportAll("com.twitter.bijection.twitter_util"), + libraryDependencies += withCross("com.twitter" %% "util-core" % "6.3.0") ).dependsOn(bijectionCore % "test->test;compile->compile") - lazy val bijectionClojure = Project( - id = "bijection-clojure", - base = file("bijection-clojure"), - settings = sharedSettings - ).settings( - name := "bijection-clojure", - previousArtifact := youngestForwardCompatible("clojure"), + lazy val bijectionClojure = module("clojure").settings( osgiExportAll("com.twitter.bijection.clojure"), - libraryDependencies += "org.clojure" % "clojure" % "1.4.0" + libraryDependencies += "org.clojure" % "clojure" % "1.5.1" ).dependsOn(bijectionCore % "test->test;compile->compile") - lazy val bijectionNetty = Project( - id = "bijection-netty", - base = file("bijection-netty"), - settings = sharedSettings - ).settings( - name := "bijection-netty", - previousArtifact := youngestForwardCompatible("netty"), + lazy val bijectionNetty = module("netty").settings( osgiExportAll("com.twitter.bijection.netty"), libraryDependencies += "io.netty" % "netty" % "3.5.5.Final" ).dependsOn(bijectionCore % "test->test;compile->compile") - - + + lazy val bijectionAvro = module("avro").settings( + osgiExportAll("com.twitter.bijection.avro"), + libraryDependencies ++= Seq( + "org.apache.avro" % "avro" % "1.7.4" + ) + ).dependsOn(bijectionCore % "test->test;compile->compile") + + lazy val bijectionHbase = module("hbase").settings( + osgiExportAll("com.twitter.bijection.hbase"), + libraryDependencies ++= Seq( + "org.apache.hbase" % "hbase" % "0.94.4" % "provided->default", + "org.apache.hadoop" % "hadoop-core" % "1.0.4" % "provided->default" + ) + ).dependsOn(bijectionCore % "test->test;compile->compile") + lazy val bijectionJodaTime = Project( id = "bijection-jodatime", base = file("bijection-jodatime"), @@ -263,5 +233,6 @@ object BijectionBuild extends Build { osgiExportAll("com.twitter.bijection.jodatime"), libraryDependencies ++= Seq("joda-time" % "joda-time" % "2.2" , "org.joda" % "joda-convert" % "1.2") ).dependsOn(bijectionCore % "test->test;compile->compile") - + + } From 79468fed76eb6417eb1b2a33bc760039a52e8aef Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sat, 20 Jul 2013 20:49:45 +0000 Subject: [PATCH 20/32] fix --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 3585ecb6f..51ece7874 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -232,7 +232,7 @@ object BijectionBuild extends Build { name := "bijection-jodatime", previousArtifact := youngestForwardCompatible("jodatime"), osgiExportAll("com.twitter.bijection.jodatime"), - libraryDependencies += "com.github.nscala-time" % "nscala-time" % "0.4.2 + libraryDependencies += "com.github.nscala-time" % "nscala-time" % "0.4.2" ).dependsOn(bijectionCore % "test->test;compile->compile") } From b151b34aed4f5e9eaab54831cb20bfcebe86f95c Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sat, 20 Jul 2013 20:55:59 +0000 Subject: [PATCH 21/32] fix --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 51ece7874..ac30c589b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -59,7 +59,7 @@ object BijectionBuild extends Build { Some("sonatype-snapshots" at nexus + "content/repositories/snapshots") else Some("sonatype-releases" at nexus + "service/local/staging/deploy/maven2") - }, + } pomExtra := ( https://github.com/twitter/bijection From 255a09daf19d50f5eeda34f774cb9d417921b590 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sat, 20 Jul 2013 21:10:03 +0000 Subject: [PATCH 22/32] fix --- project/Build.scala | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index ac30c589b..ab5194721 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -59,7 +59,7 @@ object BijectionBuild extends Build { Some("sonatype-snapshots" at nexus + "content/repositories/snapshots") else Some("sonatype-releases" at nexus + "service/local/staging/deploy/maven2") - } + }, pomExtra := ( https://github.com/twitter/bijection @@ -224,15 +224,17 @@ object BijectionBuild extends Build { ).dependsOn(bijectionCore % "test->test;compile->compile") - lazy val bijectionJodaTime = Project( - id = "bijection-jodatime", - base = file("bijection-jodatime"), - settings = sharedSettings} - ).settings( - name := "bijection-jodatime", - previousArtifact := youngestForwardCompatible("jodatime"), + + + lazy val bijectionJodaTime = module("jodatime").settings( osgiExportAll("com.twitter.bijection.jodatime"), - libraryDependencies += "com.github.nscala-time" % "nscala-time" % "0.4.2" + libraryDependencies ++= Seq( + "com.github.nscala-time" % "nscala-time" % "0.4.2" + + ) ).dependsOn(bijectionCore % "test->test;compile->compile") + + + } From 1127cc2c4481236175e7b8daf83268db0f83810f Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sun, 21 Jul 2013 01:47:23 +0000 Subject: [PATCH 23/32] fix --- .../bijection/jodatime/DateInjections.scala | 2 +- .../jodatime/DateBijectionLaws.scala | 25 ++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala index 64ea15934..cc57fae75 100644 --- a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala @@ -28,4 +28,4 @@ trait DateInjections { def apply(d: DateTime) = d.toDate() override def invert(d: Date) = attempt(d)(new DateTime(_)) } -} \ No newline at end of file +} diff --git a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala index f3a801914..58f21cbec 100644 --- a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala +++ b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala @@ -13,10 +13,25 @@ import com.twitter.bijection._ object DateBijectionsLaws extends Properties("DateBijections") with BaseProperties with DateBijections with DateInjections { - //bijections test - implicit val Long: Arbitrary[Long] = arbitraryViaFn { (s: DateTime) => (s.getMillis()) } - property("Joda <=> Long") = isBijection[DateTime, Long] +import Rep._ + implicit val strByte = arbitraryViaBijection[Date, String @@ Rep[Date]] + + implicit val date = arbitraryViaFn { (dtime: Long) => new DateTime(dtime) } + + property("Long <=> Joda") = isBijection[Long, DateTime] + + property("Date <=> Joda") = isBijection[Date, DateTime] + + property("round trips Date -> String") = isLooseInjection[DateTime,String] + + property("round trips Joda -> Date") = isLooseInjection[DateTime, Date] + +// property("round trips Date -> String") = isLooseInjection[Date, String] + + + +/** implicit val DateTime: Arbitrary[DateTime] = arbitraryViaFn { (s: Date) => (new DateTime(s)) } property("Date <=> Joda") = isBijection[Date, DateTime] @@ -25,4 +40,6 @@ object DateBijectionsLaws extends Properties("DateBijections") with BaseProperti property("round trips Joda -> String") = isLooseInjection[DateTime, String] property("round trips Joda -> Date") = isLooseInjection[DateTime, Date] -} \ No newline at end of file +**/ + +} From 19aa5efb0a42481948de07e05145b1fdf579793d Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sun, 21 Jul 2013 01:57:33 +0000 Subject: [PATCH 24/32] merge upstream --- project/Build.scala | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index ab5194721..f8f09fcac 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -223,18 +223,12 @@ object BijectionBuild extends Build { ) ).dependsOn(bijectionCore % "test->test;compile->compile") - - - lazy val bijectionJodaTime = module("jodatime").settings( osgiExportAll("com.twitter.bijection.jodatime"), libraryDependencies ++= Seq( - "com.github.nscala-time" % "nscala-time" % "0.4.2" - + "com.github.nscala-time" % "nscala-time_2.9.1" % "0.4.2" ) ).dependsOn(bijectionCore % "test->test;compile->compile") - - } From 3b54fa27d6a93c678e5f960fec57ffe821a51343 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Sun, 21 Jul 2013 02:05:16 +0000 Subject: [PATCH 25/32] merge upstream --- .../bijection/jodatime/DateBijectionLaws.scala | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala index 58f21cbec..759088400 100644 --- a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala +++ b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala @@ -27,19 +27,5 @@ import Rep._ property("round trips Joda -> Date") = isLooseInjection[DateTime, Date] -// property("round trips Date -> String") = isLooseInjection[Date, String] - - - -/** - implicit val DateTime: Arbitrary[DateTime] = arbitraryViaFn { (s: Date) => (new DateTime(s)) } - property("Date <=> Joda") = isBijection[Date, DateTime] - - //injection test - property("round trips Date -> String") = isLooseInjection[Date, String] - property("round trips Joda -> String") = isLooseInjection[DateTime, String] - property("round trips Joda -> Date") = isLooseInjection[DateTime, Date] - -**/ } From 491ee941a4d29521e5a5cbd7a7c1ab8c27f7c735 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Tue, 23 Jul 2013 14:13:37 +0000 Subject: [PATCH 26/32] conflict resolved --- CHANGES.md | 18 ------------------ 1 file changed, 18 deletions(-) mode change 100755 => 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md old mode 100755 new mode 100644 index c8b7e12e8..0e8f9eac9 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,33 +1,15 @@ # Bijection # -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> upstream/develop ### * Move bijection-algebird to the Algebird project as "algebird-bijection" -<<<<<<< HEAD ### 0.5.2 * Remove withSources ### 0.5.1 -======= -======= ->>>>>>> upstream/develop -### 0.5.2 - -* Remove withSources - -### 0.5.1 -<<<<<<< HEAD ->>>>>>> upstream/master -======= - ->>>>>>> upstream/develop * Lets bijection-util be distributed * Correct README From 4ccb0c5b29b3e49543e59da3f64567acee47c901 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Tue, 23 Jul 2013 14:15:38 +0000 Subject: [PATCH 27/32] changed nscala to jodatime --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index f8f09fcac..44528afb2 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -226,7 +226,7 @@ object BijectionBuild extends Build { lazy val bijectionJodaTime = module("jodatime").settings( osgiExportAll("com.twitter.bijection.jodatime"), libraryDependencies ++= Seq( - "com.github.nscala-time" % "nscala-time_2.9.1" % "0.4.2" + "joda-time" % "joda-time" % "2.2" ) ).dependsOn(bijectionCore % "test->test;compile->compile") From 79d2ca03c48dd9ac765b6e3afdb3f5739e1ce404 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Tue, 23 Jul 2013 14:32:57 +0000 Subject: [PATCH 28/32] merge upstream --- .../com/twitter/bijection/jodatime/DateBijections.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala index cffb2b9db..bc404fc22 100644 --- a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateBijections.scala @@ -1,14 +1,10 @@ package com.twitter.bijection.jodatime import com.twitter.bijection.{ Bijection, AbstractBijection} - - import java.util.Date - import org.joda.time.DateTime trait DateBijections { - implicit val date2joda: Bijection[java.util.Date, DateTime] = new AbstractBijection[java.util.Date, DateTime] { @@ -22,4 +18,4 @@ trait DateBijections { override def invert(l: Long) = new DateTime(l) } -} \ No newline at end of file +} From fa5f2f6f04512751d9387921c8470a34d6158e96 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Tue, 23 Jul 2013 15:08:12 +0000 Subject: [PATCH 29/32] added joda dependancy --- project/Build.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 44528afb2..b4edc8df5 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -226,7 +226,8 @@ object BijectionBuild extends Build { lazy val bijectionJodaTime = module("jodatime").settings( osgiExportAll("com.twitter.bijection.jodatime"), libraryDependencies ++= Seq( - "joda-time" % "joda-time" % "2.2" + "joda-time" % "joda-time" % "2.2", + "org.joda" % "joda-convert" % "1.3.1" ) ).dependsOn(bijectionCore % "test->test;compile->compile") From 169b7be021a33702b8ae9340671a1f2ce7e7d712 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Tue, 23 Jul 2013 19:42:12 +0000 Subject: [PATCH 30/32] removed confilict in change --- README.md | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/README.md b/README.md index ee59a3d2a..1043a1af9 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,6 @@ the inverse has the same property. See the [current API documentation](http://twitter.github.com/bijection) for more information. -<<<<<<< HEAD -<<<<<<< HEAD -======= -## Maven - -Current version is `0.5.2`. groupid=`"com.twitter"` artifact=`"bijection-core_2.9.3"`. - ->>>>>>> upstream/master -======= ->>>>>>> upstream/develop ## Examples: ```scala @@ -25,15 +15,7 @@ res0: java.lang.Integer = 42 In addition to Bijection, we have Injection. An Injection embeds a type A in a larger space of type B. Every item from A can be round-tripped through B, but not every B can be mapped to A. So -<<<<<<< HEAD -<<<<<<< HEAD -Injection is like a pair of function: `A => B, B => Try[A]`. -======= -Injection is like a pair of function: `A => B, B => Attempt[A]`. ->>>>>>> upstream/master -======= Injection is like a pair of function: `A => B, B => Try[A]`. ->>>>>>> upstream/develop ```scala import com.twitter.bijection._ @@ -42,15 +24,7 @@ scala> Injection[Int, String](100) res0: String = 100 scala> Injection.invert[Int, String](res0) -<<<<<<< HEAD -<<<<<<< HEAD res1: Try[Int] = Success(100) -======= -res1: Attempt[Int] = Success(100) ->>>>>>> upstream/master -======= -res1: Try[Int] = Success(100) ->>>>>>> upstream/develop ``` If we want to treat an Injection like a Bijection (over a restricted subspace of the larger set), we use the `B @@ Rep[A]` syntax, for instance: `String @@ Rep[Int]` @@ -89,15 +63,7 @@ scala> injection(123456789L) res1: com.twitter.bijection.GZippedBase64String = GZippedBase64String(H4sIAAAAAAAAAGNgYGBgjz4rCgBpa5WLCAAAAA==) scala> injection.invert(res1) -<<<<<<< HEAD -<<<<<<< HEAD -res2: Try[Long] = Success(123456789) -======= -res2: Attempt[Long] = Success(123456789) ->>>>>>> upstream/master -======= res2: Try[Long] = Success(123456789) ->>>>>>> upstream/develop ``` When you have bijections between a path of items you can `Bijection.connect` or `Injection.connect` them: @@ -119,15 +85,7 @@ scala> 243L.as[Base64String] res0: com.twitter.bijection.Base64String = Base64String(MjQz) scala> long2String2Bytes2B64.invert(res5) -<<<<<<< HEAD -<<<<<<< HEAD -res1: Try[Long] = Success(243) -======= -res1: Attempt[Long] = Success(243) ->>>>>>> upstream/master -======= res1: Try[Long] = Success(243) ->>>>>>> upstream/develop ``` ## Supported Bijections/Injections From da16e748f63f20367db0e6ed213cef3f0d681926 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Tue, 23 Jul 2013 19:49:16 +0000 Subject: [PATCH 31/32] cleanup --- .../scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala index 759088400..69f12f9b4 100644 --- a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala +++ b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala @@ -6,7 +6,6 @@ import org.scalacheck.Arbitrary import org.scalacheck.Prop._ import com.twitter.bijection.{ Bijection, BaseProperties, ImplicitBijection } -//import Conversion.asMethod import java.util.Date import org.joda.time.DateTime import com.twitter.bijection._ From 2fbeaafa1a497da95290c17e83a4dbf46d9d6b04 Mon Sep 17 00:00:00 2001 From: CruncherBigData Date: Tue, 23 Jul 2013 20:00:43 +0000 Subject: [PATCH 32/32] more cleanup --- .../scala/com/twitter/bijection/jodatime/DateInjections.scala | 2 -- .../com/twitter/bijection/jodatime/DateBijectionLaws.scala | 1 - 2 files changed, 3 deletions(-) diff --git a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala index cc57fae75..a40222fa1 100644 --- a/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala +++ b/bijection-jodatime/src/main/scala/com/twitter/bijection/jodatime/DateInjections.scala @@ -1,9 +1,7 @@ package com.twitter.bijection.jodatime import java.util.Date - import org.joda.time.DateTime - import com.twitter.bijection.Inversion.attempt import com.twitter.bijection.{ Injection, InversionFailure, AbstractInjection } diff --git a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala index 69f12f9b4..3132a7380 100644 --- a/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala +++ b/bijection-jodatime/src/test/scala/com/twitter/bijection/jodatime/DateBijectionLaws.scala @@ -4,7 +4,6 @@ import org.scalacheck.Properties import org.scalacheck.Gen._ import org.scalacheck.Arbitrary import org.scalacheck.Prop._ - import com.twitter.bijection.{ Bijection, BaseProperties, ImplicitBijection } import java.util.Date import org.joda.time.DateTime