2 * Copyright 2011-2012 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.shortNameOfClass
41 import gr.grnet.aquarium.util.date.MutableDateCalc
42 import collection.mutable
45 * Represents an effectivity timeframe.
47 * @author Georgios Gousios <gousiosg@gmail.com>
49 case class DSLTimeFrame (
52 repeat: List[DSLTimeFrameRepeat] /*,
53 cronRepeat: List[DSLCronSpec]*/
58 assert(x.after(from), "Time frame to (%s) must be after from (%s)"
59 .format(x.getTime, from.getTime))
63 private val start = from.getTime
64 private val infinity = new Date(Long.MaxValue)
65 private val end = to.getOrElse(infinity).getTime
68 * Given the timeslot "t" and the timeslot of this frame (start,end)
69 * compute the intersection of the two timeslots (return a list of common intervals)
70 * which satisfy the cron expressions. If no expressions are present which just
71 * take the intersection. If there exist cron specs (cron,start) we compute the
72 * intersection between "t" and the fine-grained subtimeslots (of this time frame).
75 def intervalsOf(t:Timeslot) : List[Timeslot]=
77 t.overlappingTimeslots(List(Timeslot(start,end)))
79 val result = new mutable.ListBuffer[Timeslot]()
81 firstValidTimeslot(t.from) match {
87 val step = 1000L*60L // +1 minute step
90 minValidAfter(t,offset) match {
93 case Some(next_timeslot) =>
94 result += next_timeslot
95 offset = next_timeslot.to
99 /*nextValidAfter(t.from) match {
101 val ret = addRest(t.from)
104 if(d_next.after(t.to)){
111 val ret = Timeslot(t.from,d_next) :: addRest(d_next)
117 * Find the first smallest (smallest here means with the least "end" timestamp) timeslot
118 * from beginning "offset" and ending at "d_end".
120 * Return None if "offset" is not bounded in this time frame and is not bounded by
121 * a valid "start" / "end" cron spec.
124 private def firstValidTimeslot(offset:Date) : Option[Timeslot] = {
125 def minimum (min:Option[Timeslot],d_start:Date,d_end:Date) : Option[Timeslot] = {
128 Some(Timeslot(d_start,d_end))
129 case Some(Timeslot(_,d_min)) =>
130 if(d_min.getTime < d_start.getTime)
133 Some(Timeslot(d_start,d_end))
136 val to = this.to.getOrElse(infinity)
137 repeat.foldLeft (None:Option[Timeslot]) {
139 r.getEnd.nextValidDate(from,to,offset) match {
143 r.getStart.nextValidDate(from,to,from) match {
144 case None => /* there is no valid starting date in the entire interval!*/
147 r.getStart.nextValidDate(from,to,offset) match {
148 case None => /* so there is at least one starting date before offset
149 and there are no starting dates after. So we are inside
151 if(from.getTime <= offset.getTime && offset.getTime <= to.getTime)
152 minimum(min,offset,d_end)
155 case Some(d_start) =>
156 if(d_start.getTime > d_end.getTime) {
157 /* there is another starting date which occurs after the ending date.
158 * So we are within the interval*/
159 minimum(min,offset,d_end)
160 } else { /* we are outside the interval*/
170 private def minValidAfter(t:Timeslot,offset:Date) : Option[Timeslot] =
171 repeat.foldLeft (None:Option[Timeslot]) {
173 r.getStart.nextValidDate(t.from,t.to,offset) match {
176 case Some(d_start) =>
177 r.getEnd.nextValidDate(t.from,t.to,d_start) match {
183 Some(Timeslot(d_start,d_end))
184 case Some(Timeslot(d_min,_)) =>
185 if(d_min.getTime < d_start.getTime)
188 Some(Timeslot(d_start,d_end))
195 override def toString =
196 //"%s(%s, %s,\n %s\n || cron: %s)".format(
197 "%s(%s, %s,%s)".format(
198 shortNameOfClass(classOf[DSLTimeFrame]),
199 new MutableDateCalc(from).toString,
200 to.map(t => new MutableDateCalc(t)),
205 /*def includes(d :Date) : Boolean = {
207 start <= now && now <= end && (cronRepeat.isEmpty || (cronRepeat.exists {_ occurs d}))
210 /*/* Return the next VALID, MAXIMUM and CONTINUOUS date after "d" for this time frame.
211 * The *returned time* is the end of the time frame if there exist no cron specs.
212 * Otherwise, the returned time is maximum of the dates that
213 * 1. are continuous (no interruptions) from "d"
215 * 3. belong in the time frame
217 * The input parameter "d" must occur within the time frame. If there exist
218 * cron specs "d" must belong in the time spec specified by cron.
220 def nextValidAfter1(d: Date) : Option[Date] = {
222 if (now < start || now > end) None /* undefined valid is date not contained here*/
223 else if (cronRepeat.isEmpty) Some(to.getOrElse(infinity)) /* No specs! The next valid date is the end of time frame*/
225 /* filter out cron specs that DO NOT contain d */
226 val includes_d = cronRepeat filter { _ includes d }
227 /* get the next CONTINUOUS valid date from "d" using the DSLCronSpec class */
228 val next_cont_d = includes_d map {_ nextContinuousValidDate d}
229 /* filter the resulting dates "d1" so that they occur after "d" and are within the timeframe bounds*/
230 val filter_invalid = next_cont_d filter {d1:Date => d1 != null &&
232 start <= d1.getTime &&
234 /* sort the next dates and select the MAXIMUM one*/
235 val sorted = filter_invalid sortWith {_.getTime > _.getTime}
237 case Nil => None /* Nothing interesting was found*/
238 case hd::_ => if(hd!=null) Some(hd) else None /* the MAXIMAL next date */
242 var time_var : List[Long] = Nil
243 private def time[R](block: => R): R = {
244 val t0 = System.nanoTime()
245 val result = block // call-by-name
246 val t1 = System.nanoTime()
247 time_var = ((t1-t0)/1000000L) :: time_var
248 //Console.err.println("Time__xxxx: " + time_var)
251 /*/* the nextValidDate cannot be outside the end limit of this time frame
252 Description: given date "d" get the nextValidDate after "d"
254 def nextValidAfter(d:Date) : Option[Date] = {
255 def nextDate(r:DSLTimeFrameRepeat) : Option[Date] =
256 time(r.getStart.getMaxValidBeforeDate(from,to.getOrElse(infinity),d)) match {
260 r.getEnd.nextValidDate(from,to.getOrElse(infinity),min) match {
264 if(d_max.getTime < d.getTime)
272 val d_time = d.getTime
273 if(d_time < start || d_time > end) None
274 else Some(to.getOrElse(infinity))
277 var tmpDate : Option[Date] = null
278 repeat.collectFirst {
279 case r if({tmpDate=nextDate(r);tmpDate} != None) => tmpDate.get
286 val emptyTimeFrame = DSLTimeFrame(new Date(0), None, List())//,List())