only derive settings when all dependencies are defined

This commit is contained in:
Mark Harrah 2013-05-08 12:56:59 -04:00
parent 6c0f47ef48
commit 4bb9633d6c
1 changed files with 75 additions and 27 deletions

View File

@ -105,33 +105,6 @@ trait Init[Scope]
def apply[T](k: ScopedKey[T]): ScopedKey[T] = k.copy(scope = f(k.scope))
}
private[this] def derive(init: Seq[Setting[_]]): Seq[Setting[_]] =
{
import collection.mutable
val (derived, defs) = Util.separate[Setting[_],DerivedSetting[_],Setting[_]](init) { case d: DerivedSetting[_] => Left(d); case s => Right(s) }
final class Derived[T](val setting: DerivedSetting[T]) { val inScopes = new mutable.HashSet[Scope] }
val derivs = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived[_]]]
for(s <- derived; d <- s.dependencies)
derivs.getOrElseUpdate(d.key, new mutable.ListBuffer) += new Derived(s)
val deriveFor = (sk: ScopedKey[_]) => {
val derivedForKey: List[Derived[_]] = derivs.get(sk.key).toList.flatten
derivedForKey.filter(d => d.inScopes.add(sk.scope) && d.setting.filter(sk.scope)).map(_.setting setScope sk.scope)
}
val processed = new mutable.HashSet[ScopedKey[_]]
val out = new mutable.ListBuffer[Setting[_]]
def process(rem: List[Setting[_]]): Unit = rem match {
case s :: ss =>
val sk = s.key
val ds = if(processed.add(sk)) deriveFor(sk) else Nil
out ++= ds
process(ds ::: ss)
case Nil =>
}
process(defs.toList)
out.toList ++ defs
}
private[this] def applyDefaults(ss: Seq[Setting[_]]): Seq[Setting[_]] =
{
val (defaults, others) = Util.separate[Setting[_], DefaultSetting[_], Setting[_]](ss) { case u: DefaultSetting[_] => Left(u); case s => Right(s) }
@ -283,6 +256,81 @@ trait Init[Scope]
}
}
private[this] def derive(init: Seq[Setting[_]])(implicit delegates: Scope => Seq[Scope]): Seq[Setting[_]] =
{
import collection.mutable
final class Derived(val setting: DerivedSetting[_]) {
val dependencies = setting.dependencies.map(_.key)
val inScopes = new mutable.HashSet[Scope]
}
final class Deriveds(val key: AttributeKey[_], val settings: mutable.ListBuffer[Derived]) {
def dependencies = settings.flatMap(_.dependencies)
override def toString = "Derived settings for " + key.label
}
// separate `derived` settings from normal settings (`defs`)
val (derived, defs) = Util.separate[Setting[_],Derived,Setting[_]](init) { case d: DerivedSetting[_] => Left(new Derived(d)); case s => Right(s) }
// group derived settings by the key they define
val derivsByDef = new mutable.HashMap[AttributeKey[_], Deriveds]
for(s <- derived) {
val key = s.setting.key.key
derivsByDef.getOrElseUpdate(key, new Deriveds(key, new mutable.ListBuffer)).settings += s
}
// sort derived settings so that dependencies come first
// this is necessary when verifying that a derived setting's dependencies exist
val ddeps = (d: Deriveds) => d.dependencies.flatMap(derivsByDef.get)
val sortedDerivs = Dag.topologicalSort(derivsByDef.values)(ddeps)
// index derived settings by triggering key. This maps a key to the list of settings potentially derived from it.
val derivedBy = new mutable.HashMap[AttributeKey[_], mutable.ListBuffer[Derived]]
for(s <- derived; d <- s.dependencies)
derivedBy.getOrElseUpdate(d, new mutable.ListBuffer) += s
// set of defined scoped keys, used to ensure a derived setting is only added if all dependencies are present
val defined = new mutable.HashSet[ScopedKey[_]]
def addDefs(ss: Seq[Setting[_]]) { for(s <- ss) defined += s.key }
addDefs(defs)
// true iff the scoped key is in `defined`, taking delegation into account
def isDefined(key: AttributeKey[_], scope: Scope) =
delegates(scope).exists(s => defined.contains(ScopedKey(s, key)))
// true iff all dependencies of derived setting `d` have a value (potentially via delegation) in `scope`
def allDepsDefined(d: Derived, scope: Scope): Boolean = d.dependencies.forall(dep => isDefined(dep, scope))
// list of injectable derived settings for `sk`. A derived setting is injectable if:
// 1. it has not been previously injected into this scope
// 2. it applies to this scope (as determined by its `filter`)
// 3. all of its dependencies are defined for that scope (allowing for delegation)
val deriveFor = (sk: ScopedKey[_]) => {
val derivedForKey: List[Derived] = derivedBy.get(sk.key).toList.flatten
val scope = sk.scope
val filtered = derivedForKey.filter(d => d.inScopes.add(scope) && d.setting.filter(scope) && allDepsDefined(d, scope))
val scoped = filtered.map(_.setting setScope scope)
addDefs(scoped)
scoped
}
val processed = new mutable.HashSet[ScopedKey[_]]
// valid derived settings to be added before normal settings
val out = new mutable.ListBuffer[Setting[_]]
// derives settings, transitively so that a derived setting can trigger another
def process(rem: List[Setting[_]]): Unit = rem match {
case s :: ss =>
val sk = s.key
val ds = if(processed.add(sk)) deriveFor(sk) else Nil
out ++= ds
process(ds ::: ss)
case Nil =>
}
process(defs.toList)
out.toList ++ defs
}
sealed trait Initialize[T]
{
def dependencies: Seq[ScopedKey[_]]