summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Kossakowski <grek@google.com>2009-09-24 16:54:49 -0700
committerGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2010-04-02 01:36:45 +0200
commit630113dcce18e4be54550669ccfc2c7bc6c3dff9 (patch)
treef8749b09a0f4b8d5582cd5aba86bcf26760f140b
parentcc3cf9fdc34eec1bcb54f81d94184b1e2e38be63 (diff)
downloadgimd-630113dcce18e4be54550669ccfc2c7bc6c3dff9.tar.gz
Define implicit conversions from FieldSpec to functions that extract values.
Defined FieldSpecOne2T and FieldSpecOption implicit conversions where T is a type of a value that given UserObject stores in it's class field. Thanks to that it's possible to use FieldSpec as functions: Message => T which is very convenient for defining UserType and implementing it's method toUserObject. Another improvement is defining implicit conversion FieldSpec => List[FieldSpec] that allows one to define list of FieldSpecs in an elegant way. FieldSpecMany and it's implicit conversions has not been implemented yet as it requires much more work. The problem is with sorting of fields that can be either interpreted as string and int fields. This ambiguity makes it much more difficult to define what is a valid (sorted) representation of given message that has fields named the same but storing both ints and non-int values. Change-Id: Iadeec3a97c7dac60508245e8f773e408ea9e5541 Signed-off-by: Grzegorz Kossakowski <grek@google.com>
-rw-r--r--src/main/scala/com/google/gimd/UserType.scala87
-rw-r--r--src/test/scala/com/google/gimd/UserTypeTestCase.scala41
-rw-r--r--src/test/scala/com/google/gimd/jgit/JGitDatabaseTestCase.scala13
-rw-r--r--src/test/scala/com/google/gimd/jgit/JGitFileTestCase.scala13
-rw-r--r--src/test/scala/com/google/gimd/modification/ModificationTestCase.scala13
-rw-r--r--src/test/scala/com/google/gimd/query/MessageQueryTestCase.scala11
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")