Basic support for serializing policies to YAML
[aquarium] / src / main / scala / gr / grnet / aquarium / logic / accounting / dsl / Timeslot.scala
1 /*
2  * Copyright 2011 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 gr.grnet.aquarium.util.json.JsonSupport
41
42 /**
43  * A representation of a timeslot with a start and end date.
44  *
45  * @author Georgios Gousios <gousiosg@gmail.com>
46  */
47 case class Timeslot(from: Date, to: Date)
48   extends DSLItem with Ordered[Timeslot] {
49
50   /* Preconditions to ensure correct object creations */
51   assert(from != null)
52   assert(to != null)
53   assert(from.before(to))
54
55   def startsBefore(t: Timeslot) : Boolean = this.from.before(t.from)
56
57   def startsAfter(t: Timeslot) : Boolean = this.from.after(t.from)
58
59   def endsBefore(t: Timeslot) : Boolean = this.to.before(t.to)
60
61   def endsAfter(t: Timeslot) : Boolean = this.to.after(t.to)
62
63   /**
64    * Check whether this time slot fully contains the provided one.
65    */
66   def contains(t: Timeslot) : Boolean = t.startsAfter(this) && t.endsBefore(this)
67
68   /**
69    * Check whether this timeslot contains the provided time instant.
70    */
71   def includes(t: Date) : Boolean =
72     if (from.before(t) && to.after(t)) true else false
73
74   /**
75    * Check whether this timeslot overlaps with the provided one.
76    */
77   def overlaps(t: Timeslot) : Boolean = {
78     if (contains(t) || t.contains(this))
79       return true
80
81     if (this.includes(t.from) || this.includes(t.to))
82       return true
83
84     false
85   }
86
87   /**
88    * Merges this timeslot with the provided one. If the timeslots overlap,
89    * a list with the resulting merge is returned. If the timeslots do not
90    * overlap, the returned list contains both timeslots in increasing start
91    * date order.
92    */
93   def merge(t: Timeslot) : List[Timeslot] = {
94     if (overlaps(t)) {
95       val nfrom = if (from.before(t.from)) from else t.from
96       val nto   = if (to.after(t.to)) to else t.to
97       List(Timeslot(nfrom, nto))
98     } else
99       if (this.from.before(t.from))
100         List(this, t)
101       else
102         List(t, this)
103   }
104
105   /**
106    * Split the timeslot in two parts at the provided timestamp, if the
107    * timestamp falls within the timeslot boundaries.
108    */
109   def slice(d: Date) : List[Timeslot] =
110     if (includes(d))
111       List(Timeslot(from, d), Timeslot(d,to))
112     else
113       List(this)
114
115   /**
116    * Find and return the timeslots whithin which this Timeslot overrides
117    * with the provided list of timeslots. For example if:
118    * 
119    *  - `this == Timeslot(7,20)`
120    *  - `list == List(Timeslot(1,3), Timeslot(6,8), Timeslot(11,15))`
121    *
122    * the result will be: `List(Timeslot(7,8), Timeslot(11,15))`
123    */
124   def overlappingTimeslots(list: List[Timeslot]) : List[Timeslot] = {
125
126     val result = new mutable.ListBuffer[Timeslot]()
127
128     list.foreach {
129       t =>
130         if (t.contains(this)) result += this
131         else if (this.contains(t)) result += t
132         else if (t.overlaps(this) && t.startsBefore(this)) result += this.slice(t.to).head
133         else if (t.overlaps(this) && t.startsAfter(this)) result += this.slice(t.from).tail.head
134     }
135     result.toList
136   }
137
138   /**
139    * Find and return the timeslots whithin which this Timeslot does not
140    * override with the provided list of timeslots. For example if:
141    *
142    *  - `this == Timeslot(7,20)`
143    *  - `list == List(Timeslot(1,3), Timeslot(6,8), Timeslot(11,15))`
144    *
145    * the result will be: `List(Timeslot(9,10), Timeslot(15,20))`
146    */
147   def nonOverlappingTimeslots(list: List[Timeslot]): List[Timeslot] = {
148
149     val overlaps = list.filter(t => this.overlaps(t))
150
151     if (overlaps.isEmpty)
152       return List(this)
153
154     def build(acc: List[Timeslot], listPart: List[Timeslot]): List[Timeslot] = {
155
156       listPart match {
157         case Nil => acc
158         case x :: Nil => build(acc, List())
159         case x :: y :: rest =>
160           build(acc ++ List(Timeslot(x.to,  y.from)), y :: rest)
161       }
162     }
163
164     val head = overlaps.head
165     val last = overlaps.reverse.head
166
167     val start = if (head.startsAfter(this)) List(Timeslot(this.from, head.from)) else List()
168     val end = if (last.endsBefore(this)) List(Timeslot(last.to, this.to)) else List()
169
170     start ++ build(List(), overlaps) ++ end
171   }
172
173   /**
174    * Compares the starting times of two timeslots.
175    */
176   def compare(that: Timeslot): Int = {
177     if (this.startsBefore(that)) -1
178     else if (this.startsAfter(that)) 1
179     else 0
180   }
181
182   /**
183    * Converts the timeslot to the amount of hours it represents
184    */
185   def hours: Float = ((to.getTime - from.getTime)/1000)/60F/60F
186 }