org.webical.ical.RecurrenceUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.webical.ical.RecurrenceUtil.java

Source

/*
 *    Webical - http://www.webical.org
 *    Copyright (C) 2007 Func. Internet Integration
 *
 *    This file is part of Webical.
 *
 *    $Id$
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.webical.ical;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateList;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Recur;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.property.ExRule;
import net.fortuna.ical4j.model.property.RRule;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.webical.Event;
import org.webical.manager.WebicalException;

/**
 * Collection of utility methods used on Recurrence
 * @author Ivo van Dongen
 * @author Mattijs Hoitink
 *
 * @author Harm-Jan Zwinderman, Cebuned
 * @see <a href="http://www.kanzaki.com/docs/ical/rrule.html">This</a> may be informative 
 *
 */
public class RecurrenceUtil {
    private static Log log = LogFactory.getLog(RecurrenceUtil.class);

    /**
     * Sets the recurrence information on the event's RRule
     * @param event the event to update
     * @param recurrence the recurrence information to set on the event
     */
    public static void setRecurrenceRule(Event event, Recurrence recurrence) {
        if (event == null || recurrence == null) {
            return;
        }

        if (recurrence.getFrequency() > 0 && recurrence.getInterval() > 0) {
            //Remove the recurrence to update
            if (event.getrRule().size() > 0) {
                List<String> rrules = new ArrayList<String>(event.getrRule());
                rrules.remove(0);
                event.setrRule(new HashSet<String>(rrules));
            }

            //Add the new recurrence
            Recur recur = null;
            if (recurrence.getCount() != null && recurrence.getCount() > 0) {
                recur = new Recur(getFrequentcyString(recurrence.getFrequency()), recurrence.getCount());
            } else if (recurrence.getEndDay() != null) {
                recur = new Recur(getFrequentcyString(recurrence.getFrequency()),
                        new DateTime(recurrence.getEndDay()));
            } else {
                recur = new Recur(getFrequentcyString(recurrence.getFrequency()), null);
            }
            recur.setInterval(recurrence.getInterval());
            RRule rRule = new RRule(recur);
            event.getrRule().add(rRule.getValue());
        }
    }

    /**
     * Sets the recurrence information on the event's EXRule
     * @param event the event to update
     * @param recurrence the recurrence information to set on the event
     */
    public static void setExclusionRule(Event event, Recurrence recurrence) {
        if (recurrence.getFrequency() > 0 && recurrence.getInterval() > 0) { //Remove the recurrence to update
            if (event.getrRule().size() > 0) {
                List<String> exrules = new ArrayList<String>(event.getExRule());
                exrules.remove(0);
                event.setrRule(new HashSet<String>(exrules));
            }

            //Add the new recurrence
            Recur recur = null;
            if (recurrence.getCount() > 0) {
                recur = new Recur(getFrequentcyString(recurrence.getFrequency()), recurrence.getCount());
            } else {
                recur = new Recur(getFrequentcyString(recurrence.getFrequency()), new Date(recurrence.getEndDay()));
            }
            recur.setInterval(recurrence.getInterval());
            ExRule exRule = new ExRule(recur);
            event.getExRule().add(exRule.getValue());
        }
    }

    /**
     * Clears the event of Recurrence information
     * @param event the event to clean
     */
    public static void clearRecurrence(Event event) {
        //Clear all recurrence attributes
        /*event.getrDate().clear();
        event.getrRule().clear();
        event.getExDate().clear();
        event.getExRule().clear();*/

        // Reset all recurrence attributes
        event.setrDate(null);
        event.setrRule(null);
        event.setExDate(null);
        event.setExRule(null);
    }

    /**
     * Returns Recurrence formed from the Set of recurrency strings
     * @param recurrenceRules the rules to parse
     * @return a Recurrence
     * @throws WebicalException with wrapped exception
     */
    public static Recurrence getRecurrenceFromRecurrenceRuleSet(Set<String> recurrenceRules)
            throws WebicalException {
        if (recurrenceRules == null || recurrenceRules.size() == 0) {
            return null;
        }

        Recurrence recurrence = new Recurrence();
        String recurrenceString = recurrenceRules.iterator().next();
        Recur recur = null;
        try {
            recur = new Recur(recurrenceString);
        } catch (ParseException e) {
            throw new WebicalException("Could not parse recurrence string: " + recurrenceString);
        }
        recurrence.setCount(recur.getCount());
        recurrence.setEndDay(recur.getUntil());
        recurrence.setInterval(recur.getInterval());
        recurrence.setFrequency(getFrequency(recur.getFrequency()));

        return recurrence;
    }

