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

pl.touk.nussknacker.engine.extension.ExtensionMethods.scala Maven / Gradle / Ivy

The newest version!
package pl.touk.nussknacker.engine.extension

import org.springframework.core.convert.TypeDescriptor
import org.springframework.expression.{EvaluationContext, MethodExecutor, MethodResolver, TypedValue}
import pl.touk.nussknacker.engine.definition.clazz.{ClassDefinitionSet, MethodDefinition}
import pl.touk.nussknacker.engine.extension.ExtensionMethods.extensionMethodsDefinitions

import java.util
import scala.collection.concurrent.TrieMap
import scala.reflect.{ClassTag, classTag}

class ExtensionMethodResolver(classDefinitionSet: ClassDefinitionSet) extends MethodResolver {
  private val executorsCache = new TrieMap[(String, Class[_]), Option[MethodExecutor]]()

  override def resolve(
      context: EvaluationContext,
      targetObject: Any,
      methodName: String,
      argumentTypes: util.List[TypeDescriptor]
  ): MethodExecutor =
    maybeResolve(targetObject, methodName, argumentTypes).orNull

  def maybeResolve(
      targetObject: Any,
      methodName: String,
      argumentTypes: util.List[TypeDescriptor]
  ): Option[MethodExecutor] = {
    val targetClass = targetObject.getClass
    executorsCache.getOrElseUpdate(
      (methodName, targetClass), {
        extensionMethodsDefinitions.flatMap(
          _.findMethod(targetClass, methodName, argumentTypes.size(), classDefinitionSet)
        ) match {
          case Nil           => None
          case method :: Nil => Some(createExecutor(method))
          case _ => throw new IllegalStateException(s"Found too many methods for method with name: '$methodName'")
        }
      }
    )
  }

  private def createExecutor(method: ExtensionMethod[_]): MethodExecutor = new MethodExecutor {
    private val typeDescriptor = TypeDescriptor.valueOf(method.returnType)

    override def execute(context: EvaluationContext, target: Any, args: Object*): TypedValue =
      new TypedValue(method.invoke(target, args: _*), typeDescriptor)

  }

}

object ExtensionMethods {

  val extensionMethodsDefinitions: List[ExtensionMethodsDefinition] = List(
    CastOrConversionExt,
    ArrayExt,
    ConversionExt(ToLongConversion),
    ConversionExt(ToDoubleConversion),
    ConversionExt(ToBigDecimalConversion),
    ConversionExt(ToBooleanConversion),
    ToListConversionExt,
    ToMapConversionExt,
  )

  def enrichWithExtensionMethods(set: ClassDefinitionSet): ClassDefinitionSet = {
    new ClassDefinitionSet(
      set.classDefinitionsMap.map { case (clazz, definition) =>
        clazz -> definition.copy(
          methods = definition.methods ++ extensionMethodsDefinitions.flatMap(_.extractDefinitions(clazz, set))
        )
      }.toMap // .toMap is needed by scala 2.12
    )
  }

}

abstract class ExtensionMethod[R: ClassTag] {
  val argsSize: Int
  def invoke(target: Any, args: Object*): R
  def returnType: Class[R] = classTag[R].runtimeClass.asInstanceOf[Class[R]]
}

object ExtensionMethod {

  def NoArg[R: ClassTag](method: Any => R): ExtensionMethod[R] = new ExtensionMethod {
    override val argsSize: Int                         = 0
    override def invoke(target: Any, args: Object*): R = method(target)
  }

  def SingleArg[T, R: ClassTag](method: (Any, T) => R): ExtensionMethod[R] = new ExtensionMethod {
    override val argsSize: Int                         = 1
    override def invoke(target: Any, args: Object*): R = method(target, args.head.asInstanceOf[T])
  }

  implicit class FindMethodExtension(private val registry: Map[String, ExtensionMethod[_]]) extends AnyVal {

    def findMethod(methodName: String, argsSize: Int): Option[ExtensionMethod[_]] = registry
      .get(methodName)
      .filter(_.argsSize == argsSize)

  }

}

trait ExtensionMethodsDefinition {

  def findMethod(
      clazz: Class[_],
      methodName: String,
      argsSize: Int,
      set: ClassDefinitionSet
  ): Option[ExtensionMethod[_]]

  def extractDefinitions(clazz: Class[_], set: ClassDefinitionSet): Map[String, List[MethodDefinition]]

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy