<artifactId>joda-convert</artifactId>
<version>1.1</version>
</dependency>
+
+ <dependency> <!-- quartz stuff -->
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz</artifactId>
+ <version>2.1.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz-oracle</artifactId>
+ <version>2.1.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz-weblogic</artifactId>
+ <version>2.1.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.quartz-scheduler</groupId>
+ <artifactId>quartz-jboss</artifactId>
+ <version>2.1.5</version>
+ </dependency>
</dependencies>
<!--Build configuration-->
case YAMLEmptyNode => None
}
- val effective = timeframe / Vocabulary.repeat match {
- case x: YAMLListNode => parseTimeFrameRepeat(x)
- case YAMLEmptyNode => List()
+ val repeat =
+ timeframe / Vocabulary.repeat match {
+ case x: YAMLListNode => parseTimeFrameRepeat(x)
+ case YAMLEmptyNode => Nil
}
- DSLTimeFrame(from, to, effective)
+ /*val cronRepeat = timeframe / "cron" match {
+ case x: YAMLListNode => parseCronSpecs(x)
+ case _ => Nil
+ }*/
+
+ DSLTimeFrame(from, to,repeat)
}
+ /*def parseCronSpecs(l: YAMLListNode) : List[DSLCronSpec] =
+ l.listValue map {
+ case x:YAMLStringNode => new DSLCronSpec(x.string.trim)
+ case YAMLEmptyNode => throw new DSLParseException("Bad cron expression")
+ }*/
+
/** Parse a resource frame repeat block */
def parseTimeFrameRepeat(tmr: YAMLListNode): List[DSLTimeFrameRepeat] = {
if (tmr.isEmpty)
return List()
+
/** Parse a resource frame entry (start, end tags) */
def findInMap(repeat: YAMLMapNode,
tag: String) : (String, List[DSLTimeSpec]) = {
val start = findInMap(tmr.head.asInstanceOf[YAMLMapNode], Vocabulary.start)
val end = findInMap(tmr.head.asInstanceOf[YAMLMapNode], Vocabulary.end)
-
+ assert(start._2.size == end._2.size)
DSLTimeFrameRepeat(start._2, end._2, start._1,end._1) :: parseTimeFrameRepeat(tmr.tail)
}
val input = cron.get(ii).toString
if (input.equals("*"))
(-1).until(0).toList
+ //TODO: DO NOT ACCEPT "-" and ","
else if (input.contains('-')) {
val ints = input.split('-')
ints(0).toInt.until(ints(1).toInt + 1).toList
b <- splitMultiVals(1)
c <- splitMultiVals(2)
d <- splitMultiVals(3)
- e <- splitMultiVals(4) } yield
- DSLTimeSpec(a,b,c,d,e)
+ e <- splitMultiVals(4)}
+ yield
+ DSLTimeSpec(a,b,c,d,e)
}
}
--- /dev/null
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium.logic.accounting.dsl
+
+import org.quartz.CronExpression
+import java.util.Date
+
+/**
+ * @author Prodromos Gerakios <pgerakios@grnet.gr>
+ */
+
+case class DSLCronSpec(cronSpec: String) extends DSLItem {
+
+ private val cronExpr = {
+ val e = "00 " + cronSpec.trim //IMPORTANT: WE DO NOT CARE ABOUT SECONDS!!!
+ val l = e.split(" ")
+ assert(l.size == 6,"Invalid cron specification")
+ //for {ll <- l } Console.err.println(ll)
+ (l(3),l(5)) match {
+ case ("?",_) | (_,"?") => ()
+ case (_,"*") => l.update(5,"?")
+ case ("*",_) => l.update(3,"?")
+ }
+ val e1 = l.foldLeft("") { (s,elt) => s + " " + elt}
+ //Console.err.println("e = " + e + " and e1 = " + e1)
+ new CronExpression(e1)
+ }
+
+ def includes(d:Date) : Boolean =
+ cronExpr isSatisfiedBy d
+
+ /* the next valid date cannot outlive (min,max)*/
+ def nextValidDate(min0:Date,max0:Date,d:Date) : Option[Date] =
+ (cronExpr getNextValidTimeAfter d) match {
+ case null =>
+ None
+ case d1 =>
+ val (min,max,e) = (min0.getTime,max0.getTime,d1.getTime)
+ if(e < min || e>max)
+ None
+ else
+ Some({assert(d1.getTime>=d.getTime);d1})
+ }
+
+ /*def nextValidDate(d:Date) : Date = {
+ val ret = cronExpr getNextValidTimeAfter d
+ ret
+ } */
+
+ /*def nextContinuousValidDate(d:Date) : Date = {
+ val d1 = cronExpr getNextInvalidTimeAfter d
+ if (d1 != null)
+ d1.setTime(d1.getTime - 1000) /* 1 sec before was valid so return this*/
+ d1
+ }
+
+
+ def getMaxValidBeforeDate(min0:Date , max0:Date,d:Date) : Option[Date] ={
+ val (min,max,e) = (min0.getTime,max0.getTime,d.getTime)
+ if(min > e || max < e) None
+ else if(includes(d)) Some(d)
+ else {
+ var end = e
+ var start = min //0L
+ var best:Date = null
+ var iterations=0L
+ val tmp = new Date(0L)
+ val step = 10000L * 60L // 1 minute
+ while(start < end) {
+ iterations += 1
+ val pivot = (end - start) / 2 + start
+ tmp.setTime(pivot)
+ val next = nextValidDate(tmp)
+ if(next == null){ /* no valid time after pivot*/
+ end = pivot-step; // pivot minus one
+ } else {
+ val p = next.getTime()
+ if(p < e) { /* next date occurs before e*/
+ val post = next.getTime < d.getTime
+ assert(post,"BUG!!!")
+ best = next
+ start = p + step
+ } else if( p > e) { /* next date occurs after e*/
+ end = pivot-step
+ }
+ else assert(false,"This should not happen")
+ }
+ // System.err.println("Start: " + new Date(start) + " end: " + new Date(end));
+ }
+ System.err.println("Iterations " + iterations);
+ if(best!=null) Some(best) else None
+ }
+ }
+ */
+}
+
+object DSLCronSpec {
+ val emptyCronSpec = new DSLCronSpec("? ? ? ? ?")
+}
\ No newline at end of file
import java.util.Date
import scala.collection._
-
+//TODO: REMOVE THIS CLASS
/**
* A semantic checker for the Aquarium accounting DSL.
*
* @author Georgios Gousios <gousiosg@gmail.com>
*/
-trait DSLSemanticChecks {
+/*trait DSLSemanticChecks {
/**
* Functions to apply by default when checking consistency for
sealed trait DSLConsistencyMsg
case class DSLConsistencyWarn(warn: String) extends DSLConsistencyMsg
-case class DSLConsistencyError(err: String) extends DSLConsistencyMsg
+case class DSLConsistencyError(err: String) extends DSLConsistencyMsg*/
import gr.grnet.aquarium.util.shortNameOfClass
-import java.util.Date
+import java.util.{GregorianCalendar, Date, Calendar}
import gr.grnet.aquarium.util.date.MutableDateCalc
+import collection.mutable
+import java.util
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
/**
* Represents an effectivity timeframe.
case class DSLTimeFrame (
from: Date,
to: Option[Date],
- repeat: List[DSLTimeFrameRepeat]
+ repeat: List[DSLTimeFrameRepeat] /*,
+ cronRepeat: List[DSLCronSpec]*/
) extends DSLItem {
to match {
case None =>
}
+ private val start = from.getTime
+ private val infinity = new Date(Long.MaxValue)
+ private val end = to.getOrElse(infinity).getTime
+
+ /*
+ * Given the timeslot "t" and the timeslot of this frame (start,end)
+ * compute the intersection of the two timeslots (return a list of common intervals)
+ * which satisfy the cron expressions. If no expressions are present which just
+ * take the intersection. If there exist cron specs (cron,start) we compute the
+ * intersection between "t" and the fine-grained subtimeslots (of this time frame).
+ *
+ * */
+ def intervalsOf(t:Timeslot) : List[Timeslot]=
+ if(repeat.isEmpty)
+ t.overlappingTimeslots(List(Timeslot(start,end)))
+ else {
+ val result = new mutable.ListBuffer[Timeslot]()
+ var offset = t.from
+ firstValidTimeslot(t.from) match {
+ case None => ()
+ case Some(t) =>
+ result += t
+ offset = t.to
+ }
+ val step = 1000L*60L // +1 minute step
+ var continue = true
+ while(continue)
+ minValidAfter(t,offset) match {
+ case None =>
+ continue = false
+ case Some(next_timeslot) =>
+ result += next_timeslot
+ offset = next_timeslot.to
+ }
+ result.toList
+ }
+ /*nextValidAfter(t.from) match {
+ case None =>
+ val ret = addRest(t.from)
+ ret
+ case Some(d_next) =>
+ if(d_next.after(t.to)){
+ if(repeat.isEmpty)
+ List(t)
+ else
+ Nil
+ }
+ else {
+ val ret = Timeslot(t.from,d_next) :: addRest(d_next)
+ ret
+ }
+ } */
+
+ /*
+ * Find the first smallest (smallest here means with the least "end" timestamp) timeslot
+ * from beginning "offset" and ending at "d_end".
+ *
+ * Return None if "offset" is not bounded in this time frame and is not bounded by
+ * a valid "start" / "end" cron spec.
+ *
+ * */
+ private def firstValidTimeslot(offset:Date) : Option[Timeslot] = {
+ def minimum (min:Option[Timeslot],d_start:Date,d_end:Date) : Option[Timeslot] = {
+ min match {
+ case None =>
+ Some(Timeslot(d_start,d_end))
+ case Some(Timeslot(_,d_min)) =>
+ if(d_min.getTime < d_start.getTime)
+ min
+ else
+ Some(Timeslot(d_start,d_end))
+ }
+ }
+ val to = this.to.getOrElse(infinity)
+ repeat.foldLeft (None:Option[Timeslot]) {
+ (min,r) =>
+ r.getEnd.nextValidDate(from,to,offset) match {
+ case None =>
+ min
+ case Some(d_end) =>
+ r.getStart.nextValidDate(from,to,from) match {
+ case None => /* there is no valid starting date in the entire interval!*/
+ min
+ case Some(_) => /**/
+ r.getStart.nextValidDate(from,to,offset) match {
+ case None => /* so there is at least one starting date before offset
+ and there are no starting dates after. So we are inside
+ an interval*/
+ if(from.getTime <= offset.getTime && offset.getTime <= to.getTime)
+ minimum(min,offset,d_end)
+ else
+ min
+ case Some(d_start) =>
+ if(d_start.getTime > d_end.getTime) {
+ /* there is another starting date which occurs after the ending date.
+ * So we are within the interval*/
+ minimum(min,offset,d_end)
+ } else { /* we are outside the interval*/
+ min
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ private def minValidAfter(t:Timeslot,offset:Date) : Option[Timeslot] =
+ repeat.foldLeft (None:Option[Timeslot]) {
+ (min,r) =>
+ r.getStart.nextValidDate(t.from,t.to,offset) match {
+ case None =>
+ min
+ case Some(d_start) =>
+ r.getEnd.nextValidDate(t.from,t.to,d_start) match {
+ case None =>
+ min
+ case Some(d_end) =>
+ min match {
+ case None =>
+ Some(Timeslot(d_start,d_end))
+ case Some(Timeslot(d_min,_)) =>
+ if(d_min.getTime < d_start.getTime)
+ min
+ else
+ Some(Timeslot(d_start,d_end))
+ }
+ }
+ }
+ }
+
override def toMap(): Map[String, Any] = {
val toTS = to match {
case Some(x) => Map(Vocabulary.to -> x.getTime)
}
toTS ++
- Map(Vocabulary.from -> from.getTime) ++
- repeatMap
+ Map(Vocabulary.from -> from.getTime) ++
+ repeatMap
}
- override def toString = "%s(%s, %s, %s)".format(
+ override def toString =
+ //"%s(%s, %s,\n %s\n || cron: %s)".format(
+ "%s(%s, %s,%s)".format(
shortNameOfClass(classOf[DSLTimeFrame]),
new MutableDateCalc(from).toString,
to.map(t => new MutableDateCalc(t)),
- repeat
+ repeat /*,
+ cronRepeat*/
)
+
+ /*def includes(d :Date) : Boolean = {
+ val now = d.getTime
+ start <= now && now <= end && (cronRepeat.isEmpty || (cronRepeat.exists {_ occurs d}))
+ } */
+
+ /*/* Return the next VALID, MAXIMUM and CONTINUOUS date after "d" for this time frame.
+ * The *returned time* is the end of the time frame if there exist no cron specs.
+ * Otherwise, the returned time is maximum of the dates that
+ * 1. are continuous (no interruptions) from "d"
+ * 2. occur after "d"
+ * 3. belong in the time frame
+ *
+ * The input parameter "d" must occur within the time frame. If there exist
+ * cron specs "d" must belong in the time spec specified by cron.
+ */
+ def nextValidAfter1(d: Date) : Option[Date] = {
+ val now = d.getTime
+ if (now < start || now > end) None /* undefined valid is date not contained here*/
+ else if (cronRepeat.isEmpty) Some(to.getOrElse(infinity)) /* No specs! The next valid date is the end of time frame*/
+ else {
+ /* filter out cron specs that DO NOT contain d */
+ val includes_d = cronRepeat filter { _ includes d }
+ /* get the next CONTINUOUS valid date from "d" using the DSLCronSpec class */
+ val next_cont_d = includes_d map {_ nextContinuousValidDate d}
+ /* filter the resulting dates "d1" so that they occur after "d" and are within the timeframe bounds*/
+ val filter_invalid = next_cont_d filter {d1:Date => d1 != null &&
+ d1.getTime > now &&
+ start <= d1.getTime &&
+ d1.getTime <= end}
+ /* sort the next dates and select the MAXIMUM one*/
+ val sorted = filter_invalid sortWith {_.getTime > _.getTime}
+ sorted match {
+ case Nil => None /* Nothing interesting was found*/
+ case hd::_ => if(hd!=null) Some(hd) else None /* the MAXIMAL next date */
+ }
+ }
+ }
+ var time_var : List[Long] = Nil
+ private def time[R](block: => R): R = {
+ val t0 = System.nanoTime()
+ val result = block // call-by-name
+ val t1 = System.nanoTime()
+ time_var = ((t1-t0)/1000000L) :: time_var
+ //Console.err.println("Time__xxxx: " + time_var)
+ result
+ } */
+ /*/* the nextValidDate cannot be outside the end limit of this time frame
+ Description: given date "d" get the nextValidDate after "d"
+ * */
+ def nextValidAfter(d:Date) : Option[Date] = {
+ def nextDate(r:DSLTimeFrameRepeat) : Option[Date] =
+ time(r.getStart.getMaxValidBeforeDate(from,to.getOrElse(infinity),d)) match {
+ case None =>
+ None
+ case Some(min) =>
+ r.getEnd.nextValidDate(from,to.getOrElse(infinity),min) match {
+ case None =>
+ None
+ case Some(d_max) =>
+ if(d_max.getTime < d.getTime)
+ None
+ else
+ Some(d_max)
+ }
+
+ }
+ if(repeat.isEmpty){
+ val d_time = d.getTime
+ if(d_time < start || d_time > end) None
+ else Some(to.getOrElse(infinity))
+ }
+ else {
+ var tmpDate : Option[Date] = null
+ repeat.collectFirst {
+ case r if({tmpDate=nextDate(r);tmpDate} != None) => tmpDate.get
+ }
+ }
+ }*/
}
object DSLTimeFrame{
- val emptyTimeFrame = DSLTimeFrame(new Date(0), None, List())
+ val emptyTimeFrame = DSLTimeFrame(new Date(0), None, List())//,List())
}
\ No newline at end of file
package gr.grnet.aquarium.logic.accounting.dsl
+import org.quartz.CronExpression
+import java.util.Date
+
/**
* Encapsulates a repeating item
*
endCron: String
) extends DSLItem {
+ private def makeCronExpression(s: String) : CronExpression = {
+ val e = "0 " + s.trim
+ val l = e.split(" ")
+ (l(3),l(5)) match {
+ case ("?",_) | (_,"?") => ()
+ case (_,"*") => l.update(5,"?")
+ case ("*",_) => l.update(3,"?")
+ }
+ val e1 = l.foldLeft("") { (s,elt) => s + " " + elt}
+ new CronExpression(e1)
+ }
+ val getStart = DSLCronSpec(startCron)
+ val getEnd = DSLCronSpec(endCron)
+
assert(start.size == end.size,
("start (%s) and end (%s) cron-like specs do not expand to equal" +
" number of repetition definitions").format(startCron, endCron))
assert(-1 <= mon && 12 > mon && mon != 0)
assert(-1 <= dow && 7 > dow)
+ /* calendar-related methods*/
+ private val cal = new GregorianCalendar()
+ private def initDay(init:Long) {
+ cal.setTimeInMillis(init)
+ cal.set(Calendar.MINUTE,min)
+ cal.set(Calendar.HOUR_OF_DAY,hour)
+ cal.set(Calendar.SECOND,0)
+ }
+ private def nextValidDay(end:Long) (): Date = {
+ var ret : Date = null
+ while (cal.getTimeInMillis <= end && ret == null) {
+ val valid : Boolean = (mon < 0 || cal.get(Calendar.MONTH) == getCalendarMonth()) &&
+ (dom < 0 || cal.get(Calendar.DAY_OF_MONTH) == dom) &&
+ (dow < 0 || cal.get(Calendar.DAY_OF_WEEK) == getCalendarDow())
+ if(valid)
+ ret = cal.getTime
+ cal.add(Calendar.DAY_OF_YEAR, 1)
+ }
+ ret
+ }
def expandTimeSpec(from: Date, to: Date) : List[Date] = {
- val c = new GregorianCalendar()
- c.setTime { /*adjust time given from "from" variable */
- c.setTime(from)
- c.set(Calendar.MINUTE,this.min)
- c.set(Calendar.HOUR_OF_DAY,this.hour)
- /*if(c.getTimeInMillis < from.getTime) {
- //c.add(Calendar.DAY_OF_YEAR, 1)
- // assert(c.getTimeInMillis >= from.getTime,c.getTime + " >=" + from) /* sanity check */
- }*/
- c.getTime
- }
- def equals () : Boolean =
- (this.mon < 0 || c.get(Calendar.MONTH) == this.getCalendarMonth()) &&
- (this.dom < 0 || c.get(Calendar.DAY_OF_MONTH) == this.dom) &&
- (this.dow < 0 || c.get(Calendar.DAY_OF_WEEK) == this.getCalendarDow())
-
val result = new mutable.ListBuffer[Date]()
- //Console.err.println("\n\nBEGIN\n\n")
- while (c.getTimeInMillis <= to.getTime) {
- val b : Boolean = equals
- //Console.err.println(c.getTime + "\t\t" + to + " ==>" + (if(b) "YES" else "NO"))
- if (b) result += new Date(c.getTime.getTime)
- c.add(Calendar.DAY_OF_YEAR, 1)
- }
- //Console.err.println("\n\nEND\n\n")
+ val nextDay = this.nextValidDay (to.getTime) _
+ var d : Date = null
+ initDay(from.getTime)
+ while({d=nextDay();d}!=null) result += d
result.toList
}
-
/** Day of week conversions to stay compatible with [[java.util.Calendar]] */
private def getCalendarDow(): Int = dow match {
case 0 => Calendar.SUNDAY
}
object DSLTimeSpec {
- val emtpyTimeSpec = DSLTimeSpec(0, 0, 0, 0, 0)
+ val emtpyTimeSpec = DSLTimeSpec(0,0, -1, -1, -1)
+
+ def expandTimeSpec(d0:DSLTimeSpec,d1:DSLTimeSpec, from: Date, to: Date) : List[(Date,Date)] = {
+ assert(d0!=null)
+ assert(d1!=null)
+ assert(from!=null)
+ assert(to!=null)
+ val result = new mutable.ListBuffer[(Date,Date)]()
+ val d0_nextDay = d0.nextValidDay (to.getTime) _ /* iterator for valid dates of d0*/
+ val d1_nextDay = d1.nextValidDay (to.getTime) _ /* iterator for valid dates of d1*/
+ var d0_d : Date = null
+ d0.initDay(from.getTime)
+ while({d0_d=d0_nextDay();d0_d}!=null){
+ val d1_d : Date = {d1.initDay(d0_d.getTime);d1_nextDay()}
+ if(d1_d!=null) result += ((d0_d,d1_d))
+ }
+ result.toList
+ }
}
\ No newline at end of file
immutable.SortedMap[Timeslot, DSLPriceList] =
resolveEffective[DSLPriceList](timeslot,agr.pricelist)
+ /*private def printPolicy[T <: DSLTimeBoundedItem[T]](t : T) : Unit = {
+ Console.err.println("Policy " + t.name + " " + t.toTimeslot + " DETAIL : " + t.effective)
+ t.overrides match {
+ case None => Console.println
+ case Some(t) => printPolicy(t)
+ }
+ }
+
+ private def printMap[T <: DSLTimeBoundedItem[T]](m: immutable.SortedMap[Timeslot, T]) = {
+ Console.err.println("BEGIN MAP: ")
+ for { (t,p) <- m.toList } Console.err.println("Timeslot " + t + "\t\t" + p.name)
+ Console.err.println("END MAP")
+ } */
+
+ def resolveEffective[T <: DSLTimeBoundedItem[T]](timeslot0: Timeslot,policy: T):
+ immutable.SortedMap[Timeslot, T] = {
+ //Console.err.println("\n\nInput timeslot: " + timeslot0 + "\n\n")
+ ///printPolicy(policy)
+ val ret = resolveEffective3(timeslot0,policy) //HERE
+ //printMap(ret)
+ ret
+ }
+
+ def resolveEffective3[T <: DSLTimeBoundedItem[T]](timeslot0: Timeslot,policy: T):
+ immutable.SortedMap[Timeslot, T] = {
+ assert(policy.toTimeslot contains timeslot0,"Policy does not contain timeslot")
+ val timeslot = timeslot0 //TODO: timeslot0.align(5000)
+ val subtimeslots_of_this_policy = Timeslot.mergeOverlaps(policy.effective intervalsOf timeslot)
+ val subtimeslots_NOT_IN_this_policy = Timeslot.mergeOverlaps(timeslot.nonOverlappingTimeslots
+ (subtimeslots_of_this_policy))
+ val policy_map = subtimeslots_of_this_policy.foldLeft (immutable.SortedMap[Timeslot, T]())
+ {(map,t) =>
+ //Console.err.println("Adding timeslot" + t + " for policy " + policy.name)
+ map + ((t,policy))
+ }
+ val other_policy_map = policy.overrides match {
+ case None =>
+ immutable.SortedMap[Timeslot, T]()
+ case Some(parent_policy)=>
+ subtimeslots_NOT_IN_this_policy.foldLeft (
+ (immutable.SortedMap[Timeslot, T]()))
+ {(map,t) =>
+ //Console.err.println("Residual timeslot: " + t)
+ map ++ resolveEffective3(t,parent_policy)
+ }
+ }
+ val final_map = policy_map ++ other_policy_map
+ final_map
+ }
+
+ /*def resolveEffective2[T <: DSLTimeBoundedItem[T]](timeslot0: Timeslot,policy: T):
+ immutable.SortedMap[Timeslot, T] = {
+ assert(policy.toTimeslot contains timeslot0,"Policy does not contain timeslot")
+
+ /* generate mappings from timeslots -> policies
+ * Algorithm: find next valid date (starting from timeslot.start) in this policy
+ */
+ val timeslot = timeslot0 //TODO: timeslot0.align(5000)
+ def nextDate(d:Date,p:T) : Option[(Date,T,Boolean)] =
+ (p.effective nextValidAfter d,p.overrides) match {
+ case (None,None) => None
+ case (None,Some(parent_policy)) =>
+ val d1 = nextDate(d,parent_policy)
+ d1
+ case (Some(d1),_) => /* the next valid date cannot occur after the end of timeslot*/
+ if (d1.before(timeslot.to)) Some((d1,p,true)) else Some((timeslot.to,p,false))
+ }
+ def genMap(map: immutable.SortedMap[Timeslot, T],d:Date) : immutable.SortedMap[Timeslot, T] = {
+ val step = 1000L
+ nextDate(d,policy) match {
+ case None => map
+ case Some((d1,policy,cont)) =>
+ val t = Timeslot(d,d1)
+ val map1 = map + (t -> policy)
+ if(cont) genMap(map1,new Date(d1.getTime + step)) // 1 second after d1
+ else map1 /* done */
+ }
+ }
+ val map = genMap(immutable.SortedMap[Timeslot, T](),timeslot.from)
+ map
+ }
+
/**
* Splits the provided timeslot into chunks according to the validity
* timeslots specified by the provided time bounded item. It
* returns a map whose keys are the timeslot chunks and the values
* correspond to the effective time bounded item (algorithm or pricelist).
*/
- def resolveEffective[T <: DSLTimeBoundedItem[T]](timeslot: Timeslot,policy: T):
+ def resolveEffective1[T <: DSLTimeBoundedItem[T]](timeslot: Timeslot,policy: T):
immutable.SortedMap[Timeslot, T] = {
assert(policy.toTimeslot contains timeslot,"Policy does not contain timeslot")
val timeslots_IN_policy = timeslot.overlappingTimeslots(all_timeslots)
val timeslots_NOT_IN_policy = timeslot.nonOverlappingTimeslots(all_timeslots)
- immutable.SortedMap[Timeslot, T]() ++
+ val ret = immutable.SortedMap[Timeslot, T]() ++
/*add [timeslots -> policy] covered by this policy*/
timeslots_IN_policy.flatMap {
- effective_timeslot => Map(effective_timeslot -> policy)
+ effective_timeslot => Map(effective_timeslot -> policy)
} ++
/*add [timeslots -> policy] covered by parent policies */
timeslots_NOT_IN_policy.flatMap { /* search the policy hierarchy for effective timeslots not covered by this policy.*/
not_effective_timeslot => policy.overrides match {
case None => immutable.SortedMap[Timeslot, T]() /*Nothing to do. TODO: throw exception ?*/
- case Some(parent_policy) => resolveEffective(not_effective_timeslot,parent_policy) /* search the policy hierarchy*/
+ case Some(parent_policy) => resolveEffective1(not_effective_timeslot,parent_policy) /* search the policy hierarchy*/
}
}
- }
-
- /**
- * Utility function to put timeslots in increasing start timestamp order
- */
- private def sorter(x: Timeslot, y: Timeslot) : Boolean = y.from after x.from
+ ret
+ }*/
- /**
+ /*
+ /*
* Get a list of all timeslots within which a timeframe
* is effective, whithin the provided time bounds.
*/
val toDate = if (spec.to.getOrElse(t.to).after(t.to)) t.to else spec.to.getOrElse(t.to)
List(Timeslot(fromDate, toDate))
} /* otherwise for all repetitions determine timeslots*/
- else mergeOverlaps(for { r <- spec.repeat
- ts <- effectiveTimeslots(r, t.from, t.to) }
- yield ts)
+ else {
+ val all = for { r <- spec.repeat
+ ts <- effectiveTimeslots(r, t.from, t.to)
+ } yield ts
+ //for{ i <- all} Console.err.println(i)
+ mergeOverlaps(all)
+ }
+ */
+
+
/**
* Merges overlapping timeslots.
*/
- private[logic] def mergeOverlaps(list: List[Timeslot]): List[Timeslot] =
- (list sortWith sorter).foldLeft(List[Timeslot]()) {
- case (Nil,b) =>
- List(b)
- case (hd::Nil,b) =>
- if (hd overlaps b) (hd merge b)::Nil
- else b::hd::Nil
- case (a @ hd::tl,b) =>
- if(hd overlaps b) (hd merge b)::tl
- else b :: a
- }.reverse
-
- /**
+ /*/**
* Get a list of all timeslots within which a time frame is active.
The result is returned sorted by timeframe start date.
*/
def effectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to : Date):
List[Timeslot] =
for { (h1,h2) <- spec.start zip spec.end
- (d1,d2) <- h1.expandTimeSpec(from, to) zip h2.expandTimeSpec(from, to)
+ (d1,d2) <- DSLTimeSpec.expandTimeSpec(h1,h2,from,to)
}
yield Timeslot(d1,d2)
+ */
}
/* Preconditions to ensure correct object creations */
assert(from != null)
assert(to != null)
- assert(from.before(to), "from = %s, to = %s".format(new MutableDateCalc(from), new MutableDateCalc(to)))
+ assert(start <= end, "from = %s, to = %s".format(new MutableDateCalc(from), new MutableDateCalc(to)))
def startsBefore(t: Timeslot) : Boolean = start < t.start
/**
* Check whether this timeslot contains the provided time instant.
*/
- private def includes(t: Date) : Boolean = start <= t.getTime && t.getTime <= end
+ private[dsl] def includes(t: Date) : Boolean = start <= t.getTime && t.getTime <= end
/**
}
}
+ /* align a time slot in "bound_size" boundaries so that
+ * start0 <= start and end0 >= end */
+ def align(bound_size : Long) : Timeslot = {
+ val start0 = (start / bound_size) * bound_size
+ val add_one = if (end % bound_size == 0) 0 else 1
+ val end0 = (end / bound_size + add_one) * bound_size
+ Timeslot(start0,end0)
+ }
+
+ /* returns true when the start and end address are
+ * multiples of bound_size*/
+ def isAligned(bound_size : Long) : Boolean =
+ start % bound_size == 0 && end % bound_size == 0
+
+
/**
* Compares the starting times of two timeslots.
*/
def myString : String = "Timeslot(" + this.start + "," + this.end + ")"
//override def toString() = myString
- override def toString() = toDateString
+ override def toString() =
+ toDateString
def toDateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from), new MutableDateCalc(to))
def toISODateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from).toISOString, new MutableDateCalc(to).toISOString)
def apply(x: Int, y: Int): Timeslot =
new Timeslot(new Date(x), new Date(y))
+
+ def mergeOverlaps(list: List[Timeslot]): List[Timeslot] = {
+ def sorter(x: Timeslot, y: Timeslot) : Boolean = y.from after x.from
+ (list sortWith sorter).foldLeft(List[Timeslot]()) {
+ case (Nil,b) =>
+ List(b)
+ case (hd::Nil,b) =>
+ if (hd overlaps b) (hd merge b)::Nil
+ else b::hd::Nil
+ case (a @ hd::tl,b) =>
+ if(hd overlaps b) (hd merge b)::tl
+ else b :: a
+ }.reverse
+ }
}
\ No newline at end of file
overrides: default
diskspace: 0
effective:
+ #cron: ["00 00 12-14,18-20 * *"] #5 fields!!
repeat:
- start: "00 00 12 * *"
end: "00 00 14 * *"
vmtime: 0.1
diskspace: 0.05
effective:
+ #cron: ["* 02 * * Tue,Wed"]
repeat:
- start: "00 02 * * Tue"
end: "00 02 * * Wed"
vmtime: 0.6
diskspace: 0.05
effective:
+ #cron : ["* 12-14,18-20 * * *"]
repeat:
- start: "00 12 * * *"
end: "00 14 * * *"
val to = new Date(1324214719000L) //Sun Dec 18 15:25:19 +0200 2011
var a = DSLTimeSpec(33, 12, -1, -1, 3)
- var result = a.expandTimeSpec(from, to)
+ var b = DSLTimeSpec(34, 12, -1, -1, 3)
+ var result = DSLTimeSpec.expandTimeSpec(a,b,from, to)
assertEquals(4, result.size)
a = DSLTimeSpec(33, 12, -1, 10, 3) // Timespec falling outside from-to
- result = a.expandTimeSpec(from, to)
+ b = DSLTimeSpec(34, 12, -1, -1, 3)
+ result = DSLTimeSpec.expandTimeSpec(a,b,from, to)
assertEquals(0, result.size)
// Would only return an entry if the 1rst of Dec 2011 is Thursday
a = DSLTimeSpec(33, 12, 1, -1, 3)
- result = a.expandTimeSpec(from, to)
+ b = DSLTimeSpec(34, 12, 1, -1, 3)
+ result = DSLTimeSpec.expandTimeSpec(a,b,from, to)
assertEquals(0, result.size)
// The 9th of Dec 2011 is Friday
//Console.err.println("\n\nBEGIN CALCULATION\t\t" + from + "\t\t" + to + "\n\n")
a = DSLTimeSpec(33, 12, 9, -1, 5)
- result = a.expandTimeSpec(from, to)
+ b = DSLTimeSpec(34, 12, 9, -1, 5)
+ result = DSLTimeSpec.expandTimeSpec(a,b,from, to)
//Console.err.println("\n\nEND CALCULATION: " + result +"\n\n")
assertEquals(1, result.size)
// Every day
a = DSLTimeSpec(33, 12, -1, -1, -1)
- result = a.expandTimeSpec(from, to)
+ b = DSLTimeSpec(34, 12, -1, -1, -1)
+ result = DSLTimeSpec.expandTimeSpec(a,b,from, to)
assertEquals(31, result.size)
-
- //Console.err.println("\n\n@BEGIN CALCULATION\t\t" + from + "\t\t" + to + "\n\n")
- a = DSLTimeSpec(33, 12, -1, -1, 5)
- result = a.expandTimeSpec(from, to)
- //Console.err.println("\n\n@END CALCULATION: " + result +"\n\n")
-// assertEquals(1, result.size)
-
+ from.setTime(1340614800000L) // 06/25/2012 12:00:00
+ to.setTime(1340982000000L) // 06/29/2012 18:00:00
+ a = DSLTimeSpec(00, 12, -1, -1, 1 ) // monday at 12:00
+ b = DSLTimeSpec(00, 19, -1, -1, 5) // friday at 19:00
+ result = DSLTimeSpec.expandTimeSpec(a,b,from, to)
+ assert(result.size==0)
+
+ from.setTime(1340614800000L) // 06/25/2012 12:00:00
+ to.setTime(1341068400000L) // 06/30/2012 18:00:00
+ a = DSLTimeSpec(00, 12, -1, -1, 1 ) // monday at 12:00
+ b = DSLTimeSpec(00, 19, -1, -1, 5) // friday at 19:00
+ result = DSLTimeSpec.expandTimeSpec(a,b,from, to)
+ assert(result.size==1)
}
/*@Test
assertEquals(34, result.size)
}*/
- @Test
- def testMergeOverlaps = {
- var l = List(Timeslot(new Date(3), new Date(5)),Timeslot(new Date(1), new Date(3)))
-
- var result = mergeOverlaps(l)
- assertEquals(1, result.size)
- assertEquals(Timeslot(new Date(1), new Date(5)), result.head)
-
- l = l ++ List(Timeslot(new Date(4), new Date(6)))
- result = mergeOverlaps(l)
- assertEquals(1, result.size)
- assertEquals(Timeslot(new Date(1), new Date(6)), result.head)
-
- l = l ++ List(Timeslot(new Date(7), new Date(8)))
- result = mergeOverlaps(l)
- assertEquals(2, result.size)
- assertEquals(Timeslot(new Date(1), new Date(6)), result.head)
- assertEquals(Timeslot(new Date(7), new Date(8)), result.tail.head)
-
- l = l ++ List(Timeslot(new Date(2), new Date(20)))
- result = mergeOverlaps(l)
- assertEquals(1, result.size)
- assertEquals(Timeslot(new Date(1), new Date(20)), result.head)
- }
-
- @Test
+ /*@Test
def testEffectiveTimeslots = {
val from = new Date(1321621969000L) //Fri Nov 18 15:12:49 +0200 2011
val to = new Date(1324214719000L) //Sun Dec 18 15:25:19 +0200 2011
result = effectiveTimeslots(repeat, new Date(1304121600000L),
new Date(1319932800000L))
assertNotEmpty(result)
- }
+ } */
- @Test
+ /*@Test
def testAllEffectiveTimeslots = {
var from = new Date(1321621969000L) //Fri Nov 18 15:12:49 +0200 2011
val to = new Date(1324214719000L) //Sun Dec 18 15:25:19 +0200 2011
parseCronString("00 20 * * 5"),
"00 18 * * 5",
"00 20 * * 5")
- val tf = DSLTimeFrame(from, None, List(repeat1, repeat2))
+ // val tf = DSLTimeFrame(from, None, List(repeat1, repeat2),Nil)
- var result = allEffectiveTimeslots(tf, Timeslot(from, to))
+ /*var result = allEffectiveTimeslots(tf, Timeslot(from, to))
assertEquals(36, result.size)
testSuccessiveTimeslots(result)
- result = allEffectiveTimeslots(DSLTimeFrame(new Date(0), None, List()),
+ result = allEffectiveTimeslots(DSLTimeFrame(new Date(0), None, List(),Nil),
Timeslot(new Date(14), new Date(40)))
- assertEquals(1, result.size)
- }
+ assertEquals(1, result.size)*/
+ } */
/* @Test
def testNonEffectiveTimeslots = {
return
} */
+ private def times[R](times:Int,block: => R): R = {
+ val count = times
+ val (ret,s) = time(block)
+ var sum = s
+ for { i <- 2 to times} {
+ val (_,t) = time(block)
+ sum += t
+ //System.err.println("time" + t + " count " + count)
+ }
+ //Console.err.println("Elapsed time: " + (sum/count)/1000000L + "\tms")
+ ret
+ }
+
+ private def time[R](block: => R): (R,Long) = {
+ val t0 = System.nanoTime()
+ val result = block // call-by-name
+ val t1 = System.nanoTime()
+ (result,t1-t0)
+ }
+
@Test
def testFindEffective = {
val ts5 = 1322689082000L //Wed, 30 Nov 2011 23:38:02 EET
val ts6 = 1322555880000L //Tue, 29 Nov 2011 10:38:00 EET
- var pricelists = resolveEffectivePricelistsForTimeslot(Timeslot(new Date(ts1), new Date(ts2)), agr)
+ var check = new Timeslot(new Date(1322649482000L), new Date(1322654400000L))
+ //Console.err.println("Check " + check)
+ var pricelists = times(1,resolveEffectivePricelistsForTimeslot(Timeslot(new Date(ts1), new Date(ts2)), agr))
+
assertEquals(2, pricelists.keySet.size)
- assertNotNone(pricelists.get(new Timeslot(new Date(1322654402000L), new Date(1322656682000L))))
+ assertNotNone(pricelists.get(check))
assertEquals("foobar", pricelists.head._2.name)
pricelists = resolveEffectivePricelistsForTimeslot(Timeslot(new Date(ts2), new Date(ts3)), agr)
pricelists = resolveEffectivePricelistsForTimeslot(Timeslot(new Date(ts1), new Date(ts5)), agr)
assertEquals(4, pricelists.keySet.size)
- pricelists = resolveEffectivePricelistsForTimeslot(Timeslot(new Date(ts6), new Date(ts5)), agr)
+ pricelists = times(1,resolveEffectivePricelistsForTimeslot(Timeslot(new Date(ts6), new Date(ts5)), agr))
assertEquals(9, pricelists.keySet.size)
}
var rndEnd = new Date((min + (scala.math.random * (max - min) + 1)).toLong)
while (rndEnd.before(rndStart)) rndEnd = new Date((min + (scala.math.random * (max - min) + 1)).toLong)
- val tf = DSLTimeFrame(rndStart, Some(rndEnd), List(repeat1, repeat2))
+ val tf = DSLTimeFrame(rndStart, Some(rndEnd), List(repeat1, repeat2))//,Nil)
- numResolved += allEffectiveTimeslots(tf, Timeslot(new Date(min), new Date(max))).size
+ numResolved += tf.intervalsOf(Timeslot(new Date(min), new Date(max))).size
+ //allEffectiveTimeslots(tf, Timeslot(new Date(min), new Date(max))).size
}
var total = TimeHelpers.nowMillis() - start
assertEquals(1, result.size)
assertEquals(t, result.head)
}
+
+ @Test
+ def testMergeOverlaps = {
+ var l = List(Timeslot(new Date(3), new Date(5)),Timeslot(new Date(1), new Date(3)))
+
+ var result = Timeslot.mergeOverlaps(l)
+ assertEquals(1, result.size)
+ assertEquals(Timeslot(new Date(1), new Date(5)), result.head)
+
+ l = l ++ List(Timeslot(new Date(4), new Date(6)))
+ result = Timeslot.mergeOverlaps(l)
+ assertEquals(1, result.size)
+ assertEquals(Timeslot(new Date(1), new Date(6)), result.head)
+
+ l = l ++ List(Timeslot(new Date(7), new Date(8)))
+ result = Timeslot.mergeOverlaps(l)
+ assertEquals(2, result.size)
+ assertEquals(Timeslot(new Date(1), new Date(6)), result.head)
+ assertEquals(Timeslot(new Date(7), new Date(8)), result.tail.head)
+
+ l = l ++ List(Timeslot(new Date(2), new Date(20)))
+ result = Timeslot.mergeOverlaps(l)
+ assertEquals(1, result.size)
+ assertEquals(Timeslot(new Date(1), new Date(20)), result.head)
+ }
+
+
}
\ No newline at end of file