diff options
6 files changed, 140 insertions, 38 deletions
diff --git a/src/main/scala/com/google/gimd/UserType.scala b/src/main/scala/com/google/gimd/UserType.scala index a5fb242..9bf08b1 100644 --- a/src/main/scala/com/google/gimd/UserType.scala +++ b/src/main/scala/com/google/gimd/UserType.scala @@ -24,11 +24,90 @@ abstract class UserType[T] { def toUserObject(itr: Message): T def toMessage(obj: T) = toMessageBuffer(obj).readOnly - def toMessageBuffer(obj: T): MessageBuffer = new MessageBuffer ++ fields.map { - case FieldSpec(name, f1, f2) => f1(name, f2(obj)) - } + def toMessageBuffer(obj: T): MessageBuffer = + new MessageBuffer ++ fields.flatMap(fieldSpec2Field(obj, _).toList) def fields: List[FieldSpec[T, _]] def children: Seq[NestedMember[_]] = Seq() + + protected final def FieldSpecOne[F](name: String, f1: (String, F) => Field, f2: T => F) = + new FieldSpecOne(name, f1, f2) + + protected final def FieldSpecOption[F](name: String, f1: (String, F) => Field, f2: T => F, + default: F) = new FieldSpecOption(name, f1, f2, default) + + private def fieldSpec2Field[F](obj: T, x: FieldSpec[T, F]) = + x.fieldFactoryOption(x.name, x.f2(obj)) } -case class FieldSpec[T, F](name: String, f1: (String, F) => Field, f2: T => F) +object UserType { + implicit def fieldSpec2SingletonList[T,F](x: FieldSpec[T, F]) = List(x) + + implicit def FieldSpecOne2Int[T](spec: FieldSpecOne[T, Int]) = + (m: Message) => m.one(spec.name).intField.value + + implicit def FieldSpecOne2String[T](spec: FieldSpecOne[T, String]) = + (m: Message) => m.one(spec.name).stringField.value + + implicit def FieldSpecOne2Timestamp[T](spec: FieldSpecOne[T, Timestamp]) = + (m: Message) => m.one(spec.name).timestampField.value + + implicit def FieldSpecOne2Long[T](spec: FieldSpecOne[T, Long]) = + (m: Message) => m.one(spec.name).longField.value + + implicit def FieldSpecOne2BigInt[T](spec: FieldSpecOne[T, BigInt]) = + (m: Message) => m.one(spec.name).bigIntField.value + + implicit def FieldSpecOne2BigDecimalField[T](spec: FieldSpecOne[T, BigDecimal]) = + (m: Message) => m.one(spec.name).bigDecimalField.value + + implicit def FieldSpecOption2Int[T](spec: FieldSpecOption[T, Int]) = + (m: Message) => m.oneOption(spec.name).map(_.intField.value).getOrElse(spec.defaultValue) + + implicit def FieldSpecOption2String[T](spec: FieldSpecOption[T, String]) = + (m: Message) => m.oneOption(spec.name).map(_.stringField.value).getOrElse(spec.defaultValue) + + implicit def FieldSpecOption2Timestamp[T](spec: FieldSpecOption[T, Timestamp]) = + (m: Message) => m.oneOption(spec.name).map(_.timestampField.value).getOrElse(spec.defaultValue) + + implicit def FieldSpecOption2Long[T](spec: FieldSpecOption[T, Long]) = + (m: Message) => m.oneOption(spec.name).map(_.longField.value).getOrElse(spec.defaultValue) + + implicit def FieldSpecOption2BigInt[T](spec: FieldSpecOption[T, BigInt]) = + (m: Message) => m.oneOption(spec.name).map(_.bigIntField.value).getOrElse(spec.defaultValue) + + implicit def FieldSpecOption2BigDecimalField[T](spec: FieldSpecOption[T, BigDecimal]) = + (m: Message) => m.oneOption(spec.name).map(_.bigDecimalField.value).getOrElse(spec.defaultValue) +} + +abstract sealed class FieldSpec[T, F](val name: String, + val fieldFactoryOption: (String, F) => Option[Field], + val f2: T => F) + +final case class FieldSpecOne[T, F](override val name: String, + val fieldFactory: (String, F) => Field, + override val f2: T => F) + extends FieldSpec(name, + (name: String, value: F) => Some(fieldFactory(name, value)), + { userObject: T => + val result = f2(userObject) + if (result == null) + error("UserObject '1%s' returned null for field '%2s'.".format(userObject, name)) + else + result + }) + +final case class FieldSpecOption[T, F](override val name: String, + val fieldFactory: (String, F) => Field, + override val f2: T => F, + val defaultValue: F) + extends FieldSpec(name, + (name: String, value: F) => if (value != defaultValue) + Some(fieldFactory(name, value)) + else + None, + f2) + + +//TODO FieldSpecMany has to be implemented in a future + + diff --git a/src/test/scala/com/google/gimd/UserTypeTestCase.scala b/src/test/scala/com/google/gimd/UserTypeTestCase.scala index 9d239c0..7310156 100644 --- a/src/test/scala/com/google/gimd/UserTypeTestCase.scala +++ b/src/test/scala/com/google/gimd/UserTypeTestCase.scala @@ -17,15 +17,20 @@ package com.google.gimd import org.junit.Test import org.junit.Assert._ +import UserType._ class UserTypeTestCase { - class MyUserType extends UserType[String] { - def toUserObject(itr: Message): String = "" - def fields = Nil + case class Person(id: Int, name: String) + + class PersonUserType extends UserType[Person] { + val id = FieldSpecOne("id", IntField, _.id) + val name = FieldSpecOption("name", StringField, _.name, null) + def toUserObject(m: Message) = Person(id(m), name(m)) + def fields = id :: name } - class MyChildUserType extends MyUserType + class MyChildUserType extends PersonUserType abstract class MyWrongUserTypeBase[T] extends UserType[T] class MyWrongUserType extends MyWrongUserTypeBase[String] { @@ -35,14 +40,14 @@ class UserTypeTestCase { @Test def userTypeClass { - val ut = new MyUserType - assertEquals(classOf[String], ut.userTypeClass) + val ut = new PersonUserType + assertEquals(classOf[Person], ut.userTypeClass) } @Test def userTypeClassInSubtype { val ut = new MyChildUserType - assertEquals(classOf[String], ut.userTypeClass) + assertEquals(classOf[Person], ut.userTypeClass) } @Test{val expected = classOf[IllegalStateException]} @@ -51,4 +56,26 @@ class UserTypeTestCase { ut.userTypeClass } + @Test + def optionalValueNotPresentInMessage { + val msg = Message(Field("id", 2)) + val ut = new PersonUserType + val person = ut.toUserObject(msg) + assertEquals(Person(2, null), person) + } + + @Test + def defaultForOptionalValueInUserObject { + val ut = new PersonUserType + val msg = ut.toMessage(Person(3, null)) + assertEquals(Message(Field("id", 3)), msg) + } + + @Test + def nonDefaultForOptionalValueInUserObject { + val ut = new PersonUserType + val msg = ut.toMessage(Person(3, "John")) + assertEquals(Message(Field("id", 3), Field("name", "John")), msg) + } + } diff --git a/src/test/scala/com/google/gimd/jgit/JGitDatabaseTestCase.scala b/src/test/scala/com/google/gimd/jgit/JGitDatabaseTestCase.scala index 5cad05c..d82e378 100644 --- a/src/test/scala/com/google/gimd/jgit/JGitDatabaseTestCase.scala +++ b/src/test/scala/com/google/gimd/jgit/JGitDatabaseTestCase.scala @@ -20,25 +20,24 @@ import org.junit.Test import org.junit.Assert._ import org.spearce.jgit.lib.{Constants, ObjectId} import query.Predicate +import UserType._ final class JGitDatabaseTestCase extends AbstractJGitTestCase { case class SimpleMessage(name: String, value: Int) object SimpleMessageType extends UserType[SimpleMessage] { - def toUserObject(m: Message): SimpleMessage = { - val name = m.one("name").stringField.value - val value = m.one("value").intField.value - SimpleMessage(name, value) - } - def fields = List(FieldSpec("name", StringField, _.name), FieldSpec("value", IntField, _.value)) + val name = FieldSpecOne("name", StringField, _.name) + val value = FieldSpecOne("value", IntField, _.value) + def fields = name :: value + def toUserObject(m: Message): SimpleMessage = SimpleMessage(name(m), value(m)) } object SimpleMessageFileType extends FileType[SimpleMessage] { val pathPrefix = Some("sm/") val pathSuffix = Some(".sm") val userType = SimpleMessageType - def name(m: Message) = m.one("name").stringField.value + def name(m: Message) = userType.name(m) } @Test diff --git a/src/test/scala/com/google/gimd/jgit/JGitFileTestCase.scala b/src/test/scala/com/google/gimd/jgit/JGitFileTestCase.scala index f11da02..9797b00 100644 --- a/src/test/scala/com/google/gimd/jgit/JGitFileTestCase.scala +++ b/src/test/scala/com/google/gimd/jgit/JGitFileTestCase.scala @@ -18,25 +18,24 @@ import file.FileType import junit.framework.Assert._ import org.junit.Test import org.spearce.jgit.lib.ObjectId +import UserType._ class JGitFileTestCase extends AbstractJGitTestCase { case class SimpleMessage(name: String, value: Int) object SimpleMessageType extends UserType[SimpleMessage] { - def toUserObject(m: Message): SimpleMessage = { - val name = m.one("name").stringField.value - val value = m.one("value").intField.value - SimpleMessage(name, value) - } - def fields = List(FieldSpec("name", StringField, _.name), FieldSpec("value", IntField, _.value)) + val name = FieldSpecOne("name", StringField, _.name) + val value = FieldSpecOne("value", IntField, _.value) + def fields = name :: value + def toUserObject(m: Message) = SimpleMessage(name(m), value(m)) } object SimpleMessageFileType extends FileType[SimpleMessage] { val pathPrefix = None val pathSuffix = None val userType = SimpleMessageType - def name(m: Message) = m.one("name").stringField.value + def name(m: Message) = userType.name(m).toString } @Test diff --git a/src/test/scala/com/google/gimd/modification/ModificationTestCase.scala b/src/test/scala/com/google/gimd/modification/ModificationTestCase.scala index d30f1cf..2f9e09c 100644 --- a/src/test/scala/com/google/gimd/modification/ModificationTestCase.scala +++ b/src/test/scala/com/google/gimd/modification/ModificationTestCase.scala @@ -19,6 +19,7 @@ import file.{FileType, File} import org.junit.Test import org.junit.Assert._ import query._ +import UserType._ final class ModificationTestCase { @@ -26,19 +27,17 @@ final class ModificationTestCase { case class TreeNode(id: Int, name: String) object TreeNodeType extends UserType[TreeNode] { - def toUserObject(m: Message): TreeNode = - new TreeNode( - m.one("id").intField.value, - m.one("name").stringField.value - ) + val id = FieldSpecOne("id", IntField, _.id) + val name = FieldSpecOne("name", StringField, _.name) + def fields = id :: name override def children = Seq(nestedMember) - def fields = List(FieldSpec("id", IntField, _.id), FieldSpec("name", StringField, _.name)) + def toUserObject(m: Message) = new TreeNode(id(m), name(m)) } object MockFileType extends FileType[TreeNode] { val pathPrefix = None val pathSuffix = None val userType = TreeNodeType - def name(m: Message) = m.one("id").stringField.value + def name(m: Message) = userType.id(m).toString } case class MockFile(message: Message) extends File[TreeNode] { val path = "" diff --git a/src/test/scala/com/google/gimd/query/MessageQueryTestCase.scala b/src/test/scala/com/google/gimd/query/MessageQueryTestCase.scala index 21b461b..c8c58eb 100644 --- a/src/test/scala/com/google/gimd/query/MessageQueryTestCase.scala +++ b/src/test/scala/com/google/gimd/query/MessageQueryTestCase.scala @@ -17,17 +17,16 @@ package com.google.gimd.query import file.{File, FileType} import org.junit.Test import org.junit.Assert._ +import UserType._ class MessageQueryTestCase { case class TreeNode(id: Int, name: String) object TreeNodeType extends UserType[TreeNode] { - def toUserObject(m: Message): TreeNode = - new TreeNode( - m.one("id").intField.value, - m.one("name").stringField.value - ) + val id = FieldSpecOne("id", IntField, _.id) + val name = FieldSpecOne("name", StringField, _.name) + def fields = id :: name override def children = Seq(new NestedMember("node", TreeNodeType)) - def fields = List(FieldSpec("id", IntField, _.id), FieldSpec("name", StringField, _.name)) + def toUserObject(m: Message) = new TreeNode(id(m), name(m)) } val root = new TreeNode(-1, "a") |