    /**
     * turns a frequency into a string representation
     * @param frequency the frequency
     * @return
     */
    private static String getFrequentcyString(int frequency) {
        switch (frequency) {
        case Recurrence.DAILY:
            return Recur.DAILY;
        case Recurrence.WEEKLY:
            return Recur.WEEKLY;
        case Recurrence.MONTHLY:
            return Recur.MONTHLY;
        case Recurrence.YEARLY:
            return Recur.YEARLY;
        }
        return "";
    }

    /**
     * turns a frequency string into the int representation
     * @param frequency
     * @return
     */
    private static int getFrequency(String frequency) {
        if (Recur.DAILY.equals(frequency)) {
            return Recurrence.DAILY;
        } else if (Recur.WEEKLY.equals(frequency)) {
            return Recurrence.WEEKLY;
        } else if (Recur.MONTHLY.equals(frequency)) {
            return Recurrence.MONTHLY;
        } else if (Recur.YEARLY.equals(frequency)) {
            return Recurrence.YEARLY;
        }
        return -1;
    }

    /**
     * Converts the frequency to a field identifier used by {@see java.util.Calendar}.
     * @param frequency The frequency identifier
     * @return the @see java.util.Calendar} field identifier
     */
    private static int getCalendarIdentifierForFrequency(int frequency) {
        switch (frequency) {
        case Recurrence.DAILY:
            return GregorianCalendar.DAY_OF_YEAR;
        case Recurrence.WEEKLY:
            return GregorianCalendar.WEEK_OF_YEAR;
        case Recurrence.MONTHLY:
            return GregorianCalendar.MONTH;
        case Recurrence.YEARLY:
            return GregorianCalendar.YEAR;
        }
        return -1;
    }

    /**
     * Determines if the event is recurrent
     * @param event the {@link Event} to check
     * @return true if the {@link Event} is recurrent
     */
    public static boolean isRecurrent(Event event) {
        if (!(event.getrRule().isEmpty() && event.getrDate().isEmpty())) {
            return true;
        }
        return false;
    }

    /**
     * Checks if the {@link Event} is applicable for the given range taking into account recurrence information
     * @param event the {@link Event}
     * @param startDate the start of the range
     * @param endDate the end of the range
     * @return true if the {@link Event} is applicable for this range
     * @throws ParseException
     */
    public static boolean isApplicableForDateRange(Event event, java.util.Date startDate, java.util.Date endDate)
            throws ParseException {
        if (event.getDtStart() != null) {
            if (isRecurrent(event)) {
                //Property: RRULE
                Recur recur = null;
                try {
                    if (event.getrRule() != null) {
                        recur = new Recur(event.getrRule().iterator().next());
                    }
                } catch (ParseException e) {
                    log.error("Could not parse recurrence information", e);
                    throw e;
                }

                ExRule exRule = new ExRule();
                exRule.setRecur(recur);

                DateTime fromDate = new DateTime(event.getDtStart());
                DateTime dtStart = new DateTime(startDate.getTime() - 1000);
                DateTime dtEnd = new DateTime(endDate.getTime() - 1000);

                DateList datesFromRecur = recur.getDates(fromDate, dtStart, dtEnd, Value.DATE_TIME);
                Set<java.util.Date> dates = event.getrDate();

                //Property RDate
                for (java.util.Date date : dates) {
                    datesFromRecur.add(date);
                }

                if (datesFromRecur.size() > 0) {
                    //Property: EXDATE
                    for (Iterator<java.util.Date> i = event.getExDate().iterator(); i.hasNext();) {
                        if (startDate.equals(new Date(((java.util.Date) i.next()).getTime()))) {
                            return false;
                        }
                    }

                    //PROPERTY: EXRULE
                    if (event.getExRule().iterator().hasNext()) {
                        Recur exRecur;
                        try {
                            exRecur = new Recur(event.getExRule().iterator().next());
                        } catch (ParseException e) {
                            log.error("Could not parse recurrence information", e);
                            throw e;
                        }

                        DateList datesFromExRule = exRecur.getDates(fromDate, dtStart, dtEnd, Value.DATE_TIME);
                        if (datesFromExRule.size() > 0) {
                            return false;
                        }
                    }
                    return true;
                }
            } else if ((event.getDtStart().before(startDate) && event.getDtEnd().before(startDate))
                    || (event.getDtStart().after(endDate) && event.getDtEnd().after(endDate))) {
                //Event is not in daterange
                return false;
            } else {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if the {@link Event} is applicable on the given day taking into account recurrence information
     * @param event the {@link Event}
     * @param date the {@link Date}
     * @return true if the {@link Event} is applicable for this range
     * @throws ParseException
     */
    public static boolean isApplicableOnDate(Event event, java.util.Date date) throws ParseException {
        return isApplicableForDateRange(event, date, date);
    }

    /**
     * Excludes the first Event from the second Event's recurrence rule
     * @param eventToExclude The Event to exclude
     * @param eventWithRange The event which recurrence rule should be updated
     * @param clearRecurrenceFromExcludedEvent True if the recurrence from the exlcuded event should be cleared
     */
    public static void excludeEventFromRecurrenceRule(Event eventToExclude, Event eventWithRange,
            boolean clearRecurrenceFromExcludedEvent) {
        // Exclude the new event from its old range
        eventWithRange.getExDate().add(eventToExclude.getDtStart());
        if (clearRecurrenceFromExcludedEvent) {
            clearRecurrence(eventToExclude);
        }
    }

    /**
     * Excludes the given Date from the Events recurrence rule.
     * @param dateToExclude The date to exclude
     * @param eventWithRange The Event with recurrence to exclude the Date from
     */
    public static void excludeDateFromRecurrenceRule(java.util.Date dateToExclude, Event eventWithRange) {
        eventWithRange.getExDate().add(dateToExclude);
    }

    public static java.util.Date getNextOccurrenceDate(Event event, java.util.Date occurrenceDate)
            throws WebicalException, ParseException {
        if (!(event != null && isRecurrent(event))) {
            throw new WebicalException("This event is not recurrent: " + event);
        }

        Recurrence recurrence = getRecurrenceFromRecurrenceRuleSet(event.getrRule());
        if (recurrence != null) { // Check for use of count to determine the end date
            if (recurrence.getCount() > 0 && recurrence.getInterval() > 0) {
                GregorianCalendar endDateCalendar = new GregorianCalendar();

                endDateCalendar.setTime(event.getDtStart());
                int amountToAdd = (recurrence.getCount() * recurrence.getInterval());
                endDateCalendar.add(getCalendarIdentifierForFrequency(recurrence.getFrequency()), amountToAdd);

                if (isApplicableOnDate(event, endDateCalendar.getTime())) {
                    return endDateCalendar.getTime();
                }
                return null;
            }
        }
        return null;
    }

    public static java.util.Date getPreviousOccurrenceDate(Event event, java.util.Date occurrenceDate)
            throws WebicalException {
        if (!(event != null && isRecurrent(event))) {
            throw new WebicalException("This event is not recurrent: " + event);
        } else if (occurrenceDate == null) {
            throw new WebicalException("The occurence date can not be null");
        }
        return null;
    }

    public static java.util.Date getLastOccurrenceDate(Event event) throws WebicalException {
        if (!(event != null && isRecurrent(event))) {
            throw new WebicalException("This event is not recurrent: " + event);
        }

        Recurrence recurrence = getRecurrenceFromRecurrenceRuleSet(event.getrRule());
        if (recurrence != null) { // Check if the recurrence end date is set. Use this as last date value
            if (recurrence.getEndDay() != null) {
                return recurrence.getEndDay();
            }

            // Check for use of count to determine the end date
            if (recurrence.getCount() >= 1 && recurrence.getInterval() >= 1) {
                GregorianCalendar endDateCalendar = new GregorianCalendar();
                endDateCalendar.setTime(event.getDtStart());
                int amountToAdd = (recurrence.getCount() * recurrence.getInterval());
                endDateCalendar.add(getCalendarIdentifierForFrequency(recurrence.getFrequency()), amountToAdd);

                return endDateCalendar.getTime();
            }
        }
        return null;
    }
}