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

com.github.mauricio.async.db.postgresql.column.PostgreSQLColumnEncoderRegistry.scala Maven / Gradle / Ivy

/*
 * Copyright 2013 Maurício Linhares
 *
 * Maurício Linhares licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package com.github.mauricio.async.db.postgresql.column

import java.nio.ByteBuffer

import com.github.mauricio.async.db.column._
import io.netty.buffer.ByteBuf
import org.joda.time._
import scala.jdk.CollectionConverters._

object PostgreSQLColumnEncoderRegistry {
  val Instance = new PostgreSQLColumnEncoderRegistry()
}

class PostgreSQLColumnEncoderRegistry extends ColumnEncoderRegistry {

  private val classesSequence_ : List[(Class[_], (ColumnEncoder, Int))] = List(
    classOf[Int] -> (IntegerEncoderDecoder -> ColumnTypes.Integer),
    classOf[
      java.lang.Integer
    ]                        -> (IntegerEncoderDecoder -> ColumnTypes.Integer),
    classOf[java.lang.Short] -> (ShortEncoderDecoder   -> ColumnTypes.Smallint),
    classOf[Short]           -> (ShortEncoderDecoder   -> ColumnTypes.Smallint),
    classOf[Long]           -> (LongEncoderDecoder   -> ColumnTypes.Bigserial),
    classOf[java.lang.Long] -> (LongEncoderDecoder   -> ColumnTypes.Bigserial),
    classOf[String]         -> (StringEncoderDecoder -> ColumnTypes.Varchar),
    classOf[java.lang.String] -> (StringEncoderDecoder -> ColumnTypes.Varchar),
    classOf[Float]            -> (FloatEncoderDecoder  -> ColumnTypes.Real),
    classOf[java.lang.Float]  -> (FloatEncoderDecoder  -> ColumnTypes.Real),
    classOf[Double]           -> (DoubleEncoderDecoder -> ColumnTypes.Double),
    classOf[java.lang.Double] -> (DoubleEncoderDecoder -> ColumnTypes.Double),
    classOf[BigDecimal] -> (BigDecimalEncoderDecoder -> ColumnTypes.Numeric),
    classOf[
      java.math.BigDecimal
    ] -> (BigDecimalEncoderDecoder -> ColumnTypes.Numeric),
    classOf[
      java.net.InetAddress
    ]                       -> (InetAddressEncoderDecoder -> ColumnTypes.Inet),
    classOf[java.util.UUID] -> (UUIDEncoderDecoder        -> ColumnTypes.UUID),
    classOf[LocalDate]      -> (DateEncoderDecoder        -> ColumnTypes.Date),
    classOf[
      LocalDateTime
    ] -> (TimestampEncoderDecoder.Instance -> ColumnTypes.Timestamp),
    classOf[DateTime] -> (TimestampWithTimezoneEncoderDecoder -> ColumnTypes.TimestampWithTimezone),
    classOf[
      ReadableDateTime
    ] -> (TimestampWithTimezoneEncoderDecoder -> ColumnTypes.TimestampWithTimezone),
    classOf[ReadableInstant] -> (DateEncoderDecoder -> ColumnTypes.Date),
    classOf[
      ReadablePeriod
    ] -> (PostgreSQLIntervalEncoderDecoder -> ColumnTypes.Interval),
    classOf[
      ReadableDuration
    ] -> (PostgreSQLIntervalEncoderDecoder -> ColumnTypes.Interval),
    classOf[
      java.util.Date
    ] -> (TimestampWithTimezoneEncoderDecoder -> ColumnTypes.TimestampWithTimezone),
    classOf[java.sql.Date] -> (DateEncoderDecoder -> ColumnTypes.Date),
    classOf[java.sql.Time] -> (SQLTimeEncoder     -> ColumnTypes.Time),
    classOf[
      java.sql.Timestamp
    ] -> (TimestampWithTimezoneEncoderDecoder -> ColumnTypes.TimestampWithTimezone),
    classOf[
      java.util.Calendar
    ] -> (TimestampWithTimezoneEncoderDecoder -> ColumnTypes.TimestampWithTimezone),
    classOf[
      java.util.GregorianCalendar
    ] -> (TimestampWithTimezoneEncoderDecoder -> ColumnTypes.TimestampWithTimezone),
    classOf[Array[Byte]] -> (ByteArrayEncoderDecoder -> ColumnTypes.ByteA),
    classOf[ByteBuffer]  -> (ByteArrayEncoderDecoder -> ColumnTypes.ByteA),
    classOf[ByteBuf]     -> (ByteArrayEncoderDecoder -> ColumnTypes.ByteA)
  )

  private final val classesSequence =
    (classOf[LocalTime] -> (TimeEncoderDecoder.Instance -> ColumnTypes.Time)) ::
      (classOf[
        ReadablePartial
      ] -> (TimeEncoderDecoder.Instance -> ColumnTypes.Time)) ::
      classesSequence_

  private final val classes = classesSequence.toMap

  override def encode(value: Any): String = {

    if (value == null) {
      return null
    }

    value match {
      case Some(v) => encode(v)
      case None    => null
      case _       => encodeValue(value)
    }

  }

  /**
   * Used to encode a value that is not null and not an Option.
   */
  private def encodeValue(value: Any): String = {

    val encoder = this.classes.get(value.getClass)

    if (encoder.isDefined) {
      encoder.get._1.encode(value)
    } else {
      value match {
        case i: java.lang.Iterable[_] => encodeArray(i.asScala)
        case i: Iterable[_]           => encodeArray(i)
        case i: Array[_]              => encodeArray(i)
        case p: Product               => encodeComposite(p)
        case _ => {
          this.classesSequence
            .find(entry => entry._1.isAssignableFrom(value.getClass)) match {
            case Some(parent) => parent._2._1.encode(value)
            case None         => value.toString
          }
        }
      }

    }

  }

  private def encodeComposite(p: Product): String = {
    p.productIterator.map { item =>
      if (item == null || item == None) {
        "NULL"
      } else {
        if (this.shouldQuote(item)) {
          "\"" + this
            .encode(item)
            .replace("\\", """\\""")
            .replace("\"", """\"""") + "\""
        } else {
          this.encode(item)
        }
      }
    }.mkString("(", ",", ")")
  }

  private def encodeArray(collection: Iterable[_]): String = {
    collection.map { item =>
      if (item == null || item == None) {
        "NULL"
      } else {
        if (this.shouldQuote(item)) {
          "\"" + this
            .encode(item)
            .replace("\\", """\\""")
            .replace("\"", """\"""") + "\""
        } else {
          this.encode(item)
        }
      }
    }.mkString("{", ",", "}")
  }

  private def shouldQuote(value: Any): Boolean = {
    value match {
      case n: java.lang.Number      => false
      case n: Int                   => false
      case n: Short                 => false
      case n: Long                  => false
      case n: Float                 => false
      case n: Double                => false
      case n: java.lang.Iterable[_] => false
      case n: Iterable[_]           => false
      case n: Array[_]              => false
      case Some(v)                  => shouldQuote(v)
      case _                        => true
    }
  }

  override def kindOf(value: Any): Int = {
    if (value == null || value == None) {
      0
    } else {
      value match {
        case Some(v)   => kindOf(v)
        case v: String => ColumnTypes.Untyped
        case _ => {
          this.classes.get(value.getClass) match {
            case Some(entry) => entry._2
            case None        => ColumnTypes.Untyped
          }
        }
      }
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy