All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bson.decoding.OptimizedLazyBSONObject.scala Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2010 10gen, Inc. 
 * Copyright (c) 2009, 2010 Novus Partners, Inc. 
 * 
 * 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.
 *
 * For questions and comments about this product, please see the project page at:
 *
 *     http://github.com/mongodb/casbah
 * 
 */

package com.mongodb.casbah.util;
package bson.decoding;

import org.bson._
import org.bson.types._

import com.mongodb.DBObject 

import java.util.{ Date, UUID }

import scala.annotation.{ tailrec, switch }
import com.mongodb.casbah.util.bson.decoding.io.BSONByteBuffer;

import scala.util.control.Exception._
import scala.collection.JavaConversions._
import scala.collection.mutable.{ HashMap, HashSet } 

/**
 * @author Brendan McAdams 
 */
class OptimizedLazyBSONObject(val input: BSONByteBuffer,
                              val callback: OptimizedLazyBSONCallback, 
                              val doc_start: Int = 0) extends BSONObject with Logging { self =>
  type LookupRecord = Pair[Byte, Int]

  final val FirstElementOffset = 4
  final val fieldIndex = HashMap.empty[String, LookupRecord] 
  final val noHitIndex = HashSet.empty[String] 

  /** 
   * We are implementing a java based API so of course 
   * we get the fun of returning null.
   *
   * ... ugh.
   */
  def get(key: String): AnyRef = {

    if (noHitIndex.contains( key )) 
      null
    
    @tailrec def findField(offset: Int): Option[LookupRecord] = {
      if ( elementEmpty_?( offset ) )
        None
      else {
        val _type = elementType( offset )
        val name = input.cString( offset )
        val fieldOff = sizeCString( offset )
        val v = offset + ( fieldOff )
      
        val t_record = (_type, v)

        fieldIndex put (name, t_record)

        if ( name == key ) 
          Some( t_record )
        else
          findField( offset + ( fieldOff +  elementBSONSize( offset, _type ) ) )

      }
    }

    fieldIndex.get( key ) match {
      case Some( record ) =>
        elementValue( record )
      case None => findField( doc_start + FirstElementOffset ) match {
        case Some( record ) =>
          elementValue( record )
        case None =>
          noHitIndex += key
          null // Goddamn Java based interfaces, I hate returning null
      }
    }

  }

  @Deprecated
  def containsKey(key: String): Boolean = 
    containsField( key )

  def containsField(key: String): Boolean = 
    if (noHitIndex.contains( key )) 
      false 
    else if (fieldIndex.contains( key )) 
      true
    else 
      keySet().contains( key )


  def keySet: java.util.Set[String] = new OptimizedLazyBSONKeySet()

  def elementEmpty_?(offset: Int): Boolean = elementType( offset ) == BSON.EOO

  def empty_?(): Boolean = elementEmpty_?( doc_start + FirstElementOffset )

  def isEmpty: Boolean = empty_?

  def bsonSize(offset: Int = doc_start) = input.int(offset)
  
  protected def elementFieldName(offset: Int) = input.cString(offset)

  protected def elementType(offset: Int) = input(offset)

  protected def elementBSONSize(offset: Int, _type: Byte = -1): Int = {
    import BSON._

    val t = if (_type == -1) elementType(offset) else _type
    val n = sizeCString( offset + 1 )
    val v = (offset + 1 ) + n // Value Offset

    (t: @switch) match {
      case EOO | UNDEFINED | NULL | MAXKEY | MINKEY => 0
      case BOOLEAN => 1
      case NUMBER_INT => 4
      case TIMESTAMP | DATE | NUMBER_LONG | NUMBER => 8
      case OID => 12
      case SYMBOL | CODE | STRING => bsonSize( v ) + 4
      case CODE_W_SCOPE => bsonSize( v ) 
      case REF => bsonSize( 4 ) + 12
      case OBJECT | ARRAY => bsonSize( v )
      case BINARY => bsonSize( v ) + 4 + 1 /* subtype */ 
      case REGEX => 
        // Comprised of 2 cStrings
        val _1 = sizeCString( v ) + 1 
        _1 + ( sizeCString( v + _1 ) + 1 ) + 4
      case _ =>
        throw new BSONException("Invalid BSON Type '" + t + "'.")
    }
  }

  protected def sizeCString(offset: Int) = {
    @tailrec def cStrSz(x: Int): Int = input(x) match {
      case 0 => x
      case b => cStrSz(x + 1)
    }
    cStrSz(offset + 1) - offset + 1
  }

  protected def elementValue(record: LookupRecord): AnyRef = {
    import BSON._ 
    val v: Int = record._2 // Value Offset

    /** Switch lookup on type */
    ( (record._1: @switch) match {
      case EOO | UNDEFINED | NULL => 
        null
      case MAXKEY => 
        callback.createMaxKey()
      case MINKEY => 
        callback.createMinKey()
      case BOOLEAN => 
        callback.createBoolean( input( v ) != 0 )
      case NUMBER =>
        callback.createDouble( java.lang.Double.longBitsToDouble( input.long( v ) ) )
      case NUMBER_INT =>
        callback.createInt( input.int( v ) )
      case NUMBER_LONG =>
        callback.createLong( input.long( v ) )
      case TIMESTAMP => 
        callback.createTimestamp( input.int( v + 4 ),  input.int( v ) )
      case DATE => 
        callback.createDate( input.long( v ) )
      case OID => 
        callback.createObjectId( input.intBE( v ), input.intBE( v + 4 ),  input.intBE( v + 8 ) )
      case STRING => 
        callback.createString( input.utf8String( v ) )
      case SYMBOL => 
        callback.createSymbol( input.utf8String( v ) )
      case CODE => 
        callback.createCode( input.utf8String( v ) )
      case CODE_W_SCOPE => 
        callback.createCodeWScope( input.utf8String( v + 4 ),
                        callback.createObject( input.array,  v + 4 + 4 + bsonSize( v + 4) ).asInstanceOf[BSONObject] )
      case REF => 
        val o = v + bsonSize( v ) + 4
        val ns = input.cString( v + 4 )
        val oid = callback.createObjectId(  input.intBE( o ),
                                            input.intBE( o + 4 ),
                                            input.intBE( o + 8 ) ).asInstanceOf[ObjectId]
        callback.createDBRef( ns, oid ) 
      case OBJECT => 
        callback.createObject( input.array, v )
      case ARRAY => 
        callback.createObject( input.array, v )
      case BINARY => 
        readBinary( v )
      case REGEX =>
        callback.createRegex( input.cString( v ) /* pattern */,
                              input.cString( v + sizeCString( v ) ) /* flags*/
                            )
      case _ =>
        throw new BSONException("Invalid BSON Type '" + record._1 + "'.")

    } ).asInstanceOf[AnyRef] // f-ing boxing bullshit
  }

  protected def readBinary(offset: Int): AnyRef = {
    import BSON.{ B_GENERAL, B_BINARY, B_UUID }

    val l = bsonSize( offset ) 

    (input( offset + 4 ): @switch) match {
      case B_BINARY => 
        val _l = bsonSize( offset + 4 )
        require(_l + 4 ==  l, "Bad Data Size: Binary Subtype 2." + " { Got: " + 
                              _l + " Expected: " + l + "}")
        callback.createBinary( B_BINARY, ( for {
            n <- 0 until l
          } yield input( (offset + 4 + 4) + n ) ).toArray )
      case B_UUID =>
        require( l == 16,  "Bad Data Size: Binary Subtype 3 (UUID)." + " { Got: " + 
                           l + " Expected: 16 }")
        callback.createUUID( input.long( offset + 4 ), input.long( offset + 4 + 8 ) )
      /** B_GENERAL behavior is identical to 'default'. */
      case B_GENERAL | _ => 
        callback.createBinary( ( for {
            n <- 0 until l
          } yield input( (offset + 4) + n ) ).toArray )
    }
  }
  
  /**
   * Returns a JSON Serialization of the object
   */
  //override def toString: String = com.mongodb.util.JSON.serialize(this)
  
  def put(key: String, v: AnyRef): AnyRef = 
    throw new UnsupportedOperationException("Read Only.")

  def putAll(o: BSONObject): Unit = 
    throw new UnsupportedOperationException("Read Only.")
  
  def putAll(m: java.util.Map[_, _]): Unit = 
    throw new UnsupportedOperationException("Read Only.")

  def toMap(): java.util.Map[_, _] = 
    throw new UnsupportedOperationException("Read Only.")

  def removeField(key: String): AnyRef = 
    throw new UnsupportedOperationException("Read Only.")


  class OptimizedLazyBSONIterator extends java.util.Iterator[String] {
    var offset = doc_start + FirstElementOffset

    def hasNext(): Boolean = !elementEmpty_?(offset) 

    def next(): String = {
      val fieldSize = sizeCString(offset)
      val key = input.cString( offset )
      val elementSize = elementBSONSize(offset)
      offset += ( fieldSize + elementSize )
      key
    }

    def remove() {
      throw new UnsupportedOperationException("Read Only.")
    }
  }

  class OptimizedLazyBSONKeySet extends java.util.Set[String] {

    def size(): Int = iterator.size

    def isEmpty: Boolean = self.isEmpty

    def contains(o: AnyRef) = iterator.contains(o)

    def iterator: java.util.Iterator[String] = new OptimizedLazyBSONIterator()
    
    def toArray: Array[AnyRef] = 
      Array(iterator.map( _.asInstanceOf[AnyRef] ))

    def toArray[T](ts: Array[T with AnyRef]): Array[T with AnyRef] = {
      var i = 0
      for (k <- iterator if i < ts.length) 
        ts(i) = k.asInstanceOf[T with AnyRef]; i += 1
      ts
    }


    def add(e: String): Boolean = 
      throw new UnsupportedOperationException("Read Only.")

    def remove(e: AnyRef): Boolean = 
      throw new UnsupportedOperationException("Read Only.")

    def containsAll(coll: java.util.Collection[_]): Boolean = 
      iterator forall (coll contains)

    def addAll(coll: java.util.Collection[_ <: String]): Boolean = 
      throw new UnsupportedOperationException("Read Only.")

    def retainAll(coll: java.util.Collection[_]): Boolean = 
      throw new UnsupportedOperationException("Read Only.")

    def removeAll(coll: java.util.Collection[_]): Boolean = 
      throw new UnsupportedOperationException("Read Only.")
  
    def clear(): Unit = 
      throw new UnsupportedOperationException("Read Only.")
  }


}

class OptimizedLazyDBObject(input: BSONByteBuffer,
                            callback: OptimizedLazyBSONCallback, 
                            doc_start: Int = 0) 
  extends OptimizedLazyBSONObject(input, callback, doc_start) with DBObject {

  def markAsPartialObject = ()

  def isPartialObject: Boolean = false

}

// vim: set ts=2 sw=2 sts=2 et:




© 2015 - 2025 Weber Informatics LLC | Privacy Policy