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

.circumflex-orm.2.5.source-code.field.scala Maven / Gradle / Ivy

The newest version!
package ru.circumflex
package orm

import core._
import java.util.Date
import xml._
import java.sql.ResultSet
import scala.math.BigDecimal._

/*!# Field

The `Field` class holds atomic values of records. It wraps actual value
and provides methods for constructing column definitions for enclosing
tables. It also contains the `REFERENCES` method which is used to create
associations and various methods for composing simple predicates.
*/
class Field[T, R <: Record[_, R]](val name: String,
                                  val record: R,
                                  val sqlType: String)
    extends ValueHolder[T, R]
    with SQLable {

  def uuid = record.getClass.getName + "." + name

  def toSql: String = ormConf.dialect.columnDefinition(this)

  def read(rs: ResultSet, alias: String): Option[T] = {
    val o = rs.getObject(alias)
    if (rs.wasNull) None
    else Some(o.asInstanceOf[T])
  }

  /*!## Column Definition Methods

  Following methods help you construct a definition of the column where
  the field will be persisted:

    * `NOT_NULL` will render `NOT NULL` constraint in column's definition;
    note that starting from 2.0, by default the `NOT NULL` constraint is
    omitted and `NULLABLE` construct is no longer supported; this method
    can also be used as a shortcut for specifying the `NOT NULL` constraint
    and assigning default field value:

        // following declarations are identical
        val createdAt = "created_at".TIMESTAMP.NOT_NULL.set(new Date())
        val createdAt = "created_at".TIMESTAMP.NOT_NULL(new Date())

    * `DEFAULT` will render the `DEFAULT` expression in column's definition
    (if not overriden by dialect);
    * `UNIQUE` will create a `UNIQUE` constraint for enclosing table on
    the field.
  */
  protected var _notNull: Boolean = false
  def isNotNull: Boolean = _notNull
  def NOT_NULL: this.type = {
    _notNull = true
    this
  }
  def NOT_NULL(initialValue: T): this.type = NOT_NULL.set(initialValue)

  protected var _unique: Boolean = false
  def isUnique: Boolean = _unique
  def UNIQUE: this.type = {
    _unique = true
    this
  }

  protected var _defaultExpression: Option[String] = None
  def defaultExpression: Option[String] = _defaultExpression
  def DEFAULT(expr: String): this.type = {
    _defaultExpression = Some(expr)
    this
  }

  def REFERENCES[P <: Record[T, P]](relation: Relation[T, P]): Association[T, R, P] =
    new Association(this, relation)

  /*!## Simple expressions

  Simple expressions are used to compose predicates in a DSL-style.
  */
  def GT(value: T): Predicate =
    new SimpleExpression(ormConf.dialect.GT(aliasedName, placeholder), List(value))
  def GT(col: ColumnExpression[_, _]): Predicate =
    new SimpleExpression(ormConf.dialect.GT(aliasedName, col.toSql), Nil)
  def GE(value: T): Predicate =
    new SimpleExpression(ormConf.dialect.GE(aliasedName, placeholder), List(value))
  def GE(col: ColumnExpression[_, _]): Predicate =
    new SimpleExpression(ormConf.dialect.GE(aliasedName, col.toSql), Nil)
  def LT(value: T): Predicate =
    new SimpleExpression(ormConf.dialect.LT(aliasedName, placeholder), List(value))
  def LT(col: ColumnExpression[_, _]): Predicate =
    new SimpleExpression(ormConf.dialect.LT(aliasedName, col.toSql), Nil)
  def LE(value: T): Predicate =
    new SimpleExpression(ormConf.dialect.LE(aliasedName, placeholder), List(value))
  def LE(col: ColumnExpression[_, _]): Predicate =
    new SimpleExpression(ormConf.dialect.LE(aliasedName, col.toSql), Nil)

  def IN(params: Seq[T]): Predicate =
    new SimpleExpression(ormConf.dialect.parameterizedIn(aliasedName, params.map(p => placeholder)), params.toList)
  def BETWEEN(lowerValue: T, upperValue: T) =
    new SimpleExpression(ormConf.dialect.BETWEEN(aliasedName, placeholder, placeholder), List(lowerValue, upperValue))

  def IN(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.IN(aliasedName), query)
  def NOT_IN(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.NOT_IN(aliasedName), query)

  def EQ_ALL(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.EQ(aliasedName, ormConf.dialect.ALL), query)
  def NE_ALL(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.NE(aliasedName, ormConf.dialect.ALL), query)
  def GT_ALL(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.GT(aliasedName, ormConf.dialect.ALL), query)
  def GE_ALL(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.GE(aliasedName, ormConf.dialect.ALL), query)
  def LT_ALL(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.LT(aliasedName, ormConf.dialect.ALL), query)
  def LE_ALL(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.LE(aliasedName, ormConf.dialect.ALL), query)

  def EQ_SOME(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.EQ(aliasedName, ormConf.dialect.SOME), query)
  def NE_SOME(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.NE(aliasedName, ormConf.dialect.SOME), query)
  def GT_SOME(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.GT(aliasedName, ormConf.dialect.SOME), query)
  def GE_SOME(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.GE(aliasedName, ormConf.dialect.SOME), query)
  def LT_SOME(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.LT(aliasedName, ormConf.dialect.SOME), query)
  def LE_SOME(query: SQLQuery[_]): Predicate =
    new SubqueryExpression(ormConf.dialect.LE(aliasedName, ormConf.dialect.SOME), query)

}

trait AutoIncrementable[T, R <: Record[_, R]] extends Field[T, R] {
  protected var _autoIncrement: Boolean = false
  def isAutoIncrement: Boolean = _autoIncrement
  def AUTO_INCREMENT: this.type = {
    _autoIncrement = true
    this
  }
}

class IntField[R <: Record[_, R]](name: String, record: R)
    extends XmlSerializable[Int, R](name, record, ormConf.dialect.integerType)
    with AutoIncrementable[Int, R] {
  def fromString(str: String): Option[Int] =
    try Some(str.toInt) catch { case e: Exception => None }
}

class LongField[R <: Record[_, R]](name: String, record: R)
    extends XmlSerializable[Long, R](name, record, ormConf.dialect.longType)
    with AutoIncrementable[Long, R]{
  def fromString(str: String): Option[Long] =
    try Some(str.toLong) catch { case e: Exception => None }
}

class DoubleField[R <: Record[_, R]](
                                        name: String,
                                        record: R,
                                        val precision: Int = -1,
                                        val scale: Int = 0)
    extends XmlSerializable[Double, R](
      name, record, ormConf.dialect.numericType(precision, scale)) {
  override def read(rs: ResultSet, alias: String): Option[Double] = {
    val d = rs.getDouble(alias)
    if (rs.wasNull) None
    else Some(d)
  }
  def fromString(str: String): Option[Double] =
    try Some(str.toDouble) catch { case e: Exception => None }
}

class NumericField[R <: Record[_, R]](
                                         name: String,
                                         record: R,
                                         val precision: Int = -1,
                                         val scale: Int = 0,
                                         val roundingMode: RoundingMode.RoundingMode = RoundingMode.HALF_EVEN)
    extends XmlSerializable[BigDecimal, R](
      name, record, ormConf.dialect.numericType(precision, scale)) {
  def fromString(str: String): Option[BigDecimal] =
    try Some(BigDecimal(str)) catch { case e: Exception => None }
  override def read(rs: ResultSet, alias: String): Option[BigDecimal] = {
    val bd = rs.getBigDecimal(alias)
    if (rs.wasNull) None
    else Some(new BigDecimal(bd))
  }
  addSetter(v => v.setScale(scale, roundingMode))
}

class TextField[R <: Record[_, R]](name: String, record: R, sqlType: String)
    extends XmlSerializable[String, R](name, record, sqlType) {
  def this(name: String, record: R, length: Int = -1) =
    this(name, record, ormConf.dialect.varcharType(length))
  def fromString(str: String): Option[String] =
    if (str == "") None else Some(str)

  def LIKE(value: String) = new SimpleExpression(ormConf.dialect.LIKE(aliasedName, placeholder), List(value))
  def LIKE(col: ColumnExpression[String, _]) =
    new SimpleExpression(ormConf.dialect.LIKE(aliasedName, col.toSql), Nil)

  def ILIKE(value: String) = new SimpleExpression(ormConf.dialect.ILIKE(aliasedName, placeholder), List(value))
  def ILIKE(col: ColumnExpression[String, _]) =
    new SimpleExpression(ormConf.dialect.ILIKE(aliasedName, col.toSql), Nil)

  def LLIKE(expr: String): Predicate =
    new SimpleExpression("LOWER(" + aliasedName + ") LIKE ?", List(expr.toLowerCase))
}

class HtmlField[R <: Record[_, R]](_name: String, _record: R)
    extends TextField(_name, _record, ormConf.dialect.textType) {
  addSetter(s => core.escapeHtml(s.trim))
}

class BooleanField[R <: Record[_, R]](name: String, record: R)
    extends XmlSerializable[Boolean, R](name, record, ormConf.dialect.booleanType) {
  def fromString(str: String): Option[Boolean] =
    try Some(str.toBoolean) catch { case e: Exception => None }
}

