[california/wip/725785-create-recurring] Tighten up rrule-adding code in details parser



commit c2904479961b61f2d689dece2334e930a3c723fa
Author: Jim Nelson <jim yorba org>
Date:   Fri Jun 20 15:57:44 2014 -0700

    Tighten up rrule-adding code in details parser

 src/component/component-details-parser.vala |  145 +++++++++++++++------------
 src/tests/tests-quick-add-recurring.vala    |   23 ++++-
 2 files changed, 101 insertions(+), 67 deletions(-)
---
diff --git a/src/component/component-details-parser.vala b/src/component/component-details-parser.vala
index ecebe74..e5ee3cc 100644
--- a/src/component/component-details-parser.vala
+++ b/src/component/component-details-parser.vala
@@ -495,33 +495,22 @@ public class DetailsParser : BaseObject {
         if (rrule != null || specifier == null)
             return false;
         
-        if (specifier.casefolded == DAILY) {
-            set_rrule_daily(1);
-            
-            return true;
-        }
+        if (specifier.casefolded == DAILY)
+            return set_rrule_daily(1);
         
         if (specifier.casefolded == WEEKLY) {
             // set the start date to today unless already known
             if (start_date == null)
                 start_date = Calendar.System.today;
             
-            set_rrule_weekly(iterate<Calendar.DayOfWeek>(start_date.day_of_week).to_array(), 1);
-            
-            return true;
+            return set_rrule_weekly(iterate<Calendar.DayOfWeek>(start_date.day_of_week).to_array(), 1);
         }
         
-        if (specifier.casefolded in UNIT_WEEKDAYS) {
-            set_rrule_weekly(Calendar.DayOfWeek.weekdays, 1);
-            
-            return true;
-        }
+        if (specifier.casefolded in UNIT_WEEKDAYS)
+            return set_rrule_weekly(Calendar.DayOfWeek.weekdays, 1);
         
-        if (specifier.casefolded in UNIT_WEEKENDS) {
-            set_rrule_weekly(Calendar.DayOfWeek.weekend_days, 1);
-            
-            return true;
-        }
+        if (specifier.casefolded in UNIT_WEEKENDS)
+            return set_rrule_weekly(Calendar.DayOfWeek.weekend_days, 1);
         
         return false;
     }
