<commit_before>package io.fintrospect.formats.json

import java.math.BigInteger

import com.twitter.finagle.http.Status
import io.fintrospect.ContentTypes.APPLICATION_JSON
import io.fintrospect.ResponseSpec
import io.fintrospect.parameters.{Body, BodySpec, ObjectParamType, ParameterSpec}
import org.json4s.Extraction.decompose
import org.json4s.{JValue, JsonMethods, Serialization}

/**
  * Json4S support (application/json content type)
  */
object Json4s {

  class Json4sFormat[T](jsonMethods: JsonMethods[T],
                        serialization: Serialization,
                        useBigDecimalForDouble: Boolean) extends JsonFormat[JValue, JValue] {

    import org.json4s._

    override def pretty(in: JValue): String = jsonMethods.pretty(jsonMethods.render(in))

    override def parse(in: String): JValue = jsonMethods.parse(in, useBigDecimalForDouble)

    override def compact(in: JValue): String = jsonMethods.compact(jsonMethods.render(in))

    override def obj(fields: Iterable[Field]): JValue = JObject(fields.toList)

    override def obj(fields: (String, JValue)*): JValue = JObject(fields: _*)

    override def string(value: String): JValue = JString(value)

    override def array(elements: JValue*): JValue = JArray(elements.toList)

    override def array(elements: Iterable[JValue]): JValue = JArray(elements.toList)

    override def boolean(value: Boolean): JValue = JBool(value)

    override def number(value: Int): JValue = JInt(value)

    override def number(value: BigDecimal): JValue = JDecimal(value)

    override def number(value: Long): JValue = JInt(value)

    override def number(value: BigInteger): JValue = JInt(value)

    override def nullNode(): JValue = JNull

    def encode(in: AnyRef, formats: Formats = serialization.formats(NoTypeHints)): JValue = decompose(in)(formats)

    def decode[R](in: JValue,
                  formats: Formats = serialization.formats(NoTypeHints))
                 (implicit mf: scala.reflect.Manifest[R]): R = in.extract[R](formats, mf)

    /**
      * Convenience method for creating Body that just use straight JSON encoding/decoding logic
      */
    def body[R](description: Option[String] = None, example: R = null, formats: Formats = serialization.formats(NoTypeHints))
               (implicit mf: scala.reflect.Manifest[R]) = Body(bodySpec[R](description, formats)(mf), example, ObjectParamType)

    /**
      * Convenience method for creating BodySpecs that just use straight JSON encoding/decoding logic
      */
    def bodySpec[R](description: Option[String] = None, formats: Formats = serialization.formats(NoTypeHints))
                   (implicit mf: scala.reflect.Manifest[R]) =
      BodySpec[R](description, APPLICATION_JSON,
        s => decode[R](parse(s), formats)(mf),
        (u: R) => compact(encode(u.asInstanceOf[AnyRef])))

    /**
      * Convenience method for creating ResponseSpecs that just use straight JSON encoding/decoding logic for examples
      */
    def responseSpec[R](statusAndDescription: (Status, String), example: R, formats: Formats = serialization.formats(NoTypeHints))
                       (implicit mf: scala.reflect.Manifest[R]) =
      ResponseSpec.json(statusAndDescription, encode(example.asInstanceOf[AnyRef]), this)

    /**
      * Convenience method for creating ParameterSpecs that just use straight JSON encoding/decoding logic
      */
    def parameterSpec[R](name: String, description: Option[String] = None, formats: Formats = serialization.formats(NoTypeHints))
                        (implicit mf: scala.reflect.Manifest[R]) =
      ParameterSpec[R](name, description, ObjectParamType,
        s => decode[R](parse(s), formats)(mf),
        (u: R) => compact(encode(u.asInstanceOf[AnyRef])))
  }

  /**
    * Native Json4S support - uses BigDecimal for decimal
    */
  object Native extends JsonLibrary[JValue, JValue] {

    val JsonFormat = new Json4sFormat(org.json4s.native.JsonMethods, org.json4s.native.Serialization, true)

  }

  /**
    * Native Json4S support - uses Doubles for decimal
    */
  object NativeDoubleMode extends JsonLibrary[JValue, JValue] {

    val JsonFormat = new Json4sFormat(org.json4s.native.JsonMethods, org.json4s.native.Serialization, false)

  }

  /**
    * Jackson Json4S support - uses BigDecimal for decimal
    */
  object Jackson extends JsonLibrary[JValue, JValue] {

    val JsonFormat = new Json4sFormat(org.json4s.jackson.JsonMethods, org.json4s.jackson.Serialization, true)

  }

  /**
    * Jackson Json4S support - uses Doubles for decimal
    */
  object JacksonDoubleMode extends JsonLibrary[JValue, JValue] {

    val JsonFormat = new Json4sFormat(org.json4s.jackson.JsonMethods, org.json4s.jackson.Serialization, false)

  }

}<commit_msg>daveD - more auto-conversion. added to Json4S<commit_after>package io.fintrospect.formats.json

import java.math.BigInteger

import com.twitter.finagle.http.Status.{NotFound, Ok}
import com.twitter.finagle.http.{Request, Response, Status}
import com.twitter.finagle.{Filter, Service}
import io.fintrospect.ContentTypes.APPLICATION_JSON
import io.fintrospect.ResponseSpec
import io.fintrospect.parameters.{Body, BodySpec, ObjectParamType, ParameterSpec}
import org.json4s.Extraction.decompose
import org.json4s.{Formats, JValue, JsonMethods, NoTypeHints, Serialization}

/**
  * Json4S support (application/json content type)
  */
object Json4s {

  /**
    * Auto-marshalling filters which can be used to create Services which take and return domain objects
    * instead of HTTP responses
    */
  class Json4sFilters[T](json4sFormat: Json4sFormat[T], jsonLibrary: JsonLibrary[JValue, JValue]) {

    /**
      * Wrap the enclosed service with auto-marshalling of input and output case class instances for HTTP POST scenarios
      * which return an object.
      * HTTP OK is returned by default in the auto-marshalled response (overridable).
      */
    def AutoInOut[BODY, OUT <: AnyRef](svc: Service[BODY, OUT], successStatus: Status = Ok,
                                       formats: Formats = json4sFormat.serialization.formats(NoTypeHints))
                                      (implicit example: BODY = null, mf: scala.reflect.Manifest[BODY]): Service[Request, Response] = {
      val body = Body[BODY](json4sFormat.bodySpec[BODY](None)(mf), example, ObjectParamType)
      AutoIn[BODY, Response](body).andThen(AutoOut[BODY, OUT](successStatus, formats).andThen(svc))
    }

    /**
      * Wrap the enclosed service with auto-marshalling of input and output case class instances for HTTP POST scenarios
      * which may return an object.
      * HTTP OK is returned by default in the auto-marshalled response (overridable), otherwise a 404 is returned
      */
    def AutoInOptionalOut[BODY, OUT <: AnyRef](svc: Service[BODY, Option[OUT]], successStatus: Status = Ok,
                                               formats: Formats = json4sFormat.serialization.formats(NoTypeHints))
                                              (implicit example: BODY = null, mf: scala.reflect.Manifest[BODY]): Service[Request, Response] = {
      val body = Body[BODY](json4sFormat.bodySpec[BODY](None)(mf), example, ObjectParamType)
      AutoIn[BODY, Response](body).andThen(AutoOptionalOut[BODY, OUT](successStatus, formats).andThen(svc))
    }

    /**
      * Filter to provide auto-marshalling of input case class instances for HTTP POST scenarios
      */
    def AutoIn[IN, OUT](body: Body[IN]) = Filter.mk[Request, OUT, IN, OUT] { (req, svc) => svc(body <-- req) }

    /**
      * Filter to provide auto-marshalling of output case class instances for HTTP scenarios where an object is returned.
      * HTTP OK is returned by default in the auto-marshalled response (overridable).
      */
    def AutoOut[IN, OUT <: AnyRef](successStatus: Status = Ok, formats: Formats = json4sFormat.serialization.formats(NoTypeHints)): Filter[IN, Response, IN, OUT]
    = Filter.mk[IN, Response, IN, OUT] {
      { (req, svc) => svc(req)
        .map(t => {
          val encode: JValue = json4sFormat.encode(t, formats)
          jsonLibrary.ResponseBuilder.HttpResponse(successStatus)
            .withContent(encode).build()
        })
      }
    }

    /**
      * Filter to provide auto-marshalling of case class instances for HTTP scenarios where an object may not be returned
      * HTTP OK is returned by default in the auto-marshalled response (overridable), otherwise a 404 is returned
      */
    def AutoOptionalOut[IN, OUT <: AnyRef](successStatus: Status = Ok, formats: Formats = json4sFormat.serialization.formats(NoTypeHints))
    : Filter[IN, Response, IN, Option[OUT]] = Filter.mk[IN, Response, IN, Option[OUT]] {
      import Json4s.Native.ResponseBuilder.implicits._
      (req, svc) => svc(req).map(optT => optT.map(t => Json4s.Native.ResponseBuilder.HttpResponse(successStatus)
        .withContent(json4sFormat.encode(t, formats)).build()).getOrElse(NotFound().build()))
    }

    /**
      * Filter to provide auto-marshalling of case class instances for HTTP POST scenarios
      * HTTP OK is returned by default in the auto-marshalled response (overridable).
      */
    def AutoInOutFilter[BODY, OUT <: AnyRef](successStatus: Status = Ok,
                                             formats: Formats = json4sFormat.serialization.formats(NoTypeHints))
                                            (implicit example: BODY = null, mf: scala.reflect.Manifest[BODY]): Filter[Request, Response, BODY, OUT] = {
      val body = Body[BODY](json4sFormat.bodySpec[BODY](None)(mf), example, ObjectParamType)
      AutoIn[BODY, Response](body).andThen(AutoOut[BODY, OUT](successStatus, formats))
    }
  }

  class Json4sFormat[T](jsonMethods: JsonMethods[T],
                        val serialization: Serialization,
                        useBigDecimalForDouble: Boolean) extends JsonFormat[JValue, JValue] {

    import org.json4s._

    override def pretty(in: JValue): String = jsonMethods.pretty(jsonMethods.render(in))

    override def parse(in: String): JValue = jsonMethods.parse(in, useBigDecimalForDouble)

    override def compact(in: JValue): String = jsonMethods.compact(jsonMethods.render(in))

    override def obj(fields: Iterable[Field]): JValue = JObject(fields.toList)

    override def obj(fields: (String, JValue)*): JValue = JObject(fields: _*)

    override def string(value: String): JValue = JString(value)

    override def array(elements: JValue*): JValue = JArray(elements.toList)

    override def array(elements: Iterable[JValue]): JValue = JArray(elements.toList)

    override def boolean(value: Boolean): JValue = JBool(value)

    override def number(value: Int): JValue = JInt(value)

    override def number(value: BigDecimal): JValue = JDecimal(value)

    override def number(value: Long): JValue = JInt(value)

    override def number(value: BigInteger): JValue = JInt(value)

    override def nullNode(): JValue = JNull

    def encode(in: AnyRef, formats: Formats = serialization.formats(NoTypeHints)): JValue = decompose(in)(formats)

    def decode[R](in: JValue,
                  formats: Formats = serialization.formats(NoTypeHints))
                 (implicit mf: scala.reflect.Manifest[R]): R = in.extract[R](formats, mf)

    /**
      * Convenience method for creating Body that just use straight JSON encoding/decoding logic
      */
    def body[R](description: Option[String] = None, example: R = null, formats: Formats = serialization.formats(NoTypeHints))
               (implicit mf: scala.reflect.Manifest[R]) = Body(bodySpec[R](description, formats)(mf), example, ObjectParamType)

    /**
      * Convenience method for creating BodySpecs that just use straight JSON encoding/decoding logic
      */
    def bodySpec[R](description: Option[String] = None, formats: Formats = serialization.formats(NoTypeHints))
                   (implicit mf: scala.reflect.Manifest[R]) =
      BodySpec[R](description, APPLICATION_JSON,
        s => decode[R](parse(s), formats)(mf),
        (u: R) => compact(encode(u.asInstanceOf[AnyRef])))

    /**
      * Convenience method for creating ResponseSpecs that just use straight JSON encoding/decoding logic for examples
      */
    def responseSpec[R](statusAndDescription: (Status, String), example: R, formats: Formats = serialization.formats(NoTypeHints))
                       (implicit mf: scala.reflect.Manifest[R]) =
      ResponseSpec.json(statusAndDescription, encode(example.asInstanceOf[AnyRef]), this)

    /**
      * Convenience method for creating ParameterSpecs that just use straight JSON encoding/decoding logic
      */
    def parameterSpec[R](name: String, description: Option[String] = None, formats: Formats = serialization.formats(NoTypeHints))
                        (implicit mf: scala.reflect.Manifest[R]) =
      ParameterSpec[R](name, description, ObjectParamType,
        s => decode[R](parse(s), formats)(mf),
        (u: R) => compact(encode(u.asInstanceOf[AnyRef])))
  }

  /**
    * Native Json4S support - uses BigDecimal for decimal
    */
  object Native extends JsonLibrary[JValue, JValue] {

    val JsonFormat = new Json4sFormat(org.json4s.native.JsonMethods, org.json4s.native.Serialization, true)

    val Filters = new Json4sFilters(JsonFormat, Native)
  }

  /**
    * Native Json4S support - uses Doubles for decimal
    */
  object NativeDoubleMode extends JsonLibrary[JValue, JValue] {

    val JsonFormat = new Json4sFormat(org.json4s.native.JsonMethods, org.json4s.native.Serialization, false)

    val Filters = new Json4sFilters(JsonFormat, NativeDoubleMode)
  }

  /**
    * Jackson Json4S support - uses BigDecimal for decimal
    */
  object Jackson extends JsonLibrary[JValue, JValue] {

    val JsonFormat = new Json4sFormat(org.json4s.jackson.JsonMethods, org.json4s.jackson.Serialization, true)

    val Filters = new Json4sFilters(JsonFormat, Jackson)
  }

  /**
    * Jackson Json4S support - uses Doubles for decimal
    */
  object JacksonDoubleMode extends JsonLibrary[JValue, JValue] {

    val JsonFormat = new Json4sFormat(org.json4s.jackson.JsonMethods, org.json4s.jackson.Serialization, false)

    val Filters = new Json4sFilters(JsonFormat, JacksonDoubleMode)
  }
}
{'fraction_non_alphanumeric': 0.10874155405405406, 'fraction_numerical': 0.004926801801801802, 'mean_word_length': 3.4780964387015443, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3628982', 'n_tokens_mistral': 4244, 'n_tokens_neox': 4115, 'n_words': 1057}