From 657d842238974d4ebfc2ffa098c5cdd15f505bd6 Mon Sep 17 00:00:00 2001 From: Benjy Date: Wed, 10 Oct 2012 18:21:44 -0700 Subject: [PATCH] Analysis.groupBy implementation. --- compile/inc/APIs.scala | 8 ++++++-- compile/inc/Analysis.scala | 31 ++++++++++++++++++++++++++++++- compile/inc/Relations.scala | 20 ++++++++++++++++++++ compile/inc/SourceInfo.scala | 2 ++ compile/inc/Stamp.scala | 17 +++++++++++++++++ util/relation/Relation.scala | 11 ++++++++--- 6 files changed, 83 insertions(+), 6 deletions(-) diff --git a/compile/inc/APIs.scala b/compile/inc/APIs.scala index 892fc62aa..42fe2df23 100644 --- a/compile/inc/APIs.scala +++ b/compile/inc/APIs.scala @@ -24,10 +24,11 @@ trait APIs def markInternalSource(src: File, api: Source): APIs def markExternalAPI(ext: String, api: Source): APIs - + def removeInternal(remove: Iterable[File]): APIs def filterExt(keep: String => Boolean): APIs - + def groupBy[K](internal: (File) => K, keepExternal: Map[K, String => Boolean]): Map[K, APIs] + def internal: Map[File, Source] def external: Map[String, Source] } @@ -58,6 +59,9 @@ private class MAPIs(val internal: Map[File, Source], val external: Map[String, S def removeInternal(remove: Iterable[File]): APIs = new MAPIs(internal -- remove, external) def filterExt(keep: String => Boolean): APIs = new MAPIs(internal, external.filterKeys(keep)) + def groupBy[K](f: (File) => K, keepExternal: Map[K, String => Boolean]): Map[K, APIs] = + internal.groupBy(item => f(item._1)) map { group => (group._1, new MAPIs(group._2, external).filterExt(keepExternal.getOrElse(group._1, _ => false)))} + def internalAPI(src: File) = getAPI(internal, src) def externalAPI(ext: String) = getAPI(external, ext) diff --git a/compile/inc/Analysis.scala b/compile/inc/Analysis.scala index 7babe26ff..c3cf3f1ab 100644 --- a/compile/inc/Analysis.scala +++ b/compile/inc/Analysis.scala @@ -23,6 +23,8 @@ trait Analysis def addExternalDep(src: File, dep: String, api: Source): Analysis def addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis + def groupBy[K](f: (File => K)): Map[K, Analysis] + override lazy val toString = Analysis.summary(this) } @@ -61,7 +63,7 @@ private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relat def -- (sources: Iterable[File]): Analysis = { val newRelations = relations -- sources - def keep[T](f: (Relations, T) => Set[_]): T => Boolean = file => !f(newRelations, file).isEmpty + def keep[T](f: (Relations, T) => Set[_]): T => Boolean = keepFor(newRelations)(f) val newAPIs = apis.removeInternal(sources).filterExt( keep(_ usesExternal _) ) val newStamps = stamps.filter( keep(_ produced _), sources, keep(_ usesBinary _)) @@ -81,4 +83,31 @@ private class MAnalysis(val stamps: Stamps, val apis: APIs, val relations: Relat def addProduct(src: File, product: File, stamp: Stamp, name: String): Analysis = copy( stamps.markProduct(product, stamp), apis, relations.addProduct(src, product, name), infos ) + + def groupBy[K](f: File => K): Map[K, Analysis] = + { + def outerJoin(stampsMap: Map[K, Stamps], apisMap: Map[K, APIs], relationsMap: Map[K, Relations], infosMap: Map[K, SourceInfos]): Map[K, Analysis] = + { + def kAnalysis(k: K): Analysis = + new MAnalysis( + stampsMap.getOrElse(k, Stamps.empty), + apisMap.getOrElse(k, APIs.empty), + relationsMap.getOrElse(k, Relations.empty), + infosMap.getOrElse(k, SourceInfos.empty) + ) + val keys = (stampsMap.keySet ++ apisMap.keySet ++ relationsMap.keySet ++ infosMap.keySet).toList + Map( keys map( (k: K) => (k, kAnalysis(k)) ) :_*) + } + + val relationsMap: Map[K, Relations] = relations.groupBy(f) + def keepMap[T](f: (Relations, T) => Set[_]): Map[K, T => Boolean] = + relationsMap map { item => (item._1, keepFor(item._2)(f) ) } + + val keepExternal: Map[K, String => Boolean] = keepMap(_ usesExternal _) + val keepProduced: Map[K, File => Boolean] = keepMap(_ produced _) + val keepBinary: Map[K, File => Boolean] = keepMap(_ usesBinary _) + outerJoin(stamps.groupBy(keepProduced, f, keepBinary), apis.groupBy(f, keepExternal), relationsMap, infos.groupBy(f)) + } + + private def keepFor[T](newRelations: Relations)(f: (Relations, T) => Set[_]): T => Boolean = file => !f(newRelations, file).isEmpty } diff --git a/compile/inc/Relations.scala b/compile/inc/Relations.scala index d9c6dddc8..a5f0fcf15 100644 --- a/compile/inc/Relations.scala +++ b/compile/inc/Relations.scala @@ -39,6 +39,7 @@ trait Relations def ++ (o: Relations): Relations def -- (sources: Iterable[File]): Relations + def groupBy[K](f: (File => K)): Map[K, Relations] def srcProd: Relation[File, File] def binaryDep: Relation[File, File] @@ -113,6 +114,25 @@ private class MRelations(val srcProd: Relation[File, File], val binaryDep: Relat def -- (sources: Iterable[File]) = new MRelations(srcProd -- sources, binaryDep -- sources, internalSrcDep -- sources, externalDep -- sources, classes -- sources) + def groupBy[K](f: File => K): Map[K, Relations] = + { + type MapRel[T] = Map[K, Relation[File, T]] + def outerJoin(srcProdMap: MapRel[File], binaryDepMap: MapRel[File], internalSrcDepMap: MapRel[File], + externalDepMap: MapRel[String], classesMap: MapRel[String]): Map[K, Relations] = + { + def kRelations(k: K): Relations = { + def get[T](m: Map[K, Relation[File, T]]) = m.getOrElse(k, Relation.empty) + new MRelations( get(srcProdMap), get(binaryDepMap), get(internalSrcDepMap), get(externalDepMap), get(classesMap) ) + } + val keys = (srcProdMap.keySet ++ binaryDepMap.keySet ++ internalSrcDepMap.keySet ++ externalDepMap.keySet ++ classesMap.keySet).toList + Map( keys.map( (k: K) => (k, kRelations(k)) ) : _*) + } + + def f1[B](item: (File, B)): K = f(item._1) + outerJoin(srcProd.groupBy(f1), binaryDep.groupBy(f1), internalSrcDep.groupBy(f1), externalDep.groupBy(f1), classes.groupBy(f1)) + } + + /** Making large Relations a little readable. */ private val userDir = sys.props("user.dir").stripSuffix("/") + "/" private def nocwd(s: String) = s stripPrefix userDir diff --git a/compile/inc/SourceInfo.scala b/compile/inc/SourceInfo.scala index fc10c041d..c58396cbd 100644 --- a/compile/inc/SourceInfo.scala +++ b/compile/inc/SourceInfo.scala @@ -15,6 +15,7 @@ trait SourceInfos def ++(o: SourceInfos): SourceInfos def add(file: File, info: SourceInfo): SourceInfos def --(files: Iterable[File]): SourceInfos + def groupBy[K](f: (File) => K): Map[K, SourceInfos] def get(file: File): SourceInfo def allInfos: Map[File, SourceInfo] } @@ -31,6 +32,7 @@ private final class MSourceInfos(val allInfos: Map[File, SourceInfo]) extends So { def ++(o: SourceInfos) = new MSourceInfos(allInfos ++ o.allInfos) def --(sources: Iterable[File]) = new MSourceInfos(allInfos -- sources) + def groupBy[K](f: (File) => K): Map[K, SourceInfos] = allInfos.groupBy(item => f(item._1)) map { group => (group._1, new MSourceInfos(group._2)) } def add(file: File, info: SourceInfo) = new MSourceInfos(allInfos + ((file, info))) def get(file:File) = allInfos.getOrElse(file, SourceInfos.emptyInfo) } diff --git a/compile/inc/Stamp.scala b/compile/inc/Stamp.scala index 755707980..c6989a229 100644 --- a/compile/inc/Stamp.scala +++ b/compile/inc/Stamp.scala @@ -38,6 +38,7 @@ trait Stamps extends ReadStamps def filter(prod: File => Boolean, removeSources: Iterable[File], bin: File => Boolean): Stamps def ++ (o: Stamps): Stamps + def groupBy[K](prod: Map[K, File => Boolean], sourcesGrouping: File => K, bin: Map[K, File => Boolean]): Map[K, Stamps] } sealed trait Stamp @@ -111,6 +112,22 @@ private class MStamps(val products: Map[File, Stamp], val sources: Map[File, Sta def filter(prod: File => Boolean, removeSources: Iterable[File], bin: File => Boolean): Stamps = new MStamps(products.filterKeys(prod), sources -- removeSources, binaries.filterKeys(bin), classNames.filterKeys(bin)) + def groupBy[K](prod: Map[K, File => Boolean], sourcesGrouping: File => K, bin: Map[K, File => Boolean]): Map[K, Stamps] = + { + val sourcesMap: Map[K, Map[File, Stamp]] = sources.groupBy(item => sourcesGrouping(item._1)) + + val constFalse = (f: File) => false + def kStamps(k: K): Stamps = new MStamps( + products.filterKeys(prod.getOrElse(k, constFalse)), + sourcesMap.getOrElse(k, Map.empty[File,Stamp]), + binaries.filterKeys(bin.getOrElse(k, constFalse)), + classNames.filterKeys(bin.getOrElse(k, constFalse)) + ) + + val keys = (prod.keySet ++ sourcesMap.keySet ++ bin.keySet).toList + Map( keys.map( (k: K) => (k, kStamps(k)) ) : _*) + } + def product(prod: File) = getStamp(products, prod) def internalSource(src: File) = getStamp(sources, src) def binary(bin: File) = getStamp(binaries, bin) diff --git a/util/relation/Relation.scala b/util/relation/Relation.scala index 0128333bd..04efe3e3e 100644 --- a/util/relation/Relation.scala +++ b/util/relation/Relation.scala @@ -72,10 +72,13 @@ trait Relation[A,B] def contains(a: A, b: B): Boolean /** Returns a relation with only pairs (a,b) for which f(a,b) is true.*/ def filter(f: (A,B) => Boolean): Relation[A,B] - + + /** Partitions this relation into a map of relations according to some discriminator function. */ + def groupBy[K](f: ((A,B)) => K): Map[K, Relation[A,B]] + /** Returns all pairs in this relation.*/ def all: Traversable[(A,B)] - + def forwardMap: Map[A, Set[B]] def reverseMap: Map[B, Set[A]] } @@ -93,7 +96,7 @@ private final class MRelation[A,B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) ext def size = fwd.size def all: Traversable[(A,B)] = fwd.iterator.flatMap { case (a, bs) => bs.iterator.map( b => (a,b) ) }.toTraversable - + def +(pair: (A,B)) = this + (pair._1, Set(pair._2)) def +(from: A, to: B) = this + (from, to :: Nil) def +(from: A, to: Traversable[B]) = @@ -116,6 +119,8 @@ private final class MRelation[A,B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) ext def filter(f: (A,B) => Boolean): Relation[A,B] = Relation.empty[A,B] ++ all.filter(f.tupled) + def groupBy[K](f: ((A,B)) => K): Map[K, Relation[A,B]] = all.groupBy(f) mapValues { Relation.empty[A,B] ++ _ } + def contains(a: A, b: B): Boolean = forward(a)(b) override def toString = all.map { case (a,b) => a + " -> " + b }.mkString("Relation [", ", ", "]")