From 81add5ec07f1db652af581b0cee3fa090007d9e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C5=81ukasz=20Bigorajski?= <lbg@touk.pl>
Date: Mon, 26 Aug 2024 18:25:20 +0200
Subject: [PATCH] review fixes

---
 .../sql/db/schema/ColumnDefinition.scala      | 90 ++++++++-----------
 1 file changed, 39 insertions(+), 51 deletions(-)

diff --git a/components/sql/src/main/scala/pl/touk/nussknacker/sql/db/schema/ColumnDefinition.scala b/components/sql/src/main/scala/pl/touk/nussknacker/sql/db/schema/ColumnDefinition.scala
index dd6ef6dc976..9350089a53c 100644
--- a/components/sql/src/main/scala/pl/touk/nussknacker/sql/db/schema/ColumnDefinition.scala
+++ b/components/sql/src/main/scala/pl/touk/nussknacker/sql/db/schema/ColumnDefinition.scala
@@ -5,58 +5,57 @@ import pl.touk.nussknacker.engine.api.typed.typing.{Typed, TypingResult}
 import java.io.BufferedReader
 import java.sql.{Clob, ResultSet, ResultSetMetaData}
 import java.time.{Instant, LocalDate, LocalTime}
+import java.util.stream.Collectors
 import java.{sql, util}
-import scala.annotation.tailrec
-import scala.collection.mutable.ArrayBuffer
 import scala.util.Using
 
-object ColumnDefinition {
+final case class ColumnDefinition(
+    name: String,
+    typing: TypingResult,
+    valueMapping: Any => Any
+) {
+
+  def extractValue(resultSet: ResultSet): Any = {
+    // we could here use method resultSet.getObject(Int) and pass column number as argument
+    // but in case of ignite db it is not certain which column index corresponds to which column.
+    val value = resultSet.getObject(name)
+    Option(value).map(valueMapping).getOrElse(value)
+  }
 
-  private lazy val sqlTypingMap = Map(
-    classOf[sql.Array].getName     -> Typed.typedClass(classOf[util.List[Any]]),
-    classOf[sql.Time].getName      -> Typed.typedClass(classOf[LocalTime]),
-    classOf[sql.Date].getName      -> Typed.typedClass(classOf[LocalDate]),
-    classOf[sql.Timestamp].getName -> Typed.typedClass(classOf[Instant]),
-    classOf[sql.Clob].getName      -> Typed.typedClass(classOf[String]),
-  )
+}
 
-  def apply(columnNo: Int, resultMeta: ResultSetMetaData): ColumnDefinition =
+object ColumnDefinition {
+  private val sqlArrayClassName     = classOf[sql.Array].getName
+  private val sqlTimeClassName      = classOf[sql.Time].getName
+  private val sqlDateClassName      = classOf[sql.Date].getName
+  private val sqlTimestampClassName = classOf[sql.Timestamp].getName
+  private val sqlClobClassName      = classOf[sql.Clob].getName
+
+  def apply(columnNo: Int, resultMeta: ResultSetMetaData): ColumnDefinition = {
+    val (typingResult, valueMapping) = mapValueToSupportedType(resultMeta.getColumnClassName(columnNo))
     ColumnDefinition(
       name = resultMeta.getColumnName(columnNo),
-      typing = supportedTypeTypingResult(resultMeta.getColumnClassName(columnNo))
+      typing = typingResult,
+      valueMapping = valueMapping
     )
+  }
 
-  def apply(typing: (String, String)): ColumnDefinition =
+  def apply(typing: (String, String)): ColumnDefinition = {
+    val (typingResult, valueMapping) = mapValueToSupportedType(typing._2)
     ColumnDefinition(
       name = typing._1,
-      typing = supportedTypeTypingResult(typing._2)
+      typing = typingResult,
+      valueMapping = valueMapping
     )
-
-  private def supportedTypeTypingResult(className: String): TypingResult =
-    sqlTypingMap.getOrElse(className, Typed.typedClass(Class.forName(className)))
-}
-
-final case class ColumnDefinition(
-    name: String,
-    typing: TypingResult
-) {
-
-  def extractValue(resultSet: ResultSet): Any = {
-    // we could here use method resultSet.getObject(Int) and pass column number as argument
-    // but in case of ignite db it is not certain which column index corresponds to which column.
-    val value = resultSet.getObject(name)
-    Option(value)
-      .map(mapValueToSupportedType)
-      .getOrElse(value)
   }
 
-  private def mapValueToSupportedType(value: Any): Any = value match {
-    case v: sql.Array     => readArray(v)
-    case v: sql.Time      => v.toLocalTime
-    case v: sql.Date      => v.toLocalDate
-    case v: sql.Timestamp => v.toInstant
-    case v: sql.Clob      => readClob(v)
-    case _                => value
+  private def mapValueToSupportedType(className: String): (TypingResult, Any => Any) = className match {
+    case `sqlArrayClassName` => (Typed.typedClass(classOf[util.List[Any]]), v => readArray(v.asInstanceOf[sql.Array]))
+    case `sqlTimeClassName`  => (Typed.typedClass(classOf[LocalTime]), v => v.asInstanceOf[sql.Time].toLocalTime)
+    case `sqlDateClassName`  => (Typed.typedClass(classOf[LocalDate]), v => v.asInstanceOf[sql.Date].toLocalDate)
+    case `sqlTimestampClassName` => (Typed.typedClass(classOf[Instant]), v => v.asInstanceOf[sql.Timestamp].toInstant)
+    case `sqlClobClassName`      => (Typed.typedClass(classOf[String]), v => readClob(v.asInstanceOf[sql.Clob]))
+    case _                       => (Typed.typedClass(Class.forName(className)), identity)
   }
 
   private def readArray(v: sql.Array): util.List[AnyRef] = {
@@ -72,17 +71,6 @@ final case class ColumnDefinition(
     Using.resource(new BufferedReader(v.getCharacterStream))(br => readFromStream(br))
   }
 
-  @tailrec private def readFromStream(
-      br: BufferedReader,
-      acc: ArrayBuffer[String] = new ArrayBuffer[String]()
-  ): String = {
-    val string = br.readLine()
-    if (string == null) {
-      acc.mkString(System.lineSeparator)
-    } else {
-      acc.append(string)
-      readFromStream(br, acc)
-    }
-  }
-
+  private def readFromStream(br: BufferedReader): String =
+    br.lines().collect(Collectors.joining(System.lineSeparator()))
 }