@@ -537,11 +526,14 @@ public class DetailsParser : BaseObject {
         
         // look for an amount modifying the specifier (creating an interval, i.e. "every 2 days"
         // or "every 2nd day", hence parsing for ordinal)
+        bool is_ordinal = false;
         int interval = parse_ordinal(unit);
         if (interval >= 1) {
             unit = stack.pop();
             if (unit == null)
                 return false;
+            
+            is_ordinal = true;
         } else {
             interval = 1;
         }
@@ -549,33 +541,27 @@ public class DetailsParser : BaseObject {
         // a day of the week
         Calendar.DayOfWeek? dow = Calendar.DayOfWeek.parse(unit.casefolded);
         if (dow != null) {
-            set_rrule_weekly(iterate<Calendar.DayOfWeek>(
-                Calendar.System.today.upcoming(dow, true).day_of_week).to_array_list().to_array(),
-                interval);
+            Calendar.DayOfWeek[] by_days = iterate<Calendar.DayOfWeek>(
+                Calendar.System.today.upcoming(dow, true).day_of_week).to_array();
             
-            return true;
+            // if interval is an ordinal, the rule is for "nth day of the month", so it's a week number
+            if (!is_ordinal)
+                return set_rrule_weekly(by_days, interval);
+            else
+                return set_rrule_nth_day_of_week(by_days, interval);
         }
         
         // "day"
-        if (unit.casefolded in UNIT_DAYS) {
-            set_rrule_daily(interval);
-            
-            return true;
-        }
+        if (unit.casefolded in UNIT_DAYS)
+            return set_rrule_daily(interval);
         
         // "weekday"
-        if (unit.casefolded in UNIT_WEEKDAYS) {
-            set_rrule_weekly(Calendar.DayOfWeek.weekdays, interval);
-            
-            return true;
-        }
+        if (unit.casefolded in UNIT_WEEKDAYS)
+            return set_rrule_weekly(Calendar.DayOfWeek.weekdays, interval);
         
         // "weekend"
-        if (unit.casefolded in UNIT_WEEKENDS) {
-            set_rrule_weekly(Calendar.DayOfWeek.weekend_days, interval);
-            
-            return true;
-        }
+        if (unit.casefolded in UNIT_WEEKENDS)
+            return set_rrule_weekly(Calendar.DayOfWeek.weekend_days, interval);
         
         // if no start date, then parse for start date, and if so, treat as yearly event
         if (start_date == null) {
@@ -587,11 +573,8 @@ public class DetailsParser : BaseObject {
                     if (date == null)
                         date = parse_day_month(second, unit);
                     
-                    if (date != null) {
-                        set_rrule_yearly(date, interval);
-                        
-                        return true;
-                    }
+                    if (date != null)
+                        return set_rrule_yearly(date, interval);
                 }
             }
             stack.restore();
@@ -600,9 +583,27 @@ public class DetailsParser : BaseObject {
         return false;
     }
     
-    private void set_rrule_daily(int interval) {
+    // Using the supplied by days, find the first upcoming start_date that matches one of them
+    // that is also the week number (unless zero, which means "any")
+    private void set_byday_start_date(Calendar.DayOfWeek[]? by_days, int week_no) {
+        // find the earliest date in the by_days; if it's earlier than the start_date or the
+        // start_date isn't defined, use the earliest
+        if (by_days != null) {
+             Calendar.Date earliest = Calendar.System.today.upcoming_in_set(
+                from_array<Calendar.DayOfWeek>(by_days).to_hash_set(), true);
+            if (start_date == null || earliest.compare_to(start_date) < 0)
+                start_date = earliest;
+        }
+        
+        // no start_date at this point, then today is it
+        if (start_date == null)
+            start_date = Calendar.System.today;
+    }
+    
+    // "every day"
+    private bool set_rrule_daily(int interval) {
         if (rrule != null)
-            return;
+            return false;
         
         // no start_date at this point, then today is it
         if (start_date == null)
@@ -611,45 +612,59 @@ public class DetailsParser : BaseObject {
         rrule = new RecurrenceRule(iCal.icalrecurrencetype_frequency.DAILY_RECURRENCE);
         rrule.interval = interval;
         rrule.first_of_week = Calendar.System.first_of_week.as_day_of_week();
+        
+        return true;
     }
     
-    private void set_rrule_weekly(Calendar.DayOfWeek[]? by_days, int interval) {
-        Gee.Map<Calendar.DayOfWeek?, int> map = new Gee.HashMap<Calendar.DayOfWeek?, int>();
-        if (by_days != null) {
-            foreach (Calendar.DayOfWeek by_day in by_days)
-                map.set(by_day, 0);
-        }
-        
+    // "every tuesday"
+    private bool set_rrule_weekly(Calendar.DayOfWeek[]? by_days, int interval) {
         if (rrule == null) {
             rrule = new RecurrenceRule(iCal.icalrecurrencetype_frequency.WEEKLY_RECURRENCE);
             rrule.interval = interval;
             rrule.first_of_week = Calendar.System.first_of_week.as_day_of_week();
-            rrule.set_by_rule(RecurrenceRule.ByRule.DAY, RecurrenceRule.encode_days(map));
-        } else {
-            rrule.add_by_rule(RecurrenceRule.ByRule.DAY, RecurrenceRule.encode_days(map));
+        } else if (!rrule.is_weekly) {
+            return false;
         }
         
-        // find the earliest date in the by_days; if it's earlier than the start_date or the
-        // start_date isn't defined, use the earliest
-        if (by_days != null) {
-             Calendar.Date earliest = Calendar.System.today.upcoming_in_set(
-                from_array<Calendar.DayOfWeek>(by_days).to_hash_set(), true);
-            if (start_date == null || earliest.compare_to(start_date) < 0)
-                start_date = earliest;
+        Gee.Map<Calendar.DayOfWeek?, int> map = from_array<Calendar.DayOfWeek>(by_days)
+            .to_hash_map_as_keys<int>(dow => 0);
+        rrule.add_by_rule(RecurrenceRule.ByRule.DAY, RecurrenceRule.encode_days(map));
+        
+        set_byday_start_date(by_days);
+        
+        return true;
+    }
+    
+    // "every first tuesday"
+    private bool set_rrule_nth_day_of_week(Calendar.DayOfWeek[]? by_days, int week_no) {
+        if (rrule == null) {
+            rrule = new RecurrenceRule(iCal.icalrecurrencetype_frequency.MONTHLY_RECURRENCE);
+            rrule.first_of_week = Calendar.System.first_of_week.as_day_of_week();
+        } else if (!rrule.is_monthly) {
+            return false;
         }
+        Gee.Map<Calendar.DayOfWeek?, int> map = from_array<Calendar.DayOfWeek>(by_days)
+            .to_hash_map_as_keys<int>(dow => 0);
+        rrule.add_by_rule(RecurrenceRule.ByRule.DAY, RecurrenceRule.encode_days(map));
         
-        // no start_date at this point, then today is it
-        if (start_date == null)
-            start_date = Calendar.System.today;
+        set_byday_start_date(by_days);
+        
+        return true;
     }
     
-    private void set_rrule_yearly(Calendar.Date date, int interval) {
+    // "every july 4th"
+    private bool set_rrule_yearly(Calendar.Date date, int interval) {
+        if (rrule != null)
+            return false;
+        
         start_date = date;
         
         rrule = new RecurrenceRule(iCal.icalrecurrencetype_frequency.YEARLY_RECURRENCE);
         rrule.interval = interval;
         rrule.first_of_week = Calendar.System.first_of_week.as_day_of_week();
         rrule.set_by_rule(RecurrenceRule.ByRule.YEAR_DAY, iterate<int>(date.day_of_year).to_array_list());
+        
+        return true;
     }
     
     // Adds the text to the summary and location field, if adding_location is set
diff --git a/src/tests/tests-quick-add-recurring.vala b/src/tests/tests-quick-add-recurring.vala
index 8478dca..acb6ac1 100644
--- a/src/tests/tests-quick-add-recurring.vala
+++ b/src/tests/tests-quick-add-recurring.vala
@@ -34,6 +34,9 @@ private class QuickAddRecurring : UnitTest.Harness {
         add_case("every-tuesday-thursday", every_tuesday_thursday);
         add_case("every-tuesday-and-thursday", every_tuesday_and_thursday);
         add_case("every-tuesday-and-thursday-for-3-weeks", every_tuesday_and_thursday_for_3_weeks);
+        
+        // MONTHLY
+        add_case("every-first-tuesday", every_first_tuesday);
     }
     
     protected override void setup() throws Error {
@@ -237,8 +240,6 @@ private class QuickAddRecurring : UnitTest.Harness {
             if (!Component.RecurrenceRule.decode_day(value, out dow, out position))
                 return false;
             
-            debug("value %d -> %s %d", value, (dow != null) ? dow.to_string() : "null", position);
-            
             if (!by_days.has_key(dow) || by_days.get(dow) != position)
                 return false;
         }
@@ -353,6 +354,24 @@ private class QuickAddRecurring : UnitTest.Harness {
             && check_byrule_day(event, by_days)
             && event.rrule.count == 3;
     }
+    
+    //
+    // MONTHLY
+    //
+    
+    private bool every_first_tuesday(out string? dump) throws Error {
+        Gee.Map<Calendar.DayOfWeek?, int> by_days = iterate<Calendar.DayOfWeek?>(
+            Calendar.DayOfWeek.TUE).to_hash_map_as_keys<int>(dow => 1);
+        
+        Component.Event event;
+        return basic("meeting at work at 10am every 1st tuesday", out event, out dump)
+            && event.rrule.is_monthly
+            && event.rrule.interval == 1
+            && !event.rrule.has_duration
+            && event.exact_time_span.start_date.day_of_week.equal_to(Calendar.DayOfWeek.TUE)
+            && event.exact_time_span.start_date.day_of_month.value <= 7
+            && check_byrule_day(event, by_days);
+    }
 }
 
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]