added implementation for MemStoreProvider (PolicyStore methods)
[aquarium] / src / main / scala / gr / grnet / aquarium / logic / accounting / dsl / Timeslot.scala
index 90d7a13..1948d54 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 GRNET S.A. All rights reserved.
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or
  * without modification, are permitted provided that the following
@@ -38,84 +38,86 @@ package gr.grnet.aquarium.logic.accounting.dsl
 import java.util.Date
 import scala.collection.mutable
 import annotation.tailrec
+import gr.grnet.aquarium.util.date.MutableDateCalc
 
 /**
  * A representation of a timeslot with a start and end date.
  *
  * @author Georgios Gousios <gousiosg@gmail.com>
  */
-final case class Timeslot(from: Date, to: Date)
-  extends DSLItem with Ordered[Timeslot] {
+final case class Timeslot(from: Date, to: Date) extends Ordered[Timeslot] {
 
   /* Preconditions to ensure correct object creations */
   assert(from != null)
   assert(to != null)
-  assert(from.before(to))
+  assert(start <= end, "from = %s, to = %s".format(new MutableDateCalc(from), new MutableDateCalc(to)))
 
-  def startsBefore(t: Timeslot) : Boolean = this.from.before(t.from)
+  def startsBefore(t: Timeslot) : Boolean =  start < t.start
 
-  def startsAfter(t: Timeslot) : Boolean = this.from.after(t.from)
+  def startsAfter(t: Timeslot) : Boolean =   start > t.start
 
-  def endsBefore(t: Timeslot) : Boolean = this.to.before(t.to)
+  def endsBefore(t: Timeslot) : Boolean = end < t.end
 
-  def endsAfter(t: Timeslot) : Boolean = this.to.after(t.to)
+  def endsAfter(t: Timeslot) : Boolean =  end > t.end
 
-  def after(t: Timeslot): Boolean = if (this.from.after(t.to)) true else false
+  def after(t: Timeslot): Boolean =  start > t.end
 
-  def before(t: Timeslot): Boolean = if (this.to.before(t.from)) true else false
+  def before(t: Timeslot): Boolean = end < t.start
+
+  def start : Long =  this.from.getTime
+
+  def end : Long =  this.to.getTime
 
   /**
    * Check whether this time slot fully contains the provided one.
    */
-  def contains(t: Timeslot) : Boolean = t.startsAfter(this) && t.endsBefore(this)
+  def contains(t: Timeslot) : Boolean = this.start <= t.start && this.end >= t.end
+
+  def weakIncludes(t: Date) : Boolean = start < t.getTime &&  t.getTime < end
+  def weakOverlaps(t: Timeslot) : Boolean =
+    contains(t) || t.contains(this) || this.weakIncludes(t.from) || this.weakIncludes(t.to)
+
+
+  def containsTimeInMillis(millis: Long) =  start <= millis && millis <= end
+
 
   /**
    * Check whether this timeslot contains the provided time instant.
    */
-  def includes(t: Date) : Boolean =
-    if (from.before(t) && to.after(t)) true else false
+  def includes(t: Date) : Boolean = start <= t.getTime &&  t.getTime <= end
+
 
   /**
    * Check whether this timeslot overlaps with the provided one.
    */
-  def overlaps(t: Timeslot) : Boolean = {
-    if (contains(t) || t.contains(this))
-      return true
+  def overlaps(t: Timeslot) : Boolean =
+    contains(t) || t.contains(this) || this.includes(t.from) || this.includes(t.to)
 
-    if (this.includes(t.from) || this.includes(t.to))
-      return true
-
-    false
-  }
 
   /**
    * Merges this timeslot with the provided one. If the timeslots overlap,
    * a list with the resulting merge is returned. If the timeslots do not
-   * overlap, the returned list contains both timeslots in increasing start
+     * overlap, the returned list contains both timeslots in increasing start
    * date order.
    */
-  def merge(t: Timeslot) : List[Timeslot] = {
-    if (overlaps(t)) {
-      val nfrom = if (from.before(t.from)) from else t.from
-      val nto   = if (to.after(t.to)) to else t.to
-      List(Timeslot(nfrom, nto))
-    } else
-      if (this.from.before(t.from))
-        List(this, t)
-      else
-        List(t, this)
+  def merge(t: Timeslot) : Timeslot  = {
+   assert(overlaps(t),this +" has no overlap with " + t)
+   val nfrom = if (start < t.start) from else t.from
+   val nto   = if (end > t.end) to else t.to
+   Timeslot(nfrom, nto)
   }
 
   /**
    * Split the timeslot in two parts at the provided timestamp, if the
    * timestamp falls within the timeslot boundaries.
    */
-  def slice(d: Date) : List[Timeslot] =
-    if (includes(d))
+   def slice(d: Date) : List[Timeslot] =
+    if (includes(d) && d.getTime != start && d.getTime != end)
       List(Timeslot(from, d), Timeslot(d,to))
     else
       List(this)
 
+
   /**
    * Find and return the timeslots within which this Timeslot overrides
    * with the provided list of timeslots. For example if:
@@ -125,19 +127,15 @@ final case class Timeslot(from: Date, to: Date)
    *
    * the result will be: `List(Timeslot(7,8), Timeslot(11,15))`
    */
-  def overlappingTimeslots(list: List[Timeslot]) : List[Timeslot] = {
+  def overlappingTimeslots(list: List[Timeslot]) : List[Timeslot] =
+    list.foldLeft(List[Timeslot]()) { (ret,t) =>
+      if (t.contains(this)) this :: ret
+      else if (this.contains(t)) t :: ret
+      else if (t.overlaps(this) && t.startsBefore(this)) slice(t.to).head :: ret
+      else if (t.overlaps(this) && t.startsAfter(this))  slice(t.from).last :: ret
+      else ret
+    }.reverse
 
-    val result = new mutable.ListBuffer[Timeslot]()
-
-    list.foreach {
-      t =>
-        if (t.contains(this)) result += this
-        else if (this.contains(t)) result += t
-        else if (t.overlaps(this) && t.startsBefore(this)) result += this.slice(t.to).head
-        else if (t.overlaps(this) && t.startsAfter(this)) result += this.slice(t.from).last
-    }
-    result.toList
-  }
 
   /**
    * Find and return the timeslots whithin which this Timeslot does not
@@ -148,32 +146,18 @@ final case class Timeslot(from: Date, to: Date)
    *
    * the result will be: `List(Timeslot(9,10), Timeslot(15,20))`
    */
-  def nonOverlappingTimeslots(list: List[Timeslot]): List[Timeslot] = {
-
-    val overlaps = list.filter(t => this.overlaps(t))
-
-    if (overlaps.isEmpty)
-      return List(this)
-
-    def build(acc: List[Timeslot], listPart: List[Timeslot]): List[Timeslot] = {
-
-      listPart match {
-        case Nil => acc
-        case x :: Nil => build(acc, List())
-        case x :: y :: rest =>
-          build(acc ++ List(Timeslot(x.to,  y.from)), y :: rest)
-      }
+  def nonOverlappingTimeslots(list: List[Timeslot]): List[Timeslot] =
+    overlappingTimeslots(list) sortWith {_.start < _.start} match  {
+      case Nil => List(this)
+      case over =>
+        val (head,last) = (over.head,over.last)
+        val hd = if (head.start > this.start) List(Timeslot(this.from, head.from)) else List()
+        val tl = if (last.end < this.end) List(Timeslot(last.to, this.to)) else List()
+        hd ++ over.tail.foldLeft((List[Timeslot](),over.head)) {
+          case ((l,x),y) => (l ++ List(Timeslot(x.to,  y.from)),y)
+        }._1  ++ tl
     }
 
-    val head = overlaps.head
-    val last = overlaps.reverse.head
-
-    val start = if (head.startsAfter(this)) List(Timeslot(this.from, head.from)) else List()
-    val end = if (last.endsBefore(this)) List(Timeslot(last.to, this.to)) else List()
-
-    start ++ build(List(), overlaps) ++ end
-  }
-
   /**
    * Align a list of consecutive timeslots to the boundaries
    * defined by this timeslot. Elements that do not overlap
@@ -184,19 +168,34 @@ final case class Timeslot(from: Date, to: Date)
   def align(l: List[Timeslot]): List[Timeslot] = {
     if (l.isEmpty) return List()
 
-    val result =
-      if (!this.overlaps(l.head)) List()
-      else if (this.contains(l.head)) List(l.head)
-      else if (l.head.startsBefore(this)) List(Timeslot(this.from, l.head.to))
-      else if (l.head.endsAfter(this)) List(Timeslot(l.head.from, this.to))
-      else List(this)
+    val result : Option[Timeslot] =
+      if (!this.overlaps(l.head)) None
+      else if (l.head.contains(this)) Some(this)
+      else if (l.head.startsBefore(this)) Some(Timeslot(this.from, l.head.to))
+      else if (l.head.endsAfter(this)) Some(Timeslot(l.head.from, this.to))
+      else Some(this)
 
-    if (!result.isEmpty)
-      result.head :: align(l.tail)
-    else
-      align(l.tail)
+    result match {
+      case Some(x) => x :: align(l.tail)
+      case None => align(l.tail)
+    }
+  }
+
+  /* align a time slot in "bound_size" boundaries so that
+   * start0 <= start and end0 >= end */
+  def align(bound_size : Long) : Timeslot = {
+    val start0 = (start / bound_size) * bound_size
+    val add_one = if (end % bound_size == 0) 0 else 1
+    val end0  =  (end / bound_size + add_one) * bound_size
+    Timeslot(start0,end0)
   }
 
+  /* returns true when the start and end address are
+  *  multiples of bound_size*/
+  def isAligned(bound_size : Long) : Boolean =
+     start % bound_size == 0 && end % bound_size == 0
+
+
   /**
    * Compares the starting times of two timeslots.
    */
@@ -210,4 +209,37 @@ final case class Timeslot(from: Date, to: Date)
    * Converts the timeslot to the amount of hours it represents
    */
   def hours: Double = (to.getTime - from.getTime).toDouble / 1000.0 / 60.0 / 60.0
+
+  def deltaMillis = to.getTime - from.getTime
+
+
+  def myString : String = "Timeslot(" + this.start + "," + this.end + ")"
+  //override def toString() = myString
+  override def toString() =
+    toDateString
+
+  def toDateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from), new MutableDateCalc(to))
+  def toISODateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from).toISOString, new MutableDateCalc(to).toISOString)
+}
+
+object Timeslot {
+  def apply(x: Long, y: Long): Timeslot =
+    new Timeslot(new Date(x), new Date(y))
+
+  def apply(x: Int, y: Int): Timeslot =
+    new Timeslot(new Date(x), new Date(y))
+
+ def mergeOverlaps(list: List[Timeslot]): List[Timeslot] = {
+    def sorter(x: Timeslot, y: Timeslot) : Boolean =   y.from after x.from
+    (list sortWith sorter).foldLeft(List[Timeslot]()) {
+      case (Nil,b) =>
+        List(b)
+      case (hd::Nil,b) =>
+        if (hd overlaps  b) (hd merge b)::Nil
+        else b::hd::Nil
+      case (a @ hd::tl,b) =>
+        if(hd overlaps b) (hd merge b)::tl
+        else b :: a
+    }.reverse
+  }
 }
\ No newline at end of file