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

io.prediction.data.storage.DataMap.scala Maven / Gradle / Ivy

The newest version!
/** Copyright 2015 TappingStone, 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 io.prediction.data.storage

import org.json4s._
import org.json4s.native.JsonMethods.parse

import scala.collection.GenTraversableOnce
import scala.collection.JavaConversions

/** Exception class for [[DataMap]]
  *
  * @group Event Data
  */
case class DataMapException(msg: String, cause: Exception)
  extends Exception(msg, cause) {
  def this(msg: String) = this(msg, null)
}

/** A DataMap stores properties of the event or entity. Internally it is a Map
  * whose keys are property names and values are corresponding JSON values
  * respectively. Use the [[get]] method to retrieve the value of a mandatory
  * property or use [[getOpt]] to retrieve the value of an optional property.
  *
  * @param fields Map of property name to JValue
  * @group Event Data
  */
class DataMap (
  val fields: Map[String, JValue]
) extends Serializable {
  @transient lazy implicit private val formats = DefaultFormats +
    new DateTimeJson4sSupport.Serializer

  /** Check the existence of a required property name. Throw an exception if
    * it does not exist.
    *
    * @param name The property name
    */
  def require(name: String): Unit = {
    if (!fields.contains(name)) {
      throw new DataMapException(s"The field $name is required.")
    }
  }

  /** Check if this DataMap contains a specific property.
    *
    * @param name The property name
    * @return Return true if the property exists, else false.
    */
  def contains(name: String): Boolean = {
    fields.contains(name)
  }

  /** Get the value of a mandatory property. Exception is thrown if the property
    * does not exist.
    *
    * @tparam T The type of the property value
    * @param name The property name
    * @return Return the property value of type T
    */
  def get[T: Manifest](name: String): T = {
    require(name)
    fields(name) match {
      case JNull => throw new DataMapException(
        s"The required field $name cannot be null.")
      case x: JValue => x.extract[T]
    }
  }

  /** Get the value of an optional property. Return None if the property does
    * not exist.
    *
    * @tparam T The type of the property value
    * @param name The property name
    * @return Return the property value of type Option[T]
    */
  def getOpt[T: Manifest](name: String): Option[T] = {
    // either the field doesn't exist or its value is null
    fields.get(name).flatMap(_.extract[Option[T]])
  }

  /** Get the value of an optional property. Return default value if the
    * property does not exist.
    *
    * @tparam T The type of the property value
    * @param name The property name
    * @param default The default property value of type T
    * @return Return the property value of type T
    */
  def getOrElse[T: Manifest](name: String, default: T): T = {
    getOpt[T](name).getOrElse(default)
  }

  /** Java-friendly method for getting the value of a property. Return null if the
    * property does not exist.
    *
    * @tparam T The type of the property value
    * @param name The property name
    * @param clazz The class of the type of the property value
    * @return Return the property value of type T
    */
  def get[T](name: String, clazz: java.lang.Class[T]): T = {
    val manifest =  new Manifest[T] {
      override def erasure: Class[_] = clazz
      override def runtimeClass: Class[_] = clazz
    }

    fields.get(name) match {
      case None => null.asInstanceOf[T]
      case Some(JNull) => null.asInstanceOf[T]
      case Some(x) => x.extract[T](formats, manifest)
    }
  }

  /** Java-friendly method for getting a list of values of a property. Return null if the
    * property does not exist.
    *
    * @param name The property name
    * @return Return the list of property values
    */
  def getStringList(name: String): java.util.List[String] = {
    fields.get(name) match {
      case None => null
      case Some(JNull) => null
      case Some(x) =>
        JavaConversions.seqAsJavaList(x.extract[List[String]](formats, manifest[List[String]]))
    }
  }

  /** Return a new DataMap with elements containing elements from the left hand
    * side operand followed by elements from the right hand side operand.
    *
    * @param that Right hand side DataMap
    * @return A new DataMap
    */
  def ++ (that: DataMap): DataMap = DataMap(this.fields ++ that.fields)

  /** Creates a new DataMap from this DataMap by removing all elements of
    * another collection.
    *
    * @param that A collection containing the removed property names
    * @return A new DataMap
    */
  def -- (that: GenTraversableOnce[String]): DataMap =
    DataMap(this.fields -- that)

  /** Tests whether the DataMap is empty.
    *
    * @return true if the DataMap is empty, false otherwise.
    */
  def isEmpty: Boolean = fields.isEmpty

  /** Collects all property names of this DataMap in a set.
    *
    * @return a set containing all property names of this DataMap.
    */
  def keySet: Set[String] = this.fields.keySet

  /** Converts this DataMap to a List.
    *
    * @return a list of (property name, JSON value) tuples.
    */
  def toList(): List[(String, JValue)] = fields.toList

  /** Converts this DataMap to a JObject.
    *
    * @return the JObject initialized by this DataMap.
    */
  def toJObject(): JObject = JObject(toList())

  /** Converts this DataMap to case class of type T.
    *
    * @return the object of type T.
    */
  def extract[T: Manifest]: T = {
    toJObject().extract[T]
  }

  override
  def toString: String = s"DataMap($fields)"

  override
  def hashCode: Int = 41 + fields.hashCode

  override
  def equals(other: Any): Boolean = other match {
    case that: DataMap => that.canEqual(this) && this.fields.equals(that.fields)
    case _ => false
  }

  def canEqual(other: Any): Boolean = other.isInstanceOf[DataMap]
}

/** Companion object of the [[DataMap]] class
  *
  * @group Event Data
  */
object DataMap {
  /** Create an empty DataMap
    * @return an empty DataMap
    */
  def apply(): DataMap = new DataMap(Map[String, JValue]())

  /** Create an DataMap from a Map of String to JValue
    * @param fields a Map of String to JValue
    * @return a new DataMap initialized by fields
    */
  def apply(fields: Map[String, JValue]): DataMap = new DataMap(fields)

  /** Create an DataMap from a JObject
    * @param jObj JObject
    * @return a new DataMap initialized by a JObject
    */
  def apply(jObj: JObject): DataMap = {
    if (jObj == null) {
      apply()
    } else {
      new DataMap(jObj.obj.toMap)
    }
  }

  /** Create an DataMap from a JSON String
    * @param js JSON String. eg """{ "a": 1, "b": "foo" }"""
    * @return a new DataMap initialized by a JSON string
    */
  def apply(js: String): DataMap = apply(parse(js).asInstanceOf[JObject])

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy