package org.sakaiproject.calendar.impl.readers; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import net.fortuna.ical4j.model.Recur; import net.fortuna.ical4j.model.WeekDayList; import net.fortuna.ical4j.model.property.DateProperty; import net.fortuna.ical4j.model.property.RRule; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.calendar.impl.DailyRecurrenceRule; import org.sakaiproject.calendar.impl.MWFRecurrenceRule; import org.sakaiproject.calendar.impl.MonthlyRecurrenceRule; import org.sakaiproject.calendar.impl.TThRecurrenceRule; import org.sakaiproject.calendar.impl.WeeklyRecurrenceRule; import org.sakaiproject.calendar.impl.YearlyRecurrenceRule; import org.sakaiproject.exception.ImportException; import org.sakaiproject.time.api.Time; public class ICalRecurrence { private static Log M_log = LogFactory.getLog(ICalRecurrence.class); private String rrule_text = ""; private RRule rrule = null; private Recur recur = null; private static final Map<String,String> frequencyLookup; private static List moWeFr = Arrays.asList("MO","WE","FR"); static { Map<String, String>m = new HashMap<String,String>(); m.put("DAILY",new DailyRecurrenceRule().getFrequency()); m.put("MONTHLY",new MonthlyRecurrenceRule().getFrequency()); m.put("WEEKLY", new WeeklyRecurrenceRule().getFrequency()); m.put("YEARLY",new YearlyRecurrenceRule().getFrequency()); // These don't have corresponding explicit frequency // designators in iCal. The corresponding Sakai // frequency string will be set below. m.put(new MWFRecurrenceRule().getFrequency(),new MWFRecurrenceRule().getFrequency()); m.put(new TThRecurrenceRule().getFrequency(),new TThRecurrenceRule().getFrequency()); // iCal has additional frequencies for hour, minute, day. // They are not handled at all. frequencyLookup = Collections.unmodifiableMap(m); } public ICalRecurrence(String rrule_text) throws ImportException { if (rrule_text == null) { return; } try { this.rrule_text = rrule_text; this.rrule = new RRule(rrule_text); } catch (ParseException e) { M_log.warn("Parse exception for iCal recurrence rule: "+rrule_text); throw new ImportException(e); } // The RRule has been successfully created, now use it. recur = rrule.getRecur(); // Make sure the rule makes sense. isValidateRRule(); } // Get declared event frequency, e.g weekly, monthly, daily public String getFrequency() { if (recur == null) { return null; } String extendedFrequency = recur.getFrequency(); WeekDayList dayList = recur.getDayList(); String dayListAsString = dayList.toString(); if (! dayList.isEmpty()) { if ("MO,WE,FR".equals(dayListAsString)) { extendedFrequency = new MWFRecurrenceRule().getFrequency(); } if ("TU,TH".equals(dayListAsString)) { extendedFrequency = new TThRecurrenceRule().getFrequency(); } // if ("MO,TU,WE,TH,FR".equals(dayListAsString)) { // extendedFrequency = new TThRecurrenceRule().getFrequency(); // } } String sakaiFrequency = frequencyLookup.get(extendedFrequency); return sakaiFrequency; } // No instances past this time are created. public Date getEND_TIME() { if (recur == null) { return null; } Date d = recur.getUntil(); return d; } // How frequently should the event be scheduled? // Every other year? Every week? // Default to scheduling every possible time. public Integer getINTERVAL() { if (recur == null) { return null; } Integer i = recur.getInterval(); if (i.equals(-1)) { i= new Integer(1); } return i; } // Count of how many times this event should be repeated. // same as count? This many occurrences. 0 means no limit. public Integer getREPEAT() { if (recur == null) { return null; } Integer c = recur.getCount(); if (c.equals(-1)) { return null; } return c; } /* * Take a recurrence and validate the settings to ensure * they make sense. Currently this will only log the problem * and return a flag indicating whether or not the the rule is valid. * Calling code can determine what to do with the invalid recurrence. * The order of the tests is important. These tests are also * applied in GenericCalendarImporter. */ public Boolean isValidateRRule() { Boolean valid = Boolean.valueOf(true); String reason = ""; // It is ok to specify no modifiers if (getEND_TIME() == null && getREPEAT() == null && getINTERVAL() == null) { valid = Boolean.valueOf(true); return valid; } // can't specify both end time and repeat if (valid && getEND_TIME() != null && getREPEAT() != null) { reason = "specifies both ending time and repeat count"; valid = Boolean.valueOf(false); } // must have an interval if (valid && getINTERVAL() == null) { reason = "specifies ending time or repeat but not interval"; valid = Boolean.valueOf(false); } if (valid && getINTERVAL() == null && getREPEAT() == null && getEND_TIME() != null) { reason = "specifies only end time which Sakai does not support"; valid = Boolean.valueOf(false); } if (!valid) { M_log.warn("iCal recurrence was not valid: "+reason+" ["+rrule_text+"]"); } return valid; } }