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}
42 * Utility functions to use when working with DSL types.
44 * @author Georgios Gousios <gousiosg@gmail.com>
48 trait DSLUtils extends DateUtils {
50 def resolveEffectiveAlgorithmsForTimeslot(timeslot: (Date, Date),
52 Map[(Date, Date), DSLAlgorithm] =
53 resolveEffective[DSLAlgorithm](timeslot, Some(agr.algorithm))
56 def resolveEffectivePricelistsForTimeslot(timeslot: (Date, Date),
58 Map[(Date, Date), DSLPriceList] =
59 resolveEffective[DSLPriceList](timeslot, Some(agr.pricelist))
62 * Resolves the DSLTimeBoundedItem which is active within the
63 * provided timeslot. If the provided timeslot does not fit entirely or at all
64 * into a timeslot within which a DSLTimeBoundedItem is active, then the
65 * resolution takes the following paths:
67 * - If the provided timeslot (a) partially fits into the DSLTimeBoundedItem
68 * timeslot (b) and the next active time slot is (c), then the provided
69 * timeslot is split in three parts `(a.start...b.end)`,
70 * `(b.end...c.start)` and `(c.start...a.end)`
73 def resolveEffective[T <: DSLTimeBoundedItem[T]](timeslot: (Date, Date),
75 Map[(Date, Date), T] = {
77 val item = tbi match {
78 case None => return Map()
82 val eff = allEffectiveTimeslots(item.effective,
83 item.effective.from, timeslot._2)
85 val res = eff.find(t => contains(t, timeslot)) match {
86 case Some(x) => Map(x -> item)
87 case None => eff.find(t => contains(t, timeslot._1)) match {
89 val next = if (eff.lastIndexOf(y) == eff.size - 1)
90 (new Date(Int.MaxValue), new Date(Int.MaxValue))
92 eff.apply(eff.lastIndexOf(y) + 1)
93 Map((timeslot._1, y._2) -> item) ++ (
94 if (timeslot._2.before(next._1))
95 resolveEffective((y._2, timeslot._2), item.overrides)
97 resolveEffective((y._2, next._1), item.overrides) ++
98 resolveEffective((next._1, timeslot._2), item.overrides)
100 case None => resolveEffective(timeslot, item.overrides)
108 * Get a list of timeslots within which a timeframe is not effective.
110 def ineffectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to: Option[Date]):
111 List[(Date, Date)] = {
113 buildNotEffectiveList(effectiveTimeslots(spec, from, to)) sortWith sorter
116 private def buildNotEffectiveList(l :List[(Date, Date)]) :
117 List[(Date, Date)] = {
119 if (l.isEmpty) return List()
120 if (l.tail.isEmpty) return List()
122 assert(l.head._2.getTime < l.tail.head._1.getTime)
124 List[(Date, Date)]() ++
125 List((new Date(l.head._2.getTime + 1),
126 new Date(l.tail.head._1.getTime - 1))) ++
127 buildNotEffectiveList(l.tail)
131 * Get a list of all timeslots within which the provided time frame
134 def allEffectiveTimeslots(spec: DSLTimeFrame):
135 List[(Date, Date)] = {
137 spec.repeat.flatMap {
138 r => effectiveTimeslots(r, spec.from, spec.to)
143 * Get a list of all timeslots within which a timeframe
144 * is effective, whithin the provided time bounds.
146 def allEffectiveTimeslots(spec: DSLTimeFrame, from: Date, to: Date):
147 List[(Date, Date)] = {
149 spec.repeat.flatMap {
150 r => effectiveTimeslots(r, from, Some(to))
155 * Get a list of all time periods within which a time frame is active.
156 * If the to date is None, the expansion takes place within a timeframe
157 * between `from .. from` + 1 year. The result is returned sorted by
158 * timeframe start date.
160 def effectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to: Option[Date]):
161 List[(Date, Date)] = {
163 assert(spec.start.size == spec.end.size)
165 val endDate = to match {
166 case None => //One year from now
167 val c = new GregorianCalendar()
169 c.add(Calendar.YEAR, 1)
174 coExpandTimespecs(spec.start.zip(spec.end), from, endDate) sortWith sorter
178 * Utility function to put timeslots in increasing start timestamp order
180 def sorter(x: (Date, Date), y: (Date, Date)) : Boolean =
181 if (y._1 after x._1) true else false
184 * Calculate periods of activity for a list of timespecs
186 private def coExpandTimespecs(input : List[(DSLTimeSpec, DSLTimeSpec)],
187 from: Date, to: Date) : List[(Date, Date)] = {
188 if (input.size == 0) return List()
190 expandTimeSpec(input.head._1, from, to).zip(
191 expandTimeSpec(input.head._2, from, to)) ++
192 coExpandTimespecs(input.tail, from, to)
196 * Expand a List of timespecs.
198 def expandTimeSpecs(spec: List[DSLTimeSpec], from: Date, to: Date):
200 spec.flatMap { t => expandTimeSpec(t, from, to)}
203 * Get the list of time points prescribed by the provided timespec,
204 * within the timeframe between from and to.
206 def expandTimeSpec(spec: DSLTimeSpec, from: Date, to: Date) : List[Date] = {
207 val adjusted = adjustToTime(from, spec.hour, spec.min)
208 findDays(adjusted, to, {
210 (if (spec.mon >= 0) {c.get(Calendar.MONTH) == spec.getCalendarMonth()} else true) &&
211 (if (spec.dom >= 0) {c.get(Calendar.DAY_OF_MONTH) == spec.dom} else true) &&
212 (if (spec.dow >= 0) {c.get(Calendar.DAY_OF_WEEK) == spec.getCalendarDow()} else true)