Skip to content
This repository has been archived by the owner on Dec 22, 2022. It is now read-only.

Commit

Permalink
Restricted the max number of JsonConversionsProviders to 1.
Browse files Browse the repository at this point in the history
Simplified the conversion from fact to json so that it now also supports lists. No longer required to mess about with a needlessly complicated map.
Was unable to achieve the same level of elegance for a solution for lists from json to facts in the given time. This will need to be brute-forced through the map-solution until a more clever way is devised, but it's impeded progress for too long.
  • Loading branch information
NRBPerdijk committed Jan 27, 2017
1 parent 07ae7be commit 0daad19
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 49 deletions.
92 changes: 51 additions & 41 deletions app/controllers/conversion/Conversion.scala
Expand Up @@ -4,59 +4,54 @@ import controllers.conversion.ImplicitConversions._
import org.scalarules.facts.Fact
import org.scalarules.finance.nl._
import play.api.data.validation.ValidationError
import play.api.libs.json._
import play.api.libs.json.{JsObject, _}

import scala.reflect.runtime.universe._

trait JsonConversionsProvider {
def contextToJsonConversions: Map[Class[_], (Fact[Any], Any) => JsObject]
def jsonToFactConversions: Map[String, ConvertToFunc]
}

object DefaultJsonConversion extends JsonConversionsProvider {
override def contextToJsonConversions: Map[Class[_], ConvertBackFunc] = ContextToJsonConversionMap.contextToJsonConversionMap
override def jsonToFactConversions: Map[String, ConvertToFunc] = JsonToFactConversionMap.jsonToFactConversionMap

object ContextToJsonConversionMap {
val contextToJsonConversionMap: Map[Class[_], ConvertBackFunc] = Map[Class[_], ConvertBackFunc](
classOf[String] -> { contextStringToJsObject(_, _) },
classOf[Bedrag] -> { contextBedragToJsObject(_, _) },
classOf[Percentage] -> { contextPercentageToJsObject(_, _) },
classOf[BigDecimal] -> { contextBigDecimalToJsObject(_, _) },
classOf[Boolean] -> { contextBooleanToJsObject(_, _) },
classOf[java.lang.Boolean] -> { contextBooleanToJsObject(_, _) }
)

private def contextStringToJsObject(fact: Fact[Any], factValue: Any): JsObject = factValue match {
case string: String => JsObject(Map(fact.name -> Json.toJson(factValue.toString)))
case _ => throw new IllegalArgumentException
}

private def contextBedragToJsObject(fact: Fact[Any], factValue: Any): JsObject = factValue match {
case bedrag: Bedrag => JsObject(Map(fact.name -> Json.toJson[Bedrag](bedrag)))
case _ => throw new IllegalArgumentException
private def convertFacts(fact: Fact[Any], factValue: Any): JsObject = JsObject(Map(fact.name -> turnFactsIntoJson(factValue)))

private def turnFactsIntoJson(factValue: Any): JsValue = //scalastyle:ignore cyclomatic.complexity
try { userSpecifiedConversionsToJson(factValue) }
catch { case e: Exception => factValue match {
case x :: xs => JsArray(for { elem <- (x :: xs)} yield turnFactsIntoJson(elem))
case bedrag: Bedrag => Json.toJson(bedrag)
case string: String => Json.toJson(string)
case bool: Boolean => JsBoolean(bool)
case bool: java.lang.Boolean => JsBoolean(bool)
case bigDecimal: BigDecimal => Json.toJson(bigDecimal)
case percentage: Percentage => Json.toJson(percentage)
case other: Any => throw new IllegalStateException(s"No legal conversion found for $other, with type ${other.getClass} " + e.fillInStackTrace())
}

private def contextPercentageToJsObject(fact: Fact[Any], factValue: Any): JsObject = factValue match {
case percentage: Percentage => JsObject(Map(fact.name -> Json.toJson[Percentage](percentage)))
case _ => throw new IllegalArgumentException
}

private def contextBigDecimalToJsObject(fact: Fact[Any], factValue: Any): JsObject = factValue match {
case bigDecimal: BigDecimal => JsObject(Map(fact.name -> Json.toJson[BigDecimal](bigDecimal)))
case _ => throw new IllegalArgumentException
}
def contextToJsonConversions(fact: Fact[Any], factValue: Any): JsObject = convertFacts(fact, factValue)

/**
* override this method in your JsonConversionsProvider to add conversions to Json for types you have implemented,
* without losing all the predefined conversions (custom-specified takes precendence).
* Example:
* override def userSpecifiedConversionsToJson(factValue: Any): JsValue = factValue match {
case thingOfYourType: YourType => Json.toJson[YourType](thingOfYourType)
}
* @param factValue
* @return
*/
def userSpecifiedConversionsToJson(factValue: Any): JsValue = factValue match {
case _ => throw new IllegalStateException("None of the default matches succeeded and no other matches were provided")
}

private def contextBooleanToJsObject(fact: Fact[Any], factValue: Any): JsObject = factValue match {
case bool: Boolean => JsObject(Map(fact.name -> JsBoolean(bool)))
case bool: java.lang.Boolean => JsObject(Map(fact.name -> JsBoolean(bool)))
case _ => throw new IllegalArgumentException
}
def jsonToFactConversions: Map[String, ConvertToFunc]
}

}
object DefaultJsonConversion extends JsonConversionsProvider {
override def jsonToFactConversions: Map[String, ConvertToFunc] = JsonToFactConversionMap.jsonToFactConversionMap

object JsonToFactConversionMap {
val jsonToFactConversionMap: Map[String, ConvertToFunc] = Map[String, ConvertToFunc](
weakTypeOf[List[List[List[Bedrag]]]].toString.replace("scala.", "") -> { bedragLijstLijstLijstFunct(_, _) },
weakTypeOf[List[List[Bedrag]]].toString.replace("scala.", "") -> { bedragLijstLijstFunct(_, _) },
weakTypeOf[List[Bedrag]].toString.replace("scala.", "") -> { bedragLijstFunct(_, _) },
classOf[String].getTypeName -> { stringFunct(_, _) },
weakTypeOf[String].toString -> { stringFunct(_, _) },
classOf[Bedrag].getTypeName -> { bedragFunct(_, _) },
Expand All @@ -77,6 +72,21 @@ object DefaultJsonConversion extends JsonConversionsProvider {
case _ => JsError(ValidationError(s"Conversion for BigDecimal fact ${fact.name} failed, corresponding value was not of expected type JsNumber"))
}

private def bedragLijstLijstLijstFunct(fact: Fact[Any], factValue: JsValue): JsResult[List[List[List[Bedrag]]]] = factValue match {
case jsNumber: JsArray => Json.fromJson[List[List[List[Bedrag]]]](jsNumber)
case _ => JsError(ValidationError(s"Conversion for Bedrag fact ${fact.name} failed, corresponding value was not of expected type JsNumber"))
}

private def bedragLijstLijstFunct(fact: Fact[Any], factValue: JsValue): JsResult[List[List[Bedrag]]] = factValue match {
case jsNumber: JsArray => Json.fromJson[List[List[Bedrag]]](jsNumber)
case _ => JsError(ValidationError(s"Conversion for Bedrag fact ${fact.name} failed, corresponding value was not of expected type JsNumber"))
}

private def bedragLijstFunct(fact: Fact[Any], factValue: JsValue): JsResult[List[Bedrag]] = factValue match {
case jsNumber: JsArray => Json.fromJson[List[Bedrag]](jsNumber)
case _ => JsError(ValidationError(s"Conversion for Bedrag fact ${fact.name} failed, corresponding value was not of expected type JsNumber"))
}

private def bedragFunct(fact: Fact[Any], factValue: JsValue): JsResult[Bedrag] = factValue match {
case jsNumber: JsNumber => Json.fromJson[Bedrag](jsNumber)
case _ => JsError(ValidationError(s"Conversion for Bedrag fact ${fact.name} failed, corresponding value was not of expected type JsNumber"))
Expand Down
6 changes: 1 addition & 5 deletions app/controllers/conversion/ImplicitConversions.scala
Expand Up @@ -21,11 +21,7 @@ object ImplicitConversions {
*/
def writes(context: Context, conversionMap: JsonConversionsProvider): JsObject = {
context.map{ case (fact:Fact[Any], factValue: Any) =>
conversionMap.contextToJsonConversions.get(factValue.getClass) match {
case function: Some[ConvertBackFunc] => function.get(fact, factValue)
case None => throw new IllegalStateException(s"Unable to find suitable toJson conversion for Fact with name ${fact.name} with " +
s"valuetype ${factValue.getClass.getTypeName} in factConversionMap")
}
conversionMap.contextToJsonConversions(fact, factValue)
}.reduceLeft(_ ++ _)
}
}
Expand Down
9 changes: 6 additions & 3 deletions app/services/JsonConversionMapsService.scala
Expand Up @@ -4,6 +4,7 @@ import javax.inject.{Inject, Singleton}

import controllers.conversion._
import play.api.Configuration
import play.api.libs.json.JsValue

@Singleton
class JsonConversionMapsService @Inject()(configuration: Configuration, jarLoaderService: JarLoaderService) {
Expand All @@ -17,9 +18,11 @@ class JsonConversionMapsService @Inject()(configuration: Configuration, jarLoade
case (_, map: JsonConversionsProvider) => map.jsonToFactConversions
}

override def contextToJsonConversions: Map[Class[_], ConvertBackFunc] = DefaultJsonConversion.contextToJsonConversions ++ jsonConversionMaps.flatMap{
case (_, map: JsonConversionsProvider) => map.contextToJsonConversions
}
override def userSpecifiedConversionsToJson(factValue: Any): JsValue =
if (jsonConversionMaps.isEmpty) { DefaultJsonConversion.userSpecifiedConversionsToJson(factValue) }
else if (jsonConversionMaps.size > 1) throw new IllegalStateException("Only a single instance of JsonConversionsProvider may be provided!")
else { jsonConversionMaps.toList.head._2.userSpecifiedConversionsToJson(factValue) }

}

}

0 comments on commit 0daad19

Please sign in to comment.