diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2017-10-29 12:50:57 -0700 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2017-10-29 12:50:57 -0700 |
commit | cdf5461a5fc9999b3fe496af1e4e8dfbc3e8f9c0 (patch) | |
tree | b50bc807159c35e3506fbf84dd89ee327f4c53df /src | |
parent | 59ccaaa286fcc01fce9fd53415c526ef22b97e72 (diff) | |
download | mill-cdf5461a5fc9999b3fe496af1e4e8dfbc3e8f9c0.tar.gz mill-cdf5461a5fc9999b3fe496af1e4e8dfbc3e8f9c0.tar.bz2 mill-cdf5461a5fc9999b3fe496af1e4e8dfbc3e8f9c0.zip |
Split up `Utils.scala` and move the pieces into the `utils/` package
Diffstat (limited to 'src')
-rw-r--r-- | src/main/scala/forge/Evaluator.scala | 23 | ||||
-rw-r--r-- | src/main/scala/forge/Target.scala | 1 | ||||
-rw-r--r-- | src/main/scala/forge/Util.scala | 151 | ||||
-rw-r--r-- | src/main/scala/forge/util/MultiBiMap.scala | 42 | ||||
-rw-r--r-- | src/main/scala/forge/util/OSet.scala | 97 | ||||
-rw-r--r-- | src/main/scala/forge/util/PathRef.scala | 26 | ||||
-rw-r--r-- | src/test/scala/forge/EvaluationTests.scala | 1 | ||||
-rw-r--r-- | src/test/scala/forge/GraphTests.scala | 1 | ||||
-rw-r--r-- | src/test/scala/forge/Main.scala | 2 | ||||
-rw-r--r-- | src/test/scala/forge/TestUtil.scala | 1 |
10 files changed, 178 insertions, 167 deletions
diff --git a/src/main/scala/forge/Evaluator.scala b/src/main/scala/forge/Evaluator.scala index 888f6639..e6366f42 100644 --- a/src/main/scala/forge/Evaluator.scala +++ b/src/main/scala/forge/Evaluator.scala @@ -1,10 +1,11 @@ package forge -import play.api.libs.json.{JsValue, Json, Reads} +import play.api.libs.json.{JsValue, Json} import scala.collection.mutable import ammonite.ops._ +import forge.util.{MultiBiMap, OSet} class Evaluator(workspacePath: Path, labeling: Map[Target[_], Seq[String]]){ @@ -16,7 +17,7 @@ class Evaluator(workspacePath: Path, labeling ) - val evaluated = new MutableOSet[Target[_]] + val evaluated = new OSet.Mutable[Target[_]] val results = mutable.LinkedHashMap.empty[Target[_], Any] for (groupIndex <- sortedGroups.keys()){ @@ -52,7 +53,7 @@ class Evaluator(workspacePath: Path, val metadataPath = targetDestPath / up / (targetDestPath.last + ".forge.json") val cached = for{ - json <- util.Try(Json.parse(read.getInputStream(metadataPath))).toOption + json <- scala.util.Try(Json.parse(read.getInputStream(metadataPath))).toOption (cachedHash, terminalResults) <- Json.fromJson[(Int, Seq[JsValue])](json).asOpt if cachedHash == inputsHash } yield terminalResults @@ -126,7 +127,7 @@ object Evaluator{ def groupAroundNamedTargets(topoSortedTargets: TopoSorted, labeling: Map[Target[_], Seq[String]]): MultiBiMap[Int, Target[_]] = { - val grouping = new MutableMultiBiMap[Int, Target[_]]() + val grouping = new MultiBiMap.Mutable[Int, Target[_]]() var groupCount = 0 @@ -151,7 +152,7 @@ object Evaluator{ } val targetOrdering = topoSortedTargets.values.items.zipWithIndex.toMap - val output = new MutableMultiBiMap[Int, Target[_]] + val output = new MultiBiMap.Mutable[Int, Target[_]] // Sort groups amongst themselves, and sort the contents of each group // before aggregating it into the final output @@ -161,22 +162,12 @@ object Evaluator{ output } - def checkTopological(targets: OSet[Target[_]]) = { - val seen = mutable.Set.empty[Target[_]] - for(t <- targets.items.reverseIterator){ - seen.add(t) - for(upstream <- t.inputs){ - assert(!seen(upstream)) - } - } - } - /** * Takes the given targets, finds all the targets they transitively depend * on, and sort them topologically. Fails if there are dependency cycles */ def topoSortedTransitiveTargets(sourceTargets: OSet[Target[_]]): TopoSorted = { - val transitiveTargets = new MutableOSet[Target[_]] + val transitiveTargets = new OSet.Mutable[Target[_]] def rec(t: Target[_]): Unit = { if (transitiveTargets.contains(t)) () // do nothing else { diff --git a/src/main/scala/forge/Target.scala b/src/main/scala/forge/Target.scala index fe975638..3b4ba2a8 100644 --- a/src/main/scala/forge/Target.scala +++ b/src/main/scala/forge/Target.scala @@ -2,6 +2,7 @@ package forge import ammonite.ops.{ls, mkdir} +import forge.util.PathRef import play.api.libs.json.{Format, JsValue, Json} abstract class Target[T](implicit formatter: Format[T]) extends Target.Ops[T]{ /** diff --git a/src/main/scala/forge/Util.scala b/src/main/scala/forge/Util.scala deleted file mode 100644 index 9a225f8b..00000000 --- a/src/main/scala/forge/Util.scala +++ /dev/null @@ -1,151 +0,0 @@ -package forge - - -import ammonite.ops.ls -import play.api.libs.json.{Format, Json} - -import scala.collection.mutable - -object PathRef{ - implicit def jsonFormatter: Format[PathRef] = Json.format -} -case class PathRef(path: ammonite.ops.Path){ - override def hashCode() = { - if (!path.isDir) path.hashCode() + path.mtime.toMillis.toInt - else ls.rec.iter(path) - .filter(_.isFile) - .map(x => x.toString.hashCode + x.mtime.toMillis) - .sum - .toInt - } -} - - -trait MultiBiMap[K, V]{ - def containsValue(v: V): Boolean - def lookupKey(k: K): OSet[V] - def lookupValue(v: V): K - def lookupValueOpt(v: V): Option[K] - def add(k: K, v: V): Unit - def removeAll(k: K): OSet[V] - def addAll(k: K, vs: TraversableOnce[V]): Unit - def keys(): Iterator[K] - def values(): Iterator[OSet[V]] -} -class MutableMultiBiMap[K, V]() extends MultiBiMap[K, V]{ - private[this] val valueToKey = mutable.LinkedHashMap.empty[V, K] - private[this] val keyToValues = mutable.LinkedHashMap.empty[K, MutableOSet[V]] - def containsValue(v: V) = valueToKey.contains(v) - def lookupKey(k: K) = keyToValues(k) - def lookupValue(v: V) = valueToKey(v) - def lookupValueOpt(v: V) = valueToKey.get(v) - def add(k: K, v: V): Unit = { - valueToKey(v) = k - keyToValues.getOrElseUpdate(k, new MutableOSet[V]()).append(v) - } - def removeAll(k: K): OSet[V] = keyToValues.get(k) match { - case None => OSet() - case Some(vs) => - vs.foreach(valueToKey.remove) - - keyToValues.remove(k) - vs - } - def addAll(k: K, vs: TraversableOnce[V]): Unit = vs.foreach(this.add(k, _)) - - def keys() = keyToValues.keysIterator - - def values() = keyToValues.valuesIterator -} - -/** - * A collection with enforced uniqueness, fast contains and deterministic - * ordering. Raises an exception if a duplicate is found; call - * `toSeq.distinct` if you explicitly want to make it swallow duplicates - */ -trait OSet[V] extends TraversableOnce[V]{ - def contains(v: V): Boolean - def items: IndexedSeq[V] - def flatMap[T](f: V => TraversableOnce[T]): OSet[T] - def map[T](f: V => T): OSet[T] - def filter(f: V => Boolean): OSet[V] - def collect[T](f: PartialFunction[V, T]): OSet[T] - def zipWithIndex: OSet[(V, Int)] - def reverse: OSet[V] -} - -object OSet{ - def apply[V](items: V*) = from(items) - - def from[V](items: TraversableOnce[V]): OSet[V] = { - val set = new MutableOSet[V]() - items.foreach(set.append) - set - } -} - -class MutableOSet[V]() extends OSet[V]{ - - private[this] val set0 = mutable.LinkedHashSet.empty[V] - def contains(v: V) = set0.contains(v) - def append(v: V) = if (!contains(v)){ - set0.add(v) - - }else { - throw new Exception("Duplicated item inserted into OrderedSet: " + v) - } - def appendAll(vs: Seq[V]) = vs.foreach(append) - def items: IndexedSeq[V] = set0.toIndexedSeq - def set: collection.Set[V] = set0 - - def map[T](f: V => T): OSet[T] = { - val output = new MutableOSet[T] - for(i <- items) output.append(f(i)) - output - } - def flatMap[T](f: V => TraversableOnce[T]): OSet[T] = { - val output = new MutableOSet[T] - for(i <- items) for(i0 <- f(i)) output.append(i0) - output - } - def filter(f: V => Boolean): OSet[V] = { - val output = new MutableOSet[V] - for(i <- items) if (f(i)) output.append(i) - output - } - - def collect[T](f: PartialFunction[V, T]) = this.filter(f.isDefinedAt).map(x => f(x)) - - def zipWithIndex = { - var i = 0 - this.map{ x => - i += 1 - (x, i-1) - } - } - - def reverse = OSet.from(items.reverseIterator) - - // Members declared in scala.collection.GenTraversableOnce - def isTraversableAgain: Boolean = items.isTraversableAgain - def toIterator: Iterator[V] = items.toIterator - def toStream: Stream[V] = items.toStream - - // Members declared in scala.collection.TraversableOnce - def copyToArray[B >: V](xs: Array[B],start: Int,len: Int): Unit = items.copyToArray(xs, start, len) - def exists(p: V => Boolean): Boolean = items.exists(p) - def find(p: V => Boolean): Option[V] = items.find(p) - def forall(p: V => Boolean): Boolean = items.forall(p) - def foreach[U](f: V => U): Unit = items.foreach(f) - def hasDefiniteSize: Boolean = items.hasDefiniteSize - def isEmpty: Boolean = items.isEmpty - def seq: scala.collection.TraversableOnce[V] = items - def toTraversable: Traversable[V] = items - - override def hashCode() = items.hashCode() - override def equals(other: Any) = other match{ - case s: OSet[_] => items.equals(s.items) - case _ => super.equals(other) - } - override def toString = items.mkString("OSet(", ", ", ")") -}
\ No newline at end of file diff --git a/src/main/scala/forge/util/MultiBiMap.scala b/src/main/scala/forge/util/MultiBiMap.scala new file mode 100644 index 00000000..3eff686e --- /dev/null +++ b/src/main/scala/forge/util/MultiBiMap.scala @@ -0,0 +1,42 @@ +package forge.util + +import scala.collection.mutable + +trait MultiBiMap[K, V]{ + def containsValue(v: V): Boolean + def lookupKey(k: K): OSet[V] + def lookupValue(v: V): K + def lookupValueOpt(v: V): Option[K] + def add(k: K, v: V): Unit + def removeAll(k: K): OSet[V] + def addAll(k: K, vs: TraversableOnce[V]): Unit + def keys(): Iterator[K] + def values(): Iterator[OSet[V]] +} +object MultiBiMap{ + class Mutable[K, V]() extends MultiBiMap[K, V]{ + private[this] val valueToKey = mutable.LinkedHashMap.empty[V, K] + private[this] val keyToValues = mutable.LinkedHashMap.empty[K, OSet.Mutable[V]] + def containsValue(v: V) = valueToKey.contains(v) + def lookupKey(k: K) = keyToValues(k) + def lookupValue(v: V) = valueToKey(v) + def lookupValueOpt(v: V) = valueToKey.get(v) + def add(k: K, v: V): Unit = { + valueToKey(v) = k + keyToValues.getOrElseUpdate(k, new OSet.Mutable[V]()).append(v) + } + def removeAll(k: K): OSet[V] = keyToValues.get(k) match { + case None => OSet() + case Some(vs) => + vs.foreach(valueToKey.remove) + + keyToValues.remove(k) + vs + } + def addAll(k: K, vs: TraversableOnce[V]): Unit = vs.foreach(this.add(k, _)) + + def keys() = keyToValues.keysIterator + + def values() = keyToValues.valuesIterator + } +} diff --git a/src/main/scala/forge/util/OSet.scala b/src/main/scala/forge/util/OSet.scala new file mode 100644 index 00000000..34e297bd --- /dev/null +++ b/src/main/scala/forge/util/OSet.scala @@ -0,0 +1,97 @@ +package forge.util + + +import scala.collection.mutable + +/** + * A collection with enforced uniqueness, fast contains and deterministic + * ordering. Raises an exception if a duplicate is found; call + * `toSeq.distinct` if you explicitly want to make it swallow duplicates + */ +trait OSet[V] extends TraversableOnce[V]{ + def contains(v: V): Boolean + def items: IndexedSeq[V] + def flatMap[T](f: V => TraversableOnce[T]): OSet[T] + def map[T](f: V => T): OSet[T] + def filter(f: V => Boolean): OSet[V] + def collect[T](f: PartialFunction[V, T]): OSet[T] + def zipWithIndex: OSet[(V, Int)] + def reverse: OSet[V] +} + +object OSet{ + def apply[V](items: V*) = from(items) + + def from[V](items: TraversableOnce[V]): OSet[V] = { + val set = new OSet.Mutable[V]() + items.foreach(set.append) + set + } + + + class Mutable[V]() extends OSet[V]{ + + private[this] val set0 = mutable.LinkedHashSet.empty[V] + def contains(v: V) = set0.contains(v) + def append(v: V) = if (!contains(v)){ + set0.add(v) + + }else { + throw new Exception("Duplicated item inserted into OrderedSet: " + v) + } + def appendAll(vs: Seq[V]) = vs.foreach(append) + def items: IndexedSeq[V] = set0.toIndexedSeq + def set: collection.Set[V] = set0 + + def map[T](f: V => T): OSet[T] = { + val output = new OSet.Mutable[T] + for(i <- items) output.append(f(i)) + output + } + def flatMap[T](f: V => TraversableOnce[T]): OSet[T] = { + val output = new OSet.Mutable[T] + for(i <- items) for(i0 <- f(i)) output.append(i0) + output + } + def filter(f: V => Boolean): OSet[V] = { + val output = new OSet.Mutable[V] + for(i <- items) if (f(i)) output.append(i) + output + } + + def collect[T](f: PartialFunction[V, T]) = this.filter(f.isDefinedAt).map(x => f(x)) + + def zipWithIndex = { + var i = 0 + this.map{ x => + i += 1 + (x, i-1) + } + } + + def reverse = OSet.from(items.reverseIterator) + + // Members declared in scala.collection.GenTraversableOnce + def isTraversableAgain: Boolean = items.isTraversableAgain + def toIterator: Iterator[V] = items.toIterator + def toStream: Stream[V] = items.toStream + + // Members declared in scala.collection.TraversableOnce + def copyToArray[B >: V](xs: Array[B],start: Int,len: Int): Unit = items.copyToArray(xs, start, len) + def exists(p: V => Boolean): Boolean = items.exists(p) + def find(p: V => Boolean): Option[V] = items.find(p) + def forall(p: V => Boolean): Boolean = items.forall(p) + def foreach[U](f: V => U): Unit = items.foreach(f) + def hasDefiniteSize: Boolean = items.hasDefiniteSize + def isEmpty: Boolean = items.isEmpty + def seq: scala.collection.TraversableOnce[V] = items + def toTraversable: Traversable[V] = items + + override def hashCode() = items.hashCode() + override def equals(other: Any) = other match{ + case s: OSet[_] => items.equals(s.items) + case _ => super.equals(other) + } + override def toString = items.mkString("OSet(", ", ", ")") + } +} diff --git a/src/main/scala/forge/util/PathRef.scala b/src/main/scala/forge/util/PathRef.scala new file mode 100644 index 00000000..3621d0b6 --- /dev/null +++ b/src/main/scala/forge/util/PathRef.scala @@ -0,0 +1,26 @@ +package forge +package util + +import ammonite.ops.ls +import play.api.libs.json.{Format, Json} + + +/** + * A wrapper around `ammonite.ops.Path` that calculates it's hashcode based + * on the contents of the filesystem underneath it. Used to ensure filesystem + * changes can bust caches which are keyed off hashcodes. + */ +case class PathRef(path: ammonite.ops.Path){ + override def hashCode() = { + if (!path.isDir) path.hashCode() + path.mtime.toMillis.toInt + else ls.rec.iter(path) + .filter(_.isFile) + .map(x => x.toString.hashCode + x.mtime.toMillis) + .sum + .toInt + } +} + +object PathRef{ + implicit def jsonFormatter: Format[PathRef] = Json.format +}
\ No newline at end of file diff --git a/src/test/scala/forge/EvaluationTests.scala b/src/test/scala/forge/EvaluationTests.scala index 2c18be2f..313ceaaa 100644 --- a/src/test/scala/forge/EvaluationTests.scala +++ b/src/test/scala/forge/EvaluationTests.scala @@ -1,6 +1,7 @@ package forge +import forge.util.OSet import utest._ import utest.framework.TestPath diff --git a/src/test/scala/forge/GraphTests.scala b/src/test/scala/forge/GraphTests.scala index 5cfea75d..d197fb5f 100644 --- a/src/test/scala/forge/GraphTests.scala +++ b/src/test/scala/forge/GraphTests.scala @@ -2,6 +2,7 @@ package forge import utest._ import Target.test +import forge.util.OSet object GraphTests extends TestSuite{ diff --git a/src/test/scala/forge/Main.scala b/src/test/scala/forge/Main.scala index 2ef6083b..6f281f54 100644 --- a/src/test/scala/forge/Main.scala +++ b/src/test/scala/forge/Main.scala @@ -1,8 +1,10 @@ package forge import java.io.FileOutputStream import java.util.jar.JarEntry + import collection.JavaConverters._ import ammonite.ops._ +import forge.util.{OSet, PathRef} object Main{ val sourceRoot = Target.path(pwd / 'src / 'test / 'resources / 'example / 'src) val resourceRoot = Target.path(pwd / 'src / 'test / 'resources / 'example / 'resources) diff --git a/src/test/scala/forge/TestUtil.scala b/src/test/scala/forge/TestUtil.scala index 8d944c65..8405c87c 100644 --- a/src/test/scala/forge/TestUtil.scala +++ b/src/test/scala/forge/TestUtil.scala @@ -1,5 +1,6 @@ package forge +import forge.util.OSet import utest.assert import scala.collection.mutable |