FileDocCategorySizeDatePackage
RecurrenceSet.javaAPI DocAndroid 1.5 API15736Wed May 06 22:41:56 BST 2009android.pim

RecurrenceSet

public class RecurrenceSet extends Object
Basic information about a recurrence, following RFC 2445 Section 4.8.5. Contains the RRULEs, RDATE, EXRULEs, and EXDATE properties.

Fields Summary
private static final String
TAG
private static final String
RULE_SEPARATOR
public EventRecurrence[]
rrules
public long[]
rdates
public EventRecurrence[]
exrules
public long[]
exdates
Constructors Summary
public RecurrenceSet(android.content.ContentValues values)
Creates a new RecurrenceSet from information stored in the events table in the CalendarProvider.

param
values The values retrieved from the Events table.


                                
       
        String rruleStr = values.getAsString(Calendar.Events.RRULE);
        String rdateStr = values.getAsString(Calendar.Events.RDATE);
        String exruleStr = values.getAsString(Calendar.Events.EXRULE);
        String exdateStr = values.getAsString(Calendar.Events.EXDATE);
        init(rruleStr, rdateStr, exruleStr, exdateStr);
    
public RecurrenceSet(android.database.Cursor cursor)
Creates a new RecurrenceSet from information stored in a database {@link Cursor} pointing to the events table in the CalendarProvider. The cursor must contain the RRULE, RDATE, EXRULE, and EXDATE columns.

param
cursor The cursor containing the RRULE, RDATE, EXRULE, and EXDATE columns.

        int rruleColumn = cursor.getColumnIndex(Calendar.Events.RRULE);
        int rdateColumn = cursor.getColumnIndex(Calendar.Events.RDATE);
        int exruleColumn = cursor.getColumnIndex(Calendar.Events.EXRULE);
        int exdateColumn = cursor.getColumnIndex(Calendar.Events.EXDATE);
        String rruleStr = cursor.getString(rruleColumn);
        String rdateStr = cursor.getString(rdateColumn);
        String exruleStr = cursor.getString(exruleColumn);
        String exdateStr = cursor.getString(exdateColumn);
        init(rruleStr, rdateStr, exruleStr, exdateStr);
    
public RecurrenceSet(String rruleStr, String rdateStr, String exruleStr, String exdateStr)

        init(rruleStr, rdateStr, exruleStr, exdateStr);
    
Methods Summary
private static voidaddPropertiesForRuleStr(ICalendar.Component component, java.lang.String propertyName, java.lang.String ruleStr)

        if (TextUtils.isEmpty(ruleStr)) {
            return;
        }
        String[] rrules = ruleStr.split(RULE_SEPARATOR);
        for (String rrule : rrules) {
            ICalendar.Property prop = new ICalendar.Property(propertyName);
            prop.setValue(rrule);
            component.addProperty(prop);
        }
    
private static voidaddPropertyForDateStr(ICalendar.Component component, java.lang.String propertyName, java.lang.String dateStr)

        if (TextUtils.isEmpty(dateStr)) {
            return;
        }

        ICalendar.Property prop = new ICalendar.Property(propertyName);
        String tz = null;
        int tzidx = dateStr.indexOf(";");
        if (tzidx != -1) {
            tz = dateStr.substring(0, tzidx);
            dateStr = dateStr.substring(tzidx + 1);
        }
        if (!TextUtils.isEmpty(tz)) {
            prop.addParameter(new ICalendar.Parameter("TZID", tz));
        }
        prop.setValue(dateStr);
        component.addProperty(prop);
    
private static java.lang.StringcomputeDuration(android.text.format.Time start, ICalendar.Component component)

        // see if a duration is defined
        ICalendar.Property durationProperty =
                component.getFirstProperty("DURATION");
        if (durationProperty != null) {
            // just return the duration
            return durationProperty.getValue();
        }

        // must compute a duration from the DTEND
        ICalendar.Property dtendProperty =
                component.getFirstProperty("DTEND");
        if (dtendProperty == null) {
            // no DURATION, no DTEND: 0 second duration
            return "+P0S";
        }
        ICalendar.Parameter endTzidParameter =
                dtendProperty.getFirstParameter("TZID");
        String endTzid = (endTzidParameter == null)
                ? start.timezone : endTzidParameter.value;

        Time end = new Time(endTzid);
        end.parse(dtendProperty.getValue());
        long durationMillis = end.toMillis(false /* use isDst */) 
                - start.toMillis(false /* use isDst */);
        long durationSeconds = (durationMillis / 1000);
        return "P" + durationSeconds + "S";
    
private static java.lang.StringextractDates(ICalendar.Property recurrence)

        if (recurrence == null) {
            return null;
        }
        ICalendar.Parameter tzidParam =
                recurrence.getFirstParameter("TZID");
        if (tzidParam != null) {
            return tzidParam.value + ";" + recurrence.getValue();
        }
        return recurrence.getValue();
    
private static java.lang.StringflattenProperties(ICalendar.Component component, java.lang.String name)

        List<ICalendar.Property> properties = component.getProperties(name);
        if (properties == null || properties.isEmpty()) {
            return null;
        }

        if (properties.size() == 1) {
            return properties.get(0).getValue();
        }

        StringBuilder sb = new StringBuilder();

        boolean first = true;
        for (ICalendar.Property property : component.getProperties(name)) {
            if (first) {
                first = false;
            } else {
                // TODO: use commas.  our RECUR parsing should handle that
                // anyway.
                sb.append(RULE_SEPARATOR);
            }
            sb.append(property.getValue());
        }
        return sb.toString();
    
public booleanhasRecurrence()
Returns whether or not a recurrence is defined in this RecurrenceSet.

return
Whether or not a recurrence is defined in this RecurrenceSet.

        return (rrules != null || rdates != null);
    
private voidinit(java.lang.String rruleStr, java.lang.String rdateStr, java.lang.String exruleStr, java.lang.String exdateStr)

        if (!TextUtils.isEmpty(rruleStr) || !TextUtils.isEmpty(rdateStr)) {

            if (!TextUtils.isEmpty(rruleStr)) {
                String[] rruleStrs = rruleStr.split(RULE_SEPARATOR);
                rrules = new EventRecurrence[rruleStrs.length];
                for (int i = 0; i < rruleStrs.length; ++i) {
                    EventRecurrence rrule = new EventRecurrence();
                    rrule.parse(rruleStrs[i]);
                    rrules[i] = rrule;
                }
            }

            if (!TextUtils.isEmpty(rdateStr)) {
                rdates = parseRecurrenceDates(rdateStr);
            }

            if (!TextUtils.isEmpty(exruleStr)) {
                String[] exruleStrs = exruleStr.split(RULE_SEPARATOR);
                exrules = new EventRecurrence[exruleStrs.length];
                for (int i = 0; i < exruleStrs.length; ++i) {
                    EventRecurrence exrule = new EventRecurrence();
                    exrule.parse(exruleStr);
                    exrules[i] = exrule;
                }
            }

            if (!TextUtils.isEmpty(exdateStr)) {
                exdates = parseRecurrenceDates(exdateStr);
            }
        }
    
public static long[]parseRecurrenceDates(java.lang.String recurrence)
Parses the provided RDATE or EXDATE string into an array of longs representing each date/time in the recurrence.

param
recurrence The recurrence to be parsed.
return
The list of date/times.

        // TODO: use "local" time as the default.  will need to handle times
        // that end in "z" (UTC time) explicitly at that point.
        String tz = Time.TIMEZONE_UTC;
        int tzidx = recurrence.indexOf(";");
        if (tzidx != -1) {
            tz = recurrence.substring(0, tzidx);
            recurrence = recurrence.substring(tzidx + 1);
        }
        Time time = new Time(tz);
        String[] rawDates = recurrence.split(",");
        int n = rawDates.length;
        long[] dates = new long[n];
        for (int i = 0; i<n; ++i) {
            // The timezone is updated to UTC if the time string specified 'Z'.
            time.parse(rawDates[i]);
            dates[i] = time.toMillis(false /* use isDst */);
            time.timezone = tz;
        }
        return dates;
    
