Second cut of the new policy configuration system
[aquarium] / src / main / scala / gr / grnet / aquarium / logic / accounting / dsl / DSLUtils.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.immutable
40 import gr.grnet.aquarium.policy.{EffectiveUnitPrice, AdHocFullPriceTableRef, PolicyDefinedFullPriceTableRef, PolicyModel, ResourceType, EffectivePriceTable, UserAgreementModel}
41 import gr.grnet.aquarium.AquariumInternalError
42
43 /**
44  * Utility functions to use when working with DSL types.
45  *
46  * @author Georgios Gousios <gousiosg@gmail.com>
47  */
48
49 trait DSLUtils {
50   /**
51    * Resolves the effective price list for each chunk of the
52    * provided timeslot and returns it as a Map
53    */
54   def resolveEffectiveUnitPricesForTimeslot(
55       timeslot: Timeslot,
56       policy: PolicyModel,
57       agreement: UserAgreementModel,
58       resourceType: ResourceType
59   ): immutable.SortedMap[Timeslot, Double] = {
60
61     val role = agreement.role
62     val fullPriceTable = agreement.fullPriceTableRef match {
63       case PolicyDefinedFullPriceTableRef ⇒
64         policy.roleMapping.get(role) match {
65           case Some(fullPriceTable) ⇒
66             fullPriceTable
67
68           case None ⇒
69             throw new AquariumInternalError("Unknown role %s".format(role))
70         }
71
72       case AdHocFullPriceTableRef(fullPriceTable) ⇒
73         fullPriceTable
74     }
75
76     val effectivePriceTable = fullPriceTable.perResource.get(resourceType.name) match {
77       case None ⇒
78         throw new AquariumInternalError("Unknown resource type %s".format(role))
79
80       case Some(effectivePriceTable) ⇒
81         effectivePriceTable
82     }
83
84     resolveEffective3(timeslot, effectivePriceTable)
85   }
86
87
88   def resolveEffective3(
89       timeslot0: Timeslot,
90       effectivePriceTable: EffectivePriceTable
91   ): immutable.SortedMap[Timeslot, Double/*unit price*/] = {
92 //    assert(policy.toTimeslot contains timeslot0,"Policy does not contain timeslot")
93     val timeslot = timeslot0 //TODO: timeslot0.align(5000)
94     val subtimeslots_of_this_policy = Timeslot.mergeOverlaps(policy.effective intervalsOf timeslot)
95     val subtimeslots_NOT_IN_this_policy = Timeslot.mergeOverlaps(timeslot.nonOverlappingTimeslots
96                                                                                           (subtimeslots_of_this_policy))
97     val policy_map =  subtimeslots_of_this_policy.foldLeft  (immutable.SortedMap[Timeslot, T]())
98                                                             {(map,t) =>
99                                                                   //Console.err.println("Adding timeslot" + t + " for policy " + policy.name)
100                                                                   map + ((t,policy))
101                                                             }
102     val other_policy_map = policy.overrides match {
103                                               case None =>
104                                                  immutable.SortedMap[Timeslot, T]()
105                                               case Some(parent_policy)=>
106                                                   subtimeslots_NOT_IN_this_policy.foldLeft (
107                                                       (immutable.SortedMap[Timeslot, T]()))
108                                                       {(map,t) =>
109                                                         //Console.err.println("Residual timeslot: " + t)
110                                                         map ++ resolveEffective3(t,parent_policy)
111                                                       }
112                                             }
113     val final_map = policy_map ++ other_policy_map
114     final_map
115   }
116
117   /*def resolveEffective2[T <: DSLTimeBoundedItem[T]](timeslot0: Timeslot,policy: T):
118   immutable.SortedMap[Timeslot, T] = {
119     assert(policy.toTimeslot contains timeslot0,"Policy does not contain timeslot")
120
121     /* generate mappings from timeslots -> policies
122      * Algorithm: find next valid date (starting from timeslot.start) in this policy
123      */
124     val timeslot = timeslot0 //TODO: timeslot0.align(5000)
125     def nextDate(d:Date,p:T) : Option[(Date,T,Boolean)] =
126       (p.effective nextValidAfter d,p.overrides) match {
127         case (None,None) => None
128         case (None,Some(parent_policy)) =>
129           val d1 = nextDate(d,parent_policy)
130           d1
131         case (Some(d1),_) => /* the next valid date cannot occur after the end of timeslot*/
132            if (d1.before(timeslot.to)) Some((d1,p,true)) else Some((timeslot.to,p,false))
133       }
134     def genMap(map: immutable.SortedMap[Timeslot, T],d:Date) : immutable.SortedMap[Timeslot, T] = {
135         val step = 1000L
136         nextDate(d,policy) match {
137         case None => map
138         case Some((d1,policy,cont)) =>
139           val t = Timeslot(d,d1)
140           val map1 = map + (t -> policy)
141           if(cont) genMap(map1,new Date(d1.getTime + step)) // 1 second after d1
142           else map1 /* done */
143       }
144     }
145     val map = genMap(immutable.SortedMap[Timeslot, T](),timeslot.from)
146     map
147   }
148
149   /**
150    * Splits the provided timeslot into chunks according to the validity
151    * timeslots specified by the provided time bounded item. It
152    * returns a map whose keys are the timeslot chunks and the values
153    * correspond to the effective time bounded item (algorithm or pricelist).
154    */
155   def resolveEffective1[T <: DSLTimeBoundedItem[T]](timeslot: Timeslot,policy: T):
156   immutable.SortedMap[Timeslot, T] = {
157       assert(policy.toTimeslot contains timeslot,"Policy does not contain timeslot")
158
159      /* Get a list of all effective/expanded policy timeslots within
160         a timeslot specified by "variable timeslot".
161       * NOTICE: The returned timeslots may be slightly out of "timeslot" bounds
162       *         so we need to invoke overlappingTimeslots and nonOverlapping timeslots
163       */
164      val all_timeslots = allEffectiveTimeslots(policy.effective,timeslot)
165      val timeslots_IN_policy = timeslot.overlappingTimeslots(all_timeslots)
166      val timeslots_NOT_IN_policy = timeslot.nonOverlappingTimeslots(all_timeslots)
167
168      val ret =  immutable.SortedMap[Timeslot, T]() ++
169       /*add [timeslots -> policy] covered by this policy*/
170       timeslots_IN_policy.flatMap {
171          effective_timeslot =>  Map(effective_timeslot -> policy)
172       } ++
173       /*add [timeslots -> policy] covered by parent policies */
174       timeslots_NOT_IN_policy.flatMap { /* search the policy hierarchy for effective timeslots not covered by this policy.*/
175         not_effective_timeslot => policy.overrides match {
176           case None => immutable.SortedMap[Timeslot, T]() /*Nothing to do. TODO: throw exception ?*/
177           case Some(parent_policy) => resolveEffective1(not_effective_timeslot,parent_policy) /* search the policy hierarchy*/
178         }
179       }
180     ret
181   }*/
182
183    /*
184   /*
185    * Get a list of all timeslots within which a timeframe
186    * is effective, whithin the provided time bounds.
187    */
188   def allEffectiveTimeslots(spec: DSLTimeFrame, t: Timeslot):
189   List[Timeslot] =
190     if (spec.repeat.isEmpty) { //A  simple timeframe with no repetition defined
191       val fromDate = if (spec.from.before(t.from)) t.from else spec.from
192       val toDate = if (spec.to.getOrElse(t.to).after(t.to)) t.to else spec.to.getOrElse(t.to)
193       List(Timeslot(fromDate, toDate))
194     } /* otherwise for all repetitions determine timeslots*/
195     else {
196         val all =  for { r <- spec.repeat
197                          ts <- effectiveTimeslots(r, t.from, t.to)
198                       }  yield ts
199       //for{ i <- all} Console.err.println(i)
200         mergeOverlaps(all)
201     }
202   */
203
204
205
206   /**
207    * Merges overlapping timeslots.
208    */
209
210   /*/**
211    * Get a list of all timeslots within which a time frame is active.
212      The result is returned sorted by timeframe start date.
213    */
214   def effectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to : Date):
215     List[Timeslot] =
216       for { (h1,h2) <- spec.start zip spec.end
217             (d1,d2) <- DSLTimeSpec.expandTimeSpec(h1,h2,from,to)
218           }
219       yield Timeslot(d1,d2)
220       */
221 }