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

org.mongodb.scala.bson.codecs.IterableCodec.scala Maven / Gradle / Ivy

/*
 * Copyright 2008-present MongoDB, 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.
 */

package org.mongodb.scala.bson.codecs

import java.util.UUID

import scala.collection.mutable

import org.bson._
import org.bson.codecs._
import org.bson.codecs.configuration.CodecRegistry

/**
 * IterableCodec companion object
 *
 * @since 1.2
 */
object IterableCodec {

  def apply(registry: CodecRegistry, bsonTypeClassMap: BsonTypeClassMap): IterableCodec =
    apply(registry, bsonTypeClassMap, None)

  def apply(
      registry: CodecRegistry,
      bsonTypeClassMap: BsonTypeClassMap,
      valueTransformer: Option[Transformer]
  ): IterableCodec = {
    new IterableCodec(registry, bsonTypeClassMap, valueTransformer.getOrElse(DEFAULT_TRANSFORMER))
  }

  private val DEFAULT_TRANSFORMER = new Transformer() {
    def transform(objectToTransform: Object): Object = objectToTransform
  }
}

/**
 * Encodes and decodes `Iterable` objects.
 *
 * @since 1.2
 */
@SuppressWarnings(Array("rawtypes"))
case class IterableCodec(registry: CodecRegistry, bsonTypeClassMap: BsonTypeClassMap, valueTransformer: Transformer)
    extends Codec[Iterable[_ <: Any]] {
  lazy val bsonTypeCodecMap = new BsonTypeCodecMap(bsonTypeClassMap, registry)

  override def decode(reader: BsonReader, decoderContext: DecoderContext): Iterable[_] =
    readValue(reader, decoderContext).asInstanceOf[Iterable[_]]

  override def encode(writer: BsonWriter, value: Iterable[_ <: Any], encoderContext: EncoderContext): Unit =
    writeValue(writer, encoderContext, value)

  override def getEncoderClass: Class[Iterable[_]] = classOf[Iterable[_]]

  @SuppressWarnings(Array("unchecked", "rawtypes"))
  private def writeValue[T](writer: BsonWriter, encoderContext: EncoderContext, value: T): Unit = {
    value match {
      case isNull if value == null => writer.writeNull() // scalastyle:ignore
      case map: Map[_, _] =>
        writeMap(writer, map.asInstanceOf[Map[String, Any]], encoderContext.getChildContext)
      case list: Iterable[_] =>
        writeIterable(writer, list, encoderContext.getChildContext)
      case _ =>
        val codec = registry.get(value.getClass).asInstanceOf[Encoder[T]]
        encoderContext.encodeWithChildContext(codec, writer, value)
    }
  }

  private def writeMap(writer: BsonWriter, map: Map[String, Any], encoderContext: EncoderContext): Unit = {
    writer.writeStartDocument()
    map.foreach(kv => {
      writer.writeName(kv._1)
      writeValue(writer, encoderContext, kv._2)
    })
    writer.writeEndDocument()
  }

  private def writeIterable(writer: BsonWriter, list: Iterable[_], encoderContext: EncoderContext): Unit = {
    writer.writeStartArray()
    list.foreach(value => writeValue(writer, encoderContext, value))
    writer.writeEndArray()
  }

  private def readValue(reader: BsonReader, decoderContext: DecoderContext): Any = {
    reader.getCurrentBsonType match {
      case BsonType.NULL =>
        reader.readNull()
        null // scalastyle:ignore
      case BsonType.ARRAY    => readList(reader, decoderContext)
      case BsonType.DOCUMENT => readMap(reader, decoderContext)
      case BsonType.BINARY if BsonBinarySubType.isUuid(reader.peekBinarySubType) && reader.peekBinarySize == 16 =>
        registry.get(classOf[UUID]).decode(reader, decoderContext)
      case bsonType: BsonType =>
        valueTransformer.transform(bsonTypeCodecMap.get(bsonType).decode(reader, decoderContext))
    }
  }

  private def readMap(reader: BsonReader, decoderContext: DecoderContext): Map[String, _] = {
    val map = mutable.Map[String, Any]()
    reader.readStartDocument()
    while (reader.readBsonType ne BsonType.END_OF_DOCUMENT) {
      map += (reader.readName -> readValue(reader, decoderContext))
    }
    reader.readEndDocument()
    map.toMap
  }

  private def readList(reader: BsonReader, decoderContext: DecoderContext): List[_] = {
    reader.readStartArray()
    val list = mutable.ListBuffer[Any]()
    while (reader.readBsonType ne BsonType.END_OF_DOCUMENT) {
      list.append(readValue(reader, decoderContext))
    }
    reader.readEndArray()
    list.toList
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy