package net.liftweb.mapper
/*
* Copyright 2006-2008 WorldWide Conferencing, LLC
*
* Licensed 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.
*/
import _root_.net.liftweb.util.{FatLazy, Box, Empty, Full, Failure}
import _root_.scala.collection.mutable.HashSet
class HasManyThrough[From <: KeyedMapper[ThroughType, From],
To <: Mapper[To],
Through <: Mapper[Through],
ThroughType <: Any](owner: From,
otherSingleton: MetaMapper[To],
through: MetaMapper[Through],
throughFromField: MappedField[ThroughType, Through],
throughToField: MappedField[ThroughType, Through]) extends LifecycleCallbacks
{
private var theSetList: Seq[ThroughType] = Nil
private val others = FatLazy[List[To]] {
DB.use(owner.connectionIdentifier) {
conn =>
val query = "SELECT DISTINCT "+otherSingleton.dbTableName+".* FROM "+otherSingleton.dbTableName+","+
through.dbTableName+" WHERE "+
otherSingleton.dbTableName+"."+otherSingleton.indexedField(otherSingleton.asInstanceOf[To]).open_!.dbColumnName+" = "+
through.dbTableName+"."+throughToField.dbColumnName+" AND "+
through.dbTableName+"."+throughFromField.dbColumnName+" = ?"
DB.prepareStatement(query, conn) {
st =>
owner.getSingleton.indexedField(owner).map {
indVal =>
st.setObject(1, indVal.jdbcFriendly, indVal.targetSQLType)
DB.exec(st) {
rs =>
otherSingleton.createInstances(owner.connectionIdentifier, rs, Empty, Empty)
}
} openOr Nil
}
}
}
def apply(): List[To] = others.get
def get: List[To] = this()
def reset = others.reset
def set(what: Seq[ThroughType]): Seq[ThroughType] = {
theSetList = what
theSetList
}
override def beforeDelete {
through.findAll(By(throughFromField, owner.primaryKeyField)).foreach {
toDelete => toDelete.delete_!
}
}
override def afterUpdate {
val current = through.findAll(By(throughFromField,owner.primaryKeyField))
val newKeys = new HashSet[ThroughType];
theSetList.foreach(i => newKeys += i)
val toDelete = current.filter(c => !newKeys.contains(throughToField.actualField(c).is))
toDelete.foreach(_.delete_!)
val oldKeys = new HashSet[ThroughType];
current.foreach(i => oldKeys += throughToField.actualField(i))
theSetList.toList.removeDuplicates.filter(i => !oldKeys.contains(i)).foreach {
i =>
val toCreate = through.createInstance
throughFromField.actualField(toCreate).set(owner.primaryKeyField)
throughToField.actualField(toCreate).set(i)
toCreate.save
}
theSetList = Nil
others.reset
super.afterUpdate
}
override def afterCreate {
theSetList.toList.removeDuplicates.foreach {
i =>
val toCreate = through.createInstance
throughFromField.actualField(toCreate)(owner.primaryKeyField)
throughToField.actualField(toCreate)(i)
toCreate.save
}
theSetList = Nil
others.reset
super.afterCreate
}
}