
datomisca.toAndFromDatomic.scala Maven / Gradle / Ivy
/*
* Copyright 2012 Pellucid and Zenexity
*
* 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 datomisca
import scala.annotation.implicitNotFound
/** Injective form of DatomicData to Scala converter :
* - 1 DD => 1 Scala type
* - used when precise type inference by compiler
*/
@implicitNotFound("There is no unique conversion from Datomic data type ${DD} to type ${A}")
sealed trait FromDatomicInj[DD <: AnyRef, A] {
def from(dd: DD): A
}
object FromDatomicInj extends FromDatomicInjImplicits {
def apply[DD <: AnyRef, A](f: DD => A) = new FromDatomicInj[DD, A]{
def from(dd: DD): A = f(dd)
}
}
/** Surjective for DatomicData to Scala converter :
* - n DD => 1 Scala type
*/
@implicitNotFound("There is no conversion from Datomic data type ${DD} to type ${A}. Consider implementing an instance of the FromDatomic type class.")
trait FromDatomic[DD <: AnyRef, A] {
def from(dd: DD): A
}
object FromDatomic extends FromDatomicImplicits {
def apply[DD <: AnyRef, A](f: DD => A) = new FromDatomic[DD, A]{
def from(dd: DD): A = f(dd)
}
}
/** Generic DatomicData to Scala type
* Multi-valued "function" (not real function actually)
* which inverse is surjective ToDatomic or ToDatomicCast
* 1 DatomicData -> n Scala type
*/
@implicitNotFound("There is no cast available from Datomic data to type ${A}")
trait FromDatomicCast[A] {
def from(dd: AnyRef): A
}
object FromDatomicCast extends FromDatomicCastImplicits {
def apply[A](f: AnyRef => A) = new FromDatomicCast[A] {
def from(dd: AnyRef): A = f(dd)
}
}
/** Injective form of Scala to Specific DatomicData converters
* 1 Scala type => 1 DD
*/
@implicitNotFound("There is no unique conversion from type ${A} to Datomic data type ${DD}")
sealed trait ToDatomicInj[DD <: AnyRef, A] {
def to(a: A): DD
}
object ToDatomicInj extends ToDatomicInjImplicits {
def apply[DD <: AnyRef, A](f: A => DD) = new ToDatomicInj[DD, A] {
def to(a: A) = f(a)
}
}
/** Surjective form of Scala to Specific DatomicData converters
* n Scala type => 1 DD
*/
@implicitNotFound("There is no conversion from type ${A} to Datomic data type ${DD}. Consider implementing an instance of the ToDatomic type class.")
trait ToDatomic[DD <: AnyRef, A] {
def to(a: A): DD
}
object ToDatomic extends ToDatomicImplicits{
def apply[DD <: AnyRef, A](f: A => DD) = new ToDatomic[DD, A] {
def to(a: A) = f(a)
}
}
/** Scala type to Generic DatomicData (surjective)
* n Scala type -> DatomicData
*/
@implicitNotFound("There is no cast available from type ${A} to Datomic data")
trait ToDatomicCast[A] {
def to(a: A): AnyRef
}
object ToDatomicCast extends ToDatomicCastImplicits {
def apply[A](f: A => AnyRef) = new ToDatomicCast[A] {
def to(a: A): AnyRef = f(a)
}
}
import java.{lang => jl}
import java.math.{BigInteger => JBigInt, BigDecimal => JBigDecimal}
import java.{util => ju}
import java.util.{Date, UUID}
import java.net.URI
import java.time.Instant
import java.time.OffsetDateTime
import java.time.ZonedDateTime
/**
* Think of FromDatomicInj[DD, T] as a type-level function: DD => T
* The implicits here construct a multi-parameter type class,
* and there is a functional dependency from DD to T: DD uniquely
* determines T. In fact, this is an injective function, as there
* is at most one FromDatomicInj for each DatomicData subtype, and each
* map to distinct Scala/Java types. As a consequence, its inverse
* is a partial function.
*/
private[datomisca] trait FromDatomicInjImplicits {
implicit val DString2String: FromDatomicInj[String, String] = FromDatomicInj(identity)
implicit val DBoolean2Boolean: FromDatomicInj[jl.Boolean, Boolean] = FromDatomicInj(b => b)
implicit val DLong2Long: FromDatomicInj[jl.Long, Long] = FromDatomicInj(l => l)
implicit val DDouble2Double: FromDatomicInj[jl.Double, Double] = FromDatomicInj(d => d)
implicit val DFloat2Float: FromDatomicInj[jl.Float, Float] = FromDatomicInj(f => f)
implicit val DBigInt2BigInt: FromDatomicInj[JBigInt, BigInt] = FromDatomicInj(i => new BigInt(i))
implicit val DBigDec2BigDec: FromDatomicInj[JBigDecimal, BigDecimal] = FromDatomicInj(d => new BigDecimal(d))
implicit val DInstant2Date: FromDatomicInj[Date, Date] = FromDatomicInj(identity)
implicit val DUuid2UUID: FromDatomicInj[UUID, UUID] = FromDatomicInj(identity)
implicit val DUri2URI: FromDatomicInj[URI, URI] = FromDatomicInj(identity)
implicit val DBytes2Bytes: FromDatomicInj[Array[Byte], Array[Byte]] = FromDatomicInj(identity)
implicit val DKeyword2Keyword: FromDatomicInj[Keyword, Keyword] = FromDatomicInj(identity)
implicit val entity2Entity: FromDatomicInj[datomic.Entity, Entity] = FromDatomicInj(e => new Entity(e))
}
/**
* A multi-valued function, or relation, from DD => T,
* So the type T is no longer uniquely determined by DD.
* For example, DLong maps to DLong, Long, Int, Short,
* Char, and Byte.
*/
trait FromDatomicImplicits {
implicit def FromDatomicInj2FromDatomic[DD <: AnyRef, T]
(implicit fd: FromDatomicInj[DD, T]): FromDatomic[DD, T] =
FromDatomic[DD, T](fd.from(_))
implicit val DLong2Int: FromDatomic[jl.Long, Int] = FromDatomic(_.toInt)
implicit val DLong2Char: FromDatomic[jl.Long, Short] = FromDatomic(_.toShort)
implicit val DLong2Short: FromDatomic[jl.Long, Char] = FromDatomic(_.toChar)
implicit val DLong2Byte: FromDatomic[jl.Long, Byte] = FromDatomic(_.toByte)
implicit val DBigInt2JBigInt: FromDatomic[JBigInt, JBigInt] = FromDatomic(identity)
implicit val DBigDec2JBigDec: FromDatomic[JBigDecimal, JBigDecimal] = FromDatomic(identity)
implicit val Date2Instant: FromDatomic[Date, Instant] = FromDatomic(_.toInstant)
// implicit def DD2DD[DD <: DatomicData] = FromDatomic[DD, DD]( dd => dd )
/**
* Implicit to convert Datomic tuples, which are expressed simply as Java lists of `Object` proper Scala tuples.
* There is one of these implicits for every size tuple that datomic allows, which is 2-8.
*
* @tparam A the type of the first element in the tuple
* @tparam B the type of the second element in the tuple
* @param conv1 converter to create the first element in the tuple
* @param conv2 converter to create the second element in the tuple
*
* @return a converter from type `java.util.List[AnyRef]` to a tuple of type `(A,B)`
*/
implicit def javaListToTuple2[A,B](implicit conv1: FromDatomicCast[A],
conv2: FromDatomicCast[B]): FromDatomic[ju.List[AnyRef], (A,B)] = new FromDatomic[ju.List[AnyRef], (A,B)] {
override def from(l: ju.List[AnyRef]) = (conv1.from(l.get(0)), conv2.from(l.get(1)))
}
/**
* Implicit to convert Datomic tuples, which are expressed simply as Java lists of `Object` proper Scala tuples.
* There is one of these implicits for every size tuple that datomic allows, which is 2-8.
*
* @tparam A the type of the first element in the tuple
* @tparam B the type of the second element in the tuple
* @tparam C the type of the third element in the tuple
* @param conv1 converter to create the first element in the tuple
* @param conv2 converter to create the second element in the tuple
* @param conv3 converter to create the third element in the tuple
*
* @return a converter from type `java.util.List[AnyRef]` to a tuple of type `(A,B,C,D)`
*/
implicit def javaListToTuple3[A,B,C](implicit conv1: FromDatomicCast[A],
conv2: FromDatomicCast[B],
conv3: FromDatomicCast[C]): FromDatomic[ju.List[AnyRef], (A,B,C)] = new FromDatomic[ju.List[AnyRef], (A,B,C)] {
override def from(l: ju.List[AnyRef]) = (conv1.from(l.get(0)), conv2.from(l.get(1)), conv3.from(l.get(2)))
}
implicit def javaListToTuple4[A,B,C,D](implicit conv1: FromDatomicCast[A],
conv2: FromDatomicCast[B],
conv3: FromDatomicCast[C],
conv4: FromDatomicCast[D]): FromDatomic[ju.List[AnyRef], (A,B,C,D)] = new FromDatomic[ju.List[AnyRef], (A,B,C,D)] {
override def from(l: ju.List[AnyRef]) = (conv1.from(l.get(0)), conv2.from(l.get(1)), conv3.from(l.get(2)), conv4.from(l.get(3)))
}
implicit def javaListToTuple5[A,B,C,D,E](implicit conv1: FromDatomicCast[A],
conv2: FromDatomicCast[B],
conv3: FromDatomicCast[C],
conv4: FromDatomicCast[D],
conv5: FromDatomicCast[E]): FromDatomic[ju.List[AnyRef], (A,B,C,D,E)] = new FromDatomic[ju.List[AnyRef], (A,B,C,D,E)] {
override def from(l: ju.List[AnyRef]) = (conv1.from(l.get(0)), conv2.from(l.get(1)), conv3.from(l.get(2)), conv4.from(l.get(3)), conv5.from(l.get(4)))
}
implicit def javaListToTuple6[A,B,C,D,E,F](implicit conv1: FromDatomicCast[A],
conv2: FromDatomicCast[B],
conv3: FromDatomicCast[C],
conv4: FromDatomicCast[D],
conv5: FromDatomicCast[E],
conv6: FromDatomicCast[F]): FromDatomic[ju.List[AnyRef], (A,B,C,D,E,F)] = new FromDatomic[ju.List[AnyRef], (A,B,C,D,E,F)] {
override def from(l: ju.List[AnyRef]) = (conv1.from(l.get(0)),
conv2.from(l.get(1)),
conv3.from(l.get(2)),
conv4.from(l.get(3)),
conv5.from(l.get(4)),
conv6.from(l.get(5)))
}
implicit def javaListToTuple7[A,B,C,D,E,F,G](implicit conv1: FromDatomicCast[A],
conv2: FromDatomicCast[B],
conv3: FromDatomicCast[C],
conv4: FromDatomicCast[D],
conv5: FromDatomicCast[E],
conv6: FromDatomicCast[F],
conv7: FromDatomicCast[G]): FromDatomic[ju.List[AnyRef], (A,B,C,D,E,F,G)] = new FromDatomic[ju.List[AnyRef], (A,B,C,D,E,F,G)] {
override def from(l: ju.List[AnyRef]) = (conv1.from(l.get(0)),
conv2.from(l.get(1)),
conv3.from(l.get(2)),
conv4.from(l.get(3)),
conv5.from(l.get(4)),
conv6.from(l.get(5)),
conv7.from(l.get(6)))
}
implicit def javaListToTuple8[A,B,C,D,E,F,G,H](implicit conv1: FromDatomicCast[A],
conv2: FromDatomicCast[B],
conv3: FromDatomicCast[C],
conv4: FromDatomicCast[D],
conv5: FromDatomicCast[E],
conv6: FromDatomicCast[F],
conv7: FromDatomicCast[G],
conv8: FromDatomicCast[H]): FromDatomic[ju.List[AnyRef], (A,B,C,D,E,F,G,H)] = new FromDatomic[ju.List[AnyRef], (A,B,C,D,E,F,G,H)] {
override def from(l: ju.List[AnyRef]) = (conv1.from(l.get(0)),
conv2.from(l.get(1)),
conv3.from(l.get(2)),
conv4.from(l.get(3)),
conv5.from(l.get(4)),
conv6.from(l.get(5)),
conv7.from(l.get(6)),
conv8.from(l.get(7)))
}
implicit def JavaSetToScalaSet[T](implicit conv: FromDatomicCast[T]): FromDatomic[ju.Set[AnyRef], Set[T]] = new FromDatomic[ju.Set[AnyRef], Set[T]] {
override def from(l: ju.Set[AnyRef]) = {
val builder = Set.newBuilder[T]
val iter = l.iterator
while (iter.hasNext) {
builder += conv.from(iter.next())
}
builder.result
}
}
implicit def JavaListToScalaSeq[T](implicit conv: FromDatomicCast[T]): FromDatomic[ju.List[AnyRef], Seq[T]] = new FromDatomic[ju.List[AnyRef], Seq[T]] {
override def from(l: ju.List[AnyRef]) = {
val builder = Seq.newBuilder[T]
val iter = l.iterator
while (iter.hasNext) {
builder += conv.from(iter.next())
}
builder.result
}
}
}
/**
* FromDatomicCast fixes the source type
* of FromDatomic as DatomicData
* Trivially, is a multi-valued function
* from DatomicData => T
*/
trait FromDatomicCastImplicits {
implicit def FromDatomic2FromDatomicCast[DD <: AnyRef, A](implicit fdat: FromDatomic[DD, A]) =
FromDatomicCast{ (dd: Any) => fdat.from(dd.asInstanceOf[DD]) }
}
/**
* Think of ToDatomicInj[DD, T] as a type-level function: T => DD
* The implicits here construct a multi-parameter type class,
* and there is a functional dependency from T to DD: T uniquely
* determines DD. In fact, this is an injective function, as there
* is at most one ToDatomicInj for any Scala type, and each
* map to distinct DatomicData subtypes. As a consequence, its inverse
* is a partial function.
*/
trait ToDatomicInjImplicits {
implicit val String2DString = ToDatomicInj[String, String](identity)
implicit val Boolean2DBoolean = ToDatomicInj[jl.Boolean, Boolean](identity)
implicit val Long2DLong = ToDatomicInj[jl.Long, Long](identity)
implicit val Double2DDouble = ToDatomicInj[jl.Double, Double](identity)
implicit val Float2DFloat = ToDatomicInj[jl.Float, Float](identity)
implicit val BigInt2DBigInt = ToDatomicInj[JBigInt, BigInt]((i: BigInt) => i.bigInteger)
implicit val BigDec2DBigDec = ToDatomicInj[JBigDecimal, BigDecimal]((i: BigDecimal) => i.bigDecimal)
implicit val Date2DDate = ToDatomicInj[Date, Date](identity)
implicit val UUID2DUuid = ToDatomicInj[UUID, UUID](identity)
implicit val URI2DUri = ToDatomicInj[URI, URI](identity)
implicit val Bytes2DBytes = ToDatomicInj[Array[Byte], Array[Byte]](identity)
implicit val Keyword2DKeyword = ToDatomicInj[Keyword, Keyword](identity)
}
/**
* ToDatomic extends ToDatomicInj by widening the domain
* and also destroying the injectivity property
* (both Long and Int map to DLong)
* But it is still a function (unlike FromDatomic)
*/
trait ToDatomicImplicits {
implicit def ToDatomicInj2ToDatomic[DD <: AnyRef, T]
(implicit tdat: ToDatomicInj[DD, T]): ToDatomic[DD, T] =
ToDatomic[DD, T](tdat.to(_))
implicit val Int2DLong = ToDatomic[jl.Long, Int](_.toLong)
implicit val Short2DLong = ToDatomic[jl.Long, Short](_.toLong)
implicit val Char2DLong = ToDatomic[jl.Long, Char](_.toLong)
implicit val Byte2DLong = ToDatomic[jl.Long, Byte](_.toLong)
implicit val JBigInt2DBigInt = ToDatomic[JBigInt, JBigInt](identity)
implicit val JBigDec2DBigDec = ToDatomic[JBigDecimal, JBigDecimal](identity)
// Converters for java.time classes
implicit val Instant2Date = ToDatomic[Date, Instant](Date.from)
implicit val OffsetDateTime2Date = ToDatomic[Date, OffsetDateTime](odt => Date.from(odt.toInstant))
implicit val ZonedDateTime2Date = ToDatomic[Date, ZonedDateTime](zdt => Date.from(zdt.toInstant))
// Converters for the various tuple types. Datomic only supports
/**
* Converts a Scala tuple to a Datomic tuple, which is expressed in the Java API simply as a
* `java.util.List[AnyRef]`. This function simultaniously converts each element in the list to its corresponding
* Datomic type, as well as wrapps everything up into a Java List.
*
* Note that there is one of these implicit definitions for each sized tuple that Datomic suppors (2-8).
*
* @param conv1 needed to convert the first element in the tuple to its corresponding Datomic type
* @param conv2 needed to convert the second element in the tuple to its corresponding Datomic type
*
* @return the implicit converter
*/
implicit def tuple2ToList[A,B](implicit conv1: ToDatomicCast[A], conv2: ToDatomicCast[B]) = new ToDatomic[ju.List[AnyRef], (A,B)] {
override def to(c: (A,B)) = {
datomic.Util.list(conv1.to(c._1), conv2.to(c._2)).asInstanceOf[ju.List[AnyRef]]
}
}
implicit def tuple3ToList[A,B,C](implicit conv1: ToDatomicCast[A],
conv2: ToDatomicCast[B],
conv3: ToDatomicCast[C]) = new ToDatomic[ju.List[AnyRef], (A,B,C)] {
override def to(c: (A,B,C)) = {
datomic.Util.list(conv1.to(c._1), conv2.to(c._2), conv3.to(c._3)).asInstanceOf[ju.List[AnyRef]]
}
}
implicit def tuple4ToList[A,B,C,D](implicit conv1: ToDatomicCast[A],
conv2: ToDatomicCast[B],
conv3: ToDatomicCast[C],
conv4: ToDatomicCast[D]) = new ToDatomic[ju.List[AnyRef], (A,B,C,D)] {
override def to(c: (A,B,C,D)) = {
datomic.Util.list(conv1.to(c._1), conv2.to(c._2), conv3.to(c._3), conv4.to(c._4)).asInstanceOf[ju.List[AnyRef]]
}
}
implicit def tuple5ToList[A,B,C,D,E](implicit conv1: ToDatomicCast[A],
conv2: ToDatomicCast[B],
conv3: ToDatomicCast[C],
conv4: ToDatomicCast[D],
conv5: ToDatomicCast[E]) = new ToDatomic[ju.List[AnyRef], (A,B,C,D,E)] {
override def to(c: (A,B,C,D,E)) = {
datomic.Util.list(conv1.to(c._1), conv2.to(c._2), conv3.to(c._3), conv4.to(c._4), conv5.to(c._5)).asInstanceOf[ju.List[AnyRef]]
}
}
implicit def tuple6ToList[A,B,C,D,E,F](implicit conv1: ToDatomicCast[A],
conv2: ToDatomicCast[B],
conv3: ToDatomicCast[C],
conv4: ToDatomicCast[D],
conv5: ToDatomicCast[E],
conv6: ToDatomicCast[F]) = new ToDatomic[ju.List[AnyRef], (A,B,C,D,E,F)] {
override def to(c: (A,B,C,D,E,F)) = {
datomic.Util.list(conv1.to(c._1),
conv2.to(c._2),
conv3.to(c._3),
conv4.to(c._4),
conv5.to(c._5),
conv6.to(c._6)).asInstanceOf[ju.List[AnyRef]]
}
}
implicit def tuple7ToList[A,B,C,D,E,F,G](implicit conv1: ToDatomicCast[A],
conv2: ToDatomicCast[B],
conv3: ToDatomicCast[C],
conv4: ToDatomicCast[D],
conv5: ToDatomicCast[E],
conv6: ToDatomicCast[F],
conv7: ToDatomicCast[G]) = new ToDatomic[ju.List[AnyRef], (A,B,C,D,E,F,G)] {
override def to(c: (A,B,C,D,E,F,G)) = {
datomic.Util.list(conv1.to(c._1),
conv2.to(c._2),
conv3.to(c._3),
conv4.to(c._4),
conv5.to(c._5),
conv6.to(c._6),
conv7.to(c._7)).asInstanceOf[ju.List[AnyRef]]
}
}
implicit def tuple8ToList[A,B,C,D,E,F,G,H](implicit conv1: ToDatomicCast[A],
conv2: ToDatomicCast[B],
conv3: ToDatomicCast[C],
conv4: ToDatomicCast[D],
conv5: ToDatomicCast[E],
conv6: ToDatomicCast[F],
conv7: ToDatomicCast[G],
conv8: ToDatomicCast[H]) = new ToDatomic[ju.List[AnyRef], (A,B,C,D,E,F,G,H)] {
override def to(c: (A,B,C,D,E,F,G,H)) = {
datomic.Util.list(conv1.to(c._1),
conv2.to(c._2),
conv3.to(c._3),
conv4.to(c._4),
conv5.to(c._5),
conv6.to(c._6),
conv7.to(c._7),
conv8.to(c._8)).asInstanceOf[ju.List[AnyRef]]
}
}
implicit def DColl2SetWrites[C, A](implicit ev: C <:< Iterable[A], conv: ToDatomicCast[A]) = new ToDatomic[ju.List[AnyRef], C] {
override def to(c: C) = {
val builder = Seq.newBuilder[AnyRef]
for (e <- c) builder += conv.to(e)
datomic.Util.list(builder.result: _*).asInstanceOf[ju.List[AnyRef]]
}
}
implicit val dbConv = ToDatomic[datomic.Database, Database](_.underlying)
implicit val datomConv = ToDatomic[datomic.Datom, Datom](_.underlying)
implicit val rulesConv = ToDatomic[clojure.lang.IPersistentCollection, QueryRules](_.edn)
implicit val logConv = ToDatomic[datomic.Log, Log](_.log)
}
/**
* ToDatomicCast fixes the return type of ToDatomic as DatomicData
*/
trait ToDatomicCastImplicits {
implicit def DDWriter2ToDatomicCast[DD <: AnyRef, A](implicit tdat: ToDatomic[DD, A]) =
ToDatomicCast[A] { (a: A) => tdat.to(a): AnyRef }
implicit def DIdCast[I <: DId] = ToDatomicCast[I] { (i: I) => i.toDatomicId }
implicit def KeywordIdentified2DRef[I <: KeywordIdentified] = ToDatomicCast[I] { (i: I) => i.ident }
implicit def TempIdentified2DRef [I <: TempIdentified] = ToDatomicCast[I] { (i: I) => i.id.toDatomicId }
implicit def FinalIdentified2DRef [I <: FinalIdentified] = ToDatomicCast[I] { (i: I) => i.id }
implicit val JavaListCast = ToDatomicCast[ju.List[AnyRef]](identity)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy