ongo.reactivemongo-bson_2.10.0.11.13.source-code.types.scala Maven / Gradle / Ivy
/*
* Copyright 2013 Stephane Godbillon (@sgodbillon)
*
* 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.
*/
package reactivemongo.bson
import exceptions.DocumentKeyNotFound
import scala.util.{ Failure, Success, Try }
import buffer._
import utils.Converters
/** A BSON Double. */
case class BSONDouble(value: Double) extends BSONValue {
val code = 0x01.toByte
}
case class BSONString(value: String) extends BSONValue {
val code = 0x02.toByte
}
/**
* A `BSONDocument` structure (BSON type `0x03`).
*
* A `BSONDocument` is basically a stream of tuples `(String, BSONValue)`.
* It is completely lazy. The stream it wraps is a `Stream[Try[(String, BSONValue)]]` since
* we cannot be sure that a not yet deserialized value will be processed without error.
*
* @define keyParam the key to be found in the document
*/
case class BSONDocument(stream: Stream[Try[BSONElement]]) extends BSONValue {
val code = 0x03.toByte
/**
* Checks whether the given key is found in the document.
*
* @param key $keyParam
* @return true if the key is found
*/
def contains(key: String): Boolean = elements.exists(_._1 == key)
/**
* Returns the [[BSONValue]] associated with the given `key`.
* If the key is not found or the matching value cannot be deserialized, returns `None`.
*
* @param key $keyParam
*/
def get(key: String): Option[BSONValue] = elements.collectFirst {
case (`key`, value) => value
}
/**
* Returns the [[BSONValue]] associated with the given `key`.
*
* If the key is not found or the matching value cannot be deserialized, returns a `Failure`.
* The `Failure` holds a [[exceptions.DocumentKeyNotFound]] if the key could not be found.
*
* @param key $keyParam
*/
def getTry(key: String): Try[BSONValue] = stream.collectFirst {
case Success((k, cause)) if k == key => Success(cause)
case Failure(e) => Failure(e)
}.getOrElse(Failure(DocumentKeyNotFound(key)))
/**
* Returns the [[BSONValue]] associated with the given `key`.
*
* If the key could not be found, the resulting option will be `None`.
* If the matching value could not be deserialized, returns a `Failure`.
*
* @param key $keyParam
*/
def getUnflattenedTry(key: String): Try[Option[BSONValue]] = getTry(key) match {
case Failure(DocumentKeyNotFound(_)) => Success(None)
case Failure(e) => Failure(e)
case Success(e) => Success(Some(e))
}
/**
* Returns the [[BSONValue]] associated with the given `key`, and converts it with the given implicit [[BSONReader]].
*
* If there is no matching value, or the value could not be deserialized or converted, returns a `None`.
*
* @param key $keyParam
*
* @note When implementing a [[http://reactivemongo.org/releases/latest/documentation/bson/typeclasses.html custom reader]], [[getAsTry]] must be preferred.
*/
def getAs[T](key: String)(implicit reader: BSONReader[_ <: BSONValue, T]): Option[T] = get(key).flatMap { element =>
reader match {
case r: BSONReader[BSONValue, T] @unchecked => r.readOpt(element)
case _ => None
}
}
/**
* Returns the [[BSONValue]] associated with the given `key`, and converts it with the given implicit [[BSONReader]].
*
* If there is no matching value, or the value could not be deserialized or converted, returns a `Failure`.
* The `Failure` holds a [[exceptions.DocumentKeyNotFound]] if the key could not be found.
*
* @param key $keyParam
*/
def getAsTry[T](key: String)(implicit reader: BSONReader[_ <: BSONValue, T]): Try[T] = {
val tt = getTry(key)
tt.flatMap { element => Try(reader.asInstanceOf[BSONReader[BSONValue, T]].read(element)) }
}
/**
* Returns the [[BSONValue]] associated with the given `key`, and converts it with the given implicit [[BSONReader]].
*
* If there is no matching value, returns a `Success` holding `None`.
* If the value could not be deserialized or converted, returns a `Failure`.
*/
def getAsUnflattenedTry[T](key: String)(implicit reader: BSONReader[_ <: BSONValue, T]): Try[Option[T]] = getAsTry(key)(reader) match {
case Failure(e: DocumentKeyNotFound) => Success(None)
case Failure(e) => Failure(e)
case Success(e) => Success(Some(e))
}
/** Creates a new [[BSONDocument]] containing all the elements of this one and the elements of the given document. */
def add(doc: BSONDocument): BSONDocument = new BSONDocument(stream ++ doc.stream)
/** Creates a new [[BSONDocument]] containing all the elements of this one and the given `elements`. */
def add(elements: Producer[(String, BSONValue)]*): BSONDocument =
new BSONDocument(stream ++ elements.flatMap { el =>
el.produce.map(value => Seq(Try(value))).getOrElse(Seq.empty)
}.toStream)
/** Creates a new [[BSONDocument]] without the elements corresponding the given `keys`. */
def remove(keys: String*): BSONDocument = new BSONDocument(stream.filter {
case Success((key, _)) if (keys contains key) => false
case _ => true
})
/** Alias for `add(doc: BSONDocument): BSONDocument` */
def ++(doc: BSONDocument): BSONDocument = add(doc)
/** Alias for `add(elements: Producer[(String, BSONValue)]*): BSONDocument` */
def ++(elements: Producer[(String, BSONValue)]*): BSONDocument = add(elements: _*)
/** Alias for `remove(names: String*)` */
def --(keys: String*): BSONDocument = remove(keys: _*)
/** Returns a `Stream` for all the elements of this `BSONDocument`. */
lazy val elements: Stream[BSONElement] = stream.filter(_.isSuccess).map(_.get)
/** Is this document empty? */
def isEmpty: Boolean = stream.isEmpty
override def toString: String = "BSONDocument(<" + (if (isEmpty) "empty" else "non-empty") + ">)"
}
object BSONDocument {
/** Creates a new [[BSONDocument]] containing all the given `elements`. */
def apply(elements: Producer[BSONElement]*): BSONDocument = new BSONDocument(
elements.flatMap { el =>
el.produce.map(value => Seq(Try(value))).getOrElse(Seq.empty)
}.toStream)
/** Creates a new [[BSONDocument]] containing all the `elements` in the given `Traversable`. */
def apply(elements: Traversable[BSONElement]): BSONDocument =
new BSONDocument(elements.toStream.map(Success(_)))
/** Returns a String representing the given [[BSONDocument]]. */
def pretty(doc: BSONDocument) = BSONIterator.pretty(doc.stream.iterator)
/** Writes the `document` into the `buffer`. */
def write(value: BSONDocument, buffer: WritableBuffer)(implicit bufferHandler: BufferHandler = DefaultBufferHandler): WritableBuffer =
bufferHandler.writeDocument(value, buffer)
/**
* Reads a `document` from the `buffer`.
*
* Note that the buffer's readerIndex must be set on the start of a document, or it will fail.
*/
def read(buffer: ReadableBuffer)(implicit bufferHandler: BufferHandler = DefaultBufferHandler): BSONDocument = bufferHandler.readDocument(buffer).get
/** An empty BSONDocument. */
val empty: BSONDocument = BSONDocument()
}
/**
* A `BSONArray` structure (BSON type `0x04`).
*
* A `BSONArray` is a straightforward `BSONDocument` where keys are a sequence of positive integers.
*
* A `BSONArray` is basically a stream of tuples `(String, BSONValue)` where the first member is a string representation of an index.
* It is completely lazy. The stream it wraps is a `Stream[Try[(String, BSONValue)]]` since
* we cannot be sure that a not yet deserialized value will be processed without error.
*/
case class BSONArray(stream: Stream[Try[BSONValue]]) extends BSONValue {
val code = 0x04.toByte
/**
* Returns the [[BSONValue]] at the given `index`.
*
* If there is no such `index` or the matching value cannot be deserialized, returns `None`.
*/
def get(index: Int): Option[BSONValue] = getTry(index).toOption
/**
* Returns the [[BSONValue]] at the given `index`.
*
* If there is no such `index` or the matching value cannot be deserialized, returns a `Failure`.
* The `Failure` holds a [[exceptions.DocumentKeyNotFound]] if the key could not be found.
*/
def getTry(index: Int): Try[BSONValue] = stream.drop(index).headOption.getOrElse(Failure(DocumentKeyNotFound(index.toString)))
/**
* Returns the [[BSONValue]] at the given `index`.
*
* If there is no such `index`, the resulting option will be `None`.
* If the matching value could not be deserialized, returns a `Failure`.
*/
def getUnflattenedTry(index: Int): Try[Option[BSONValue]] = getTry(index) match {
case Failure(e: DocumentKeyNotFound) => Success(None)
case Failure(e) => Failure(e)
case Success(e) => Success(Some(e))
}
/**
* Gets the [[BSONValue]] at the given `index`, and converts it with the given implicit [[BSONReader]].
*
* If there is no matching value, or the value could not be deserialized or converted, returns a `None`.
*/
def getAs[T](index: Int)(implicit reader: BSONReader[_ <: BSONValue, T]): Option[T] = {
getTry(index).toOption.flatMap { element =>
Try(reader.asInstanceOf[BSONReader[BSONValue, T]].read(element)).toOption
}
}
/**
* Gets the [[BSONValue]] at the given `index`, and converts it with the given implicit [[BSONReader]].
*
* If there is no matching value, or the value could not be deserialized or converted, returns a `Failure`.
* The `Failure` holds a [[exceptions.DocumentKeyNotFound]] if the key could not be found.
*/
def getAsTry[T](index: Int)(implicit reader: BSONReader[_ <: BSONValue, T]): Try[T] = {
val tt = getTry(index)
tt.flatMap { element => Try(reader.asInstanceOf[BSONReader[BSONValue, T]].read(element)) }
}
/**
* Gets the [[BSONValue]] at the given `index`, and converts it with the given implicit [[BSONReader]].
*
* If there is no matching value, returns a `Success` holding `None`.
* If the value could not be deserialized or converted, returns a `Failure`.
*/
def getAsUnflattenedTry[T](index: Int)(implicit reader: BSONReader[_ <: BSONValue, T]): Try[Option[T]] = getAsTry(index)(reader) match {
case Failure(e: DocumentKeyNotFound) => Success(None)
case Failure(e) => Failure(e)
case Success(e) => Success(Some(e))
}
/** Creates a new [[BSONArray]] containing all the elements of this one and the elements of the given document. */
def add(doc: BSONArray): BSONArray = new BSONArray(stream ++ doc.stream)
/** Creates a new [[BSONArray]] containing all the elements of this one and the given `elements`. */
def add(elements: Producer[BSONValue]*): BSONArray = new BSONArray(
stream ++ elements.flatMap { el =>
el.produce.map(value => Seq(Try(value))).getOrElse(Seq.empty)
}.toStream)
/** Alias for `add(arr: BSONArray): BSONArray` */
def ++(array: BSONArray): BSONArray = add(array)
/** Alias for `add(elements: Producer[BSONValue]*): BSONArray` */
def ++(elements: Producer[BSONValue]*): BSONArray = add(elements: _*)
def iterator: Iterator[Try[(String, BSONValue)]] = stream.zipWithIndex.map { vv =>
vv._1.map(vv._2.toString -> _)
}.toIterator
def values: Stream[BSONValue] = stream.filter(_.isSuccess).map(_.get)
lazy val length = stream.size
/** Is this array empty? */
def isEmpty: Boolean = stream.isEmpty
override def toString: String = s"BSONArray(<${if (isEmpty) "empty" else "non-empty"}>)"
}
object BSONArray {
/** Creates a new [[BSONArray]] containing all the given `elements`. */
def apply(elements: Producer[BSONValue]*): BSONArray = new BSONArray(
elements.flatMap { el =>
el.produce.map(value => Seq(Try(value))).getOrElse(Seq.empty)
}.toStream)
/** Creates a new [[BSONArray]] containing all the `elements` in the given `Traversable`. */
def apply(elements: Traversable[BSONValue]): BSONArray = {
new BSONArray(elements.toStream.map(Success(_)))
}
/** Returns a String representing the given [[BSONArray]]. */
def pretty(array: BSONArray) = BSONIterator.pretty(array.iterator)
/** An empty BSONArray. */
val empty: BSONArray = BSONArray()
}
/**
* A BSON binary value.
*
* @param value The binary content.
* @param subtype The type of the binary content.
*/
case class BSONBinary(value: ReadableBuffer, subtype: Subtype)
extends BSONValue {
val code = 0x05.toByte
/** Returns the whole binary content as array. */
def byteArray: Array[Byte] = value.duplicate().readArray(value.size)
}
object BSONBinary {
def apply(value: Array[Byte], subtype: Subtype): BSONBinary =
BSONBinary(ArrayReadableBuffer(value), subtype)
}
/** BSON Undefined value */
case object BSONUndefined extends BSONValue { val code = 0x06.toByte }
/** BSON ObjectId value. */
@SerialVersionUID(1L)
class BSONObjectID private (private val raw: Array[Byte]) extends BSONValue with Serializable with Equals {
val code = 0x07.toByte
import java.util.Arrays
import java.nio.ByteBuffer
/** ObjectId hexadecimal String representation */
lazy val stringify = Converters.hex2Str(raw)
override def toString = s"""BSONObjectID("${stringify}")"""
override def canEqual(that: Any): Boolean = that.isInstanceOf[BSONObjectID]
override def equals(that: Any): Boolean = {
canEqual(that) && Arrays.equals(this.raw, that.asInstanceOf[BSONObjectID].raw)
}
override lazy val hashCode: Int = Arrays.hashCode(raw)
/** The time of this BSONObjectId, in milliseconds */
def time: Long = this.timeSecond * 1000L
/** The time of this BSONObjectId, in seconds */
def timeSecond: Int = ByteBuffer.wrap(raw.take(4)).getInt
def valueAsArray = Arrays.copyOf(raw, 12)
}
object BSONObjectID {
private val maxCounterValue = 16777216
private val increment = new java.util.concurrent.atomic.AtomicInteger(scala.util.Random.nextInt(maxCounterValue))
private def counter = (increment.getAndIncrement + maxCounterValue) % maxCounterValue
/**
* The following implemtation of machineId work around openjdk limitations in
* version 6 and 7
*
* Openjdk fails to parse /proc/net/if_inet6 correctly to determine macaddress
* resulting in SocketException thrown.
*
* Please see:
* * https://github.com/openjdk-mirror/jdk7u-jdk/blob/feeaec0647609a1e6266f902de426f1201f77c55/src/solaris/native/java/net/NetworkInterface.c#L1130
* * http://lxr.free-electrons.com/source/net/ipv6/addrconf.c?v=3.11#L3442
* * http://lxr.free-electrons.com/source/include/linux/netdevice.h?v=3.11#L1130
* * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7078386
*
* and fix in openjdk8:
* * http://hg.openjdk.java.net/jdk8/tl/jdk/rev/b1814b3ea6d3
*/
private val machineId = {
import java.net._
val validPlatform = Try {
val correctVersion = System.getProperty("java.version").substring(0, 3).toFloat >= 1.8
val noIpv6 = System.getProperty("java.net.preferIPv4Stack") == true
val isLinux = System.getProperty("os.name") == "Linux"
!isLinux || correctVersion || noIpv6
}.getOrElse(false)
// Check java policies
val permitted = {
val sec = System.getSecurityManager();
Try { sec.checkPermission(new NetPermission("getNetworkInformation")) }.toOption.map(_ => true).getOrElse(false);
}
if (validPlatform && permitted) {
val networkInterfacesEnum = NetworkInterface.getNetworkInterfaces
val networkInterfaces = scala.collection.JavaConverters.enumerationAsScalaIteratorConverter(networkInterfacesEnum).asScala
val ha = networkInterfaces.find(ha => Try(ha.getHardwareAddress).isSuccess && ha.getHardwareAddress != null && ha.getHardwareAddress.length == 6)
.map(_.getHardwareAddress)
.getOrElse(InetAddress.getLocalHost.getHostName.getBytes)
Converters.md5(ha).take(3)
} else {
val threadId = Thread.currentThread.getId.toInt
val arr = new Array[Byte](3)
arr(0) = (threadId & 0xFF).toByte
arr(1) = (threadId >> 8 & 0xFF).toByte
arr(2) = (threadId >> 16 & 0xFF).toByte
arr
}
}
/**
* Constructs a BSON ObjectId element from a hexadecimal String representation.
* Throws an exception if the given argument is not a valid ObjectID.
*
* `parse(str: String): Try[BSONObjectID]` should be considered instead of this method.
*/
def apply(id: String): BSONObjectID = {
if (id.length != 24)
throw new IllegalArgumentException(s"wrong ObjectId: '$id'")
/** Constructs a BSON ObjectId element from a hexadecimal String representation */
new BSONObjectID(Converters.str2Hex(id))
}
def apply(array: Array[Byte]): BSONObjectID = {
if (array.length != 12)
throw new IllegalArgumentException(s"wrong byte array for an ObjectId (size ${array.length})")
new BSONObjectID(java.util.Arrays.copyOf(array, 12))
}
def unapply(id: BSONObjectID): Option[Array[Byte]] = Some(id.valueAsArray)
/** Tries to make a BSON ObjectId element from a hexadecimal String representation. */
def parse(str: String): Try[BSONObjectID] = Try(apply(str))
/**
* Generates a new BSON ObjectID.
*
* +------------------------+------------------------+------------------------+------------------------+
* + timestamp (in seconds) + machine identifier + thread identifier + increment +
* + (4 bytes) + (3 bytes) + (2 bytes) + (3 bytes) +
* +------------------------+------------------------+------------------------+------------------------+
*
* The returned BSONObjectID contains a timestamp set to the current time (in seconds),
* with the `machine identifier`, `thread identifier` and `increment` properly set.
*/
def generate(): BSONObjectID = fromTime(System.currentTimeMillis, false)
/**
* Generates a new BSON ObjectID from the given timestamp in milliseconds.
*
* +------------------------+------------------------+------------------------+------------------------+
* + timestamp (in seconds) + machine identifier + thread identifier + increment +
* + (4 bytes) + (3 bytes) + (2 bytes) + (3 bytes) +
* +------------------------+------------------------+------------------------+------------------------+
*
* The included timestamp is the number of seconds since epoch, so a BSONObjectID time part has only
* a precision up to the second. To get a reasonably unique ID, you _must_ set `onlyTimestamp` to false.
*
* Crafting a BSONObjectID from a timestamp with `fillOnlyTimestamp` set to true is helpful for range queries,
* eg if you want of find documents an _id field which timestamp part is greater than or lesser than
* the one of another id.
*
* If you do not intend to use the produced BSONObjectID for range queries, then you'd rather use
* the `generate` method instead.
*
* @param fillOnlyTimestamp if true, the returned BSONObjectID will only have the timestamp bytes set; the other will be set to zero.
*/
def fromTime(timeMillis: Long, fillOnlyTimestamp: Boolean = true): BSONObjectID = {
// n of seconds since epoch. Big endian
val timestamp = (timeMillis / 1000).toInt
val id = new Array[Byte](12)
id(0) = (timestamp >>> 24).toByte
id(1) = (timestamp >> 16 & 0xFF).toByte
id(2) = (timestamp >> 8 & 0xFF).toByte
id(3) = (timestamp & 0xFF).toByte
if (!fillOnlyTimestamp) {
// machine id, 3 first bytes of md5(macadress or hostname)
id(4) = machineId(0)
id(5) = machineId(1)
id(6) = machineId(2)
// 2 bytes of the pid or thread id. Thread id in our case. Low endian
val threadId = Thread.currentThread.getId.toInt
id(7) = (threadId & 0xFF).toByte
id(8) = (threadId >> 8 & 0xFF).toByte
// 3 bytes of counter sequence, which start is randomized. Big endian
val c = counter
id(9) = (c >> 16 & 0xFF).toByte
id(10) = (c >> 8 & 0xFF).toByte
id(11) = (c & 0xFF).toByte
}
BSONObjectID(id)
}
}
/** BSON boolean value */
case class BSONBoolean(value: Boolean) extends BSONValue { val code = 0x08.toByte }
/** BSON date time value */
case class BSONDateTime(value: Long) extends BSONValue { val code = 0x09.toByte }
/** BSON null value */
case object BSONNull extends BSONValue { val code = 0x0A.toByte }
/**
* BSON Regex value.
*
* @param flags Regex flags.
*/
case class BSONRegex(value: String, flags: String) extends BSONValue { val code = 0x0B.toByte }
/** BSON DBPointer value. */
case class BSONDBPointer(value: String, id: Array[Byte]) extends BSONValue {
val code = 0x0C.toByte
/** The BSONObjectID representation of this reference. */
val objectId = BSONObjectID(id)
override def canEqual(that: Any): Boolean = that.isInstanceOf[BSONDBPointer]
override def equals(that: Any): Boolean = {
canEqual(that) && {
val other = that.asInstanceOf[BSONDBPointer]
this.value.equals(other.value) &&
java.util.Arrays.equals(this.id, other.id)
}
}
}
/**
* BSON JavaScript value.
*
* @param value The JavaScript source code.
*/
case class BSONJavaScript(value: String) extends BSONValue { val code = 0x0D.toByte }
/** BSON Symbol value. */
case class BSONSymbol(value: String) extends BSONValue { val code = 0x0E.toByte }
/**
* BSON scoped JavaScript value.
*
* @param value The JavaScript source code. TODO
*/
case class BSONJavaScriptWS(value: String) extends BSONValue { val code = 0x0F.toByte }
/** BSON Integer value */
case class BSONInteger(value: Int) extends BSONValue { val code = 0x10.toByte }
/** BSON Timestamp value */
case class BSONTimestamp(value: Long) extends BSONValue {
val code = 0x11.toByte
/** Seconds since the Unix epoch */
val time = value >>> 32
/** Ordinal (with the second) */
val ordinal = value.toInt
}
/** Timestamp companion */
object BSONTimestamp {
/**
* Returns the timestamp corresponding to the given `time` and `ordinal`.
*
* @param time the 32bits time value (seconds since the Unix epoch)
* @param ordinal an incrementing ordinal for operations within a same second
*/
def apply(time: Long, ordinal: Int): BSONTimestamp =
BSONTimestamp((time << 32) ^ ordinal)
}
/** BSON Long value */
case class BSONLong(value: Long) extends BSONValue { val code = 0x12.toByte }
/** BSON Min key value */
object BSONMinKey extends BSONValue { val code = 0xFF.toByte }
/** BSON Max key value */
object BSONMaxKey extends BSONValue { val code = 0x7F.toByte }
/** Binary Subtype */
sealed trait Subtype {
/** Subtype code */
val value: Byte
}
object Subtype {
case object GenericBinarySubtype extends Subtype { val value = 0x00.toByte }
case object FunctionSubtype extends Subtype { val value = 0x01.toByte }
case object OldBinarySubtype extends Subtype { val value = 0x02.toByte }
case object OldUuidSubtype extends Subtype { val value = 0x03.toByte }
case object UuidSubtype extends Subtype { val value = 0x04.toByte }
case object Md5Subtype extends Subtype { val value = 0x05.toByte }
case object UserDefinedSubtype extends Subtype { val value = 0x80.toByte }
def apply(code: Byte) = code match {
case 0 => GenericBinarySubtype
case 1 => FunctionSubtype
case 2 => OldBinarySubtype
case 3 => OldUuidSubtype
case 4 => UuidSubtype
case 5 => Md5Subtype
case -128 => UserDefinedSubtype
case _ => throw new NoSuchElementException(s"binary type = $code")
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy