<commit_before>package io.hydrosphere.mist.master.interfaces

import java.time.LocalDateTime
import java.time.format.{DateTimeFormatter, DateTimeParseException}

import io.hydrosphere.mist.core.CommonData.{Action, JobParams, WorkerInitInfo}
import io.hydrosphere.mist.core.logging.LogEvent
import io.hydrosphere.mist.master.Messages.StatusMessages._
import io.hydrosphere.mist.master.execution.{WorkerFullInfo, WorkerLink}
import io.hydrosphere.mist.master.interfaces.http._
import io.hydrosphere.mist.master.models._
import io.hydrosphere.mist.master.{JobDetails, JobResult}
import mist.api.{data => mdata}
import spray.json._

import scala.collection.JavaConversions._
import scala.concurrent.duration._
import scala.util.Try

trait AnyJsonFormat extends DefaultJsonProtocol {

  /** We must implement json parse/serializer for [[Any]] type */
  implicit object AnyFormat extends JsonFormat[Any] {
    def write(x: Any): JsValue = x match {
      case number: Int => JsNumber(number)
      case number: java.lang.Double => JsNumber(number)
      case number: java.lang.Long => JsNumber(number)
      case string: String => JsString(string)
      case sequence: Seq[_] => seqFormat[Any].write(sequence)
      case javaList: java.util.ArrayList[_] => seqFormat[Any].write(javaList.toList)
      case array: Array[_] => seqFormat[Any].write(array.toList)
      case map: Map[_, _] => mapFormat[String, Any].write(map.asInstanceOf[Map[String, Any]])
      case boolean: Boolean if boolean => JsTrue
      case boolean: Boolean if !boolean => JsFalse
      case opt: Option[_] if opt.isDefined => write(opt.get)
      case opt: Option[_] if opt.isEmpty => JsNull
      case unknown: Any => serializationError("Do not understand object of type " + unknown.getClass.getCanonicalName)
    }

    def read(value: JsValue): Any = value match {
      case JsNumber(number) =>
        Try(number.toIntExact)
          .orElse(Try(number.toLongExact))
          .getOrElse(number.toDouble)
      case JsString(string) => string
      case _: JsArray => listFormat[Any].read(value)
      case _: JsObject => mapFormat[String, Any].read(value)
      case JsTrue => true
      case JsFalse => false
      case unknown: Any => deserializationError("Do not understand how to deserialize " + unknown)
    }
  }
}

trait MDataFormat {

  implicit val mDataFormat = new RootJsonFormat[mdata.JsData] {
    override def read(json: JsValue): mdata.JsData = {
      json match {
        case JsObject(fields) => mdata.JsMap(fields.mapValues(v => read(v)))
        case JsString(s) => mdata.JsString(s)
        case JsNumber(d) => mdata.JsNumber(d)
        case JsFalse => mdata.JsFalse //TODO MBoolean can has default false and true
        case JsTrue  => mdata.JsTrue
        case JsNull  => mdata.JsNull
        case JsArray(values) => mdata.JsList(values.map(read))
      }
    }

    override def write(obj: mdata.JsData): JsValue = {
      obj match {
        case mdata.JsNumber(d) => JsNumber(d)
        case mdata.JsTrue      => JsTrue
        case mdata.JsString(s) => JsString(s)
        case mdata.JsNull      => JsNull
        case mdata.JsUnit      => JsObject(Map.empty[String, JsValue])
        case mdata.JsList(values) => JsArray(values.map(v => write(v)).toVector)
        case mdata.JsMap(map)     => JsObject(map.mapValues(v => write(v)))
      }
    }
  }

  implicit val jsLikeMapFormat = new RootJsonFormat[mdata.JsMap] {
    override def write(obj: mdata.JsMap): JsValue = JsObject(obj.map.mapValues(v => mDataFormat.write(v)))
    override def read(json: JsValue): mdata.JsMap = json match {
      case JsObject(fields) => mdata.JsMap(fields.mapValues(v => mDataFormat.read(v)))
      case other => throw new DeserializationException(s"Json object required: got $other")
    }
  }
}


trait JobDetailsJsonFormat extends DefaultJsonProtocol with AnyJsonFormat with MDataFormat {