public static booleanpopulateComponent(android.database.Cursor cursor, ICalendar.Component component)

        
        int dtstartColumn = cursor.getColumnIndex(Calendar.Events.DTSTART);
        int durationColumn = cursor.getColumnIndex(Calendar.Events.DURATION);
        int tzidColumn = cursor.getColumnIndex(Calendar.Events.EVENT_TIMEZONE);
        int rruleColumn = cursor.getColumnIndex(Calendar.Events.RRULE);
        int rdateColumn = cursor.getColumnIndex(Calendar.Events.RDATE);
        int exruleColumn = cursor.getColumnIndex(Calendar.Events.EXRULE);
        int exdateColumn = cursor.getColumnIndex(Calendar.Events.EXDATE);
        int allDayColumn = cursor.getColumnIndex(Calendar.Events.ALL_DAY);


        long dtstart = -1;
        if (!cursor.isNull(dtstartColumn)) {
            dtstart = cursor.getLong(dtstartColumn);
        }
        String duration = cursor.getString(durationColumn);
        String tzid = cursor.getString(tzidColumn);
        String rruleStr = cursor.getString(rruleColumn);
        String rdateStr = cursor.getString(rdateColumn);
        String exruleStr = cursor.getString(exruleColumn);
        String exdateStr = cursor.getString(exdateColumn);
        boolean allDay = cursor.getInt(allDayColumn) == 1;

        if ((dtstart == -1) ||
            (TextUtils.isEmpty(duration))||
            ((TextUtils.isEmpty(rruleStr))&&
                (TextUtils.isEmpty(rdateStr)))) {
                // no recurrence.
                return false;
        }

        ICalendar.Property dtstartProp = new ICalendar.Property("DTSTART");
        Time dtstartTime = null;
        if (!TextUtils.isEmpty(tzid)) {
            if (!allDay) {
                dtstartProp.addParameter(new ICalendar.Parameter("TZID", tzid));
            }
            dtstartTime = new Time(tzid);
        } else {
            // use the "floating" timezone
            dtstartTime = new Time(Time.TIMEZONE_UTC);
        }
        
        dtstartTime.set(dtstart);
        // make sure the time is printed just as a date, if all day.
        // TODO: android.pim.Time really should take care of this for us.
        if (allDay) {
            dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE"));
            dtstartTime.allDay = true;
            dtstartTime.hour = 0;
            dtstartTime.minute = 0;
            dtstartTime.second = 0;
        }

        dtstartProp.setValue(dtstartTime.format2445());
        component.addProperty(dtstartProp);
        ICalendar.Property durationProp = new ICalendar.Property("DURATION");
        durationProp.setValue(duration);
        component.addProperty(durationProp);

        addPropertiesForRuleStr(component, "RRULE", rruleStr);
        addPropertyForDateStr(component, "RDATE", rdateStr);
        addPropertiesForRuleStr(component, "EXRULE", exruleStr);
        addPropertyForDateStr(component, "EXDATE", exdateStr);
        return true;
    
public static booleanpopulateContentValues(ICalendar.Component component, android.content.ContentValues values)
Populates the database map of values with the appropriate RRULE, RDATE, EXRULE, and EXDATE values extracted from the parsed iCalendar component.

param
component The iCalendar component containing the desired recurrence specification.
param
values The db values that should be updated.
return
true if the component contained the necessary information to specify a recurrence. The required fields are DTSTART, one of DTEND/DURATION, and one of RRULE/RDATE. Returns false if there was an error, including if the date is out of range.

        ICalendar.Property dtstartProperty =
                component.getFirstProperty("DTSTART");
        String dtstart = dtstartProperty.getValue();
        ICalendar.Parameter tzidParam =
                dtstartProperty.getFirstParameter("TZID");
        // NOTE: the timezone may be null, if this is a floating time.
        String tzid = tzidParam == null ? null : tzidParam.value;
        Time start = new Time(tzidParam == null ? Time.TIMEZONE_UTC : tzid);
        boolean inUtc = start.parse(dtstart);
        boolean allDay = start.allDay;

        if (inUtc) {
            tzid = Time.TIMEZONE_UTC;
        }
                
        String duration = computeDuration(start, component);
        String rrule = flattenProperties(component, "RRULE");
        String rdate = extractDates(component.getFirstProperty("RDATE"));
        String exrule = flattenProperties(component, "EXRULE");
        String exdate = extractDates(component.getFirstProperty("EXDATE"));

        if ((TextUtils.isEmpty(dtstart))||
                (TextUtils.isEmpty(duration))||
                ((TextUtils.isEmpty(rrule))&&
                        (TextUtils.isEmpty(rdate)))) {
                if (Config.LOGD) {
                    Log.d(TAG, "Recurrence missing DTSTART, DTEND/DURATION, "
                                + "or RRULE/RDATE: "
                                + component.toString());
                }
                return false;
        }
        
        if (allDay) {
        	// TODO: also change tzid to be UTC?  that would be consistent, but
        	// that would not reflect the original timezone value back to the
        	// server.
        	start.timezone = Time.TIMEZONE_UTC;
        }
        long millis = start.toMillis(false /* use isDst */);
        values.put(Calendar.Events.DTSTART, millis);
        if (millis == -1) {
            if (Config.LOGD) {
                Log.d(TAG, "DTSTART is out of range: " + component.toString());
            }
            return false;
        }
        
        values.put(Calendar.Events.RRULE, rrule);
        values.put(Calendar.Events.RDATE, rdate);
        values.put(Calendar.Events.EXRULE, exrule);
        values.put(Calendar.Events.EXDATE, exdate);
        values.put(Calendar.Events.EVENT_TIMEZONE, tzid);
        values.put(Calendar.Events.DURATION, duration);
        values.put(Calendar.Events.ALL_DAY, allDay ? 1 : 0);
        return true;