From: Chuck Hagenbuch Date: Thu, 29 Jan 2009 02:56:02 +0000 (-0500) Subject: initial port of handler methods X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=4aa16bd8985ccbf590ae4adbf587c1412df8e40d;p=horde.git initial port of handler methods --- diff --git a/framework/Date_Parser/lib/Horde/Date/Parser/Locale/Base.php b/framework/Date_Parser/lib/Horde/Date/Parser/Locale/Base.php index aae5818c8..c6537af56 100644 --- a/framework/Date_Parser/lib/Horde/Date/Parser/Locale/Base.php +++ b/framework/Date_Parser/lib/Horde/Date/Parser/Locale/Base.php @@ -1,6 +1,8 @@ [Handler.new([:repeater_time, :repeater_day_portion?], nil)], - - :date => [Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy), - Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy), - Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy), - Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd), - Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od), - Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy), - Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy), - Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy), - Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy), - Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd), - Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)], - - # tonight at 7pm - :anchor => [Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r), - Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r), - Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)], - - # 3 weeks from now, in 2 months - :arrow => [Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p), - Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r), - Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)], - - # 3rd week in march - :narrow => [Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r), - Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)] - } - end - - def tokens_to_span(tokens, options) #:nodoc: - # maybe it's a specific date - - self.definitions[:date].each do |handler| - if handler.match(tokens, self.definitions) - puts "-date" if Chronic.debug - good_tokens = tokens.select { |o| !o.get_tag Separator } - return self.send(handler.handler_method, good_tokens, options) - end - end - - # I guess it's not a specific date, maybe it's just an anchor - - self.definitions[:anchor].each do |handler| - if handler.match(tokens, self.definitions) - puts "-anchor" if Chronic.debug - good_tokens = tokens.select { |o| !o.get_tag Separator } - return self.send(handler.handler_method, good_tokens, options) - end - end - - # not an anchor, perhaps it's an arrow - - self.definitions[:arrow].each do |handler| - if handler.match(tokens, self.definitions) - puts "-arrow" if Chronic.debug - good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlashOrDash) || o.get_tag(SeparatorComma) } - return self.send(handler.handler_method, good_tokens, options) - end - end - - # not an arrow, let's hope it's a narrow - - self.definitions[:narrow].each do |handler| - if handler.match(tokens, self.definitions) - puts "-narrow" if Chronic.debug - #good_tokens = tokens.select { |o| !o.get_tag Separator } - return self.send(handler.handler_method, tokens, options) - end - end - - # I guess you're out of luck! - puts "-none" if Chronic.debug - return nil - end - - #-------------- - - def day_or_time(day_start, time_tokens, options) - outer_span = Span.new(day_start, day_start + (24 * 60 * 60)) - - if !time_tokens.empty? - @now = outer_span.begin - time = get_anchor(dealias_and_disambiguate_times(time_tokens, options), options) - return time - else - return outer_span - end - end - - #-------------- - - def handle_m_d(month, day, time_tokens, options) #:nodoc: - month.start = @now - span = month.this(options[:context]) - - day_start = Time.local(span.begin.year, span.begin.month, day) - - day_or_time(day_start, time_tokens, options) - end - - def handle_rmn_sd(tokens, options) #:nodoc: - handle_m_d(tokens[0].get_tag(RepeaterMonthName), tokens[1].get_tag(ScalarDay).type, tokens[2..tokens.size], options) - end - - def handle_rmn_od(tokens, options) #:nodoc: - handle_m_d(tokens[0].get_tag(RepeaterMonthName), tokens[1].get_tag(OrdinalDay).type, tokens[2..tokens.size], options) - end - - def handle_rmn_sy(tokens, options) #:nodoc: - month = tokens[0].get_tag(RepeaterMonthName).index - year = tokens[1].get_tag(ScalarYear).type - - if month == 12 - next_month_year = year + 1 - next_month_month = 1 - else - next_month_year = year - next_month_month = month + 1 - end - - begin - Span.new(Time.local(year, month), Time.local(next_month_year, next_month_month)) - rescue ArgumentError - nil - end - end - - def handle_rdn_rmn_sd_t_tz_sy(tokens, options) #:nodoc: - month = tokens[1].get_tag(RepeaterMonthName).index - day = tokens[2].get_tag(ScalarDay).type - year = tokens[5].get_tag(ScalarYear).type - - begin - day_start = Time.local(year, month, day) - day_or_time(day_start, [tokens[3]], options) - rescue ArgumentError - nil - end - end - - def handle_rmn_sd_sy(tokens, options) #:nodoc: - month = tokens[0].get_tag(RepeaterMonthName).index - day = tokens[1].get_tag(ScalarDay).type - year = tokens[2].get_tag(ScalarYear).type - - time_tokens = tokens.last(tokens.size - 3) - - begin - day_start = Time.local(year, month, day) - day_or_time(day_start, time_tokens, options) - rescue ArgumentError - nil - end - end - - def handle_sd_rmn_sy(tokens, options) #:nodoc: - new_tokens = [tokens[1], tokens[0], tokens[2]] - time_tokens = tokens.last(tokens.size - 3) - self.handle_rmn_sd_sy(new_tokens + time_tokens, options) - end - - def handle_sm_sd_sy(tokens, options) #:nodoc: - month = tokens[0].get_tag(ScalarMonth).type - day = tokens[1].get_tag(ScalarDay).type - year = tokens[2].get_tag(ScalarYear).type - - time_tokens = tokens.last(tokens.size - 3) - - begin - day_start = Time.local(year, month, day) #:nodoc: - day_or_time(day_start, time_tokens, options) - rescue ArgumentError - nil - end - end - - def handle_sd_sm_sy(tokens, options) #:nodoc: - new_tokens = [tokens[1], tokens[0], tokens[2]] - time_tokens = tokens.last(tokens.size - 3) - self.handle_sm_sd_sy(new_tokens + time_tokens, options) - end - - def handle_sy_sm_sd(tokens, options) #:nodoc: - new_tokens = [tokens[1], tokens[2], tokens[0]] - time_tokens = tokens.last(tokens.size - 3) - self.handle_sm_sd_sy(new_tokens + time_tokens, options) - end - - def handle_sm_sy(tokens, options) #:nodoc: - month = tokens[0].get_tag(ScalarMonth).type - year = tokens[1].get_tag(ScalarYear).type - - if month == 12 - next_month_year = year + 1 - next_month_month = 1 - else - next_month_year = year - next_month_month = month + 1 - end - - begin - Span.new(Time.local(year, month), Time.local(next_month_year, next_month_month)) - rescue ArgumentError - nil - end - end - - # anchors - - def handle_r(tokens, options) #:nodoc: - dd_tokens = dealias_and_disambiguate_times(tokens, options) - self.get_anchor(dd_tokens, options) - end - - def handle_r_g_r(tokens, options) #:nodoc: - new_tokens = [tokens[1], tokens[0], tokens[2]] - self.handle_r(new_tokens, options) - end - - # arrows - - def handle_srp(tokens, span, options) #:nodoc: - distance = tokens[0].get_tag(Scalar).type - repeater = tokens[1].get_tag(Repeater) - pointer = tokens[2].get_tag(Pointer).type - - repeater.offset(span, distance, pointer) - end - - def handle_s_r_p(tokens, options) #:nodoc: - repeater = tokens[1].get_tag(Repeater) - - # span = - # case true - # when [RepeaterYear, RepeaterSeason, RepeaterSeasonName, RepeaterMonth, RepeaterMonthName, RepeaterFortnight, RepeaterWeek].include?(repeater.class) - # self.parse("this hour", :guess => false, :now => @now) - # when [RepeaterWeekend, RepeaterDay, RepeaterDayName, RepeaterDayPortion, RepeaterHour].include?(repeater.class) - # self.parse("this minute", :guess => false, :now => @now) - # when [RepeaterMinute, RepeaterSecond].include?(repeater.class) - # self.parse("this second", :guess => false, :now => @now) - # else - # raise(ChronicPain, "Invalid repeater: #{repeater.class}") - # end - - span = self.parse("this second", :guess => false, :now => @now) - - self.handle_srp(tokens, span, options) - end - - def handle_p_s_r(tokens, options) #:nodoc: - new_tokens = [tokens[1], tokens[2], tokens[0]] - self.handle_s_r_p(new_tokens, options) - end - - def handle_s_r_p_a(tokens, options) #:nodoc: - anchor_span = get_anchor(tokens[3..tokens.size - 1], options) - self.handle_srp(tokens, anchor_span, options) - end - - # narrows - - def handle_orr(tokens, outer_span, options) #:nodoc: - repeater = tokens[1].get_tag(Repeater) - repeater.start = outer_span.begin - 1 - ordinal = tokens[0].get_tag(Ordinal).type - span = nil - ordinal.times do - span = repeater.next(:future) - if span.begin > outer_span.end - span = nil - break - end - end - span - end - - def handle_o_r_s_r(tokens, options) #:nodoc: - outer_span = get_anchor([tokens[3]], options) - handle_orr(tokens[0..1], outer_span, options) - end - - def handle_o_r_g_r(tokens, options) #:nodoc: - outer_span = get_anchor(tokens[2..3], options) - handle_orr(tokens[0..1], outer_span, options) - end - - # support methods - - def get_anchor(tokens, options) #:nodoc: - grabber = Grabber.new(:this) - pointer = :future - - repeaters = self.get_repeaters(tokens) - repeaters.size.times { tokens.pop } - - if tokens.first && tokens.first.get_tag(Grabber) - grabber = tokens.first.get_tag(Grabber) - tokens.pop - end - - head = repeaters.shift - head.start = @now - - case grabber.type - when :last - outer_span = head.next(:past) - when :this - if repeaters.size > 0 - outer_span = head.this(:none) - else - outer_span = head.this(options[:context]) - end - when :next - outer_span = head.next(:future) - else raise(ChronicPain, "Invalid grabber") - end - - puts "--#{outer_span}" if Chronic.debug - anchor = find_within(repeaters, outer_span, pointer) - end - - def get_repeaters(tokens) #:nodoc: - repeaters = [] - tokens.each do |token| - if t = token.get_tag(Repeater) - repeaters << t - end - end - repeaters.sort.reverse - end - - # Recursively finds repeaters within other repeaters. - # Returns a Span representing the innermost time span - # or nil if no repeater union could be found - def find_within(tags, span, pointer) #:nodoc: - puts "--#{span}" if Chronic.debug - return span if tags.empty? - - head, *rest = tags - head.start = pointer == :future ? span.begin : span.end - h = head.this(:none) - - if span.include?(h.begin) || span.include?(h.end) - return find_within(rest, h, pointer) - else - return nil - end - end - - def dealias_and_disambiguate_times(tokens, options) #:nodoc: - # handle aliases of am/pm - # 5:00 in the morning -> 5:00 am - # 7:00 in the evening -> 7:00 pm - - day_portion_index = nil - tokens.each_with_index do |t, i| - if t.get_tag(RepeaterDayPortion) - day_portion_index = i - break - end - end - - time_index = nil - tokens.each_with_index do |t, i| - if t.get_tag(RepeaterTime) - time_index = i - break - end - end - - if (day_portion_index && time_index) - t1 = tokens[day_portion_index] - t1tag = t1.get_tag(RepeaterDayPortion) - - if [:morning].include?(t1tag.type) - puts '--morning->am' if Chronic.debug - t1.untag(RepeaterDayPortion) - t1.tag(RepeaterDayPortion.new(:am)) - elsif [:afternoon, :evening, :night].include?(t1tag.type) - puts "--#{t1tag.type}->pm" if Chronic.debug - t1.untag(RepeaterDayPortion) - t1.tag(RepeaterDayPortion.new(:pm)) - end - end - - # tokens.each_with_index do |t0, i| - # t1 = tokens[i + 1] - # if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime) - # if [:morning].include?(t1tag.type) - # puts '--morning->am' if Chronic.debug - # t1.untag(RepeaterDayPortion) - # t1.tag(RepeaterDayPortion.new(:am)) - # elsif [:afternoon, :evening, :night].include?(t1tag.type) - # puts "--#{t1tag.type}->pm" if Chronic.debug - # t1.untag(RepeaterDayPortion) - # t1.tag(RepeaterDayPortion.new(:pm)) - # end - # end - # end - - # handle ambiguous times if :ambiguous_time_range is specified - if options[:ambiguous_time_range] != :none - ttokens = [] - tokens.each_with_index do |t0, i| - ttokens << t0 - t1 = tokens[i + 1] - if t0.get_tag(RepeaterTime) && t0.get_tag(RepeaterTime).type.ambiguous? && (!t1 || !t1.get_tag(RepeaterDayPortion)) - distoken = Token.new('disambiguator') - distoken.tag(RepeaterDayPortion.new(options[:ambiguous_time_range])) - ttokens << distoken - end - end - tokens = ttokens - end - - tokens - end - - end + public function initDefinitions() + { + if ($this->definitions) { return; } + + $this->definitions = array( + 'time' => array( + new Horde_Date_Parser_Handler(array('repeater_time', 'repeater_day_portion?'), null), + ), + + 'date' => array( + new Horde_Date_Parser_Handler(array('repeater_day_name', 'repeater_month_name', 'scalar_day', 'repeater_time', 'time_zone', 'scalar_year'), 'handle_rdn_rmn_sd_t_tz_sy'), + new Horde_Date_Parser_Handler(array('repeater_month_name', 'scalar_day', 'scalar_year'), 'handle_rmn_sd_sy'), + new Horde_Date_Parser_Handler(array('repeater_month_name', 'scalar_day', 'scalar_year', 'separator_at?', 'time?'), 'handle_rmn_sd_sy'), + new Horde_Date_Parser_Handler(array('repeater_month_name', 'scalar_day', 'separator_at?', 'time?'), 'handle_rmn_sd'), + new Horde_Date_Parser_Handler(array('repeater_month_name', 'ordinal_day', 'separator_at?', 'time?'), 'handle_rmn_od'), + new Horde_Date_Parser_Handler(array('repeater_month_name', 'scalar_year'), 'handle_rmn_sy'), + new Horde_Date_Parser_Handler(array('scalar_day', 'repeater_month_name', 'scalar_year', 'separator_at?', 'time?'), 'handle_sd_rmn_sy'), + new Horde_Date_Parser_Handler(array('scalar_month', 'separator_slash_or_dash', 'scalar_day', 'separator_slash_or_dash', 'scalar_year', 'separator_at?', 'time?'), 'handle_sm_sd_sy'), + new Horde_Date_Parser_Handler(array('scalar_day', 'separator_slash_or_dash', 'scalar_month', 'separator_slash_or_dash', 'scalar_year', 'separator_at?', 'time?'), 'handle_sd_sm_sy'), + new Horde_Date_Parser_Handler(array('scalar_year', 'separator_slash_or_dash', 'scalar_month', 'separator_slash_or_dash', 'scalar_day', 'separator_at?', 'time?'), 'handle_sy_sm_sd'), + new Horde_Date_Parser_Handler(array('scalar_month', 'separator_slash_or_dash', 'scalar_year'), 'handle_sm_sy'), + ), + + // tonight at 7pm + 'anchor' => array( + new Horde_Date_Parser_Handler(array('grabber?', 'repeater', 'separator_at?', 'repeater?', 'repeater?'), 'handle_r'), + new Horde_Date_Parser_Handler(array('grabber?', 'repeater', 'repeater', 'separator_at?', 'repeater?', 'repeater?'), 'handle_r'), + new Horde_Date_Parser_Handler(array('repeater', 'grabber', 'repeater'), 'handle_r_g_r'), + ), + + // 3 weeks from now, in 2 months + 'arrow' => array( + new Horde_Date_Parser_Handler(array('scalar', 'repeater', 'pointer'), 'handle_s_r_p'), + new Horde_Date_Parser_Handler(array('pointer', 'scalar', 'repeater'), 'handle_p_s_r'), + new Horde_Date_Parser_Handler(array('scalar', 'repeater', 'pointer', 'anchor'), 'handle_s_r_p_a'), + ), + + // 3rd week in march + 'narrow' => array( + new Horde_Date_Parser_Handler(array('ordinal', 'repeater', 'separator_in', 'repeater'), 'handle_o_r_s_r'), + new Horde_Date_Parser_Handler(array('ordinal', 'repeater', 'grabber', 'repeater'), 'handle_o_r_g_r'), + ), + ); + } + + public function tokensToSpans($tokens, $options) + { + $this->initDefinitions(); + + // maybe it's a specific date + foreach ($this->definitions['date'] as $handler) { + if ($handler->match($tokens, $this->definitions)) { + if (Horde_Date_Parser::$debug) { echo "-date\n"; } + $goodTokens = array_filter($tokens, create_function('$o', 'return !$o->getTag("Separator");')); + return call_user_func(array($this, $handler->handlerMethod), $goodTokens, $options); + } + } + + // I guess it's not a specific date, maybe it's just an anchor + foreach ($this->definitions['anchor'] as $handler) { + if ($handler->match($tokens, $this->definitions)) { + if (Horde_Date_Parser::$debug) { echo "-anchor\n"; } + $goodTokens = array_filter($tokens, create_function('$o', 'return !$o->getTag("Separator");')); + return call_user_func(array($this, $handler->handlerMethod), $goodTokens, $options); + } + } + + // not an anchor, perhaps it's an arrow + foreach ($this->definitions['arrow'] as $handler) { + if ($handler->match($tokens, $this->definitions)) { + if (Horde_Date_Parser::$debug) { echo "-arrow\n"; } + $goodTokens = array_filter($tokens, create_function('$o', 'return !$o->getTag("SeparatorAt") && !$o->getTag("SeparatorSlashOrDash") && !$o->getTag("SeparatorComma");')); + return call_user_func(array($this, $handler->handlerMethod), $goodTokens, $options); + } + } + + // not an arrow, let's hope it's a narrow + foreach ($this->definitions['narrow'] as $handler) { + if ($handler->match($tokens, $this->definitions)) { + if (Horde_Date_Parser::$debug) { echo "-narrow\n"; } + //good_tokens = tokens.select { |o| !o.get_tag Separator } + return call_user_func(array($this, $handler->handlerMethod), $tokens, $options); + } + } + + // I guess you're out of luck! + if (Horde_Date_Parser::$debug) { echo "-none\n"; } + return null; + } + + + public function dayOrTime($dayStart, $timeTokens, $options) + { + $outerSpan = new Horde_Date_Span($dayStart, $dayStart + (24 * 60 * 60)); + + if (!empty($timeTokens)) { + $this->now = $outerSpan->begin; + return $this->getAnchor($this->dealiasAndDisambiguateTimes($timeTokens, $options), $options); + } else { + return $outerSpan; + } + } + + + public function handle_m_d($month, $day, $timeTokens, $options) + { + $month->start = $this->now; + $span = $month->this($options['context']); + + $dayStart = new Horde_Date(array('year' => $span->begin->year, 'month' => $span->begin->month, 'day' => $day)); + return $this->dayOrTime($dayStart, $timeTokens, $options); + } + + public function handle_rmn_sd($tokens, $options) + { + return $this->handle_m_d($tokens[0]->getTag('RepeaterMonthName'), $tokens[1]->getTag('ScalarDay')->type, array_slice($tokens, 2), $options); + } + + public function handle_rmn_od($tokens, $options) + { + return $this->handle_m_d($tokens[0]->getTag('RepeaterMonthName'), $tokens[1]->getTag('OrdinalDay')->type, array_slice($tokens, 2), $options); + } + + public function handle_rmn_sy($tokens, $options) + { + $month = $tokens[0]->getTag('RepeaterMonthName')->index; + $year = $tokens[1]->getTag('ScalarYear')->type; + + try { + return new Horde_Date_Span(new Horde_Date(array('year' => $year, 'month' => $month)), new Horde_Date(array('year' => $year, 'month' => $month + 1))); + } catch (Exception $e) { + return null; + } + } + + public function handle_rdn_rmn_sd_t_tz_sy($tokens, $options) + { + $month = $tokens[1]->getTag('RepeaterMonthName')->index; + $day = $tokens[2]->getTag('ScalarDay')->type; + $year = $tokens[5]->getTag('ScalarYear')->type; + + try { + $dayStart = new Horde_Date(array('year' => $year, 'month' => $month, 'day' => $day)); + return $this->dayOrTime($daystart, array($tokens[3]), $options); + } catch (Exception $e) { + return null; + } + } + + public function handle_rmn_sd_sy($tokens, $options) + { + $month = $tokens[0]->getTag('RepeaterMonthName')->index; + $day = $tokens[1]->getTag('ScalarDay')->type; + $year = $tokens[2]->getTag('ScalarYear')->type; + + $timeTokens = array_slice($tokens, 3); + + try { + $dayStart = new Horde_Date(array('year' => $year, 'month' => $month, 'day' => $day)); + return $this->dayOrTime($dayStart, $timeTokens, $options); + } catch (Exception $e) { + return null; + } + } + + public function handle_sd_rmn_sy($tokens, $options) + { + $newTokens = array($tokens[1], $tokens[0], $tokens[2]); + $timeTokens = array_slice($tokens, 3); + return $this->handle_rmn_sd_sy($newTokens + $timeTokens, $options); + } + + public function handle_sm_sd_sy($tokens, $options) + { + $month = $tokens[0]->getTag('ScalarMonth')->type; + $day = $tokens[1]->getTag('ScalarDay')->type; + $year = $tokens[2]->getTag('ScalarYear')->type; + + $timeTokens = array_slice($tokens, 3); + + try { + $dayStart = new Horde_Date(array('year' => $year, 'month' => $month, 'day' => $day)); + return $this->dayOrTime($dayStart, $timeTokens, $options); + } catch (Exception $e) { + return null; + } + } + + public function handle_sd_sm_sy($tokens, $options) + { + $newTokens = array($tokens[1], $tokens[0], $tokens[2]); + $timeTokens = array_slice($tokens, 3); + return $this->handle_sm_sd_sy($newTokens + $timeTokens, $options); + } + + public function handle_sy_sm_sd($tokens, $options) + { + $newTokens = array($tokens[1], $tokens[2], $tokens[0]); + $timeTokens = array_slice($tokens, 3); + return $this->handle_sm_sd_sy($newTokens + $timeTokens, $options); + } + + public function handle_sm_sy($tokens, $options) + { + $month = $tokens[0]->getTag('ScalarMonth')->type; + $year = $tokens[1]->getTag('ScalarYear')->type; + + try { + return new Horde_Date_Span(new Horde_Date(array('year' => $year, 'month' => $month)), new Horde_Date(array('year' => $year, 'month' => $month = 1))); + } catch (Exception $e) { + return null; + } + } + + + /*########################################################################## + # Anchors + ##########################################################################*/ + + public function handle_r($tokens, $options) + { + $ddTokens = $this->dealiasAndDisambiguateTimes($tokens, $options); + return $this->getAnchor($ddTokens, $options); + } + + public function handle_r_g_r($tokens, $options) + { + $newTokens = array($tokens[1], $tokens[0], $tokens[2]); + return $this->handle_r($newTokens, $options); + } + + + /*########################################################################## + # Arrows + ##########################################################################*/ + + public function handle_srp($tokens, $span, $options) + { + $distance = $tokens[0]->getTag('Scalar')->type; + $repeater = $tokens[1]->getTag('Repeater'); + $pointer = $tokens[2]->getTag('Pointer')->type; + + return $repeater->offset($span, $distance, $pointer); + } + + public function handle_s_r_p($tokens, $options) + { + $repeater = $tokens[1]->getTag('Repeater'); + + /* + # span = + # case true + # when [RepeaterYear, RepeaterSeason, RepeaterSeasonName, RepeaterMonth, RepeaterMonthName, RepeaterFortnight, RepeaterWeek].include?(repeater.class) + # self.parse("this hour", :guess => false, :now => @now) + # when [RepeaterWeekend, RepeaterDay, RepeaterDayName, RepeaterDayPortion, RepeaterHour].include?(repeater.class) + # self.parse("this minute", :guess => false, :now => @now) + # when [RepeaterMinute, RepeaterSecond].include?(repeater.class) + # self.parse("this second", :guess => false, :now => @now) + # else + # raise(ChronicPain, "Invalid repeater: #{repeater.class}") + # end + */ + + $span = $this->parse('this second', array('guess' => false, 'now' => $this->now)); + return $this->handle_srp($tokens, $span, $options); + } + + public function handle_p_s_r($tokens, $options) + { + $newTokens = array($tokens[1], $tokens[2], $tokens[0]); + return $this->handle_s_r_p($newTokens, $options); + } + + public function handle_s_r_p_a($tokens, $options) + { + $anchorSpan = $this->getAnchor(array_slice($tokens, 3), $options); + return $this->handle_srp($tokens, $anchorSpan, $options); + } + + + /*########################################################################## + # Narrows + ##########################################################################*/ + + public function handle_orr($tokens, $outerSpan, $options) + { + $repeater = $tokens[1]->getTag('Repeater'); + $repeater->start = $outerSpan->begin - 1; + $ordinal = $tokens[0]->getTag('Ordinal')->type; + $span = null; + + for ($i = 0; $i < $ordinal; $i++) { + $span = $repeater->next('future'); + if ($span->begin > $outerSpan->end) { + $span = null; + break; + } + } + return $span; + } + + public function handle_o_r_s_r($tokens, $options) + { + $outerSpan = $this->getAnchor(array($tokens[3]), $options); + return $this->handle_orr(array($tokens[0], $tokens[1]), $outerSpan, $options); + } + + public function handle_o_r_g_r($tokens, $options) + { + $outerSpan = $this->getAnchor(array($tokens[2], $tokens[3]), $options); + return $this->handle_orr(array($tokens[0], $tokens[1]), $outerSpan, $options); + } + + + /*########################################################################## + # Support Methods + ##########################################################################*/ + + public function getAnchor($tokens, $options) + { + $grabber = $this->componentFactory('Grabber', array('this')); + $pointer = 'future'; + + $repeaters = $this->getRepeaters($tokens); + for ($i = 0, $size = count($repeaters); $i < $size; $i++) { + array_pop($tokens); + } + + if (count($tokens) && $tokens[0]->getTag('Grabber')) { + $grabber = $tokens[0]->getTag('Grabber'); + array_pop($tokens); + } + + $head = array_shift($repeaters); + $head->start = $this->now; + + switch ($grabber->type) { + case 'last': + $outerSpan = $head->next('past'); + break; + + case 'this': + if (count($repeaters)) { + $outerSpan = $head->this('none'); + } else { + $outerSpan = $head->this($options['context']); + } + break; + + case 'next': + $outerSpan = $head->next('future'); + break; + + default: + throw new Horde_Date_Parser_Exception('Invalid grabber ' . $grabber->type); + } + + if (Horde_Date_Parser::$debug) { echo "--$outerSpan\n"; } + return $this->findWithin($repeaters, $outerSpan, $pointer); + } + + public function getRepeaters($tokens) + { + $repeaters = array(); + foreach ($tokens as $token) { + if ($t = $token->getTag('Repeater')) { + $repeaters[] = $t; + } + } + + rsort($repeaters); + return $repeaters; + } + + /** + * Recursively finds repeaters within other repeaters. Returns a Span + * representing the innermost time span or null if no repeater union could + * be found + */ + public function findWithin($tags, $span, $pointer) + { + if (Horde_Date_Parser::$debug) { echo "--$span\n"; } + if (empty($tags)) { return $span; } + + $head = array_shift($tags); + $rest = $tags; + $head->start = ($pointer == 'future') ? $span->begin : $span->end; + $h = $head->this('none'); + + if ($span->include($h->begin) || $span->include($h->end)) { + return $this->findWithin($rest, $h, $pointer); + } else { + return null; + } + } + + /** + * handle aliases of am/pm + * 5:00 in the morning -> 5:00 am + * 7:00 in the evening -> 7:00 pm + */ + public function dealiasAndDisambiguateTimes($tokens, $options) + { + $dayPortionIndex = null; + foreach ($tokens as $i => $t) { + if ($t->getTag('RepeaterDayPortion')) { + $dayPortionIndex = $i; + break; + } + } + + $timeIndex = null; + foreach ($tokens as $i => $t) { + if ($t->getTag('RepeaterTime')) { + $timeIndex = $i; + break; + } + } + + if ($dayPortionIndex && $timeIndex) { + $t1 = $tokens[$dayPortionIndex]; + $t1tag = $t1->getTag('RepeaterDayPortion'); + + if ($t1tag->type == 'morning') { + if (Horde_Date_Parser::$debug) { echo "--morning->am\n"; } + $t1->untag('RepeaterDayPortion'); + $t1->tag(new Horde_Date_Parser_Locale_Base_RepeaterDayPortion('am')); + } elseif (in_array($t1tag->type, array('afternoon', 'evening', 'night'))) { + if (Horde_Date_Parser::$debug) { echo "--{$t1tag->type}->pm\n"; } + $t1->untag('RepeaterDayPortion'); + $t1->tag(new Horde_Date_Parser_Locale_Base_RepeaterDayPortion('pm')); + } + } + + /* + # tokens.each_with_index do |t0, i| + # t1 = tokens[i + 1] + # if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime) + # if [:morning].include?(t1tag.type) + # puts '--morning->am' if Chronic.debug + # t1.untag(RepeaterDayPortion) + # t1.tag(RepeaterDayPortion.new(:am)) + # elsif [:afternoon, :evening, :night].include?(t1tag.type) + # puts "--#{t1tag.type}->pm" if Chronic.debug + # t1.untag(RepeaterDayPortion) + # t1.tag(RepeaterDayPortion.new(:pm)) + # end + # end + # end + */ + + // handle ambiguous times if :ambiguousTimeRange is specified + if ($options['ambiguousTimeRange'] != 'none') { + $ttokens = array(); + foreach ($tokens as $i => $t0) { + $ttokens[] = $t0; + $t1 = isset($tokens[$i + 1]) ? $tokens[$i + 1] : null; + if ($t0->getTag('RepeaterTime') && $t0->getTag('RepeaterTime')->type->ambiguous() && (!$t1 || !$t1->getTag('RepeaterDayPortion'))) { + $distoken = new Horde_Date_Parser_Token('disambiguator'); + $distoken->tag(new Horde_Date_Parser_Locale_Base_RepeaterDayPortion($options['ambiguousTimeRange'])); + $ttokens[] = $distoken; + } + } + + $tokens = $ttokens; + } + + return $tokens; + } }