  implicit object JobStatusSupport extends RootJsonFormat[JobDetails.Status] {
    override def write(obj: JobDetails.Status): JsValue = JsString(obj.toString)

    override def read(json: JsValue): JobDetails.Status = json match {
      case JsString(str) => JobDetails.Status(str)
      case _ => throw DeserializationException("JobDetails.Status must be a string")
    }
  }<commit_msg>fix codec when using default parameter on boolean args<commit_after>package io.hydrosphere.mist.master.interfaces

import java.time.LocalDateTime
import java.time.format.{DateTimeFormatter, DateTimeParseException}

import io.hydrosphere.mist.core.CommonData.{Action, JobParams, WorkerInitInfo}
import io.hydrosphere.mist.core.logging.LogEvent
import io.hydrosphere.mist.master.Messages.StatusMessages._
import io.hydrosphere.mist.master.execution.{WorkerFullInfo, WorkerLink}
import io.hydrosphere.mist.master.interfaces.http._
import io.hydrosphere.mist.master.models._
import io.hydrosphere.mist.master.{JobDetails, JobResult}
import mist.api.{data => mdata}
import spray.json._

import scala.collection.JavaConversions._
import scala.concurrent.duration._
import scala.util.Try

trait AnyJsonFormat extends DefaultJsonProtocol {

  /** We must implement json parse/serializer for [[Any]] type */
  implicit object AnyFormat extends JsonFormat[Any] {
    def write(x: Any): JsValue = x match {
      case number: Int => JsNumber(number)
      case number: java.lang.Double => JsNumber(number)
      case number: java.lang.Long => JsNumber(number)
      case string: String => JsString(string)
      case sequence: Seq[_] => seqFormat[Any].write(sequence)
      case javaList: java.util.ArrayList[_] => seqFormat[Any].write(javaList.toList)
      case array: Array[_] => seqFormat[Any].write(array.toList)
      case map: Map[_, _] => mapFormat[String, Any].write(map.asInstanceOf[Map[String, Any]])
      case boolean: Boolean if boolean => JsTrue
      case boolean: Boolean if !boolean => JsFalse
      case opt: Option[_] if opt.isDefined => write(opt.get)
      case opt: Option[_] if opt.isEmpty => JsNull
      case unknown: Any => serializationError("Do not understand object of type " + unknown.getClass.getCanonicalName)
    }

    def read(value: JsValue): Any = value match {
      case JsNumber(number) =>
        Try(number.toIntExact)
          .orElse(Try(number.toLongExact))
          .getOrElse(number.toDouble)
      case JsString(string) => string
      case _: JsArray => listFormat[Any].read(value)
      case _: JsObject => mapFormat[String, Any].read(value)
      case JsTrue => true
      case JsFalse => false
      case unknown: Any => deserializationError("Do not understand how to deserialize " + unknown)
    }
  }
}

trait MDataFormat {

  implicit val mDataFormat = new RootJsonFormat[mdata.JsData] {
    override def read(json: JsValue): mdata.JsData = {
      json match {
        case JsObject(fields) => mdata.JsMap(fields.mapValues(v => read(v)))
        case JsString(s) => mdata.JsString(s)
        case JsNumber(d) => mdata.JsNumber(d)
        case JsFalse => mdata.JsFalse //TODO MBoolean can has default false and true
        case JsTrue  => mdata.JsTrue
        case JsNull  => mdata.JsNull
        case JsArray(values) => mdata.JsList(values.map(read))
      }
    }

    override def write(obj: mdata.JsData): JsValue = {
      obj match {
        case mdata.JsNumber(d) => JsNumber(d)
        case mdata.JsTrue      => JsTrue
        case mdata.JsFalse     => JsFalse
        case mdata.JsString(s) => JsString(s)
        case mdata.JsNull      => JsNull
        case mdata.JsUnit      => JsObject(Map.empty[String, JsValue])
        case mdata.JsList(values) => JsArray(values.map(v => write(v)).toVector)
        case mdata.JsMap(map)     => JsObject(map.mapValues(v => write(v)))
      }
    }
  }

  implicit val jsLikeMapFormat = new RootJsonFormat[mdata.JsMap] {
    override def write(obj: mdata.JsMap): JsValue = JsObject(obj.map.mapValues(v => mDataFormat.write(v)))
    override def read(json: JsValue): mdata.JsMap = json match {
      case JsObject(fields) => mdata.JsMap(fields.mapValues(v => mDataFormat.read(v)))
      case other => throw new DeserializationException(s"Json object required: got $other")
    }
  }
}


trait JobDetailsJsonFormat extends DefaultJsonProtocol with AnyJsonFormat with MDataFormat {

  implicit object JobStatusSupport extends RootJsonFormat[JobDetails.Status] {
    override def write(obj: JobDetails.Status): JsValue = JsString(obj.toString)

    override def read(json: JsValue): JobDetails.Status = json match {
      case JsString(str) => JobDetails.Status(str)
      case _ => throw DeserializationException("JobDetails.Status must be a string")
    }
  }
{'fraction_non_alphanumeric': 0.11420289855072464, 'fraction_numerical': 0.0, 'mean_word_length': 3.5209643605870022, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 92, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6819710', 'n_tokens_mistral': 2737, 'n_tokens_neox': 2682, 'n_words': 625}