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

com.outr.query.Table.scala Maven / Gradle / Ivy

package com.outr.query

import scala.collection.mutable.ListBuffer
import com.outr.query.column.property.{ColumnProperty, AutoIncrement, ForeignKey, PrimaryKey}
import scala.language.existentials
import com.outr.query.datatype._
import com.outr.query.table.property.{Linking, TableProperty}

/**
 * @author Matt Hicks 
 */
abstract class Table(val datastore: Datastore, name: String, tableProperties: TableProperty*) {
  def this(datastore: Datastore, tableProperties: TableProperty*) = this(datastore, null.asInstanceOf[String], tableProperties: _*)

  lazy val tableName = if (name == null) Table.generateName(getClass) else name
  datastore.add(this)   // Make sure the Datastore knows about this table

  implicit def thisTable = this

  implicit def booleanConverter = BooleanDataType
  implicit def intConverter = IntDataType
  implicit def longConverter = LongDataType
  implicit def doubleConverter = DoubleDataType
  implicit def bigDecimalConverter = BigDecimalDataType
  implicit def stringConverter = StringDataType
  implicit def wrappedStringConverter = WrappedStringDataType
  implicit def byteArrayConverter = ByteArrayDataType
  implicit def blobConverter = BlobDataType
  implicit def timestampConverter = TimestampDataType
  implicit def javaIntConverter = JavaIntDataType
  implicit def javaLongConverter = JavaLongDataType
  implicit def javaDoubleConverter = JavaDoubleDataType

  private var _properties = Map.empty[String, TableProperty]
  private var _columns = ListBuffer.empty[Column[_]]
  private var _foreignColumns = ListBuffer.empty[Column[_]]
  private lazy val columnMap = Map(columns.map(c => c.name.toLowerCase -> c): _*)
  lazy val primaryKeys = columns.collect {
    case c if c.has(PrimaryKey) => c
  }
  lazy val foreignKeys = columns.collect {
    case c if c.has(ForeignKey.name) => c
  }
  lazy val autoIncrement = columns.find(c => c.has(AutoIncrement))
  lazy val (one2One, one2Many, many2One, many2Many) = loadRelationships()

  props(tableProperties: _*)      // Add properties from constructor

  def properties = _properties.values

  protected[query] def addColumn[T](column: Column[T]) = synchronized {
    _columns += column
  }
  protected[query] def addForeignColumn[T](column: Column[T]) = synchronized {
    _foreignColumns += column
  }

  def as(alias: String) = TableAlias(this, alias)

  def columns = _columns.toList

  def * = columns

  def getColumn[T](name: String) = columnMap.get(name.toLowerCase).asInstanceOf[Option[Column[T]]]
  def columnsByName[T](names: String*) = names.map(name => getColumn[T](name)).flatten

  def column[T](name: String, properties: ColumnProperty*)
               (implicit converter: DataType[T], manifest: Manifest[T]) = {
    val c = new Column[T](name, converter, manifest, this)
    c.props(properties: _*)
  }

  def column[T](name: String, converter: DataType[T], properties: ColumnProperty*)
               (implicit manifest: Manifest[T]) = {
    val c = new Column[T](name, converter, manifest, this)
    c.props(properties: _*)
  }

  private def loadRelationships() = {
    val local2Foreign = Map(columns.collect {
      case c if c.has(ForeignKey.name) => ForeignKey(c).foreignColumn.table -> c
    }: _*)
    val foreign2Local = Map(_foreignColumns.map(c => c.table -> c): _*)
    val foreignTables = local2Foreign.keySet ++ foreign2Local.keySet
    var o2o = List.empty[Column[_]]
    var o2m = List.empty[Column[_]]
    var m2o = List.empty[Column[_]]
    var m2m = List.empty[Column[_]]
    foreignTables.foreach {
      case foreignTable => if (local2Foreign.contains(foreignTable) && foreign2Local.contains(foreignTable)) {
        o2o = local2Foreign(foreignTable) :: o2o
      } else if (local2Foreign.contains(foreignTable)) {
        o2m = local2Foreign(foreignTable) :: o2m
      } else if (foreign2Local.contains(foreignTable)) {
        if (foreignTable.has(Linking)) {
          m2m = foreign2Local(foreignTable) :: m2m
        } else {
          m2o = foreign2Local(foreignTable) :: m2o
        }
      }
    }
    (o2o.reverse, o2m.reverse, m2o.reverse, m2m.reverse)
  }

  /**
   * Adds the supplied properties to this table.
   *
   * @param properties the properties to add
   * @return this
   */
  def props(properties: TableProperty*) = synchronized {
    properties.foreach {
      case p => {
        _properties += p.name -> p
        p.addedTo(this)
      }
    }
    this
  }

  def has(property: TableProperty): Boolean = has(property.name)
  def has(propertyName: String): Boolean = _properties.contains(propertyName)
  def get[P <: TableProperty](propertyName: String) = _properties.get(propertyName).asInstanceOf[Option[P]]
  def prop[P <: TableProperty](propertyName: String) = get[P](propertyName).getOrElse(throw new NullPointerException(s"Unable to find property by name '$propertyName' in table '$tableName'."))

  override def toString = s"Table($tableName)"
}

object Table {
  def generateName(c: Class[_]) = {
    val n = c.getSimpleName
    "([A-Z])".r.replaceAllIn(n.charAt(0).toLower + n.substring(1, n.length - 1), m => "_" + m.group(0).toLowerCase)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy