added implementation for MemStoreProvider (PolicyStore methods)
[aquarium] / src / main / scala / gr / grnet / aquarium / logic / accounting / dsl / Timeslot.scala
1 /*
2  * Copyright 2011-2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.aquarium.logic.accounting.dsl
37
38 import java.util.Date
39 import scala.collection.mutable
40 import annotation.tailrec
41 import gr.grnet.aquarium.util.date.MutableDateCalc
42
43 /**
44  * A representation of a timeslot with a start and end date.
45  *
46  * @author Georgios Gousios <gousiosg@gmail.com>
47  */
48 final case class Timeslot(from: Date, to: Date) extends Ordered[Timeslot] {
49
50   /* Preconditions to ensure correct object creations */
51   assert(from != null)
52   assert(to != null)
53   assert(start <= end, "from = %s, to = %s".format(new MutableDateCalc(from), new MutableDateCalc(to)))
54
55   def startsBefore(t: Timeslot) : Boolean =  start < t.start
56
57   def startsAfter(t: Timeslot) : Boolean =   start > t.start
58
59   def endsBefore(t: Timeslot) : Boolean = end < t.end
60
61   def endsAfter(t: Timeslot) : Boolean =  end > t.end
62
63   def after(t: Timeslot): Boolean =  start > t.end
64
65   def before(t: Timeslot): Boolean = end < t.start
66
67   def start : Long =  this.from.getTime
68
69   def end : Long =  this.to.getTime
70
71   /**
72    * Check whether this time slot fully contains the provided one.
73    */
74   def contains(t: Timeslot) : Boolean = this.start <= t.start && this.end >= t.end
75
76   def weakIncludes(t: Date) : Boolean = start < t.getTime &&  t.getTime < end
77   def weakOverlaps(t: Timeslot) : Boolean =
78     contains(t) || t.contains(this) || this.weakIncludes(t.from) || this.weakIncludes(t.to)
79
80
81   def containsTimeInMillis(millis: Long) =  start <= millis && millis <= end
82
83
84   /**
85    * Check whether this timeslot contains the provided time instant.
86    */
87   def includes(t: Date) : Boolean = start <= t.getTime &&  t.getTime <= end
88
89
90   /**
91    * Check whether this timeslot overlaps with the provided one.
92    */
93   def overlaps(t: Timeslot) : Boolean =
94     contains(t) || t.contains(this) || this.includes(t.from) || this.includes(t.to)
95
96
97   /**
98    * Merges this timeslot with the provided one. If the timeslots overlap,
99    * a list with the resulting merge is returned. If the timeslots do not
100      * overlap, the returned list contains both timeslots in increasing start
101    * date order.
102    */
103   def merge(t: Timeslot) : Timeslot  = {
104    assert(overlaps(t),this +" has no overlap with " + t)
105    val nfrom = if (start < t.start) from else t.from
106    val nto   = if (end > t.end) to else t.to
107    Timeslot(nfrom, nto)
108   }
109
110   /**
111    * Split the timeslot in two parts at the provided timestamp, if the
112    * timestamp falls within the timeslot boundaries.
113    */
114    def slice(d: Date) : List[Timeslot] =
115     if (includes(d) && d.getTime != start && d.getTime != end)
116       List(Timeslot(from, d), Timeslot(d,to))
117     else
118       List(this)
119
120
121   /**
122    * Find and return the timeslots within which this Timeslot overrides
123    * with the provided list of timeslots. For example if:
124    * 
125    *  - `this == Timeslot(7,20)`
126    *  - `list == List(Timeslot(1,3), Timeslot(6,8), Timeslot(11,15))`
127    *
128    * the result will be: `List(Timeslot(7,8), Timeslot(11,15))`
129    */
130   def overlappingTimeslots(list: List[Timeslot]) : List[Timeslot] =
131     list.foldLeft(List[Timeslot]()) { (ret,t) =>
132       if (t.contains(this)) this :: ret
133       else if (this.contains(t)) t :: ret
134       else if (t.overlaps(this) && t.startsBefore(this)) slice(t.to).head :: ret
135       else if (t.overlaps(this) && t.startsAfter(this))  slice(t.from).last :: ret
136       else ret
137     }.reverse
138
139
140   /**
141    * Find and return the timeslots whithin which this Timeslot does not
142    * override with the provided list of timeslots. For example if:
143    *
144    *  - `this == Timeslot(7,20)`
145    *  - `list == List(Timeslot(1,3), Timeslot(6,8), Timeslot(11,15))`
146    *
147    * the result will be: `List(Timeslot(9,10), Timeslot(15,20))`
148    */
149   def nonOverlappingTimeslots(list: List[Timeslot]): List[Timeslot] =
150     overlappingTimeslots(list) sortWith {_.start < _.start} match  {
151       case Nil => List(this)
152       case over =>
153         val (head,last) = (over.head,over.last)
154         val hd = if (head.start > this.start) List(Timeslot(this.from, head.from)) else List()
155         val tl = if (last.end < this.end) List(Timeslot(last.to, this.to)) else List()
156         hd ++ over.tail.foldLeft((List[Timeslot](),over.head)) {
157           case ((l,x),y) => (l ++ List(Timeslot(x.to,  y.from)),y)
158         }._1  ++ tl
159     }
160
161   /**
162    * Align a list of consecutive timeslots to the boundaries
163    * defined by this timeslot. Elements that do not overlap
164    * with this timeslot are rejected, while elements not
165    * contained in the timeslot are trimmed to this timeslot's
166    * start and end time.
167    */
168   def align(l: List[Timeslot]): List[Timeslot] = {
169     if (l.isEmpty) return List()
170
171     val result : Option[Timeslot] =
172       if (!this.overlaps(l.head)) None
173       else if (l.head.contains(this)) Some(this)
174       else if (l.head.startsBefore(this)) Some(Timeslot(this.from, l.head.to))
175       else if (l.head.endsAfter(this)) Some(Timeslot(l.head.from, this.to))
176       else Some(this)
177
178     result match {
179       case Some(x) => x :: align(l.tail)
180       case None => align(l.tail)
181     }
182   }
183
184   /* align a time slot in "bound_size" boundaries so that
185    * start0 <= start and end0 >= end */
186   def align(bound_size : Long) : Timeslot = {
187     val start0 = (start / bound_size) * bound_size
188     val add_one = if (end % bound_size == 0) 0 else 1
189     val end0  =  (end / bound_size + add_one) * bound_size
190     Timeslot(start0,end0)
191   }
192
193   /* returns true when the start and end address are
194   *  multiples of bound_size*/
195   def isAligned(bound_size : Long) : Boolean =
196      start % bound_size == 0 && end % bound_size == 0
197
198
199   /**
200    * Compares the starting times of two timeslots.
201    */
202   def compare(that: Timeslot): Int = {
203     if (this.startsBefore(that)) -1
204     else if (this.startsAfter(that)) 1
205     else 0
206   }
207
208   /**
209    * Converts the timeslot to the amount of hours it represents
210    */
211   def hours: Double = (to.getTime - from.getTime).toDouble / 1000.0 / 60.0 / 60.0
212
213   def deltaMillis = to.getTime - from.getTime
214
215
216   def myString : String = "Timeslot(" + this.start + "," + this.end + ")"
217   //override def toString() = myString
218   override def toString() =
219     toDateString
220
221   def toDateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from), new MutableDateCalc(to))
222   def toISODateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from).toISOString, new MutableDateCalc(to).toISOString)
223 }
224
225 object Timeslot {
226   def apply(x: Long, y: Long): Timeslot =
227     new Timeslot(new Date(x), new Date(y))
228
229   def apply(x: Int, y: Int): Timeslot =
230     new Timeslot(new Date(x), new Date(y))
231
232  def mergeOverlaps(list: List[Timeslot]): List[Timeslot] = {
233     def sorter(x: Timeslot, y: Timeslot) : Boolean =   y.from after x.from
234     (list sortWith sorter).foldLeft(List[Timeslot]()) {
235       case (Nil,b) =>
236         List(b)
237       case (hd::Nil,b) =>
238         if (hd overlaps  b) (hd merge b)::Nil
239         else b::hd::Nil
240       case (a @ hd::tl,b) =>
241         if(hd overlaps b) (hd merge b)::tl
242         else b :: a
243     }.reverse
244   }
245 }