Logo Search packages:      
Sourcecode: libical version File versions  Download package

icalrecur_iterator* icalrecur_iterator_new ( struct icalrecurrencetype  rule,
struct icaltimetype  dtstart 
)

Create a new recurrence rule iterator

Definition at line 748 of file icalrecur.c.

References icalrecurrencetype::freq, icalrecur_one_byrule(), icalrecur_two_byrule(), icalrecurrencetype_day_day_of_week(), and icalrecurrencetype_day_position().

Referenced by icalrecur_expand_recurrence().

{
    icalrecur_iterator* impl;
    icalrecurrencetype_frequency freq;

    if ( ( impl = (icalrecur_iterator*)
         malloc(sizeof(icalrecur_iterator))) == 0) {
      icalerror_set_errno(ICAL_NEWFAILED_ERROR);
      return 0;
    }

    memset(impl,0,sizeof(icalrecur_iterator));

    impl->rule = rule;
    impl->last = dtstart;
    impl->dtstart = dtstart;
    impl->days_index =0;
    impl->occurrence_no = 0;
    freq = impl->rule.freq;

    /* Set up convienience pointers to make the code simpler. Allows
       us to iterate through all of the BY* arrays in the rule. */

    impl->by_ptrs[BY_MONTH]=impl->rule.by_month;
    impl->by_ptrs[BY_WEEK_NO]=impl->rule.by_week_no;
    impl->by_ptrs[BY_YEAR_DAY]=impl->rule.by_year_day;
    impl->by_ptrs[BY_MONTH_DAY]=impl->rule.by_month_day;
    impl->by_ptrs[BY_DAY]=impl->rule.by_day;
    impl->by_ptrs[BY_HOUR]=impl->rule.by_hour;
    impl->by_ptrs[BY_MINUTE]=impl->rule.by_minute;
    impl->by_ptrs[BY_SECOND]=impl->rule.by_second;
    impl->by_ptrs[BY_SET_POS]=impl->rule.by_set_pos;

    memset(impl->orig_data,0,9*sizeof(short));

    /* Note which by rules had data in them when the iterator was
       created. We can't use the actuall by_x arrays, because the
       empty ones will be given default values later in this
       routine. The orig_data array will be used later in has_by_data */

    impl->orig_data[BY_MONTH]
      = (short)(impl->rule.by_month[0]!=ICAL_RECURRENCE_ARRAY_MAX);
    impl->orig_data[BY_WEEK_NO]
      =(short)(impl->rule.by_week_no[0]!=ICAL_RECURRENCE_ARRAY_MAX);
    impl->orig_data[BY_YEAR_DAY]
    =(short)(impl->rule.by_year_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
    impl->orig_data[BY_MONTH_DAY]
    =(short)(impl->rule.by_month_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
    impl->orig_data[BY_DAY]
      = (short)(impl->rule.by_day[0]!=ICAL_RECURRENCE_ARRAY_MAX);
    impl->orig_data[BY_HOUR]
      = (short)(impl->rule.by_hour[0]!=ICAL_RECURRENCE_ARRAY_MAX);
    impl->orig_data[BY_MINUTE]
     = (short)(impl->rule.by_minute[0]!=ICAL_RECURRENCE_ARRAY_MAX);
    impl->orig_data[BY_SECOND]
     = (short)(impl->rule.by_second[0]!=ICAL_RECURRENCE_ARRAY_MAX);
    impl->orig_data[BY_SET_POS]
     = (short)(impl->rule.by_set_pos[0]!=ICAL_RECURRENCE_ARRAY_MAX);


    /* Check if the recurrence rule is legal */

    /* If the BYYEARDAY appears, no other date rule part may appear.   */

    if(icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH) ||
       icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_WEEK_NO) ||
       icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_MONTH_DAY) ||
       icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){

      icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);

      return 0;
    }

    /* BYWEEKNO and BYMONTH rule parts may not both appear.*/

    if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){
      icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);

      icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
      return 0;
    }

    /* BYWEEKNO and BYMONTHDAY rule parts may not both appear.*/

    if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH_DAY)){
      icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);

      icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
      return 0;
    }


    /*For MONTHLY recurrences (FREQ=MONTHLY) neither BYYEARDAY nor
      BYWEEKNO may appear. */

    if(freq == ICAL_MONTHLY_RECURRENCE && 
       icalrecur_one_byrule(impl,BY_WEEK_NO)){
      icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
      return 0;
    }


    /*For WEEKLY recurrences (FREQ=WEEKLY) neither BYMONTHDAY nor
      BYYEARDAY may appear. */

    if(freq == ICAL_WEEKLY_RECURRENCE && 
       icalrecur_one_byrule(impl,BY_MONTH_DAY )) {
      icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
      return 0;
    }

    /* BYYEARDAY may only appear in YEARLY rules */
    if(freq != ICAL_YEARLY_RECURRENCE && 
       icalrecur_one_byrule(impl,BY_YEAR_DAY )) {
      icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
      return 0;
    }

    /* Rewrite some of the rules and set up defaults to make later
       processing easier. Primarily, t involves copying an element
       from the start time into the corresponding BY_* array when the
       BY_* array is empty */


    setup_defaults(impl,BY_SECOND,ICAL_SECONDLY_RECURRENCE,
               impl->dtstart.second,
               &(impl->last.second));

    setup_defaults(impl,BY_MINUTE,ICAL_MINUTELY_RECURRENCE,
               impl->dtstart.minute,
               &(impl->last.minute));

    setup_defaults(impl,BY_HOUR,ICAL_HOURLY_RECURRENCE,
               impl->dtstart.hour,
               &(impl->last.hour));

    setup_defaults(impl,BY_MONTH_DAY,ICAL_DAILY_RECURRENCE,
               impl->dtstart.day,
               &(impl->last.day));

    setup_defaults(impl,BY_MONTH,ICAL_MONTHLY_RECURRENCE,
               impl->dtstart.month,
               &(impl->last.month));


    if(impl->rule.freq == ICAL_WEEKLY_RECURRENCE ){

       if(impl->by_ptrs[BY_DAY][0] == ICAL_RECURRENCE_ARRAY_MAX){

         /* Weekly recurrences with no BY_DAY data should occur on the
            same day of the week as the start time . */
         impl->by_ptrs[BY_DAY][0] = (short)icaltime_day_of_week(impl->dtstart);

       } else {
        /* If there is BY_DAY data, then we need to move the initial
           time to the start of the BY_DAY data. That is if the
           start time is on a Wednesday, and the rule has
           BYDAY=MO,WE,FR, move the initial time back to
           monday. Otherwise, jumping to the next week ( jumping 7
           days ahead ) will skip over some occurrences in the
           second week. */
        
        /* This is probably a HACK. There should be some more
             general way to solve this problem */

        short dow = (short)(impl->by_ptrs[BY_DAY][0]-icaltime_day_of_week(impl->last));

        if(dow < 0) {
            /* initial time is after first day of BY_DAY data */

            impl->last.day += dow;
            impl->last = icaltime_normalize(impl->last);
        }
      }
      

    }

    /* For YEARLY rule, begin by setting up the year days array . The
       YEARLY rules work by expanding one year at a time. */

    if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){
        struct icaltimetype next;

      for (;;) {
            expand_year_days(impl, impl->last.year);
          if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX)
              break; /* break when no days are expanded */
          increment_year(impl,impl->rule.interval);
      }

        /* Copy the first day into last. */
      next = icaltime_from_day_of_year(impl->days[0], impl->last.year);
    
      impl->last.day =  next.day;
      impl->last.month =  next.month;
    } 


    /* If this is a monthly interval with by day data, then we need to
       set the last value to the appropriate day of the month */

    if(impl->rule.freq == ICAL_MONTHLY_RECURRENCE &&
       has_by_data(impl,BY_DAY)) {

      int dow = icalrecurrencetype_day_day_of_week(
          impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);  
      int pos =  icalrecurrencetype_day_position(
          impl->by_ptrs[BY_DAY][impl->by_indices[BY_DAY]]);  
      
      int poscount = 0;
      int days_in_month = 
            icaltime_days_in_month(impl->last.month, impl->last.year); 
      
        if(pos >= 0){
            /* Count up from the first day pf the month to find the
               pos'th weekday of dow ( like the second monday. ) */

            for(impl->last.day = 1;
                impl->last.day <= days_in_month;
                impl->last.day++){
                
                if(icaltime_day_of_week(impl->last) == dow){
                    if(++poscount == pos || pos == 0){
                        break;
                    }
                }
            }
        } else {
            /* Count down from the last day pf the month to find the
               pos'th weekday of dow ( like the second to last monday. ) */
            pos = -pos;
            for(impl->last.day = days_in_month;
                impl->last.day != 0;
                impl->last.day--){
                
                if(icaltime_day_of_week(impl->last) == dow){
                    if(++poscount == pos ){
                        break;
                    }
                }
            }
        }


      if(impl->last.day > days_in_month || impl->last.day == 0){
          icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
          return 0;
      }
      
    }



    return impl;
}


Generated by  Doxygen 1.6.0   Back to index