WIP Resource event handling
[aquarium] / src / main / scala / gr / grnet / aquarium / computation / state / parts / RoleHistory.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.computation.parts
37
38 import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
39 import scala.collection.immutable.{TreeMap, SortedMap}
40 import scala.collection.mutable.ListBuffer
41 import scala.annotation.tailrec
42
43 /**
44  *
45  * @author Christos KK Loverdos <loverdos@gmail.com>
46  */
47
48 case class RoleHistory(
49     /**
50      * The head role is the most recent. The same rule applies for the tail.
51      */
52     roles: List[RoleHistoryItem]
53 ) {
54
55   def roleNamesByTimeslot: SortedMap[Timeslot, String] = {
56     TreeMap(roles.map(role ⇒ (role.timeslot, role.name)): _*)
57   }
58
59   def rolesByTimeslot: SortedMap[Timeslot, RoleHistoryItem] = {
60     TreeMap(roles.map(role ⇒ (role.timeslot, role)): _*)
61   }
62
63   def updatedWithRole(role: String, validFrom: Long) = {
64     // TODO: Review this when Timeslot is also reviewed.
65     //       Currently, we need `fixValidTo` because Timeslot does not validate when `validFrom` and `validTo`
66     //       are equal.
67     def fixValidTo(validFrom: Long, validTo: Long): Long = {
68       if(validTo == validFrom) {
69         // Since validTo is exclusive, make at least 1ms gap
70         validFrom + 1
71       } else {
72         validTo
73       }
74     }
75
76     val newItems = roles match {
77       case Nil ⇒
78         RoleHistoryItem(role, validFrom) :: Nil
79
80       case head :: tail ⇒
81         if(head.startsStrictlyAfter(validFrom)) {
82           // must search history items to find where this fits in
83           @tailrec
84           def check(allChecked: ListBuffer[RoleHistoryItem],
85                     lastCheck: RoleHistoryItem,
86                     toCheck: List[RoleHistoryItem]): List[RoleHistoryItem] = {
87
88             toCheck match {
89               case Nil ⇒
90                 allChecked.append(RoleHistoryItem(role, validFrom, fixValidTo(validFrom, lastCheck.validFrom)))
91                 allChecked.toList
92
93               case toCheckHead :: toCheckTail ⇒
94                 if(toCheckHead.startsStrictlyAfter(validFrom)) {
95                   allChecked.append(toCheckHead)
96
97                   check(allChecked, toCheckHead, toCheckTail)
98                 } else {
99                   allChecked.append(RoleHistoryItem(role, validFrom, fixValidTo(validFrom, lastCheck.validFrom)))
100                   allChecked.toList
101                 }
102             }
103           }
104
105           val buffer = new ListBuffer[RoleHistoryItem]
106           buffer.append(head)
107           check(buffer, head, tail)
108         } else {
109           // assume head.validTo goes to infinity,
110           RoleHistoryItem(role, validFrom) :: head.copyWithValidTo(fixValidTo(head.validFrom, validFrom)) :: tail
111         }
112     }
113
114     RoleHistory(newItems)
115   }
116
117   /**
118    * Returns the first, chronologically, role.
119    */
120   def firstRole: Option[RoleHistoryItem] = {
121     rolesByTimeslot.valuesIterator.toList.headOption
122   }
123
124   /**
125    * Returns the name of the first, chronologically, role.
126    */
127   def firstRoleName: Option[String] = {
128     roleNamesByTimeslot.valuesIterator.toList.headOption
129   }
130
131   /**
132    * Returns the last, chronologically, role.
133    */
134   def lastRole: Option[RoleHistoryItem] = {
135     rolesByTimeslot.valuesIterator.toList.lastOption
136   }
137
138   /**
139    * Returns the name of the last, chronologically, role.
140    */
141   def lastRoleName: Option[String] = {
142     roleNamesByTimeslot.valuesIterator.toList.lastOption
143   }
144 }
145
146 object RoleHistory {
147   final val Empty = RoleHistory(Nil)
148
149   def initial(role: String, validFrom: Long): RoleHistory = {
150     RoleHistory(RoleHistoryItem(role, validFrom) :: Nil)
151   }
152 }