1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
|
#!/usr/bin/env ruby -w
# encoding: UTF-8
#
# = Interval.rb -- The TaskJuggler III Project Management Software
#
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
# by Chris Schlaeger <cs@taskjuggler.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
require 'taskjuggler/TjTime'
class TaskJuggler
# This is the based class used to store several kinds of intervals in
# derived classes.
class Interval
attr_reader :start, :end
# Create a new Interval object. _s_ is the interval start, _e_ the
# interval end (not included).
def initialize(s, e)
@start = s
@end = e
# The end must not be before the start.
if @end < @start
raise ArgumentError, "Invalid interval (#{s} - #{e})"
end
end
# Return true if _arg_ is contained within the Interval. It can either
# be a single TjTime or another Interval.
def contains?(arg)
if arg.is_a?(Interval)
raise ArgumentError, "Class mismatch" if self.class != arg.class
return @start <= arg.start && arg.end <= @end
else
raise ArgumentError, "Class mismatch" if @start.class != arg.class
return @start <= arg && arg < @end
end
end
# Check whether the Interval _arg_ overlaps with this Interval.
def overlaps?(arg)
if arg.is_a?(Interval)
raise ArgumentError, "Class mismatch" if self.class != arg.class
return (@start <= arg.start && arg.start < @end) ||
(arg.start <= @start && @start < arg.end)
else
raise ArgumentError, "Class mismatch" if @start.class != arg.class
return @start <= arg && arg < @end
end
end
# Return a new Interval that contains the overlap of self and the Interval
# _iv_. In case there is no overlap, nil is returned.
def intersection(iv)
raise ArgumentError, "Class mismatch" if self.class != iv.class
newStart = @start > iv.start ? @start : iv.start
newEnd = @end < iv.end ? @end : iv.end
newStart < newEnd ? self.class.new(newStart, newEnd) : nil
end
# Append or prepend the Interval _iv_ to self. If _iv_ does not directly
# attach to self, just return self.
def combine(iv)
raise ArgumentError, "Class mismatch" if self.class != iv.class
if iv.end == @start
# Prepend iv
Array.new self.class.new(iv.start, @end)
elsif @end == iv.start
# Append iv
Array.new self.class.new(@start, iv.end)
else
self
end
end
# Compare self with Interval _iv_. This function only works for
# non-overlapping Interval objects.
def <=>(iv)
raise ArgumentError, "Class mismatch" if self.class != iv.class
if @end < iv.start
-1
elsif iv.end < @start
1
end
0
end
# Return true if the Interval _iv_ describes an identical time period.
def ==(iv)
raise ArgumentError, "Class mismatch" if self.class != iv.class
@start == iv.start && @end == iv.end
end
end
# The TimeInterval class provides objects that model a time interval. The
# start end end time are represented as seconds after Jan 1, 1970. The start
# is part of the interval, the end is not.
class TimeInterval < Interval
attr_accessor :start, :end
# Create a new TimeInterval. _args_ can be three different kind of arguments.
#
# a and b should be TjTime objects.
#
# TimeInterval.new(a, b) | -> Interval(a, b)
# TimeInterval.new(a) | -> Interval(a, a)
# TimeInterval.new(iv) | -> Interval(iv.start, iv.end)
#
def initialize(*args)
if args.length == 1
if args[0].is_a?(TjTime)
# Just one argument, a date
super(args[0], args[0])
elsif args[0].is_a?(TimeInterval)
# Just one argument, a TimeInterval
super(args[0].start, args[0].end)
else
raise ArgumentError, "Illegal argument 1: #{args[0].class}"
end
elsif args.length == 2
# Two arguments, a start and end date
unless args[0].is_a?(TjTime)
raise ArgumentError, "Interval start must be a date, not a " +
"#{args[0].class}"
end
unless args[1].is_a?(TjTime)
raise ArgumentError, "Interval end must be a date, not a" +
"#{args[1].class}"
end
super(args[0], args[1])
else
raise ArgumentError, "Too many arguments: #{args.length}"
end
end
# Return the duration of the TimeInterval.
def duration
@end - @start
end
# Turn the TimeInterval into a human readable form.
def to_s
@start.to_s + ' - ' + @end.to_s
end
end
# This class describes an interval of a scoreboard. The start and end of the
# interval are stored as indexes but can always be converted back to TjTime
# objects if needed.
class ScoreboardInterval < Interval
attr_reader :sbStart, :slotDuration
# Create a new ScoreboardInterval. _args_ can be three different kind of
# arguments.
#
# sbStart must be a TjTime of the scoreboard start
# slotDuration must be the duration of the scoreboard slots in seconds
# a and b should be TjTime or Integer objects that describe the start and
# end time or index of the interval.
#
# TimeInterval.new(iv)
# TimeInterval.new(sbStart, slotDuration, a)
# TimeInterval.new(sbStart, slotDuration, a, b)
#
def initialize(*args)
case args.length
when 1
# If there is only one argument, it must be a ScoreboardInterval.
if args[0].is_a?(ScoreboardInterval)
@sbStart = args[0].sbStart
@slotDuration = args[0].slotDuration
# Just one argument, a TimeInterval
super(args[0].start, args[0].end)
else
raise ArgumentError, "Illegal argument 1: #{args[0].class}"
end
when 3
@sbStart = args[0]
@slotDuration = args[1]
# If the third argument is a date we convert it to a scoreboard index.
args[2] = dateToIndex(args[2]) if args[2].is_a?(TjTime)
if args[2].is_a?(Integer)
super(args[2], args[2])
else
raise ArgumentError, "Illegal argument 3: #{args[0].class}"
end
when 4
@sbStart = args[0]
@slotDuration = args[1]
# If the third and forth arguments are a date we convert them to a
# scoreboard index.
args[2] = dateToIndex(args[2]) if args[2].is_a?(TjTime)
args[3] = dateToIndex(args[3]) if args[3].is_a?(TjTime)
if !(args[2].is_a?(Integer))
raise ArgumentError, "Interval start must be an index or TjTime, " +
"not a #{args[2].class}"
end
if !(args[3].is_a?(Integer))
raise ArgumentError, "Interval end must be an index or TjTime, " +
"not a #{args[3].class}"
end
super(args[2], args[3])
else
raise ArgumentError, "Wrong number of arguments: #{args.length}"
end
unless @sbStart.is_a?(TjTime)
raise ArgumentError, "sbStart must be a TjTime object, not a" +
"#{@sbStart.class}"
end
unless @slotDuration.is_a?(Integer)
raise ArgumentError, "slotDuration must be an Integer, not a " +
"#{@slotDuration.class}"
end
end
# Assign the start of the interval. +arg+ can be an Integer or
# TjTime object.
def start=(arg)
case arg
when Integer
@start = arg
when TjTime
@start = dateToIndex(arg)
else
raise ArgumentError, "Unsupported class #{arg.class}"
end
end
# Assign the start of the interval. +arg+ can be an Integer or
# TjTime object.
def end=(arg)
case arg
when Integer
@end = arg
when TjTime
@end = dateToIndex(arg)
else
raise ArgumentError, "Unsupported class #{arg.class}"
end
end
# Return the interval start as TjTime object.
def startDate
indexToDate(@start)
end
# Return the interval end as TjTime object.
def endDate
indexToDate(@end)
end
# Return the duration of the ScoreboardInterval.
def duration
indexToDate(@end) - indexToDate(@start)
end
# Turn the ScoreboardInterval into a human readable form.
def to_s
indexToDate(@start).to_s + ' - ' + indexToDate(@end).to_s
end
private
def dateToIndex(date)
(date - @sbStart).to_i / @slotDuration
end
def indexToDate(index)
@sbStart + (index * @slotDuration)
end
end
end
|