/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2006 - 2007 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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 Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package com.funambol.common.pim.model.calendar;
import java.util.ArrayList;
import java.util.List;
import java.text.ParseException;
import com.funambol.common.pim.model.common.Property;
import com.funambol.common.pim.model.common.PropertyWithTimeZone;
import com.funambol.common.pim.model.common.XTag;
import com.funambol.common.pim.model.converter.TimeZoneHelper;
import com.funambol.common.pim.model.utility.TimeUtils;
/**
* This class represents the common features of PIM events and tasks (todo's).
* It's an abstract class because its type can be defined only when one of its
* subclasses is instantiated.
*
* @version $Id: CalendarContent.java,v 1.7 2008-07-17 15:53:15 luigiafassina Exp $
*/
public abstract class CalendarContent {
//--------------------------------------------------------------- Properties
protected PropertyWithTimeZone dalarm;
protected PropertyWithTimeZone palarm;
protected Property categories;
protected Property accessClass;
protected Property description;
protected Property latitude;
protected Property longitude;
protected Property location;
protected Property priority;
protected Property status;
protected Property summary;
protected PropertyWithTimeZone dtEnd; // Also used for the DUE property of
//VTODO objects
protected PropertyWithTimeZone dtStart;
protected Property duration;
protected Property organizer;
protected Property url;
protected Property uid;
protected Property contact;
protected PropertyWithTimeZone created;
protected PropertyWithTimeZone dtStamp;
protected PropertyWithTimeZone lastModified;
protected Property sequence;
protected Property folder;
protected List<XTag> xTags;
/*
* Since these properties are not part of the iCalendar specifications,
* we do not need to use the Property object to store them:
*/
protected Boolean allDay;
protected Integer mileage;
protected Short meetingStatus; // 0 non meeting
// 1 meeting
// 3 meeting received
// 5 meeting cancelled
protected Short busyStatus; // 0 olFree
// 1 olTentative
// 2 olBusy
// 3 olOutOfOffice
/*
* Complex components:
*/
protected Reminder reminder; // may be null
protected RecurrencePattern recurrencePattern; // may be null
protected List<Attendee> attendees; // may be empty
/**
* Returns the access classification for a calendar component.
*
* @return the accessClass property
*/
public Property getAccessClass() {
return accessClass;
}
/**
* Returns the date and time that the calendar information was created.
*
* @return the created property
*/
public PropertyWithTimeZone getCreated() {
return created;
}
/**
* Returns the most complete description of the calendar component.
*
* @return the description property
*/
public Property getDescription() {
return description;
}
/**
* Returns the start date and time for the calendar item.
*
* @return the dtStart property
*/
public PropertyWithTimeZone getDtStart() {
return dtStart;
}
/**
* Returns the latitude of the location of this event or task.
*
* @return the latitude property
*/
public Property getLatitude() {
return latitude;
}
/**
* Returns the longitude of the location of this event or task.
*
* @return the longitude property
*/
public Property getLongitude() {
return longitude;
}
/**
* Returns the date and time of the last revision of this calendar item.
*
* @return the lastModified property
*/
public PropertyWithTimeZone getLastModified() {
return lastModified;
}
/**
* Returns the location of this calendar item.
*
* @return the location property
*/
public Property getLocation() {
return location;
}
/**
* Returns the organizer for the event or task.
*
* @return the organizer property
*/
public Property getOrganizer() {
return organizer;
}
/**
* Returns the relative priority for the calendar item.
*
* @return the priority property
*/
public Property getPriority() {
return priority;
}
/**
* Returns the date and time that the iCalendar representation of the
* calendar item was created.
*
* @return the dtStamp property
*
* @deprecated This information is too strictly related to a particular
* representation of the data than the data itself. The created
* property can be used to indicate the creation time of the
* calendar item without any reference to the iCalendar format.
*/
public PropertyWithTimeZone getDtStamp() {
return dtStamp;
}
/**
* Returns the revision sequence number.
*
* @return the sequence property
*/
public Property getSequence() {
return sequence;
}
/**
* Returns the status of the calendar item.
*
* @return the status property
*/
public Property getStatus() {
return status;
}
/**
* Returns the unique ID of this calendar item. This is not the internal ID
* of the DS server, but an ID set by the original system where this item
* was created. Its uniqueness cannot actually be guaranteed across the
* synchronisation.
*
* @return the uid property
*/
public Property getUid() {
return uid;
}
/**
* Returns the url for the calendar item.
*
* @return the url property
*/
public Property getUrl() {
return url;
}
/**
* Returns the end date and time for the event, or the due date and time for
* the task.
*
* @return the dtEnd property
*/
public PropertyWithTimeZone getDtEnd() {
return dtEnd;
}
/**
* Returns the duration of the event or task.
*
* @return the duration property
*/
public Property getDuration() {
return duration;
}
/**
* Returns the summary.
*
* @return the summary property
*/
public Property getSummary() {
return summary;
}
/**
* Returns the categories this calendar item belongs to.
*
* @return the categories property
*/
public Property getCategories() {
return categories;
}
/**
* Returns the contact information or alternately a reference to contact
* information associated with the calendar component.
*
* @return the contact property
*/
public Property getContact() {
return contact;
}
/**
* Returns a list of custom tags.
*
* @return a List of XTag objects
*/
public List<XTag> getXTags() {
return xTags;
}
/**
* Returns the display reminder (i.e. a visual alarm).
*
* @return the dalarm property
*
* @deprecated This information is too strictly related to a particular
* representation of the data than the data itself. The display
* reminder will be supported in a future version as a special
* case of the reminder property, represented by a Reminder
* object.
*/
public PropertyWithTimeZone getDAlarm() {
return dalarm;
}
/**
* Returns the procedure reminder (i.e. an alarm that launches a procedure).
*
* @return the palarm property
*
* @deprecated This information is too strictly related to a particular
* representation of the data than the data itself. The
* procedure reminder will be supported in a future version as a
* special case of the reminder property, represented by a
* Reminder object.
*/
public PropertyWithTimeZone getPAlarm() {
return palarm;
}
/**
* Returns the all-day flag of the calendar item.
*
* @return true if the event is all-day, false if it is timed; if the allDay
* property is not set, false is returned by default
*/
public boolean isAllDay() {
return (allDay != null) ? allDay.booleanValue() : false;
}
/**
* Getter for property allDay.
*
* @return the allDay property (may be null)
*
* @deprecated The null case does not have a clear semantics. Method
* isAllDay (returning a boolean instead of a Boolean) is
* clearer and must be used instead of this.
*/
public Boolean getAllDay() {
return allDay;
}
/**
* Setter for property allDay.
*
* @param allDay new value of property allDay
*/
public void setAllDay(Boolean allDay) {
this.allDay = allDay;
}
/**
* Sets the all-day flag.
*
* @param allDay a boolean that will be stored in the allDay property (a
* Boolean object)
*/
public void setAllDay(boolean allDay) {
this.allDay = allDay;
}
/**
* Returns the meeting status of the calendar item.
*
* @return value of property meetingStatus
*/
public Short getMeetingStatus() {
return meetingStatus;
}
/**
* Setter for property meetingStatus.
*
* @param meetingStatus new value of property meetingStatus
*/
public void setMeetingStatus(Short meetingStatus) {
this.meetingStatus = meetingStatus;
}
/**
* Returns the busy status of the calendar item.
*
* @return value of property busyStatus
*/
public Short getBusyStatus() {
return busyStatus;
}
/**
* Setter for property busyStatus.
*
* @param busyStatus new value of property busyStatus
*/
public void setBusyStatus(Short busyStatus) {
this.busyStatus = busyStatus;
}
/**
* Returns the mileage attached to this calendar item.
*
* @return value of property mileage
*/
public Integer getMileage() {
return mileage;
}
/**
* Setter for property mileage.
*
* @param mileage new value of property mileage
*/
public void setMileage(Integer mileage) {
this.mileage = mileage;
}
/**
* Returns the calendar item's recurrence pattern.
*
* @return value of property recurrencePattern
*/
public RecurrencePattern getRecurrencePattern() {
return recurrencePattern;
}
/**
* Setter for property recurrencePattern.
*
* @param recurrencePattern new value of property recurrencePattern
*/
public void setRecurrencePattern(RecurrencePattern recurrencePattern) {
this.recurrencePattern = recurrencePattern;
}
/**
* Setter for property categories.
*
* @param categories new value of property categories
*/
public void setCategories(Property categories) {
this.categories = categories;
}
/**
* Setter for property accessClass.
*
* @param accessClass new value of property accessClass
*/
public void setAccessClass(Property accessClass) {
this.accessClass = accessClass;
}
/**
* Setter for property description.
*
* @param description new value of property description
*/
public void setDescription(Property description) {
this.description = description;
}
/**
* Setter for property latitude.
*
* @param latitude new value of property latitude
*/
public void setLatitude(Property latitude) {
this.latitude = latitude;
}
/**
* Setter for property longitude.
*
* @param longitude new value of property longitude
*/
public void setLongitude(Property longitude) {
this.longitude = longitude;
}
/**
* Setter for property location.
*
* @param location new value of property location
*/
public void setLocation(Property location) {
this.location = location;
}
/**
* Setter for property priority.
*
* @param priority new value of property priority
*/
public void setPriority(Property priority) {
this.priority = priority;
}
/**
* Setter for property status.
*
* @param status new value of property status
*/
public void setStatus(Property status) {
this.status = status;
}
/**
* Setter for property summary.
*
* @param summary new value of property summary
*/
public void setSummary(Property summary) {
this.summary = summary;
}
/**
* Setter for property dtEnd.
*
* @param dtEnd new value of property dtEnd
*/
public void setDtEnd(PropertyWithTimeZone dtEnd) {
this.dtEnd = dtEnd;
}
/**
* Setter for property dtEnd.
*
* @param dtEnd new value of property dtEnd
*/
public void setDtEnd(Property dtEnd) {
this.dtEnd = new PropertyWithTimeZone(dtEnd, null);
}
/**
* Setter for property dtStart.
*
* @param dtStart new value of property dtStart
*/
public void setDtStart(PropertyWithTimeZone dtStart) {
this.dtStart = dtStart;
}
/**
* Setter for property dtStart.
*
* @param dtStart new value of property dtStart
*/
public void setDtStart(Property dtStart) {
this.dtStart = new PropertyWithTimeZone(dtStart, null);
}
/**
* Setter for property duration.
*
* @param duration new value of property duration
*/
public void setDuration(Property duration) {
this.duration = duration;
}
/**
* Setter for property organizer.
*
* @param organizer new value of property organizer
*/
public void setOrganizer(Property organizer) {
this.organizer = organizer;
}
/**
* Setter for property url.
*
* @param url new value of property url
*/
public void setUrl(Property url) {
this.url = url;
}
/**
* Setter for property uid.
*
* @param uid new value of property uid
*/
public void setUid(Property uid) {
this.uid = uid;
}
/**
* Setter for property contact.
*
* @param contact new value of property contact
*/
public void setContact(Property contact) {
this.contact = contact;
}
/**
* Setter for property created.
*
* @param created new value of property created
*/
public void setCreated(PropertyWithTimeZone created) {
this.created = created;
}
/**
* Setter for property created.
*
* @param created new value of property created
*/
public void setCreated(Property created) {
this.created = new PropertyWithTimeZone(created, null);
}
/**
* Setter for property dtStamp.
*
* @param dtStamp new value of property dtStamp
*/
public void setDtStamp(PropertyWithTimeZone dtStamp) {
this.dtStamp = dtStamp;
}
/**
* Setter for property dtStamp.
*
* @param dtStamp new value of property dtStamp
*/
public void setDtStamp(Property dtStamp) {
this.dtStamp = new PropertyWithTimeZone(dtStamp, null);
}
/**
* Setter for property lastModified.
*
* @param lastModified new value of property lastModified
*/
public void setLastModified(PropertyWithTimeZone lastModified) {
this.lastModified = lastModified;
}
/**
* Setter for property lastModified.
*
* @param lastModified new value of property lastModified
*/
public void setLastModified(Property lastModified) {
this.lastModified = new PropertyWithTimeZone(lastModified, null);
}
/**
* Setter for property xTags.
*
* @param xTags new value of property xTags
*/
public void setXTag(List xTags) {
this.xTags = xTags;
}
/**
* Setter for property dalarm.
*
* @param dalarm new value of property dalarm
*/
public void setDAlarm(PropertyWithTimeZone dalarm) {
this.dalarm = dalarm;
}
/**
* Setter for property dalarm on the basis of a Property (without time
* zone).
*
* @param dalarm new value of property dalarm as a Property (the time zone
* is set to null)
*/
public void setDAlarm(Property dalarm) {
this.dalarm = new PropertyWithTimeZone(dalarm, null);
}
/**
* Setter for property palarm.
*
* @param palarm new value of property palarm
*/
public void setPAlarm(PropertyWithTimeZone palarm) {
this.palarm = palarm;
}
/**
* Setter for property palarm on the basis of a Property (without time
* zone).
*
* @param palarm new value of property palarm as a Property (the time zone
* is set to null)
*/
public void setPAlarm(Property palarm) {
this.palarm = new PropertyWithTimeZone(palarm, null);
}
/**
* Setter for property sequence.
*
* @param sequence new value of property sequence
*/
public void setSequence(Property sequence) {
this.sequence = sequence;
}
/**
* Getter for property reminder.
*
* @return Value of property reminder.
*/
public Reminder getReminder() {
return reminder;
}
/**
* Setter for property reminder.
* @param reminder new value of property reminder.
*/
public void setReminder(Reminder reminder) {
this.reminder = reminder;
}
/**
* Returns the folder where this calendar item has to be stored.
*
* @return value of property folder
*/
public Property getFolder() {
return folder;
}
/**
* Setter for property folder.
*
* @param folder new value of property folder
*/
public void setFolder(Property folder) {
this.folder = folder;
}
/**
* Gets the attendee list.
*
* @return a List of Attendee objects
*/
public List<Attendee> getAttendees() {
return attendees;
}
//---------------------------------------------------------------- Constants
private final static long ONE_YEAR = 31622400000L; // 366 days
private final static long ONE_DAY = 86400000L; // 24 hours
//------------------------------------------------------------- Constructors
/**
* Creates a new empty instance of CalendarContent.
*/
public CalendarContent() {
categories = new Property();
accessClass = new Property();
description = new Property();
latitude = new Property();
longitude = new Property();
location = new Property();
lastModified = new PropertyWithTimeZone();
priority = new Property();
dtStamp = new PropertyWithTimeZone();
sequence = new Property();
status = new Property();
summary = new Property();
uid = new Property();
url = new Property();
dtEnd = new PropertyWithTimeZone();
dtStart = new PropertyWithTimeZone();
duration = new Property();
organizer = new Property();
contact = new Property();
created = new PropertyWithTimeZone();
dalarm = new PropertyWithTimeZone();
palarm = new PropertyWithTimeZone();
folder = new Property();
xTags = null;
allDay = null;
meetingStatus = null;
mileage = null;
recurrencePattern = null;
reminder = null;
attendees = new ArrayList<Attendee>();
}
//----------------------------------------------------------- Public methods
/**
* Returns whether this event is recurrent or not, i.e. if it has got a
* recurrence pattern or not.
*
* @return true if this event is recurrent, false otherwise
*/
public boolean isRecurrent() {
if (recurrencePattern != null) {
return true;
}
return false;
}
/**
* Makes this event not recurrent by removing its recurrence pattern.
*/
public void removeRecurrence() {
recurrencePattern = null;
}
/**
* Adds a custom X-tag to the xTags list. If the list does not exist yet, it
* is created.
*
* @param xTag the tag to add
*/
public void addXTag(XTag xTag) {
if (xTag == null) {
return;
}
if (xTags == null) {
xTags = new ArrayList<XTag>();
}
for (XTag t : xTags) {
if (t.getXTagValue().equals(xTag.getXTagValue())) {
t.getXTag().setPropertyValue(xTag.getXTag().getPropertyValue());
return;
}
}
xTags.add(xTag);
}
/**
* Adds an attendee to the list.
*
* @param attendee the Attendee object to add
*/
public void addAttendee(Attendee attendee) {
this.attendees.add(attendee);
}
/**
* Clears the attendee list.
*/
public void resetAttendees() {
this.attendees.clear();
}
/**
* Extracts a time interval roughly large enough to contain the whole
* event/task and, in case it's a recurrent one, all its occurrences.
*
* @return an array of 2 long integers, the first one being the lower end of
* the interval and the other one being the upper end
*/
public long[] extractInterval() {
final long DEFAULT_FROM = TimeZoneHelper.getReferenceTime()
- ONE_YEAR; // 1 year ago
final long DEFAULT_TO = TimeZoneHelper.getReferenceTime()
+ (ONE_YEAR * 2); // 2 years in the future
final long DEFAULT_TO_UNLIMITED = DEFAULT_TO
+ (ONE_YEAR * 2); // 4 years in the future
String low = null;
if (getDtStart() != null) {
// If there is a start date/time, it is the lower end of the
// interval
low = getDtStart().getPropertyValueAsString();
}
if ((low == null) || ("".equals(low))) {
if (getDtEnd() != null) {
// If there is no start date/time, but there is and end
// date/time, it is the lower end of the interval
low = getDtEnd().getPropertyValueAsString();
}
}
long from, to;
if ((low == null) || ("".equals(low))) {
// If no lower end has been set, the default one is used
from = DEFAULT_FROM;
} else {
// The lower end is moved back to the first midnight
try {
from = TimeUtils.getMidnightTime(low);
} catch (ParseException e) {
from = DEFAULT_FROM;
}
}
if (isRecurrent()){
RecurrencePattern rp = getRecurrencePattern();
if (rp.getOccurrences() != -1) { // finite number of occurrences
int period;
switch (rp.getTypeId()) {
case RecurrencePattern.TYPE_DAILY:
period = 1;
break;
case RecurrencePattern.TYPE_WEEKLY:
period = 7;
break;
case RecurrencePattern.TYPE_MONTHLY:
case RecurrencePattern.TYPE_MONTH_NTH:
period = 31; // large enough for all months
break;
case RecurrencePattern.TYPE_YEARLY:
case RecurrencePattern.TYPE_YEAR_NTH:
period = 366; // large enough for all years
break;
default:
period = 0;
}
if (period != 0) {
// The upper end is set to a sufficient distance from the
// lower end in order to be able to contain the whole
// recurrence
period *= rp.getInterval();
to = from + (period * rp.getOccurrences() * ONE_DAY);
} else {
// If something goes wrong, the default upper end is used
to = DEFAULT_TO;
}
} else { // no occurrence number specified
if (rp.isNoEndDate()) { // unlimited recurrence
// If the recurrence is unlimited, a special default value
// is used
to = DEFAULT_TO_UNLIMITED;
} else {
// If the recurrence has an end date/time, the following
// midnight is used as the interval's upper end
try {
String high = rp.getEndDatePattern();
to = TimeUtils.getMidnightTime(high) + ONE_DAY;
} catch (Exception e) {
to = DEFAULT_TO;
}
}
}
// If a positive exception extends the duration of the recurrence
// beyond the upper end of the interval, the upper end is moved
// onwards until the first midnight after that exception
for (ExceptionToRecurrenceRule etrr : rp.getExceptions()) {
if (etrr.isAddition()) {
String rdate = etrr.getDate();
try {
long extra = TimeUtils.getMidnightTime(rdate) + ONE_DAY;
if (extra > to) {
to = extra;
}
} catch (ParseException e) {
// Ignores this positive exception (RDATE)
}
} // Negative exceptions (EXDATE) are ignored
}
} else { // no recurrence
to = DEFAULT_TO;
}
// In any case, the interval is always large at least as the default
// interval
if (from > DEFAULT_FROM) {
from = DEFAULT_FROM;
}
if (to < DEFAULT_TO) {
to = DEFAULT_TO;
}
return new long[]{from, to};
}
}