2 * Copyright 2011 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
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.
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.
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.
36 package gr.grnet.aquarium.logic.accounting.dsl
38 import gr.grnet.aquarium.util.DateUtils
39 import java.util.{Date, GregorianCalendar, Calendar}
40 import scala.collection.immutable
43 * Utility functions to use when working with DSL types.
45 * @author Georgios Gousios <gousiosg@gmail.com>
48 trait DSLUtils extends DateUtils {
50 val maxdate = new Date(Int.MaxValue * 1000L)
51 val mindate = new Date(0)
54 * Resolves the effective algorithm for each chunk of the
55 * provided timeslot and returns it as a Map
57 def resolveEffectiveAlgorithmsForTimeslot(timeslot: Timeslot,
59 immutable.SortedMap[Timeslot, DSLAlgorithm] =
60 resolveEffective[DSLAlgorithm](timeslot, Some(agr.algorithm))
63 * Resolves the effective price list for each chunk of the
64 * provided timeslot and returns it as a Map
66 def resolveEffectivePricelistsForTimeslot(timeslot: Timeslot,
68 immutable.SortedMap[Timeslot, DSLPriceList] =
69 resolveEffective[DSLPriceList](timeslot, Some(agr.pricelist))
72 * Splits the provided timeslot into chunks according to the validity
73 * timeslots specified by the provided time bounded item. It
74 * returns a map whose keys are the timeslot chunks and the values
75 * correspond to the effective time bounded item (algorithm or pricelist).
77 def resolveEffective[T <: DSLTimeBoundedItem[T]](timeslot: Timeslot,
79 immutable.SortedMap[Timeslot, T] = {
81 val policy = tbi match {
82 case None => return immutable.SortedMap[Timeslot, T]()
86 // The following check that the policy is applicable within
87 // the timeframe of the requested resolution
88 assert(timeslot.to.before(policy.effective.to.getOrElse(maxdate)),
89 "Policy effectivity ends before expansion timeslot")
90 assert(timeslot.from.after(policy.effective.from),
91 "Policy effectivity starts after expansion timeslot")
93 val eff = allEffectiveTimeslots(policy.effective,
94 Timeslot(oneYearBack(timeslot.from, policy.effective.from),
95 oneYearAhead (timeslot.to, policy.effective.to.getOrElse(maxdate))))
97 // logger.debug("effective timeslots: %d".format(eff.size))
99 immutable.SortedMap[Timeslot, T]() ++
100 timeslot.overlappingTimeslots(eff).flatMap {
101 t => Map(t -> policy)
103 timeslot.nonOverlappingTimeslots(eff).flatMap {
104 t => resolveEffective(t, policy.overrides)
109 * Get a list of timeslots within which a timeframe is not effective.
111 def ineffectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to: Option[Date]):
114 buildNotEffectiveList(effectiveTimeslots(spec, from, to)) sortWith sorter
117 private def buildNotEffectiveList(l :List[Timeslot]) :
120 if (l.isEmpty) return List()
121 if (l.tail.isEmpty) return List()
123 assert(l.head.to.getTime < l.tail.head.from.getTime)
125 Timeslot(new Date(l.head.to.getTime + 1),
126 new Date(l.tail.head.from.getTime - 1)) :: buildNotEffectiveList(l.tail)
130 * Merges overlapping timeslots. The merge is exhaustive only if the
131 * provided list is sorted in increasing timeslot from order.
133 def mergeOverlaps(list: List[Timeslot]): List[Timeslot] =
134 list.foldLeft(List[Timeslot]()) {
138 else if (a.tail.isEmpty)
141 val merged = a.tail.head.merge(b)
142 a ++ (if (merged.size == 1) merged else List(b))
147 * Get a list of all timeslots within which the provided time frame
150 def allEffectiveTimeslots(spec: DSLTimeFrame):
153 val l = spec.repeat.flatMap {
154 r => effectiveTimeslots(r, spec.from, spec.to)
160 * Get a list of all timeslots within which a timeframe
161 * is effective, whithin the provided time bounds.
163 def allEffectiveTimeslots(spec: DSLTimeFrame, t: Timeslot):
166 //A timeframe with no repetition defined
167 if (spec.repeat.isEmpty) {
168 val fromDate = if (spec.from.before(t.from)) t.from else spec.from
169 val toDate = if (spec.to.getOrElse(t.to).after(t.to)) t.to else spec.to.getOrElse(t.to)
170 return List(Timeslot(fromDate, toDate))
173 val l = spec.repeat.flatMap {
174 r => effectiveTimeslots(r, t.from, Some(t.to))
180 * Get a list of all timeslots within which a time frame is active.
181 * If the to date is None, the expansion takes place within a timeframe
182 * between `from .. from` + 1 year. The result is returned sorted by
183 * timeframe start date.
185 def effectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to: Option[Date]):
188 val endDate = to match {
189 case None => //One year from now
190 val c = new GregorianCalendar()
192 c.add(Calendar.YEAR, 1)
197 coExpandTimespecs(spec.start.zip(spec.end), from, endDate) sortWith sorter
201 * Utility function to put timeslots in increasing start timestamp order
203 def sorter(x: Timeslot, y: Timeslot) : Boolean =
204 if (y.from after x.from) true else false
207 * Calculate periods of activity for a list of timespecs
209 private def coExpandTimespecs(input: List[(DSLTimeSpec, DSLTimeSpec)],
210 from: Date, to: Date): List[Timeslot] = {
211 if (input.size == 0) return List()
213 expandTimeSpec(input.head._1, from, to).zip(
214 expandTimeSpec(input.head._2, from, to)).map(
215 l => Timeslot(l._1, l._2)
217 coExpandTimespecs(input.tail, from, to)
221 * Expand a List of timespecs.
223 def expandTimeSpecs(spec: List[DSLTimeSpec], from: Date, to: Date):
225 spec.flatMap { t => expandTimeSpec(t, from, to)}
228 * Get the list of time points prescribed by the provided timespec,
229 * within the timeframe between from and to.
231 def expandTimeSpec(spec: DSLTimeSpec, from: Date, to: Date) : List[Date] = {
232 val adjusted = adjustToTime(from, spec.hour, spec.min)
233 findDays(adjusted, to, {
235 (if (spec.mon >= 0) {c.get(Calendar.MONTH) == spec.getCalendarMonth()} else true) &&
236 (if (spec.dom >= 0) {c.get(Calendar.DAY_OF_MONTH) == spec.dom} else true) &&
237 (if (spec.dow >= 0) {c.get(Calendar.DAY_OF_WEEK) == spec.getCalendarDow()} else true)