Meanwhile
Yesterday I stared thinking how dynamic languages handle databases. And I got an idea. Scala has type Dynamic that does compilation magic to provide syntactic sugar for working with dynamic languages or objects. Here's an idea: do queries in plain SQL and perform extraction of data in a dynamic way.
And how to do this? Just wrap up Cursor to provide necessary methods.
class WrappedCursor(cursor: Cursor) implements Cursor{ //delegated methods go here }Why I need this? Cake pattern of course, Dynamic cursor get's mixed in.
trait DynamicCursor extends Dynamic{ this: Cursor => def selectDynamic(name: String) = getColumn(getColumnIndex(name)) def getColumn(index: Int) = getType(index) match { case Cursor.FIELD_TYPE_BLOB => getBlob(index) case Cursor.FIELD_TYPE_FLOAT => getDouble(index) case Cursor.FIELD_TYPE_INTEGER => getLong(index) case Cursor.FIELD_TYPE_NULL => null case Cursor.FIELD_TYPE_STRING => getString(index) } def toSeq = (0 until getColumnCount) map getColumn }I targeted API level 14(Ice Cream Sandwich) since getType(method on Cursor) is available from 11 on. Key method here is getColumn that abstracts over types. So you can read a column and do pattern matching on it. Or you are evil and use implicit conversions from Any to String, Long etc... Or use implicit conversion to "converter"
implicit class Converter(val value: Any) extends AnyVal{ def blob = value.asInstanceOf[Array[Byte]] def double = value.asInstanceOf[Double] def long = value.asInstanceOf[Long] def string = value.asInstanceOf[String] }But the real deal is selectDynamic. This allows you to write code like this
val c = new WrappedCursor(result) with DynamicCursor c.someColumn.long
This compiles down to selectDynamic("someColumn") that calls getColumn and finally implicit conversion is inserted that allows for terse cast to Long.
And I threw in a conversion from row to Seq that does a snapshot of current row. This allows pattern matching on rows. Any you can now construct a Stream that will handle Cursor state and lazily evaluate and store these snapshots. Therefore you can abstract away all mutability and handle cursor as immutable collection.
Said conversion to stream
And I threw in a conversion from row to Seq that does a snapshot of current row. This allows pattern matching on rows. Any you can now construct a Stream that will handle Cursor state and lazily evaluate and store these snapshots. Therefore you can abstract away all mutability and handle cursor as immutable collection.
Said conversion to stream
def CursorStream(cursor: DynamicCursorRaw with Cursor) = { def loop(): Stream[Seq[Any]] = { if(cursor.isAfterLast) Stream.empty[Seq[Any]] else { val snapshot = cursor.toSeq cursor.moveToNext() snapshot #:: loop() } } cursor.moveToFirst() loop() }And some more implicits to help
implicit class RichCursorRaw(cursor: Cursor) extends AnyVal{ def dynamicRaw = new WrappedCursor(cursor) with DynamicCursorRaw def toStream = CursorStream(dynamicRaw) }All the source is in the project on github https://github.com/edofic/dynamic-db-android (work in progress).
No comments:
Post a Comment