package orc.compile

import java.util.WeakHashMap
import scala.ref.WeakReference
import scala.ref.ReferenceQueue

class AnalysisCache {
  private val cache = collection.mutable.HashMap[AnalysisCacheKey, Any]()

  def get[P <: Product, T](runner: AnalysisRunner[P, T])(params: P): T = {
    incrClean()
    cache.getOrElseUpdate(new AnalysisCacheKey(runner, params), runner.compute(this)(params)).asInstanceOf[T]
  }

  def clean(): Unit = {
    cache.clear()
  }

  private class AssociatedWeakRef(v: AnyRef, val key: AnalysisCacheKey, q: ReferenceQueue[AnyRef]) extends WeakReference[AnyRef](v, q)

  private class AnalysisCacheKey(val analysis: AnalysisRunner[_, _], params1: Product) {
    val params = params1.productIterator.map(v => new AssociatedWeakRef(v.asInstanceOf[AnyRef], this, queue)).toSeq
    override def hashCode() = analysis.hashCode() + params.map(_().hashCode()).sum
    override def equals(o: Any) = o match {
      case o: AnalysisCacheKey => {
        analysis == o.analysis &&
          (params zip o.params).forall({ p =>
            p._1.get == p._2.get
          })
      }
      case _ => false
    }
  }

  val queue = new ReferenceQueue[AnyRef]()

  def incrClean(n: Int = 10): Unit = {
    if (n > 0) {
      queue.poll match {
        case Some(k: AssociatedWeakRef) =>
          cache -= k.key
          incrClean(n - 1)
        case Some(_) =>
          throw new AssertionError("Not an AssociatedWeakRef")
        case None =>
          ()
      }
    }
  }
}

trait AnalysisRunner[P <: Product, T] {
  def compute(cache: AnalysisCache)(params: P): T
}

package orc.compile

class AnalysisCache {
  // TODO: Replace with LRU cache.
  val cache = collection.mutable.HashMap[(AnalysisRunner[_, _], Any), Any]()

  def get[T, P](runner: AnalysisRunner[P, T])(params: P): T = {
    cache.getOrElseUpdate((runner, params), runner.compute(this)(params)).asInstanceOf[T]
  }

  def clean(): Unit = {

  }
}

trait AnalysisRunner[P, T] {
  def compute(cache: AnalysisCache)(params: P): T
}

--- 
+++ 
@@ -1,18 +1,54 @@
 package orc.compile
 
+import java.util.WeakHashMap
+import scala.ref.WeakReference
+import scala.ref.ReferenceQueue
+
 class AnalysisCache {
-  // TODO: Replace with LRU cache.
-  val cache = collection.mutable.HashMap[(AnalysisRunner[_, _], Any), Any]()
+  private val cache = collection.mutable.HashMap[AnalysisCacheKey, Any]()
 
-  def get[T, P](runner: AnalysisRunner[P, T])(params: P): T = {
-    cache.getOrElseUpdate((runner, params), runner.compute(this)(params)).asInstanceOf[T]
+  def get[P <: Product, T](runner: AnalysisRunner[P, T])(params: P): T = {
+    incrClean()
+    cache.getOrElseUpdate(new AnalysisCacheKey(runner, params), runner.compute(this)(params)).asInstanceOf[T]
   }
 
   def clean(): Unit = {
+    cache.clear()
+  }
 
+  private class AssociatedWeakRef(v: AnyRef, val key: AnalysisCacheKey, q: ReferenceQueue[AnyRef]) extends WeakReference[AnyRef](v, q)
+
+  private class AnalysisCacheKey(val analysis: AnalysisRunner[_, _], params1: Product) {
+    val params = params1.productIterator.map(v => new AssociatedWeakRef(v.asInstanceOf[AnyRef], this, queue)).toSeq
+    override def hashCode() = analysis.hashCode() + params.map(_().hashCode()).sum
+    override def equals(o: Any) = o match {
+      case o: AnalysisCacheKey => {
+        analysis == o.analysis &&
+          (params zip o.params).forall({ p =>
+            p._1.get == p._2.get
+          })
+      }
+      case _ => false
+    }
+  }
+
+  val queue = new ReferenceQueue[AnyRef]()
+
+  def incrClean(n: Int = 10): Unit = {
+    if (n > 0) {
+      queue.poll match {
+        case Some(k: AssociatedWeakRef) =>
+          cache -= k.key
+          incrClean(n - 1)
+        case Some(_) =>
+          throw new AssertionError("Not an AssociatedWeakRef")
+        case None =>
+          ()
+      }
+    }
   }
 }
 
-trait AnalysisRunner[P, T] {
+trait AnalysisRunner[P <: Product, T] {
   def compute(cache: AnalysisCache)(params: P): T
 }
Add analysis result weak caching.
