0001: /*
0002: *
0003: * Created on August 1, 2002, 9:01 PM
0004: *
0005: * Stores an icalendar as a java object.
0006: * Included in the ICalendar is a collection of iCalendarVEvents
0007: *
0008: * Rules for these objects:
0009: * All dates to be recorded as GMT time. ie if this is EST (Sydney) we record
0010: * based on that time -11h
0011: * The advantage of this is that it becomes easy to compare dates and to convert
0012: * to FBURL if required...
0013: *
0014: * Event objects that are re-curring are expanded there and then out for
0015: * a year or whatever the Max Extension parameter states.
0016: *
0017: *
0018: * Can parse an ICalendar file and create the ICalendar Java Object from
0019: * that ICalendar file.
0020: *
0021: * Currently, this is a partial implementation.
0022: *
0023: * 1) Altered method to sort FBURLS so that they are in date/time sequence, not as found.
0024: *>>> Up To Here.
0025: * 2) Altering logic to include a blow out of events as a vector under the name
0026: * icalExpandedEventCollection. This is the true list of events for this calendar
0027: * expanded for as far as the parameter dictates.
0028: * 3) Represent XML version of ICalendar expanded events. The outside world has no interest
0029: * in the repetition, only that the event occurs.
0030: *
0031: */
0032:
0033: package org.jical;
0034:
0035: /**
0036: *
0037: * @author sfg
0038: * RFC 2445
0039: *
0040: */
0041:
0042: import java.text.SimpleDateFormat;
0043: import java.util.ArrayList;
0044: import java.util.Calendar;
0045: import java.util.Collection;
0046: import java.util.Date;
0047: import java.util.GregorianCalendar;
0048: import java.util.Iterator;
0049: import java.util.Set;
0050: import java.util.TimeZone;
0051: import java.util.TreeSet;
0052: import java.util.logging.Logger;
0053:
0054: public class ICalendar {
0055:
0056: private String calScale;
0057: private String prodId;
0058: private String iCalVersion;
0059: private TimeZone defaultTimeZone;
0060: public Collection icaltimeZoneCollection;
0061:
0062: // The most important one, the list of vevents.
0063: public Collection icalEventCollection;
0064:
0065: private static SimpleDateFormat hoursMinutes = new SimpleDateFormat(
0066: "HHmm");
0067: private static SimpleDateFormat formatter = new SimpleDateFormat(
0068: "yyyyMMdd'T'HHmmss'Z'");
0069: private static SimpleDateFormat dateFormatter = new SimpleDateFormat(
0070: "yyyyMMddHHmmss");
0071: private static SimpleDateFormat dateOnlyFormat = new SimpleDateFormat(
0072: "yyyyMMdd");
0073: private static SimpleDateFormat dayOfWeek = new SimpleDateFormat(
0074: "EEEEEEEE");
0075: private static SimpleDateFormat monthOfYear = new SimpleDateFormat(
0076: "MMMMMMMMMMMM");
0077: private static SimpleDateFormat weekNumber = new SimpleDateFormat(
0078: "ww");
0079: private static SimpleDateFormat dayNumber = new SimpleDateFormat(
0080: "d");
0081: private static SimpleDateFormat year = new SimpleDateFormat("yyyy");
0082: private static Date DEFAULTSTARTDATE = new Date(1);
0083:
0084: Calendar repeatDateStart = new GregorianCalendar();
0085: Calendar repeatDateEnd = new GregorianCalendar();
0086: private Logger logger = Logger.getLogger(this .getClass().getName());
0087:
0088: /**
0089: * The sorted Expanded Events set holds all events from repeat
0090: * events sorted by startdate/uid.
0091: **/
0092: public Set sortedExpandedEvents;
0093:
0094: /** Holds value of property organizer. */
0095: private String organizer;
0096:
0097: /** Holds value of property FBUrl. */
0098: private String FBUrl;
0099:
0100: /** Holds value of property organizerEmail. */
0101: private String organizerEmail;
0102:
0103: /** Creates a new instance of ICalendar. */
0104: public ICalendar() {
0105: icaltimeZoneCollection = new ArrayList();
0106: icalEventCollection = new ArrayList();
0107: defaultTimeZone = TimeZone.getDefault();
0108: sortedExpandedEvents = new TreeSet(
0109: new ICalendarVEvent.StartDateUIDComparator());
0110: }
0111:
0112: /** Getter for property calScale.
0113: * @return Value of property calScale.
0114: */
0115: public String getCalScale() {
0116: return calScale;
0117: }
0118:
0119: /** Setter for property calScale.
0120: * @param calScale New value of property calScale.
0121: */
0122: public void setCalScale(String calScale) {
0123: this .calScale = calScale;
0124: }
0125:
0126: /** Getter for property prodId.
0127: * @return Value of property prodId.
0128: */
0129: public String getProdId() {
0130: return prodId;
0131: }
0132:
0133: /** Setter for property prodId.
0134: * @param prodId New value of property prodId.
0135: */
0136: public void setProdId(String prodId) {
0137: this .prodId = prodId;
0138: }
0139:
0140: /** Getter for property version.
0141: * @return Value of property version.
0142: */
0143: public String getVersion() {
0144: return iCalVersion;
0145: }
0146:
0147: /** Setter for property version.
0148: * @param iCalVersion New value of property version.
0149: */
0150: public void setVersion(String iCalVersion) {
0151: this .iCalVersion = iCalVersion;
0152: }
0153:
0154: /** Getter for property organizer.
0155: * @return Value of property organizer.
0156: */
0157: public String getOrganizer() {
0158: return this .organizer;
0159: }
0160:
0161: /** Setter for property organizer.
0162: * @param organizer New value of property organizer.
0163: */
0164: public void setOrganizer(String organizer) {
0165: this .organizer = organizer;
0166: }
0167:
0168: /** Getter for property FBUrl.
0169: * @return Value of property FBUrl.
0170: */
0171: public String getFBUrl() {
0172: return this .FBUrl;
0173: }
0174:
0175: /** Setter for property FBUrl.
0176: * @param FBUrl New value of property FBUrl.
0177: */
0178: public void setFBUrl(String FBUrl) {
0179: this .FBUrl = FBUrl;
0180: }
0181:
0182: /** Getter for property organizerEmail.
0183: * @return Value of property organizerEmail.
0184: */
0185: public String getOrganizerEmail() {
0186: return this .organizerEmail;
0187: }
0188:
0189: /** Setter for property organizerEmail.
0190: * @param organizerEmail New value of property organizerEmail.
0191: */
0192: public void setOrganizerEmail(String organizerEmail) {
0193: this .organizerEmail = organizerEmail;
0194: }
0195:
0196: /*
0197: * TODO Refactor soon
0198: * This is not the optimal place for this but it works!
0199: *
0200: */
0201: public Date getDateFrom(String dateRangeOrDaysForward)
0202: throws Exception {
0203: try {
0204: return (Date) dateFormatter.parse(dateRangeOrDaysForward
0205: .substring(0, 14));
0206: } catch (Exception e) {
0207: throw e;
0208: }
0209: }
0210:
0211: public Date getDateTo(String dateRangeOrDaysForward)
0212: throws Exception {
0213: // Gets the From Date
0214: try {
0215: return (Date) dateFormatter.parse(dateRangeOrDaysForward
0216: .substring(15));
0217: } catch (Exception e) {
0218: throw e;
0219: }
0220: }
0221:
0222: /*
0223: * TODO Moved to DateTimeRange utility.
0224: */
0225: public long getDaysForwardNumeric(String dateRangeOrDaysForward)
0226: throws Exception {
0227: try {
0228: return java.lang.Integer.parseInt(dateRangeOrDaysForward);
0229: } catch (Exception e) {
0230: throw e;
0231: }
0232: }
0233:
0234: /*
0235: * TODO retire once refactored
0236: */
0237: public Date getDateToFromDaysForward(long daysForward) {
0238: Date dateTo = new Date();
0239: long rollMicroSecs = 86400000 * daysForward;
0240: dateTo.setTime(dateTo.getTime() + (rollMicroSecs));
0241: return dateTo;
0242: }
0243:
0244: public void getIcalExpandedEvents(Date dateFrom, Date dateTo,
0245: String timeRange) {
0246: /*
0247: * Get time ranges.
0248: */
0249:
0250: int timeFrom = 0;// new Integer(0);
0251: int timeTo = 2400; //new Integer(2400);
0252: if (timeRange != null) {
0253: timeFrom = Integer.parseInt(timeRange.substring(0, 4));
0254: timeTo = Integer.parseInt(timeRange.substring(5));
0255: }
0256:
0257: formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
0258:
0259: for (Iterator i = icalEventCollection.iterator(); i.hasNext();) {
0260: ICalendarVEvent icalEvent = (ICalendarVEvent) i.next();
0261: Date dateStart = icalEvent.getDateStart();
0262: Date dateEnd = icalEvent.getDateEnd();
0263:
0264: if ((dateStart.after(dateFrom) && dateStart.before(dateTo))
0265: || dateStart.equals(dateFrom)
0266: || dateStart.equals(dateTo)) {
0267: logger.fine("Create This date: " + dateStart);
0268: // This is a qualified Event with no repeat rules!
0269: createExpandedEvent(dateStart, dateEnd, icalEvent,
0270: dateFrom, dateTo, timeFrom, timeTo);
0271: }
0272:
0273: if (icalEvent.getRRule() != null) {
0274: logger.fine("Repeat Rule is not null");
0275: /*
0276: * Moved to parser
0277: RepeatRules rr = new RepeatRules();
0278: icalEvent.getRepeatRules().parseRepeatRules(icalEvent.getRRule());
0279: */
0280: /*
0281: * We have now parsed the repeat rules.
0282: *
0283: * Now evaluate the repeat rules all together.
0284: From RFC 2445
0285:
0286: Here is an example of evaluating multiple BYxxx rule parts.
0287:
0288: DTSTART;TZID=US-Eastern:19970105T083000
0289: RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;
0290: BYMINUTE=30
0291:
0292: First, the "INTERVAL=2" would be applied to "FREQ=YEARLY" to arrive
0293: at "every other year". Then, "BYMONTH=1" would be applied to arrive
0294: at "every January, every other year". Then, "BYDAY=SU" would be
0295: applied to arrive at "every Sunday in January, every other year".
0296: Then, "BYHOUR=8,9" would be applied to arrive at "every Sunday in
0297: January at 8 AM and 9 AM, every other year". Then, "BYMINUTE=30"
0298: would be applied to arrive at "every Sunday in January at 8:30 AM and
0299: 9:30 AM, every other year". Then, lacking information from RRULE, the
0300: second is derived from DTSTART, to end up in "every Sunday in January
0301: at 8:30:00 AM and 9:30:00 AM, every other year". Similarly, if the
0302: BYMINUTE, BYHOUR, BYDAY, BYMONTHDAY or BYMONTH rule part were
0303: missing, the appropriate minute, hour, day or month would have been
0304: retrieved from the "DTSTART" property.
0305: *
0306: *
0307: */
0308:
0309: // Move on to the first repeatable date.
0310: Calendar newCal = new GregorianCalendar();
0311: newCal.setTime(icalEvent.getDateStart());
0312: setRepeatDateStart(newCal);
0313: newCal = new GregorianCalendar();
0314: newCal.setTime(icalEvent.getDateEnd());
0315: setRepeatDateEnd(newCal);
0316:
0317: getNextRepeatDate(icalEvent);
0318:
0319: logger.fine("Repeat Date next date: "
0320: + getRepeatDateStart().getTime() + " Interval "
0321: + icalEvent.getRepeatRules().interval
0322: + " Repeat Unit "
0323: + icalEvent.getRepeatRules().dateRepeatUnit);
0324:
0325: Date repeatUntilDate = icalEvent.getRepeatRules().repeatUntilDate;
0326: if (repeatUntilDate == null)
0327: repeatUntilDate = dateTo;
0328: if (repeatUntilDate.after(dateTo))
0329: repeatUntilDate = dateTo;
0330:
0331: // In case we are creating up to a counter point, record the
0332: // count of events for this loop.
0333: int createCtr = 0;
0334:
0335: while (getRepeatDateStart() != null
0336: && !getRepeatDateStart().getTime().after(
0337: repeatUntilDate)) {
0338: processRepeatEvent(icalEvent.getRepeatRules(),
0339: icalEvent.getRepeatRules().dateRepeatUnit,
0340: icalEvent, dateFrom, dateTo, timeFrom,
0341: timeTo);
0342:
0343: // Next repeat date to check.
0344: getNextRepeatDate(icalEvent);
0345:
0346: logger
0347: .fine("Repeat Date next date: "
0348: + repeatDateStart.getTime()
0349: + " Interval "
0350: + icalEvent.getRepeatRules().interval
0351: + " Repeat Unit "
0352: + icalEvent.getRepeatRules().dateRepeatUnit);
0353:
0354: //repeatDateStart.add(icalEvent.getRepeatRules().dateRepeatUnit, icalEvent.getRepeatRules().interval);
0355: //repeatDateEnd.add(icalEvent.getRepeatRules().dateRepeatUnit, icalEvent.getRepeatRules().interval);
0356: }
0357: }
0358: }
0359: }
0360:
0361: public void getNextRepeatDate(ICalendarVEvent icalEvent) {
0362: logger.fine("dateStart: " + getRepeatDateStart().getTime());
0363: Calendar newDate = getRepeatDateStart();
0364: Calendar newDateEnd = getRepeatDateEnd();
0365: if (icalEvent.getRepeatRules().dateRepeatUnit == Calendar.DAY_OF_WEEK) {
0366: logger.fine("DAY_OF_WEEK: "
0367: + getRepeatDateStart().getTime() + " date: "
0368: + newDate.getTime());
0369: newDate.add(Calendar.DATE,
0370: 7 * icalEvent.getRepeatRules().interval);
0371: logger.fine("DAY_OF_WEEK2 : " + newDate.getTime());
0372: setRepeatDateStart(newDate);
0373: newDate = getRepeatDateEnd();
0374: newDate.add(Calendar.DATE,
0375: 7 * icalEvent.getRepeatRules().interval);
0376: setRepeatDateEnd(newDate);
0377: } else {
0378: //
0379: logger.fine("about to step to next repeat event unit:"
0380: + icalEvent.getRepeatRules().dateRepeatUnit
0381: + " Interval: "
0382: + icalEvent.getRepeatRules().interval);
0383:
0384: newDate.add(icalEvent.getRepeatRules().dateRepeatUnit,
0385: icalEvent.getRepeatRules().interval);
0386: setRepeatDateStart(newDate);
0387:
0388: newDateEnd.add(icalEvent.getRepeatRules().dateRepeatUnit,
0389: icalEvent.getRepeatRules().interval);
0390: setRepeatDateEnd(newDateEnd);
0391: logger.fine("After date add - DateStart: "
0392: + getRepeatDateStart().getTime());
0393: }
0394: }
0395:
0396: public void processRepeatEvent(RepeatRules repeatRules,
0397: int dateConstraint, ICalendarVEvent icalEvent,
0398: Date dateFrom, Date dateTo, int timeFrom, int timeTo) {
0399: /*
0400: * @Comment. This method will process an event and see if further examination is required prior to creating
0401: * an expanded event.
0402: */
0403: if (dateConstraint == Calendar.YEAR) {
0404: if (repeatRules.repeatByYearDay != null)
0405: repeatByYearDay(repeatDateStart, repeatDateEnd,
0406: repeatRules, dateConstraint, icalEvent,
0407: dateFrom, dateTo, timeFrom, timeTo);
0408: else if (repeatRules.repeatByMonth != null)
0409: repeatByMonth(repeatDateStart, repeatDateEnd,
0410: repeatRules, dateConstraint, icalEvent,
0411: dateFrom, dateTo, timeFrom, timeTo);
0412: // Too hard at present. if (repeatRules.repeatByWeekNo !=null)
0413: // repeatByWeekNo(repeatDateStart, repeatDateEnd, repeatRules, dateConstraint);
0414: else {
0415: // CREATE REPEATED EVENT!!!!
0416: logger.fine("Year: Create: "
0417: + getRepeatDateStart().getTime());
0418: createExpandedEvent(repeatDateStart.getTime(),
0419: repeatDateEnd.getTime(), icalEvent, dateFrom,
0420: dateTo, timeFrom, timeTo);
0421: }
0422: } else if (dateConstraint == Calendar.MONTH) {
0423: logger.fine("Repeat by Month, repeat by monthday:"
0424: + repeatRules.repeatByMonthDay);
0425: logger.fine("Repeat by Month, repeat by day:"
0426: + repeatRules.repeatByDay);
0427: if (repeatRules.repeatByMonthDay != null) {
0428: repeatByMonthDay(repeatDateStart, repeatDateEnd,
0429: repeatRules, dateConstraint, icalEvent,
0430: dateFrom, dateTo, timeFrom, timeTo);
0431: } else if (repeatRules.repeatByDay != null) {
0432: repeatByDay(repeatRules, dateConstraint, icalEvent,
0433: dateFrom, dateTo, timeFrom, timeTo);
0434: } else {
0435: // CREATE REPEATED EVENT!!!!
0436: logger.fine("MONTH: Create: "
0437: + getRepeatDateStart().getTime());
0438: createExpandedEvent(repeatDateStart.getTime(),
0439: repeatDateEnd.getTime(), icalEvent, dateFrom,
0440: dateTo, timeFrom, timeTo);
0441: }
0442: }
0443: // ie. Weekly.
0444: else if (dateConstraint == Calendar.DAY_OF_WEEK) {
0445: if (repeatRules.repeatByDay != null) {
0446: logger.fine("RepeatByDay:" + repeatRules.repeatByDay);
0447: repeatByWeekDay(repeatRules, dateConstraint, icalEvent,
0448: dateFrom, dateTo, timeFrom, timeTo);
0449: logger.fine("End RepeatByDay");
0450: } else {
0451: logger.fine("Create Weekly Event"
0452: + getRepeatDateStart().getTime());
0453: createExpandedEvent(getRepeatDateStart().getTime(),
0454: getRepeatDateEnd().getTime(), icalEvent,
0455: dateFrom, dateTo, timeFrom, timeTo);
0456: }
0457: } else if (dateConstraint == Calendar.DATE) {
0458: // This is probably the lowest common denominator at present.
0459: logger.fine("DATE: Create: "
0460: + getRepeatDateStart().getTime());
0461: createExpandedEvent(repeatDateStart.getTime(),
0462: repeatDateEnd.getTime(), icalEvent, dateFrom,
0463: dateTo, timeFrom, timeTo);
0464: } else if (dateConstraint == Calendar.HOUR) {
0465: logger.fine("HOUR: Create: "
0466: + getRepeatDateStart().getTime());
0467: createExpandedEvent(repeatDateStart.getTime(),
0468: repeatDateEnd.getTime(), icalEvent, dateFrom,
0469: dateTo, timeFrom, timeTo);
0470: } else if (dateConstraint == Calendar.MINUTE) {
0471: logger.fine("MINUTE: Create: "
0472: + getRepeatDateStart().getTime());
0473: createExpandedEvent(repeatDateStart.getTime(),
0474: repeatDateEnd.getTime(), icalEvent, dateFrom,
0475: dateTo, timeFrom, timeTo);
0476: } else if (dateConstraint == Calendar.SECOND) {
0477: logger.fine("SECOND: Create: "
0478: + getRepeatDateStart().getTime());
0479: createExpandedEvent(repeatDateStart.getTime(),
0480: repeatDateEnd.getTime(), icalEvent, dateFrom,
0481: dateTo, timeFrom, timeTo);
0482: } else {
0483: logger.severe("NO MATCHING INTERVAL");
0484: }
0485: }
0486:
0487: public void repeatByYearDay(Calendar repeatDateStart,
0488: Calendar repeatDateEnd, RepeatRules repeatRules,
0489: int dateRepeatUnit, ICalendarVEvent icalEvent,
0490: Date dateFrom, Date dateTo, int timeFrom, int timeTo) {
0491: /*
0492: *
0493: * This routine will find the days of the year for which we require to repeat this event then
0494: * create zoom into those days for more rules.
0495: *
0496: */
0497: java.util.StringTokenizer st = new java.util.StringTokenizer(
0498: repeatRules.repeatByYearDay, ",");
0499: while (st.hasMoreTokens()) {
0500: String rule = st.nextToken();
0501: int firstLast = 0;
0502: int dayOfYear = 0;
0503: if (rule.startsWith("-")) {
0504: // We are doing the Last X thingo.
0505: firstLast = 1;
0506: }
0507: try {
0508: dayOfYear = Integer.parseInt(rule);
0509: } catch (Exception e) {
0510: logger
0511: .severe("Error parsing integer value from repeatByYearDay: "
0512: + rule);
0513: return;
0514: }
0515: // OK so we know what day of year, now find it!
0516: Calendar getDate = new GregorianCalendar();
0517: getDate.setTime(repeatDateStart.getTime());
0518: if (firstLast == 0) {
0519: // Set to start of year then add the days to that.
0520: getDate.set(Calendar.DAY_OF_MONTH, 1);
0521: getDate.set(Calendar.MONTH, 1);
0522: getDate.add(Calendar.DATE, dayOfYear - 1);
0523: } else {
0524: getDate.set(Calendar.MONTH, Calendar.DECEMBER);
0525: getDate.set(Calendar.DAY_OF_MONTH, 31);
0526: getDate.add(Calendar.DATE, -1 * dayOfYear);
0527: }
0528: // Now move that back to the repeat date Start/End.
0529: repeatDateStart.set(Calendar.DAY_OF_MONTH, 1);
0530: repeatDateStart.set(Calendar.MONTH, getDate
0531: .get(Calendar.MONTH));
0532: repeatDateStart.set(Calendar.DAY_OF_MONTH, getDate
0533: .get(Calendar.DAY_OF_MONTH));
0534: repeatDateEnd.set(Calendar.DAY_OF_MONTH, 1);
0535: repeatDateEnd.set(Calendar.MONTH, getDate
0536: .get(Calendar.MONTH));
0537: repeatDateEnd.set(Calendar.DAY_OF_MONTH, getDate
0538: .get(Calendar.DAY_OF_MONTH));
0539:
0540: // This Date has been selected for processing. It may require further processing based upon the
0541: // further contents of the repeatRule..
0542: processRepeatEvent(repeatRules, Calendar.DATE, icalEvent,
0543: dateFrom, dateTo, timeFrom, timeTo);
0544: }
0545: }
0546:
0547: public void repeatByMonthDay(Calendar repeatDateStart,
0548: Calendar repeatDateEnd, RepeatRules repeatRules,
0549: int dateRepeatUnit, ICalendarVEvent icalEvent,
0550: Date dateFrom, Date dateTo, int timeFrom, int timeTo) {
0551: /*
0552: *
0553: * This routine will find the days of the Month for which we require to repeat this event then
0554: *
0555: */
0556: java.util.StringTokenizer st = new java.util.StringTokenizer(
0557: repeatRules.repeatByMonthDay, ",");
0558: while (st.hasMoreTokens()) {
0559: String rule = st.nextToken();
0560: int firstLast = 0;
0561: int dayOfMonth = 0;
0562:
0563: logger.fine("rule:" + rule);
0564:
0565: if (rule.startsWith("-")) {
0566: // We are doing the Last X thingo.
0567: firstLast = 1;
0568: }
0569: try {
0570: dayOfMonth = Integer.parseInt(rule);
0571: } catch (Exception e) {
0572: logger
0573: .severe("Error parsing integer value from repeatByYearDay: "
0574: + rule);
0575: return;
0576: }
0577: // OK so we know what day of year, now find it!
0578: Calendar getDate = new GregorianCalendar();
0579: getDate.setTime(repeatDateStart.getTime());
0580: if (firstLast == 0) {
0581: // Set to start of year then add the days to that.
0582: getDate.set(Calendar.DAY_OF_MONTH, 1);
0583: getDate.add(Calendar.DATE, dayOfMonth - 1);
0584: } else {
0585: // Get the last day of the month by going forward a month then back a day.
0586: getDate.set(Calendar.DAY_OF_MONTH, 1);
0587: getDate.add(Calendar.MONTH, 1);
0588: getDate.add(Calendar.DATE, -1);
0589: // Now we are on the last day of the month, subtract the days (+1).
0590: getDate.set(Calendar.DATE, 1 + dayOfMonth);
0591: }
0592: // Now move that back to the repeat date Start/End.
0593: repeatDateStart.set(Calendar.DAY_OF_MONTH, 1);
0594: repeatDateStart.set(Calendar.MONTH, getDate
0595: .get(Calendar.MONTH));
0596: repeatDateStart.set(Calendar.DAY_OF_MONTH, getDate
0597: .get(Calendar.DAY_OF_MONTH));
0598: repeatDateEnd.set(Calendar.DAY_OF_MONTH, 1);
0599: repeatDateEnd.set(Calendar.MONTH, getDate
0600: .get(Calendar.MONTH));
0601: repeatDateEnd.set(Calendar.DAY_OF_MONTH, getDate
0602: .get(Calendar.DAY_OF_MONTH));
0603:
0604: // This Date has been selected for processing. It may require further processing based upon the
0605: // further contents of the repeatRule..
0606: processRepeatEvent(repeatRules, Calendar.DATE, icalEvent,
0607: dateFrom, dateTo, timeFrom, timeTo);
0608: }
0609: }
0610:
0611: public void repeatByMonth(Calendar repeatDateStart,
0612: Calendar repeatDateEnd, RepeatRules repeatRules,
0613: int dateRepeatUnit, ICalendarVEvent icalEvent,
0614: Date dateFrom, Date dateTo, int timeFrom, int timeTo) {
0615: /*
0616: *
0617: * This routine will find the days of the year for which we require to repeat this event then
0618: * zoom into those days for more rules.
0619: *
0620: */
0621: java.util.StringTokenizer st = new java.util.StringTokenizer(
0622: repeatRules.repeatByMonth, ",");
0623: while (st.hasMoreTokens()) {
0624: int monthOfYear = 0;
0625: String rule = st.nextToken();
0626: try {
0627: monthOfYear = Integer.parseInt(rule);
0628: } catch (Exception e) {
0629: logger
0630: .severe("Error parsing integer value from repeatByYearDay: "
0631: + rule);
0632: return;
0633: }
0634:
0635: // Now move that back to the repeat date Start/End.
0636:
0637: //logger.severe(repeatDateStart.getTime());
0638: repeatDateStart.set(Calendar.MONTH, monthOfYear - 1);
0639: if (repeatDateStart.get(Calendar.MONTH) != monthOfYear - 1) {
0640:
0641: logger.severe("Error setting MonthOfYear for date: "
0642: + repeatDateStart.getTime() + " to Month "
0643: + monthOfYear + " From DateStartMonth"
0644: + repeatDateStart.get(Calendar.MONTH)
0645: + " Event: " + icalEvent.getSummary());
0646: return;
0647: }
0648:
0649: //logger.severe(repeatDateEnd.getTime());
0650: repeatDateEnd.set(Calendar.MONTH, monthOfYear - 1);
0651: if (repeatDateEnd.get(Calendar.MONTH) != monthOfYear - 1) {
0652: logger.severe("Error setting MonthOfYear for date: "
0653: + repeatDateEnd.getTime() + " to Month "
0654: + monthOfYear + " From DateEndMonth"
0655: + repeatDateEnd.get(Calendar.MONTH)
0656: + " Event: " + icalEvent.getSummary());
0657: return;
0658: }
0659: // This Date has been selected for processing. It may require further processing based upon the
0660: // further contents of the repeatRule..
0661: processRepeatEvent(repeatRules, Calendar.MONTH, icalEvent,
0662: dateFrom, dateTo, timeFrom, timeTo);
0663: }
0664: }
0665:
0666: /*
0667: * Introduced to get weekly multi-day repeats correct.
0668: */
0669: public void repeatByWeekDay(RepeatRules repeatRules,
0670: int dateRepeatUnit, ICalendarVEvent icalEvent,
0671: Date dateFrom, Date dateTo, int timeFrom, int timeTo) {
0672:
0673: // Assumes starting date is set as repeatDateStart
0674: // Does NOT increment it as we need to enable iterating through week but NOT upsetting the
0675: // overall week incrementer.
0676: java.util.StringTokenizer st = new java.util.StringTokenizer(
0677: repeatRules.repeatByDay, ",");
0678: while (st.hasMoreTokens()) {
0679:
0680: String rule = st.nextToken();
0681: int dayOfWeek = ("SUMOTUWETHFRSA".indexOf(rule.substring(0,
0682: 2)) / 2) + 1;
0683: if (dayOfWeek == -1)
0684: return;
0685:
0686: // Find this DOW in the next 7 days..
0687: int iDayCtr = 0;
0688: Calendar workDateFrom = new GregorianCalendar();
0689: workDateFrom.setTime(repeatDateStart.getTime());
0690: Calendar workDateTo = new GregorianCalendar();
0691: workDateTo.setTime(repeatDateEnd.getTime());
0692: logger.fine("Going to loop 7 days from "
0693: + workDateFrom.getTime() + " as long as "
0694: + workDateTo.getTime() + " is before " + dateTo
0695: + " and this" + !workDateTo.equals(dateTo)
0696: + " is true");
0697: // Loop through the 7 days and create for this day..
0698: while (iDayCtr <= 7
0699: && (workDateTo.getTime().before(dateTo) && !workDateTo
0700: .equals(dateTo))) {
0701: iDayCtr++;
0702: workDateFrom.add(Calendar.DATE, 1);
0703: workDateTo.add(Calendar.DATE, 1);
0704: logger.fine("Up to " + workDateFrom.getTime());
0705: if (workDateFrom.get(Calendar.DAY_OF_WEEK) == dayOfWeek) {
0706: logger.fine("Create expanded event "
0707: + workDateFrom.getTime());
0708: createExpandedEvent(workDateFrom.getTime(),
0709: workDateTo.getTime(), icalEvent, dateFrom,
0710: dateTo, timeFrom, timeTo);
0711: // found this dow so next dow of concern
0712: break;
0713: }
0714: }
0715:
0716: }
0717:
0718: }
0719:
0720: public void repeatByDay(RepeatRules repeatRules,
0721: int dateRepeatUnit, ICalendarVEvent icalEvent,
0722: Date dateFrom, Date dateTo, int timeFrom, int timeTo) {
0723: /*
0724: *
0725: * This routine will find the days of the year for which we require to repeat this event then
0726: * create zoom into those days for more rules.
0727: *
0728: */
0729: java.util.StringTokenizer st = new java.util.StringTokenizer(
0730: repeatRules.repeatByDay, ",");
0731: while (st.hasMoreTokens()) {
0732: String rule = st.nextToken();
0733:
0734: logger.fine("Next Rule: " + rule);
0735:
0736: int firstLast = 0;
0737: int sequenceInMonth = 9999;
0738: int dayOfWeek = 0;
0739:
0740: Calendar workDateFrom = new GregorianCalendar();
0741: workDateFrom.setTime(repeatDateStart.getTime());
0742: Calendar workDateTo = new GregorianCalendar();
0743: workDateTo.setTime(repeatDateEnd.getTime());
0744:
0745: if (rule.startsWith("-")) {
0746: // We are doing the Last X thingo.
0747: firstLast = 1;
0748: try {
0749: sequenceInMonth = Integer.parseInt(rule.substring(
0750: 1, 1));
0751: //dayOfWeek = ("SUMOTUWETHFRSA".indexOf(rule.substring(2,2)) / 2) + 1;
0752: // Fix a bug where the wrong index was being used in
0753: // the substring for the dayOfWeek
0754: dayOfWeek = ("SUMOTUWETHFRSA".indexOf(rule
0755: .substring(2, 4)) / 2) + 1;
0756: } catch (Exception e) {
0757: logger.severe("Error Parsing Day RRULE, rule: "
0758: + rule);
0759: e.printStackTrace(System.err);
0760: return;
0761: }
0762: }
0763: //else if ("0123456789".indexOf(rule.substring(0)) != -1)
0764: else if ("0123456789".indexOf(rule.substring(0, 1)) != -1) {
0765: try {
0766: // We have a 1st/last Sunday in Jan type of situ.
0767: sequenceInMonth = Integer.parseInt(rule
0768: .substring(0));
0769: dayOfWeek = ("SUMOTUWETHFRSA".indexOf(rule
0770: .substring(1, 2)) / 2) + 1;
0771: } catch (Exception e) {
0772: logger.severe("Error Parsing Day RRULE, rule: "
0773: + rule);
0774: e.printStackTrace(System.err);
0775: return;
0776: }
0777: } else {
0778: // A (not so) simple day of week.
0779: dayOfWeek = ("SUMOTUWETHFRSA".indexOf(rule.substring(0,
0780: 2)) / 2) + 1;
0781: ;
0782: }
0783:
0784: logger.fine("RepeatRulesBySetPos"
0785: + repeatRules.repeatBySetPos);
0786: if (repeatRules.repeatBySetPos != null) {
0787: sequenceInMonth = repeatRules.getRepeatBySetPos()
0788: .intValue();
0789: }
0790:
0791: /*
0792: * Now check the RepeatByDay rule for days of the week.
0793: * If it exists, create an event for each of those days subject to
0794: * date range etc, etc
0795: *
0796: */
0797:
0798: // First the repeated day of week.. ie MO,TU etc.
0799: if (sequenceInMonth == 9999) {
0800: if (dateRepeatUnit == Calendar.MONTH) {
0801: // Run through the month and processEvent every Calendar Day that matches.
0802: // This is like the 3rd Monday in the Month type of thing.
0803: int this Month = workDateFrom.get(Calendar.MONTH);
0804: while (workDateFrom.get(Calendar.MONTH) == this Month) {
0805: if (workDateFrom.get(Calendar.DAY_OF_WEEK) == dayOfWeek) {
0806: setRepeatDateStart(workDateFrom);
0807: setRepeatDateEnd(workDateTo);
0808: processRepeatEvent(repeatRules,
0809: Calendar.DATE, icalEvent, dateFrom,
0810: dateTo, timeFrom, timeTo);
0811: }
0812: workDateFrom.add(Calendar.DATE, 1);
0813: workDateTo.add(Calendar.DATE, 1);
0814: setRepeatDateStart(workDateFrom);
0815: setRepeatDateEnd(workDateTo);
0816: }
0817: }
0818: if (dateRepeatUnit == Calendar.DAY_OF_WEEK) {
0819: // Run through the days of the week and create an event for the day inferred.
0820: // Like Every Monday type of thing.
0821: int weekCount = 1;
0822: while (weekCount < 8) {
0823: if (workDateFrom.get(Calendar.DAY_OF_WEEK) == dayOfWeek)
0824: processRepeatEvent(repeatRules,
0825: Calendar.MONTH, icalEvent,
0826: dateFrom, dateTo, timeFrom, timeTo);
0827: workDateFrom.add(Calendar.DATE, 1);
0828: workDateTo.add(Calendar.DATE, 1);
0829: setRepeatDateStart(workDateFrom);
0830: setRepeatDateEnd(workDateTo);
0831: weekCount++;
0832: }
0833: }
0834: } else {
0835: logger.fine("Attempting nth DD in Month from "
0836: + workDateFrom.getTime());
0837: // Looking for the xth Sunday of the month etc etc.
0838: Calendar getDate = new GregorianCalendar();
0839: getDate.setTime(workDateFrom.getTime());
0840: int this Month = workDateFrom.get(Calendar.MONTH);
0841: int dayIncrement = 0;
0842: int occuranceCtr = 0;
0843: logger.fine("sequenceInMonth: " + sequenceInMonth);
0844: if (sequenceInMonth < 0) {
0845: // Set last day of month.
0846: getDate.set(Calendar.DAY_OF_MONTH, 1);
0847: // Add one month,
0848: getDate.add(Calendar.MONTH, 1);
0849: // Substract 1 day should get last day of month.
0850: getDate.add(Calendar.DATE, -1);
0851: logger.fine("Start from last day of month: "
0852: + getDate.getTime());
0853: // This determines that we are counting BACKWARDS.
0854: dayIncrement = -1;
0855:
0856: } else {
0857: getDate.set(Calendar.DAY_OF_MONTH, 1);
0858: dayIncrement = +1;
0859: }
0860:
0861: while (getDate.get(Calendar.MONTH) == this Month) {
0862: if (getDate.get(Calendar.DAY_OF_WEEK) == dayOfWeek) {
0863: logger.fine("Found that day of week:"
0864: + getDate.getTime());
0865:
0866: occuranceCtr++;
0867: if (occuranceCtr == sequenceInMonth
0868: * dayIncrement) {
0869: workDateFrom.set(Calendar.DAY_OF_MONTH,
0870: getDate.get(Calendar.DAY_OF_MONTH));
0871: workDateTo.set(Calendar.DAY_OF_MONTH,
0872: getDate.get(Calendar.DAY_OF_MONTH));
0873: setRepeatDateStart(workDateFrom);
0874: setRepeatDateEnd(workDateTo);
0875: logger.fine("Found one!" + workDateFrom);
0876: processRepeatEvent(repeatRules,
0877: Calendar.DATE, icalEvent, dateFrom,
0878: dateTo, timeFrom, timeTo);
0879: return;
0880: }
0881: }
0882:
0883: getDate.add(Calendar.DATE, dayIncrement);
0884: logger.fine("Next date is : " + getDate.getTime());
0885: }
0886: }
0887:
0888: } // Has More Days to process.
0889: }
0890:
0891: public void createExpandedEvent(Date dateStartIn, Date dateEndIn,
0892: ICalendarVEvent iCalEventIn, Date dateFrom, Date dateTo,
0893: int timeFrom, int timeTo) {
0894: /*
0895: * Moved repeat Exception check to where all repeat events are created.
0896: */
0897:
0898: logger.fine("Create Expanded Event: " + dateStartIn);
0899:
0900: // Fix a NPE for repeating event with no end
0901: if (dateEndIn == null) {
0902: dateEndIn = iCalEventIn.getDateEnd();
0903: }
0904:
0905: // Make sure date of event is within the date range we want
0906: if (dateEndIn.before(dateFrom) || dateStartIn.after(dateTo))
0907: return;
0908:
0909: int testTimeStart = Integer.parseInt(hoursMinutes
0910: .format(dateStartIn));
0911: int testTimeEnd = Integer.parseInt(hoursMinutes
0912: .format(dateEndIn));
0913:
0914: /* There are four conditions for inclusion of this event
0915: in this day based on a time range.
0916: 1) Runs right across this date and others.
0917: * ie day 23/2/2004. Event goes 22/2/2004 to 25/2/2004
0918: 2) Falls within the day
0919: ** ie day 23/2/2004. Event goes 23/2/2004 to 23/2/2004
0920: 3) End period falls within the start day point or
0921: * ie day 23/2/2004. Event goes 22/2/2004 to 23/2/2004 5am
0922: 4) Start period falls within the end day point.
0923: * ie day 23/2/2004. Event goes 23/2/2004 12 noon to 25/2/2004
0924: */
0925:
0926: // Condition 1.
0927: if (testTimeStart < timeFrom && testTimeEnd > timeTo) {
0928: }
0929: // Condition 2.
0930: else if (testTimeStart >= timeFrom && testTimeEnd <= timeTo) {
0931: }
0932: // Condition 3.
0933: else if (testTimeEnd > timeTo && testTimeStart <= timeTo) {
0934: }
0935: // Condition 4.
0936: else if (testTimeEnd >= timeFrom && testTimeStart < timeFrom) {
0937: }
0938: // All Day Event!!
0939: else if (testTimeStart == 0 && testTimeEnd == 0) {
0940: } else
0941: return;
0942:
0943: if (iCalEventIn.isExDatesExist()) {
0944: boolean foundExDate = false;
0945:
0946: for (Iterator itr = iCalEventIn.exDateCollection.iterator(); itr
0947: .hasNext();) {
0948: String exDate = (String) itr.next();
0949: // Bug fix 2004/10
0950: if (exDate.startsWith(dateOnlyFormat
0951: .format(dateStartIn)))
0952: return;
0953: }
0954: }
0955:
0956: if (iCalEventIn.getRepeatRules().repeatUntilCount <= iCalEventIn
0957: .getRepeatCount()) {
0958: return;
0959: }
0960:
0961: iCalEventIn.setRepeatCount(iCalEventIn.getRepeatCount() + 1);
0962: try {
0963: ICalendarVEvent iCalEventNew = (ICalendarVEvent) iCalEventIn
0964: .clone();
0965: iCalEventNew.setDateStart(dateStartIn);
0966: iCalEventNew.setDateEnd(dateEndIn);
0967:
0968: if ((iCalEventNew.getOrganizer() == null || iCalEventNew
0969: .getOrganizer().length() == 0)
0970: && (iCalEventNew.getOrganizer() == null || iCalEventNew
0971: .getOrganizer().length() == 0)) {
0972: //iCalEventNew.setOrganizer(getOrganizer());
0973: iCalEventNew.setOrganizer(getOrganizer());
0974: }
0975: // Make sure single custom events for a recurring event are added
0976: if (sortedExpandedEvents.contains(iCalEventNew)) {
0977: if (iCalEventNew.isRecurrenceId()) {
0978: // Replace default recurring event with a custom single occurrence
0979: sortedExpandedEvents.remove(iCalEventNew);
0980: }
0981: }
0982: sortedExpandedEvents.add(iCalEventNew);
0983:
0984: logger.fine("Create This date: " + dateStartIn);
0985: logger.fine("Create UID: " + iCalEventNew.getUid());
0986: logger.fine("RepeatSummary: " + iCalEventNew.getSummary());
0987: } catch (Exception ex) {
0988: logger.severe(ex.toString());
0989: ex.printStackTrace();
0990: }
0991:
0992: }
0993:
0994: public String getFBString(String dateRangeOrDaysForward)
0995: throws Exception {
0996: Date dateFrom = null;
0997: Date dateTo = null;
0998: try {
0999: long daysForward = getDaysForwardNumeric(dateRangeOrDaysForward);
1000: try {
1001: return getFBString(daysForward);
1002: } catch (Exception e) {
1003: logger.severe("Cannot create FBURL due to error: " + e);
1004: return "";
1005: }
1006: } catch (Exception e) {
1007: try {
1008: dateFrom = getDateFrom(dateRangeOrDaysForward);
1009: dateTo = getDateTo(dateRangeOrDaysForward);
1010:
1011: } catch (Exception ee) {
1012: logger
1013: .severe("1.Unable to read your input dates: "
1014: + dateRangeOrDaysForward
1015: + "They must be of the form ccYYmmDDhhMMss-ccYYmmDDhhMMss"
1016: + ee);
1017: throw ee;
1018: }
1019: }
1020: return getFBString(dateFrom, dateTo);
1021: }
1022:
1023: public String getFBString(long daysForward) {
1024: Date dateFrom = new Date();
1025: Date dateTo = getDateToFromDaysForward(daysForward);
1026: /*
1027: * Currently, keep this dateFrom/To as coarse. ie, dateFrom/To are DAYS not DAY/HH:MM
1028: * ie for FROM date, set to time of 0000, for TO date set to time of 235959
1029: */
1030: try {
1031: dateFrom = (Date) dateOnlyFormat.parse(dateOnlyFormat
1032: .format(dateFrom));
1033: dateTo = (Date) dateFormatter.parse(dateOnlyFormat
1034: .format(dateTo)
1035: + "235959");
1036: } catch (Exception e) {
1037: logger.severe(e.toString());
1038: }
1039: return getFBString(dateFrom, dateTo);
1040: }
1041:
1042: public String getFBString(Date dateFrom, Date dateTo) {
1043: /* Build a FBURL String for submitting to somewhere.
1044: * Remember to take into account all repeating events.
1045: */
1046:
1047: /* Read through all Vevents.
1048: The following is an example of a "VFREEBUSY" calendar component used
1049: to publish busy time information. *
1050: FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:19970308T160000Z/PT8H30M
1051: FREEBUSY;FBTYPE=FREE:19970308T160000Z/PT3H,19970308T200000Z/PT1H
1052: FREEBUSY;FBTYPE=FREE:19970308T160000Z/PT3H,19970308T200000Z/PT1H,
1053: 19970308T230000Z/19970309T000000Z
1054:
1055: BEGIN:VFREEBUSY
1056: ORGANIZER:jsmith@host.com
1057: DTSTART:19980313T141711Z
1058: DTEND:19980410T141711Z
1059: FREEBUSY:19980314T233000Z/19980315T003000Z
1060: FREEBUSY:19980316T153000Z/19980316T163000Z
1061: FREEBUSY:19980318T030000Z/19980318T040000Z
1062: URL:http://www.host.com/calendar/busytime/jsmith.ifb
1063: END:VFREEBUSY
1064: */
1065: if (this .getFBUrl() == null || this .getOrganizerEmail() == null
1066: || this .getOrganizer() == null) {
1067: logger
1068: .severe("Cannot create FBURL unless FBURL, OrganizerEmail are Organizer provided to ICalendar");
1069: return "";
1070: }
1071:
1072: StringBuffer FBString = new StringBuffer(
1073: "BEGIN:VCALENDAR\nBEGIN:VFREEBUSY\n");
1074: FBString.append("ORGANIZER:").append(this .getOrganizerEmail())
1075: .append("\n");
1076: FBString.append("DTSTART:").append(formatter.format(dateFrom))
1077: .append("\n");
1078: FBString.append("DTEND:").append(formatter.format(dateTo))
1079: .append("\n");
1080:
1081: // Must always work out expanded events from year dot as otherwise previous
1082: // dates where they repeat into this date range are missed.
1083:
1084: Date trueStartDate = new Date(0);
1085:
1086: // 2004-10 patch - um. Don't think this is right.. Ignored.
1087: //Fix bug where expanded events were only generated from the
1088: //current time rather than from the start date jical was started with
1089: // getIcalExpandedEvents(null);
1090:
1091: getIcalExpandedEvents(trueStartDate, dateTo, null);
1092:
1093: Iterator eeIterator = sortedExpandedEvents.iterator();
1094: while (eeIterator.hasNext()) {
1095: ICalendarVEvent icalEvent = (ICalendarVEvent) eeIterator
1096: .next();
1097:
1098: /* There are four conditions for inclusion of this event
1099: in this day.
1100: 1) Runs right across this date and others.
1101: * ie day 23/2/2004. Event goes 22/2/2004 to 25/2/2004
1102: 2) Falls within the day
1103: ** ie day 23/2/2004. Event goes 23/2/2004 to 23/2/2004
1104: 3) End period falls within the start day point or
1105: * ie day 23/2/2004. Event goes 22/2/2004 to 23/2/2004 5am
1106: 4) Start period falls within the end day point.
1107: * ie day 23/2/2004. Event goes 23/2/2004 12 noon to 25/2/2004
1108: */
1109: Date icalDateStart = icalEvent.getDateStart();
1110: Date icalDateEnd = icalEvent.getDateEnd();
1111:
1112: /*
1113: * Whole day events are not handled well with this method as they run
1114: * exactly on the 24hr time line. We need to recognise them and
1115: * for the purposes of testing conditions, reduce the seconds so
1116: * that they fit within a day.
1117: */
1118: Date this TimeFrom = dateFrom;
1119: Date this TimeTo = dateTo;
1120:
1121: // Condition 1.
1122: if ((this TimeFrom.after(icalDateStart) || this TimeFrom
1123: .equals(icalDateStart))
1124: && (this TimeTo.before(icalDateEnd) || this TimeTo
1125: .equals(icalDateEnd))) {
1126: // Create an all day event as this event wraps this day and others.
1127: icalEvent.setDateStart(this TimeFrom);
1128: icalEvent.setDateEnd(this TimeTo);
1129: FBString.append(createFBRow(formatter, icalEvent
1130: .getDateStart(), icalEvent.getDateEnd()));
1131: }
1132: // Condition 2.
1133: else if (this TimeFrom.before(icalDateStart)
1134: && this TimeTo.after(icalDateEnd)) {
1135: // Create event as is.
1136: FBString.append(createFBRow(formatter, icalEvent
1137: .getDateStart(), icalEvent.getDateEnd()));
1138: }
1139: // Condition 3.
1140: else if (this TimeFrom.before(icalDateEnd)
1141: && this TimeTo.after(icalDateEnd)) {
1142: // Create event with end time as thisTimeTo, start time as speced.
1143: icalEvent.setDateStart(this TimeFrom);
1144: icalEvent.setDateEnd(icalDateEnd);
1145: FBString.append(createFBRow(formatter, icalEvent
1146: .getDateStart(), icalEvent.getDateEnd()));
1147: }
1148: // Condition 4.
1149: else if (this TimeFrom.before(icalDateStart)
1150: && this TimeTo.after(icalDateStart)) {
1151: // Create event with starttime time as thisTimeFrom, end time as speced.
1152: icalEvent.setDateStart(icalDateStart);
1153: icalEvent.setDateEnd(this TimeTo);
1154: FBString.append(createFBRow(formatter, icalEvent
1155: .getDateStart(), icalEvent.getDateEnd()));
1156: } else {
1157: //Event rejected for this date
1158: }
1159: }
1160:
1161: //URL:http://www.host.com/calendar/busytime/jsmith.ifb
1162: FBString.append("URL:");
1163: FBString.append(this .getFBUrl()); // Expect this to start with http:// and end in /
1164: FBString.append(this .getOrganizer());
1165: FBString.append(".ifb");
1166: FBString.append("\n");
1167: FBString.append("END:VFREEBUSY\nEND:VCALENDAR");
1168: FBString.append("\n");
1169: return FBString.toString();
1170: }
1171:
1172: public String createFBRow(SimpleDateFormat formatter,
1173: Date repeatDateStart, Date repeatDateEnd) {
1174: StringBuffer FBData = new StringBuffer();
1175: FBData.append("FREEBUSY;FBTYPE=BUSY:");
1176: FBData.append(formatter.format(repeatDateStart));
1177: FBData.append("/");
1178: FBData.append(formatter.format(repeatDateEnd));
1179: FBData.append("\n");
1180: return FBData.toString();
1181: }
1182:
1183: /*
1184: * This returns the whole calendar as a string.
1185: * Of course, if you haven't filled in all the required bits,
1186: * it will be unreadable.
1187: */
1188: public String getVCalendar() {
1189: StringBuffer vCalBuffer = new StringBuffer();
1190: vCalBuffer.append(ICalUtil.makeVEventLines("BEGIN:VCALENDAR",
1191: ""));
1192: vCalBuffer.append(ICalUtil.makeVEventLines("PRODID:", this
1193: .getProdId()));
1194: vCalBuffer.append(ICalUtil.makeVEventLines("VERSION:", this
1195: .getVersion()));
1196: vCalBuffer.append("\n");
1197:
1198: // Now loop through the current collection of VEVENTs and append them to the ultimate buffer.
1199: for (Iterator i = icalEventCollection.iterator(); i.hasNext();) {
1200: ICalendarVEvent icalEvent = (ICalendarVEvent) i.next();
1201: vCalBuffer.append(icalEvent.toVEvent()).append("\n");
1202: }
1203:
1204: vCalBuffer
1205: .append(ICalUtil.makeVEventLines("END:VCALENDAR", ""));
1206:
1207: return vCalBuffer.toString();
1208: }
1209:
1210: public String getJiCalXML(String dateRangeOrDaysForward,
1211: String timeRange) throws Exception {
1212: Date dateFrom = null;
1213: Date dateTo = null;
1214: try {
1215: long daysForward = getDaysForwardNumeric(dateRangeOrDaysForward);
1216: try {
1217: return getJiCalXML(daysForward, timeRange);
1218: } catch (Exception e) {
1219: logger.severe("Error:" + e);
1220: }
1221: } catch (Exception e) {
1222: try {
1223: dateFrom = getDateFrom(dateRangeOrDaysForward);
1224: dateTo = getDateTo(dateRangeOrDaysForward);
1225:
1226: } catch (Exception ee) {
1227: logger
1228: .severe("Unable to read your input dates: "
1229: + dateRangeOrDaysForward
1230: + "They must be of the form ccYYmmDDhhMMss-ccYYmmDDhhMMss"
1231: + ee);
1232: throw ee;
1233: }
1234: }
1235: return getJiCalXML(dateFrom, dateTo, timeRange);
1236: }
1237:
1238: public String getJiCalXML(long daysForward, String timeRange) {
1239: Date dateFrom = new Date();
1240: Date dateTo = getDateToFromDaysForward(daysForward);
1241:
1242: /*
1243: * Currently, keep this dateFrom/To as coarse. ie, dateFrom/To are DAYS not DAY/HH:MM
1244: * ie for FROM date, set to time of 0000, for TO date set to time of 235959
1245: */
1246: try {
1247: dateFrom = (Date) dateOnlyFormat.parse(dateOnlyFormat
1248: .format(dateFrom));
1249: dateTo = (Date) dateFormatter.parse(dateOnlyFormat
1250: .format(dateTo)
1251: + "235959");
1252: } catch (Exception e) {
1253: }
1254: return getJiCalXML(dateFrom, dateTo, timeRange);
1255: }
1256:
1257: public String getJiCalXML(Date dateFrom, Date dateTo,
1258: String timeRange) {
1259: if (this .getOrganizerEmail() == null
1260: || this .getOrganizer() == null) {
1261: logger
1262: .severe("Cannot create XML unless OrganizerEmail are Organizer provided to ICalendar");
1263: return "";
1264: }
1265:
1266: /*
1267: * XML Initial take
1268: * This is NOT a version of XCAL as it unravels the events to their
1269: * detail level. It is more useful for rendering as HTML or PDF an
1270: * Evolution Calendar.
1271: *
1272: */
1273:
1274: StringBuffer XMLString = new StringBuffer("<jicalxml>\n");
1275: XMLString.append("\t<organizer>").append(this .getOrganizer())
1276: .append("</organizer>\n");
1277: XMLString.append("\t<organizeremail>").append(
1278: this .getOrganizerEmail()).append("</organizeremail>\n");
1279: XMLString.append("\t<datestart>").append(
1280: dateFormatter.format(dateFrom))
1281: .append("</datestart>\n");
1282: XMLString.append("\t<dateend>").append(
1283: dateFormatter.format(dateTo)).append("</dateend>\n");
1284: XMLString.append("<vevents>\n");
1285: /*
1286: * Modified to make sure repeat events pick up EVERYTHING!
1287: */
1288: Date trueStartDate = new Date(0);
1289: getIcalExpandedEvents(trueStartDate, dateTo, timeRange);
1290:
1291: Iterator eeIterator = sortedExpandedEvents.iterator();
1292: while (eeIterator.hasNext()) {
1293: ICalendarVEvent icalEvent = (ICalendarVEvent) eeIterator
1294: .next();
1295: /* There are four conditions for inclusion of this event
1296: in this day.
1297: 1) Runs right across this date and others.
1298: * ie day 23/2/2004. Event goes 22/2/2004 to 25/2/2004
1299: 2) Falls within the day
1300: ** ie day 23/2/2004. Event goes 23/2/2004 to 23/2/2004
1301: 3) End period falls within the start day point or
1302: * ie day 23/2/2004. Event goes 22/2/2004 to 23/2/2004 5am
1303: 4) Start period falls within the end day point.
1304: * ie day 23/2/2004. Event goes 23/2/2004 12 noon to 25/2/2004
1305: */
1306: Date icalDateStart = icalEvent.getDateStart();
1307: Date icalDateEnd = icalEvent.getDateEnd();
1308:
1309: /*
1310: * Whole day events are not handled well with this method as they run
1311: * exactly on the 24hr time line. We need to recognise them and
1312: * for the purposes of testing conditions, reduce the seconds so
1313: * that they fit within a day.
1314: */
1315: Date this TimeFrom = dateFrom;
1316: Date this TimeTo = dateTo;
1317:
1318: // Condition 1.
1319: if ((this TimeFrom.after(icalDateStart) || this TimeFrom
1320: .equals(icalDateStart))
1321: && (this TimeTo.before(icalDateEnd) || this TimeTo
1322: .equals(icalDateEnd))) {
1323: // Create an all day event as this event wraps this day and others.
1324: icalEvent.setDateStart(this TimeFrom);
1325: icalEvent.setDateEnd(this TimeTo);
1326: XMLString.append("\t\t");
1327: XMLString.append(icalEvent.toXML());
1328: XMLString.append("\n");
1329: }
1330: // Condition 2.
1331: else if (this TimeFrom.before(icalDateStart)
1332: && this TimeTo.after(icalDateEnd)) {
1333: // Create event as is.
1334: // XMLString.append("Cond2");
1335: XMLString.append("\t\t");
1336: XMLString.append(icalEvent.toXML());
1337: XMLString.append("\n");
1338: }
1339: // Condition 3.
1340: else if (this TimeFrom.before(icalDateEnd)
1341: && this TimeTo.after(icalDateEnd)) {
1342: // Create event with end time as thisTimeTo, start time as speced.
1343: // XMLString.append("Cond3" + thisTimeFrom + thisTimeTo);
1344: icalEvent.setDateStart(this TimeFrom);
1345: icalEvent.setDateEnd(icalDateEnd);
1346: XMLString.append("\t\t");
1347: XMLString.append(icalEvent.toXML());
1348: XMLString.append("\n");
1349: }
1350: // Condition 4.
1351: else if (this TimeFrom.before(icalDateStart)
1352: && this TimeTo.after(icalDateStart)) {
1353: // Create event with starttime time as thisTimeFrom, end time as speced.
1354: // XMLString.append("Cond4");
1355: icalEvent.setDateStart(icalDateStart);
1356: icalEvent.setDateEnd(this TimeTo);
1357: XMLString.append("\t\t");
1358: XMLString.append(icalEvent.toXML());
1359: XMLString.append("\n");
1360: } else {
1361: //Event rejected for this date
1362: }
1363: }
1364:
1365: XMLString.append("</vevents></jicalxml>\n");
1366: return XMLString.toString();
1367: }
1368:
1369: public String getJiCaldisplayXML(String dateRangeOrDaysForward,
1370: String timeRange) throws Exception {
1371: DateTimeRange dtr = new DateTimeRange();
1372: dtr.calcDateTimeRange(dateRangeOrDaysForward, timeRange);
1373:
1374: // Date dateFrom = null;
1375: // Date dateTo = null;
1376: // try
1377: // {
1378: // long daysForward = getDaysForwardNumeric(dateRangeOrDaysForward);
1379: // try
1380: // {
1381: // return getJiCaldisplayXML(daysForward, timeRange);
1382: // }
1383: // catch (Exception e)
1384: // {
1385: // logger.severe("Error:" + e);
1386: // }
1387: // }
1388: // catch (Exception e)
1389: // {
1390: // try
1391: // {
1392: // dateFrom = getDateFrom(dateRangeOrDaysForward);
1393: // dateTo = getDateTo(dateRangeOrDaysForward);
1394: //
1395: // }
1396: // catch (Exception ee)
1397: // {
1398: // logger.severe("Unable to read your input dates: "
1399: // + dateRangeOrDaysForward
1400: // + "They must be of the form ccYYmmDDhhMMss-ccYYmmDDhhMMss"
1401: // + ee);
1402: // throw ee;
1403: // }
1404: // }
1405: return getJiCaldisplayXML(dtr.dateFrom, dtr.dateTo, timeRange);
1406: }
1407:
1408: // public String getJiCaldisplayXML(long daysForward, String timeRange)
1409: // {
1410: // Date dateFrom = new Date();
1411: // Date dateTo = getDateToFromDaysForward(daysForward);
1412: // /*
1413: // * Currently, keep this dateFrom/To as coarse. ie, dateFrom/To are DAYS not DAY/HH:MM
1414: // * ie for FROM date, set to time of 0000, for TO date set to time of 235959
1415: // */
1416: // try
1417: // {
1418: // dateFrom = (Date)dateOnlyFormat.parse(dateOnlyFormat.format(dateFrom));
1419: // dateTo = (Date)dateFormatter.parse(dateOnlyFormat.format(dateTo) + "235959");
1420: // }
1421: // catch (Exception e)
1422: // {
1423: // logger.severe("Error setting dates to process full day range." + e);
1424: // }
1425: // return getJiCaldisplayXML(dateFrom, dateTo, timeRange);
1426: // }
1427:
1428: public String getJiCaldisplayXML(Date dateFrom, Date dateTo,
1429: String timeRange) {
1430: if (this .getOrganizerEmail() == null
1431: || this .getOrganizer() == null) {
1432: logger
1433: .severe("Cannot create XML unless OrganizerEmail are Organizer provided to ICalendar");
1434: return "";
1435: }
1436:
1437: /*
1438: * This is more useful for rendering as HTML or PDF an
1439: * Evolution Calendar.
1440: *
1441: */
1442:
1443: StringBuffer XMLString = new StringBuffer("<jicaldisplay>\n");
1444: XMLString.append("\t<organizer>").append(this .getOrganizer())
1445: .append("</organizer>\n");
1446: XMLString.append("\t<organizeremail>").append(
1447: this .getOrganizerEmail()).append("</organizeremail>\n");
1448: XMLString.append("\t<datestart>").append(
1449: dateFormatter.format(dateFrom))
1450: .append("</datestart>\n");
1451: XMLString.append("\t<dateend>").append(
1452: dateFormatter.format(dateTo)).append("</dateend>\n");
1453:
1454: // Hmmm... If we truely want to represent this date range, we must get all dates from .
1455: // to the To range. Reason being that some might start before this date and repeat in
1456: // this range....
1457:
1458: // Make starting date really old!!
1459:
1460: Date trueStartDate = new Date(0);
1461: getIcalExpandedEvents(trueStartDate, dateTo, timeRange);
1462:
1463: /*
1464: * This is the tricky bit, iterate from the datefrom date, through the
1465: * days to the dateto date. All days in between get some XML.
1466: */
1467:
1468: //int dateRepeatUnit = Calendar.HOUR_OF_DAY;
1469: Calendar repeatXMLDateStart = new GregorianCalendar();
1470: repeatXMLDateStart.setTime(dateFrom);
1471:
1472: XMLString.append("<days>\n");
1473:
1474: while (repeatXMLDateStart != null
1475: && !repeatXMLDateStart.getTime().after(dateTo)) {
1476: XMLString.append("\t\t<day>\n");
1477: XMLString.append("\t\t\t<dayofweek>").append(
1478: dayOfWeek.format(repeatXMLDateStart.getTime()))
1479: .append("</dayofweek>\n");
1480: XMLString.append("\t\t\t<monthofyear>").append(
1481: monthOfYear.format(repeatXMLDateStart.getTime()))
1482: .append("</monthofyear>\n");
1483: XMLString.append("\t\t\t<weeknumber>").append(
1484: weekNumber.format(repeatXMLDateStart.getTime()))
1485: .append("</weeknumber>\n");
1486: XMLString.append("\t\t\t<date>")
1487: .append(
1488: dateOnlyFormat.format(repeatXMLDateStart
1489: .getTime())).append("</date>\n");
1490: // Add two new XML elements which can be used when styling with XSL
1491: XMLString.append("\t\t\t<dayofweeknum>").append(
1492: repeatDateStart.get(Calendar.DAY_OF_WEEK)).append(
1493: "</dayofweeknum>\n");
1494: XMLString.append("\t\t\t<dayofmonth>").append(
1495: dayNumber.format(repeatDateStart.getTime()))
1496: .append("</dayofmonth>\n");
1497: XMLString.append("\t\t\t<year>").append(
1498: year.format(repeatDateStart.getTime())).append(
1499: "</year>\n");
1500: XMLString.append("\t\t\t<vevents>\n");
1501: // Now find all events that match this date.
1502: Calendar this DateFrom = new GregorianCalendar();
1503: this DateFrom.setTime(repeatXMLDateStart.getTime());
1504: this DateFrom.set(Calendar.HOUR_OF_DAY, 0);
1505: this DateFrom.set(Calendar.MINUTE, 0);
1506: this DateFrom.set(Calendar.SECOND, 0);
1507:
1508: Date this TimeFrom = this DateFrom.getTime();
1509: // Altered as was excluding all day events which END at 00:00 on the next day..
1510: Calendar this DateTo = new GregorianCalendar();
1511: this DateTo.setTime(repeatXMLDateStart.getTime());
1512: this DateTo.set(Calendar.HOUR_OF_DAY, 24);
1513: this DateTo.set(java.util.Calendar.MINUTE, 0);
1514: this DateTo.set(java.util.Calendar.SECOND, 0);
1515: Date this TimeTo = this DateTo.getTime();
1516:
1517: Iterator eeIterator = sortedExpandedEvents.iterator();
1518: while (eeIterator.hasNext()) {
1519: ICalendarVEvent icalEvent = (ICalendarVEvent) eeIterator
1520: .next();
1521: /* There are four conditions for inclusion of this event
1522: in this day.
1523: 1) Runs right across this date and others.
1524: * ie day 23/2/2004. Event goes 22/2/2004 to 25/2/2004
1525: 2) Falls within the day
1526: ** ie day 23/2/2004. Event goes 23/2/2004 to 23/2/2004
1527: 3) End period falls within the start day point or
1528: * ie day 23/2/2004. Event goes 22/2/2004 to 23/2/2004 5am
1529: 4) Start period falls within the end day point.
1530: * ie day 23/2/2004. Event goes 23/2/2004 12 noon to 25/2/2004
1531: */
1532: Date icalDateStart = icalEvent.getDateStart();
1533: Date icalDateEnd = icalEvent.getDateEnd();
1534:
1535: /*
1536: * Whole day events are not handled well with this method as they run
1537: * exactly on the 24hr time line. We need to recognise them and
1538: * for the purposes of testing conditions, reduce the seconds so
1539: * that they fit within a day.
1540: */
1541: this DateFrom.setTime(icalDateStart);
1542: this DateTo.setTime(icalDateEnd);
1543:
1544: // Condition 1.
1545: if ((this TimeFrom.after(icalDateStart) || this TimeFrom
1546: .equals(icalDateStart))
1547: && (this TimeTo.before(icalDateEnd) || this TimeTo
1548: .equals(icalDateEnd))) {
1549: // Create an all day event as this event wraps this day and others.
1550: icalEvent.setDateStart(this TimeFrom);
1551: icalEvent.setDateEnd(this TimeTo);
1552: XMLString.append("\t\t");
1553: XMLString.append(icalEvent.toXML());
1554: XMLString.append("\n");
1555: }
1556: // Condition 2.
1557: else if (this TimeFrom.before(icalDateStart)
1558: && this TimeTo.after(icalDateEnd)) {
1559: // Create event as is.
1560: // XMLString.append("Cond2");
1561: XMLString.append("\t\t");
1562: XMLString.append(icalEvent.toXML());
1563: XMLString.append("\n");
1564: }
1565: // Condition 3.
1566: else if (this TimeFrom.before(icalDateEnd)
1567: && this TimeTo.after(icalDateEnd)) {
1568: // Create event with end time as thisTimeTo, start time as speced.
1569: // XMLString.append("Cond3" + thisTimeFrom + thisTimeTo);
1570: icalEvent.setDateStart(this TimeFrom);
1571: icalEvent.setDateEnd(icalDateEnd);
1572: XMLString.append("\t\t");
1573: XMLString.append(icalEvent.toXML());
1574: XMLString.append("\n");
1575: }
1576: // Condition 4.
1577: else if (this TimeFrom.before(icalDateStart)
1578: && this TimeTo.after(icalDateStart)) {
1579: // Create event with starttime time as thisTimeFrom, end time as speced.
1580: // XMLString.append("Cond4");
1581: icalEvent.setDateStart(icalDateStart);
1582: icalEvent.setDateEnd(this TimeTo);
1583: XMLString.append("\t\t");
1584: XMLString.append(icalEvent.toXML());
1585: XMLString.append("\n");
1586: } else {
1587: //Event rejected for this date
1588: }
1589: }
1590:
1591: XMLString.append("\t\t\t</vevents>");
1592: XMLString.append("\t\t</day>\n");
1593:
1594: // On to the next day..
1595: repeatXMLDateStart.add(Calendar.HOUR_OF_DAY, 24);
1596:
1597: }
1598:
1599: XMLString.append("\t</days>\n");
1600: XMLString.append("</jicaldisplay>\n");
1601: return XMLString.toString();
1602: }
1603:
1604: /**
1605: * @return Returns the repeatDateEnd.
1606: */
1607: public Calendar getRepeatDateEnd() {
1608: return repeatDateEnd;
1609: }
1610:
1611: /**
1612: * @param repeatDateEnd The repeatDateEnd to set.
1613: */
1614: public void setRepeatDateEnd(Calendar repeatDateEndIn) {
1615: this .repeatDateEnd = repeatDateEndIn;
1616: }
1617:
1618: /**
1619: * @return Returns the repeatDateStart.
1620: */
1621: public Calendar getRepeatDateStart() {
1622: return repeatDateStart;
1623: }
1624:
1625: /**
1626: * @param repeatDateStart The repeatDateStart to set.
1627: */
1628: public void setRepeatDateStart(Calendar repeatDateStartIn) {
1629: this.repeatDateStart = repeatDateStartIn;
1630: }
1631: }
|