object BooleanField {
  implicit def toPredicate(f: BooleanField[_]): Predicate =
    new SimpleExpression(f.aliasedName, Nil)
}

class BinaryField[R <: Record[_, R]](name: String, record: R)
    extends Field[Array[Byte], R](name, record, ormConf.dialect.binaryType) {
  override def read(rs: ResultSet, alias: String): Option[Array[Byte]] = {
    val o = rs.getBytes(alias)
    if (rs.wasNull) None
    else Some(o)
  }
}

class TimestampField[R <: Record[_, R]](name: String, record: R)
    extends XmlSerializable[Date, R](name, record, ormConf.dialect.timestampType) {
  def fromString(str: String): Option[Date] =
    try Some(new Date(java.sql.Timestamp.valueOf(str).getTime)) catch { case e: Exception => None }
  override def toString(value: Option[Date]): String =
    value.map(v => new java.sql.Timestamp(v.getTime).toString).getOrElse("")
}

class DateField[R <: Record[_, R]](name: String, record: R)
    extends XmlSerializable[Date, R](name, record, ormConf.dialect.dateType) {
  def fromString(str: String): Option[Date] =
    try Some(new Date(java.sql.Date.valueOf(str).getTime)) catch { case e: Exception => None }
  override def toString(value: Option[Date]): String =
    value.map(v => new java.sql.Date(v.getTime).toString).getOrElse("")
}

class TimeField[R <: Record[_, R]](name: String, record: R)
    extends XmlSerializable[Date, R](name, record, ormConf.dialect.timeType) {
  def fromString(str: String): Option[Date] =
    try Some(new Date(java.sql.Time.valueOf(str).getTime)) catch { case e: Exception => None }
  override def toString(value: Option[Date]): String =
    value.map(v => new java.sql.Time(v.getTime).toString).getOrElse("")
}

class XmlField[R <: Record[_, R]](name: String, record: R, val root: String)
    extends XmlSerializable[Elem, R](name, record, ormConf.dialect.xmlType) {
  def fromString(str: String): Option[Elem] = Some(XML.loadString(
    "<" + root + ">" + str + ""))
  override def read(rs: ResultSet, alias: String) =
    any2option(rs.getString(alias)).map(x => XML.loadString(x))
  override def placeholder = ormConf.dialect.xmlPlaceholder
}

class FieldComposition2[T1, T2, R <: Record[_, R]](val _1: Field[T1, R],
                                                   val _2: Field[T2, R],
                                                   val record: R)
    extends ValueHolder[(T1, T2), R] {

  def name = ormConf.dialect.compositeFieldName(_1.name, _2.name)

  override def value: Option[(T1, T2)] = (_1.get, _2.get) match {
    case (Some(v1), Some(v2)) => Some(v1 -> v2)
    case _ => None
  }

  override def set(v: Option[(T1, T2)]): this.type = {
    v match {
      case Some(Pair(v1, v2)) =>
        _1.set(v1)
        _2.set(v2)
      case _ =>
        _1.setNull()
        _2.setNull()
    }
    this
  }

  protected def _getPrefix = aliasStack.pop().map(_ + ".").getOrElse("")

  override def aliasedName: String = {
    val prefix = _getPrefix
    ormConf.dialect.compositeFieldName(prefix + _1.name, prefix + _2.name)
  }

  override def EQ(value: (T1, T2)) = {
    val prefix = _getPrefix
    AND(new SimpleExpression(ormConf.dialect.EQ(prefix + _1.name, placeholder), List(value._1)),
      new SimpleExpression(ormConf.dialect.EQ(prefix + _2.name, placeholder), List(value._2)))
  }
  override def NE(value: (T1, T2)) = {
    val prefix = _getPrefix
    AND(new SimpleExpression(ormConf.dialect.NE(prefix + _1.name, placeholder), List(value._1)),
      new SimpleExpression(ormConf.dialect.NE(prefix + _2.name, placeholder), List(value._2)))
  }
  override def IS_NULL = {
    val prefix = _getPrefix
    AND(new SimpleExpression(ormConf.dialect.IS_NULL(prefix + _1.name), Nil),
      new SimpleExpression(ormConf.dialect.IS_NULL(prefix + _2.name), Nil))
  }
  override def IS_NOT_NULL = {
    val prefix = _getPrefix
    AND(new SimpleExpression(ormConf.dialect.IS_NOT_NULL(prefix + _1.name), Nil),
      new SimpleExpression(ormConf.dialect.IS_NOT_NULL(prefix + _2.name), Nil))
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy