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>
47 trait DSLUtils extends DateUtils {
49 val maxdate = new Date(Int.MaxValue * 1000L)
50 val mindate = new Date(0)
52 def resolveEffectiveAlgorithmsForTimeslot(timeslot: Timeslot,
54 Map[Timeslot, DSLAlgorithm] =
55 resolveEffective[DSLAlgorithm](timeslot, Some(agr.algorithm))
58 def resolveEffectivePricelistsForTimeslot(timeslot: Timeslot,
60 Map[Timeslot, DSLPriceList] =
61 resolveEffective[DSLPriceList](timeslot, Some(agr.pricelist))
64 * Resolves the DSLTimeBoundedItem which is active within the
65 * provided timeslot. If the provided timeslot does not fit entirely or at all
66 * into a timeslot within which a DSLTimeBoundedItem is active, then the
67 * resolution takes the following paths:
69 * - If the provided timeslot (a) partially fits into the DSLTimeBoundedItem
70 * timeslot (b) and the next active time slot is (c), then the provided
71 * timeslot is split in three parts `(a.start...b.end)`,
72 * `(b.end...c.start)` and `(c.start...a.end)`
75 def resolveEffective[T <: DSLTimeBoundedItem[T]](timeslot: Timeslot,
79 val policy = tbi match {
80 case None => return Map()
84 val eff = allEffectiveTimeslots(policy.effective,
85 oneYearBack(timeslot.from, policy.effective.from),
86 oneYearAhead (timeslot.to, policy.effective.to.getOrElse(maxdate)))
89 timeslot.overlappingTimeslots(eff).flatMap {
92 timeslot.nonOverlappingTimeslots(eff).flatMap {
93 t => resolveEffective(t, policy.overrides)
98 * Get a list of timeslots within which a timeframe is not effective.
100 def ineffectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to: Option[Date]):
103 buildNotEffectiveList(effectiveTimeslots(spec, from, to)) sortWith sorter
106 private def buildNotEffectiveList(l :List[Timeslot]) :
109 if (l.isEmpty) return List()
110 if (l.tail.isEmpty) return List()
112 assert(l.head.to.getTime < l.tail.head.from.getTime)
115 List(Timeslot(new Date(l.head.to.getTime + 1),
116 new Date(l.tail.head.from.getTime - 1))) ++
117 buildNotEffectiveList(l.tail)
121 * Merges overlapping timeslots. The merge is exhaustive only if the
122 * provided list is sorted in increasing timeslot from order.
124 def mergeOverlaps(list: List[Timeslot]): List[Timeslot] = {
125 list.foldLeft(List[Timeslot]()) {
129 else if (a.tail.isEmpty)
132 val merged = a.tail.head.merge(b)
133 a ++ (if (merged.size == 1) merged else List(b))
139 * Get a list of all timeslots within which the provided time frame
142 def allEffectiveTimeslots(spec: DSLTimeFrame):
145 val l = spec.repeat.flatMap {
146 r => effectiveTimeslots(r, spec.from, spec.to)
152 * Get a list of all timeslots within which a timeframe
153 * is effective, whithin the provided time bounds.
155 def allEffectiveTimeslots(spec: DSLTimeFrame, from: Date, to: Date):
158 //A timeframe with no repetition defined
159 if (spec.repeat.isEmpty) {
160 val fromDate = if (spec.from.before(from)) from else spec.from
161 val toDate = if (spec.to.getOrElse(to).after(to)) to else spec.to.getOrElse(to)
162 return List(Timeslot(fromDate, toDate))
165 val l = spec.repeat.flatMap {
166 r => effectiveTimeslots(r, from, Some(to))
172 * Get a list of all time periods within which a time frame is active.
173 * If the to date is None, the expansion takes place within a timeframe
174 * between `from .. from` + 1 year. The result is returned sorted by
175 * timeframe start date.
177 def effectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to: Option[Date]):
180 assert(spec.start.size == spec.end.size)
182 val endDate = to match {
183 case None => //One year from now
184 val c = new GregorianCalendar()
186 c.add(Calendar.YEAR, 1)
191 coExpandTimespecs(spec.start.zip(spec.end), from, endDate) sortWith sorter
195 * Utility function to put timeslots in increasing start timestamp order
197 def sorter(x: Timeslot, y: Timeslot) : Boolean =
198 if (y.from after x.from) true else false
201 * Calculate periods of activity for a list of timespecs
203 private def coExpandTimespecs(input: List[(DSLTimeSpec, DSLTimeSpec)],
204 from: Date, to: Date): List[Timeslot] = {
205 if (input.size == 0) return List()
207 expandTimeSpec(input.head._1, from, to).zip(
208 expandTimeSpec(input.head._2, from, to)).map(
209 l => Timeslot(l._1, l._2)
211 coExpandTimespecs(input.tail, from, to)
215 * Expand a List of timespecs.
217 def expandTimeSpecs(spec: List[DSLTimeSpec], from: Date, to: Date):
219 spec.flatMap { t => expandTimeSpec(t, from, to)}
222 * Get the list of time points prescribed by the provided timespec,
223 * within the timeframe between from and to.
225 def expandTimeSpec(spec: DSLTimeSpec, from: Date, to: Date) : List[Date] = {
226 val adjusted = adjustToTime(from, spec.hour, spec.min)
227 findDays(adjusted, to, {
229 (if (spec.mon >= 0) {c.get(Calendar.MONTH) == spec.getCalendarMonth()} else true) &&
230 (if (spec.dom >= 0) {c.get(Calendar.DAY_OF_MONTH) == spec.dom} else true) &&
231 (if (spec.dow >= 0) {c.get(Calendar.DAY_OF_WEEK) == spec.getCalendarDow()} else true)