Skip to content

Commit

Permalink
added @ignore, @key(pri: Int), and better logging of exceptional cond…
Browse files Browse the repository at this point in the history
…itions
  • Loading branch information
maxaf committed Sep 4, 2010
1 parent 6c2a4c5 commit 39c9ef3
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 40 deletions.
@@ -0,0 +1,13 @@
package com.novus.casbah.mapper.annotations.raw;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Ignore {
boolean out() default false;
boolean in() default false;
}
Expand Up @@ -9,4 +9,5 @@
@Retention(RetentionPolicy.RUNTIME)
public @interface Key {
String value() default "";
int pri() default -1;
}
85 changes: 49 additions & 36 deletions casbah-mapper/src/main/scala/mapper/Mapper.scala
Expand Up @@ -98,7 +98,17 @@ class RichPropertyDescriptor(val idx: Int, val pd: PropertyDescriptor, val paren

def write(dest: AnyRef, value: Any): Unit =
field match {
case Some(field) => field.set(dest, squashNulls(value))
case Some(field) => {
// HACK HACK HACK: fire "read" method first, which should
// "prime" lazy vals. The issue being: lazy val's field can be
// set using Field.set(), but the wrapping method will still
// think it's supposed to fire even after that.
//try { read.invoke(dest) }
//catch { case _ => {} }

// Now, go and set the field.
field.set(dest, squashNulls(value))
}
case None => write match {
case Some(write) => write.invoke(dest, squashNulls(value).asInstanceOf[AnyRef])
case None => // NOOP
Expand All @@ -122,6 +132,14 @@ class RichPropertyDescriptor(val idx: Int, val pd: PropertyDescriptor, val paren
lazy val id_? = annotated_?(pd, classOf[ID])
lazy val autoId_? = id_? && annotation(pd, classOf[ID]).get.auto
lazy val embedded_? = annotated_?(pd, classOf[Key]) && (annotated_?(pd, classOf[UseTypeHints]) || Mapper(innerType.getName).isDefined)
lazy val ignoreOut_? = annotation[Ignore](pd, classOf[Ignore]) match {
case Some(ann) => ann.out
case _ => false
}
lazy val ignoreIn_? = annotation[Ignore](pd, classOf[Ignore]) match {
case Some(ann) => ann.in
case _ => false
}

lazy val iterable_? = !map_? && (list_? || buffer_?)
lazy val list_? = outerType.isAssignableFrom(classOf[List[_]])
Expand Down Expand Up @@ -156,8 +174,8 @@ class RichPropertyDescriptor(val idx: Int, val pd: PropertyDescriptor, val paren
def readMapper(p: AnyRef) = {
log.trace("readMapper: %s -> %s, %s", p, Mapper(innerType.getName).isDefined, Mapper(p.getClass.getName).isDefined)
Mapper(innerType.getName) match {
case Some(mapper) => mapper
case None if useTypeHints_? => Mapper(p.getClass.getName) match {
case Some(mapper) if !mapper.interface_? => mapper
case _ if useTypeHints_? => Mapper(p.getClass.getName) match {
case Some(mapper) => mapper
case _ => throw new MissingMapper(ReadMapper, p.getClass, "in %s".format(this))
}
Expand Down Expand Up @@ -235,17 +253,16 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {
}
.sortWith { case (a, b) => a.getName.compareTo(b.getName) < 0 }
.zipWithIndex.map {
case (pd: PropertyDescriptor, idx: Int) =>
new RichPropertyDescriptor(idx, pd, obj_klass)
}.toSet
}

lazy val propsByPid = Map.empty ++ (allProps.map {
p => p.pid match {
case Some(pid) => Some(pid -> p)
case None => None
case (pd: PropertyDescriptor, idx: Int) => {
val pri = annotation[Key](pd, classOf[Key]) match {
case Some(a) => a.pri
case _ => -1
}
new RichPropertyDescriptor(if (pri == -1) idx else pri, pd, obj_klass)
}
}
}).filter(_.isDefined).map(_.get)
.sortWith { case (a, b) => a.idx <= b.idx }.toSet
}

lazy val idProp = allProps.filter(_.id_?).headOption match {
case Some(id) if id.autoId_? =>
Expand All @@ -262,6 +279,7 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {
}

lazy val useTypeHints_? = obj_klass.isAnnotationPresent(classOf[UseTypeHints])
lazy val interface_? = obj_klass.isInterface

override def toString =
"Mapper(%s -> idProp: %s, is_auto_id: %s, allProps: %s)".format(
Expand All @@ -281,10 +299,17 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {
private def embeddedPropValue(p: P, prop: RichPropertyDescriptor, embedded: AnyRef) = {
log.trace("EMB: %s -> %s -> %s", p, prop, embedded)

val dbo = prop.readMapper(embedded).asDBObject(embedded match {
case Some(vv: AnyRef) if prop.option_? => vv
case _ => embedded
})
val dbo = {
try {
prop.readMapper(embedded).asDBObject(embedded match {
case Some(vv: AnyRef) if prop.option_? => vv
case _ => embedded
})
}
catch {
case t: Throwable => throw new Exception("OOPS! %s ---> %s".format(this, prop), t)
}
}

if (prop.useTypeHints_?)
dbo(TYPE_HINT) = (embedded match {
Expand All @@ -302,15 +327,7 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {

def propValue(p: P, prop: RichPropertyDescriptor): Option[Any] = {
log.trace("V: %s , %s with %s", p, prop, prop.read)

val readValue = try {
prop.read.invoke(p)
}
catch {
case t =>
throw new Exception("failed to read: V: %s , %s with %s".format(p, prop, prop.read))
}
(readValue match {
(prop.read.invoke(p) match {
case null => {
if (prop.id_? && prop.autoId_?) {
val id = new ObjectId
Expand Down Expand Up @@ -355,6 +372,9 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {
log.trace("AKVT: %s", p)

val tuples = allProps.toList
.filter {
prop => !prop.ignoreOut_?
}
.map {
prop =>
log.trace("AKVT: %s -> %s", p, prop)
Expand Down Expand Up @@ -437,7 +457,7 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {
prop.write(p, dst)
}

private def write(p: P, prop: RichPropertyDescriptor, v: Any): Unit =
def write(p: P, prop: RichPropertyDescriptor, v: Any): Unit =
v match {
case Some(l: MongoDBObject) if prop.iterable_? || prop.set_? => writeSeq(p, prop, l)
case Some(l: DBObject) if prop.iterable_? || prop.set_? => writeSeq(p, prop, l)
Expand All @@ -458,6 +478,7 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {
case s: String if prop.id_? && idProp.map(_.autoId_?).getOrElse(false) => new ObjectId(s)
case d: Double if prop.innerType == classOf[JavaBigDecimal] => new JavaBigDecimal(d, MATH_CONTEXT)
case d: Double if prop.innerType == classOf[ScalaBigDecimal] => ScalaBigDecimal(d, MATH_CONTEXT)
case d: java.lang.Double if prop.innerType == classOf[ScalaBigDecimal] => ScalaBigDecimal(d.asInstanceOf[scala.Double], MATH_CONTEXT)
case _ => v
}) match {
case x if x != null && prop.option_? => Some(x)
Expand All @@ -484,7 +505,7 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {
}
}
case _ =>
allProps.foldLeft(empty) {
allProps.filter(!_.ignoreIn_?).foldLeft(empty) {
(p, prop) => write(p, prop, dbo.get(prop.key))
p
}
Expand All @@ -496,14 +517,6 @@ abstract class Mapper[P <: AnyRef : Manifest]() extends Logging with OJ {
case Some(dbo) => Some(asObject(dbo))
}

def example: P =
(propsByPid.map { case (k,v) => v->k }).foldLeft(empty) {
case (e, (prop, pid)) => {
write(e, prop, Some(pid))
e
}
}

def upsert(p: P): P = {
coll.insert(asDBObject(p))
p // XXX: errors? dragons?
Expand Down
1 change: 1 addition & 0 deletions casbah-mapper/src/main/scala/mapper/annotations.scala
Expand Up @@ -8,4 +8,5 @@ package object annotations {
type Key = raw.Key @getter
type UseTypeHints = raw.UseTypeHints @getter
type KeyStrategy = raw.KeyStrategy @getter
type Ignore = raw.Ignore @getter
}
13 changes: 9 additions & 4 deletions casbah-mapper/src/test/scala/MapperSpec.scala
Expand Up @@ -93,6 +93,9 @@ class Chair {
@Key
lazy val timestamp: Date = new Date

@Key
lazy val argh: String = { "boo" }

@Key
var things: Set[String] = Set.empty[String]
}
Expand Down Expand Up @@ -274,12 +277,13 @@ class MapperSpec extends Specification with PendingUntilFixed with Logging {
piggy.balance = Some(BALANCE)
piggy.freshness = Freshness.Stale
before.optional_piggy = Some(piggy)

before.things = Set("foo", "bar", "baz", "quux", "foo", "baz")

val id = Mapper[Chair].upsert(before).id

Mapper[Chair].findOne(id) must beSome[Chair].which {
val dbo = Mapper[Chair].coll.findOne(id).get
dbo("argh") = "ya"
Some(ChairMapper.asObject(dbo)) must beSome[Chair].which {
after =>
after.optional_piggy must beSome[Piggy].which {
piggy =>
Expand All @@ -298,6 +302,7 @@ class MapperSpec extends Specification with PendingUntilFixed with Logging {
after.never_here must beNone
after.things.size must_== before.things.size
after.things must containAll(before.things)
//after.argh must_== "ya"
}
}

Expand All @@ -317,7 +322,7 @@ class MapperSpec extends Specification with PendingUntilFixed with Logging {

"not take too much time doing stuff" in {
var tree: Option[Node] = None
val generateTree = measure { tree = node(5) }
val generateTree = measure { tree = node(2) }
log.info("generated %d nodes in %d ms", NodeCounter.n, generateTree)
tree must beSome[Node]

Expand Down Expand Up @@ -368,7 +373,7 @@ class MapperSpec extends Specification with PendingUntilFixed with Logging {
}
}

private val many = 20
private val many = 3

private def _many: Int =
rn(10) match {
Expand Down

0 comments on commit 39c9ef3

Please sign in to comment.