/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition 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; version 3 of the License.
//
// This community edition 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.projectforge.plugins.teamcal.event;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.DateList;
import net.fortuna.ical4j.model.Dur;
import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.Recur;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.component.VAlarm;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.property.DtStart;
import net.fortuna.ical4j.model.property.ExDate;
import net.fortuna.ical4j.model.property.RRule;
import org.apache.commons.lang.StringUtils;
import org.projectforge.calendar.ICal4JUtils;
import org.projectforge.common.DateHelper;
import org.projectforge.common.RecurrenceFrequency;
import org.projectforge.web.calendar.CalendarFeed;
/**
* @author Kai Reinhard (k.reinhard@micromata.de)
*/
public class TeamEventUtils
{
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(TeamEventUtils.class);
private static final RecurrenceFrequency[] SUPPORTED_FREQUENCIES = new RecurrenceFrequency[] { RecurrenceFrequency.NONE,
RecurrenceFrequency.DAILY, RecurrenceFrequency.WEEKLY, RecurrenceFrequency.MONTHLY, RecurrenceFrequency.YEARLY};
// needed to convert weeks into days
private static final int DURATION_OF_WEEK = 7;
public static VEvent createVEvent(final TeamEventDO eventDO, final TimeZone timezone)
{
final VEvent vEvent = ICal4JUtils.createVEvent(eventDO.getStartDate(), eventDO.getEndDate(), eventDO.getUid(), eventDO.getSubject(),
eventDO.isAllDay(), timezone);
if (eventDO.hasRecurrence() == true) {
final RRule rrule = eventDO.getRecurrenceRuleObject();
vEvent.getProperties().add(rrule);
}
return vEvent;
}
public static String calculateRRule(final TeamEventRecurrenceData recurData)
{
if (recurData == null || recurData.getFrequency() == null || recurData.getFrequency() == RecurrenceFrequency.NONE) {
return null;
}
if (recurData.isCustomized() == false) {
recurData.setInterval(1);
}
final Recur recur = new Recur();
final net.fortuna.ical4j.model.Date untilDate = ICal4JUtils.getICal4jDate(recurData.getUntil(), recurData.getTimeZone());
if (untilDate != null) {
recur.setUntil(untilDate);
}
recur.setInterval(recurData.getInterval());
recur.setFrequency(ICal4JUtils.getCal4JFrequencyString(recurData.getFrequency()));
final RRule rrule = new RRule(recur);
return rrule.getValue();
}
public static Collection<TeamEvent> getRecurrenceEvents(final Date startDate, final Date endDate, final TeamEventDO event,
final java.util.TimeZone timeZone)
{
if (event.hasRecurrence() == false) {
return null;
}
final Recur recur = event.getRecurrenceObject();
if (recur == null) {
// Shouldn't happen:
return null;
}
final java.util.TimeZone timeZone4Calc = timeZone;
final String eventStartDateString = event.isAllDay() == true ? DateHelper.formatIsoDate(event.getStartDate(), timeZone) : DateHelper
.formatIsoTimestamp(event.getStartDate(), DateHelper.UTC);
Date eventStartDate = event.getStartDate();
if (event.isAllDay() == true) {
// eventStartDate should be midnight in user's time zone.
eventStartDate = DateHelper.parseIsoDate(eventStartDateString, timeZone);
}
if (log.isDebugEnabled() == true) {
log.debug("---------- startDate=" + DateHelper.formatIsoTimestamp(eventStartDate, timeZone) + ", timeZone=" + timeZone.getID());
}
final TimeZone ical4jTimeZone = ICal4JUtils.getTimeZone(timeZone4Calc);
final net.fortuna.ical4j.model.DateTime ical4jStartDate = new net.fortuna.ical4j.model.DateTime(startDate);
ical4jStartDate.setTimeZone(ical4jTimeZone);
final net.fortuna.ical4j.model.DateTime ical4jEndDate = new net.fortuna.ical4j.model.DateTime(endDate);
ical4jEndDate.setTimeZone(ICal4JUtils.getTimeZone(timeZone4Calc));
final net.fortuna.ical4j.model.DateTime seedDate = new net.fortuna.ical4j.model.DateTime(eventStartDate);
seedDate.setTimeZone(ICal4JUtils.getTimeZone(timeZone4Calc));
if (ical4jStartDate == null || ical4jEndDate == null || seedDate == null) {
log.error("Can't get recurrence events of event "
+ event.getId()
+ ". Not all three dates are given: startDate="
+ ical4jStartDate
+ ", endDate="
+ ical4jEndDate
+ ", seed="
+ seedDate);
return null;
}
final List<net.fortuna.ical4j.model.Date> exDates = ICal4JUtils.parseISODateStringsAsICal4jDates(event.getRecurrenceExDate(),
ical4jTimeZone);
final DateList dateList = recur.getDates(seedDate, ical4jStartDate, ical4jEndDate, Value.DATE_TIME);
final Collection<TeamEvent> col = new ArrayList<TeamEvent>();
if (dateList != null) {
OuterLoop: for (final Object obj : dateList) {
final net.fortuna.ical4j.model.DateTime dateTime = (net.fortuna.ical4j.model.DateTime) obj;
final String isoDateString = event.isAllDay() == true ? DateHelper.formatIsoDate(dateTime, timeZone) : DateHelper
.formatIsoTimestamp(dateTime, DateHelper.UTC);
if (exDates != null && exDates.size() > 0) {
for (final net.fortuna.ical4j.model.Date exDate : exDates) {
if (event.isAllDay() == false) {
if (exDate.getTime() == dateTime.getTime()) {
if (log.isDebugEnabled() == true) {
log.debug("= ex-dates equals: " + isoDateString + " == " + exDate);
}
// this date is part of ex dates, so don't use it.
continue OuterLoop;
}
} else {
// Allday event.
final String isoExDateString = DateHelper.formatIsoDate(exDate, DateHelper.UTC);
if (isoDateString.equals(isoExDateString) == true) {
if (log.isDebugEnabled() == true) {
log.debug("= ex-dates equals: " + isoDateString + " == " + isoExDateString);
}
// this date is part of ex dates, so don't use it.
continue OuterLoop;
}
}
if (log.isDebugEnabled() == true) {
log.debug("ex-dates not equals: " + isoDateString + " != " + exDate);
}
}
}
if (isoDateString.equals(eventStartDateString) == true) {
// Put event itself to the list.
col.add(event);
} else {
// Now we need this event as date with the user's time-zone.
final Calendar userCal = Calendar.getInstance(timeZone);
userCal.setTime(dateTime);
final TeamRecurrenceEvent recurEvent = new TeamRecurrenceEvent(event, userCal);
col.add(recurEvent);
}
}
}
if (log.isDebugEnabled() == true) {
for (final TeamEvent ev : col) {
log.debug("startDate="
+ DateHelper.formatIsoTimestamp(ev.getStartDate(), timeZone)
+ "; "
+ DateHelper.formatAsUTC(ev.getStartDate())
+ ", endDate="
+ DateHelper.formatIsoTimestamp(ev.getStartDate(), timeZone)
+ "; "
+ DateHelper.formatAsUTC(ev.getEndDate()));
}
}
return col;
}
public static TeamEventDO createTeamEventDO(final VEvent event)
{
final TeamEventDO teamEvent = new TeamEventDO();
final DtStart dtStart = event.getStartDate();
final String value = dtStart.toString();
if (value.indexOf("VALUE=DATE") >= 0) {
teamEvent.setAllDay(true);
}
Timestamp timestamp = ICal4JUtils.getSqlTimestamp(dtStart.getDate());
teamEvent.setStartDate(timestamp);
if (teamEvent.isAllDay() == true) {
final org.joda.time.DateTime jodaTime = new org.joda.time.DateTime(event.getEndDate().getDate());
final net.fortuna.ical4j.model.Date fortunaEndDate = new net.fortuna.ical4j.model.Date(jodaTime.plusDays(-1).toDate());
timestamp = new Timestamp(fortunaEndDate.getTime());
} else {
timestamp = ICal4JUtils.getSqlTimestamp(event.getEndDate().getDate());
}
teamEvent.setEndDate(timestamp);
if (event.getUid() != null) {
teamEvent.setExternalUid(event.getUid().getValue());
}
if (event.getLocation() != null) {
teamEvent.setLocation(event.getLocation().getValue());
}
if (event.getDescription() != null) {
teamEvent.setNote(event.getDescription().getValue());
}
if (event.getSummary() != null) {
teamEvent.setSubject(event.getSummary().getValue());
} else {
teamEvent.setSubject("");
}
if (event.getOrganizer() != null) {
teamEvent.setOrganizer(event.getOrganizer().getValue());
}
@SuppressWarnings("unchecked")
final List<VAlarm> alarms = event.getAlarms();
if (alarms != null && alarms.size() >= 1) {
final Dur dur = alarms.get(0).getTrigger().getDuration();
if (dur != null) { // Might be null.
// consider weeks
int weeksToDays = 0;
if (dur.getWeeks() != 0) {
weeksToDays = dur.getWeeks() * DURATION_OF_WEEK;
}
if (dur.getDays() != 0) {
teamEvent.setReminderDuration(dur.getDays() + weeksToDays);
teamEvent.setReminderDurationUnit(ReminderDurationUnit.DAYS);
} else if (dur.getHours() != 0) {
teamEvent.setReminderDuration(dur.getHours());
teamEvent.setReminderDurationUnit(ReminderDurationUnit.HOURS);
} else if (dur.getMinutes() != 0) {
teamEvent.setReminderDuration(dur.getMinutes());
teamEvent.setReminderDurationUnit(ReminderDurationUnit.MINUTES);
}
}
}
final RRule rule = (RRule) event.getProperty(Property.RRULE);
if (rule != null) {
teamEvent.setRecurrenceRule(rule.getValue());
}
final ExDate exDate = (ExDate) event.getProperty(Property.EXDATE);
if (exDate != null) {
teamEvent.setRecurrenceExDate(exDate.getValue());
}
return teamEvent;
}
public static RecurrenceFrequency[] getSupportedRecurrenceFrequencies()
{
return SUPPORTED_FREQUENCIES;
}
public static List<VEvent> getVEvents(final net.fortuna.ical4j.model.Calendar calendar)
{
final List<VEvent> events = new ArrayList<VEvent>();
@SuppressWarnings("unchecked")
final List<Component> list = calendar.getComponents(Component.VEVENT);
if (list == null || list.size() == 0) {
return events;
}
// Temporary not used, because multiple events are not supported.
for (final Component c : list) {
final VEvent event = (VEvent) c;
if (StringUtils.equals(event.getSummary().getValue(), CalendarFeed.SETUP_EVENT) == true) {
// skip setup event!
continue;
}
events.add(event);
}
return events;
}
public static List<TeamEventDO> getTeamEvents(final net.fortuna.ical4j.model.Calendar calendar)
{
final List<VEvent> list = getVEvents(calendar);
final List<TeamEventDO> events = convert(list);
return events;
}
public static List<TeamEventDO> convert(final List<VEvent> list)
{
final List<TeamEventDO> events = new ArrayList<TeamEventDO>();
if (list == null || list.size() == 0) {
return events;
}
for (final VEvent vEvent : list) {
events.add(createTeamEventDO(vEvent));
}
Collections.sort(events, new Comparator<TeamEventDO>() {
public int compare(final TeamEventDO o1, final TeamEventDO o2)
{
final Date startDate1 = o1.getStartDate();
final Date startDate2 = o2.getStartDate();
if (startDate1 == null) {
if (startDate2 == null) {
return 0;
}
return -1;
}
return startDate1.compareTo(startDate2);
};
});
return events;
}
}