Search in sources :

Example 1 with Recurrence

use of com.zimbra.cs.mailbox.calendar.Recurrence in project zm-mailbox by Zimbra.

the class CalendarUtils method parseInviteElementCommon.

/**
     * UID, DTSTAMP, and SEQUENCE **MUST** be set by caller
     *
     * @param account
     *            user receiving invite
     * @param element
     *            invite XML element
     * @param newInv
     *            Invite we are currently building up
     * @param oldTzMap
     *            time zone map from A DIFFERENT invite; if this method is
     *            called during modify operation, this map contains time zones
     *            before the modification; null if called during create
     *            operation
     * @return
     * @throws ServiceException
     */
private static void parseInviteElementCommon(Account account, MailItem.Type type, Element element, Invite newInv, boolean recurrenceIdAllowed, boolean recurAllowed) throws ServiceException {
    //zdsync
    String invId = element.getAttribute(MailConstants.A_ID, null);
    Element compElem = element.getOptionalElement(MailConstants.E_INVITE_COMPONENT);
    if (compElem != null) {
        element = compElem;
    }
    //zdsync
    String dts = element.getAttribute(MailConstants.A_CAL_DATETIME, null);
    TimeZoneMap tzMap = newInv.getTimeZoneMap();
    parseTimeZones(element.getParent(), tzMap);
    newInv.setItemType(type);
    // UID
    String uid = element.getAttribute(MailConstants.A_UID, null);
    if (uid != null && uid.length() > 0)
        newInv.setUid(uid);
    // RECURRENCE-ID
    if (recurrenceIdAllowed) {
        Element e = element.getOptionalElement(MailConstants.E_CAL_EXCEPTION_ID);
        if (e != null) {
            ParsedDateTime dt = parseDateTime(e, tzMap);
            RecurId recurId = new RecurId(dt, RecurId.RANGE_NONE);
            newInv.setRecurId(recurId);
        }
    } else {
        if (element.getOptionalElement(MailConstants.E_CAL_EXCEPTION_ID) != null) {
            throw ServiceException.INVALID_REQUEST("May not specify an <exceptId> in this request", null);
        }
    }
    String name = element.getAttribute(MailConstants.A_NAME, "");
    String location = element.getAttribute(MailConstants.A_CAL_LOCATION, "");
    // CATEGORIES
    for (Iterator<Element> catIter = element.elementIterator(MailConstants.E_CAL_CATEGORY); catIter.hasNext(); ) {
        String cat = catIter.next().getText();
        newInv.addCategory(cat);
    }
    // COMMENTs
    for (Iterator<Element> cmtIter = element.elementIterator(MailConstants.E_CAL_COMMENT); cmtIter.hasNext(); ) {
        String cmt = cmtIter.next().getText();
        newInv.addComment(cmt);
    }
    // CONTACTs
    for (Iterator<Element> cnIter = element.elementIterator(MailConstants.E_CAL_CONTACT); cnIter.hasNext(); ) {
        String contact = cnIter.next().getTextTrim();
        newInv.addContact(contact);
    }
    // GEO
    Element geoElem = element.getOptionalElement(MailConstants.E_CAL_GEO);
    if (geoElem != null) {
        Geo geo = Geo.parse(geoElem);
        newInv.setGeo(geo);
    }
    // URL
    String url = element.getAttribute(MailConstants.A_CAL_URL, null);
    newInv.setUrl(url);
    // SEQUENCE
    int seq = (int) element.getAttributeLong(MailConstants.A_CAL_SEQUENCE, 0);
    newInv.setSeqNo(seq);
    // SUMMARY (aka Name or Subject)
    newInv.setName(name);
    // DESCRIPTION
    Element descElem = element.getOptionalElement(MailConstants.E_CAL_DESCRIPTION);
    String desc = descElem != null ? descElem.getText() : null;
    Element descHtmlElem = element.getOptionalElement(MailConstants.E_CAL_DESC_HTML);
    String descHtml = descHtmlElem != null ? descHtmlElem.getText() : null;
    newInv.setDescription(desc, descHtml);
    boolean allDay = element.getAttributeBool(MailConstants.A_CAL_ALLDAY, false);
    newInv.setIsAllDayEvent(allDay);
    // DTSTART
    Element startElem;
    if (newInv.isTodo())
        startElem = element.getOptionalElement(MailConstants.E_CAL_START_TIME);
    else
        startElem = element.getElement(MailConstants.E_CAL_START_TIME);
    if (startElem != null) {
        ParsedDateTime dt = parseDtElement(startElem, tzMap, newInv);
        // fixup for bug 30121
        if (allDay && dt.hasTime()) {
            // If this is supposed to be an all-day event but DTSTART has time part, clear the time part.
            dt.setHasTime(false);
        } else if (!allDay && !dt.hasTime()) {
            // If the event isn't marked as all-day but DTSTART is date-only, the client simply forgot
            // to mark it all-day.  Do all-day implicitly.
            allDay = true;
            newInv.setIsAllDayEvent(allDay);
        }
        newInv.setDtStart(dt);
    }
    // DTEND (for VEVENT) or DUE (for VTODO)
    Element endElem = element.getOptionalElement(MailConstants.E_CAL_END_TIME);
    if (endElem != null) {
        ParsedDateTime dt = parseDtElement(endElem, tzMap, newInv);
        // fixup for bug 30121
        if (allDay && dt.hasTime()) {
            // If this is supposed to be an all-day event but DTEND has time part, clear the time part.
            dt.setHasTime(false);
        } else if (!allDay && !dt.hasTime()) {
            // If the event isn't marked as all-day but DTEND is date-only, the client simply forgot
            // to mark it all-day.  Do all-day implicitly.
            allDay = true;
            newInv.setIsAllDayEvent(allDay);
        }
        if (allDay && !newInv.isTodo()) {
            // HACK ALERT: okay, campers, here's the deal.
            // By definition, our end dates are EXCLUSIVE: DTEND is not
            // included.. eg a meeting 7-8pm actually stops at 7:59
            //
            // This makes sense for normal appointments, but apparently
            // this rule is confusing to people when making
            // all-day-events
            //
            // For all-day-events, people want to say that a 1-day-long
            // appointment starts on 11/1 and ends on 11/1, for example
            // this is inconsistent (and incompatible with RFC2445) but
            // it is what people want. Sooo, we to a bit of a hacky
            // translation when sending/receiving all-day-events.
            //
            dt = dt.add(ParsedDuration.ONE_DAY);
        }
        newInv.setDtEnd(dt);
    } else {
        // DURATION
        Element d = element.getOptionalElement(MailConstants.E_CAL_DURATION);
        if (d != null) {
            ParsedDuration pd = ParsedDuration.parse(d);
            newInv.setDuration(pd);
        }
    }
    // LOCATION
    newInv.setLocation(location);
    // STATUS
    String status = element.getAttribute(MailConstants.A_CAL_STATUS, newInv.isEvent() ? IcalXmlStrMap.STATUS_CONFIRMED : IcalXmlStrMap.STATUS_NEEDS_ACTION);
    validateAttr(IcalXmlStrMap.sStatusMap, MailConstants.A_CAL_STATUS, status);
    newInv.setStatus(status);
    // CLASS
    String classProp = element.getAttribute(MailConstants.A_CAL_CLASS, IcalXmlStrMap.CLASS_PUBLIC);
    validateAttr(IcalXmlStrMap.sClassMap, MailConstants.A_CAL_CLASS, classProp);
    newInv.setClassProp(classProp);
    // PRIORITY
    String priority = element.getAttribute(MailConstants.A_CAL_PRIORITY, null);
    newInv.setPriority(priority);
    if (newInv.isEvent()) {
        // FreeBusy
        String fb = element.getAttribute(MailConstants.A_APPT_FREEBUSY, null);
        if (fb != null) {
            newInv.setFreeBusy(fb);
            // Intended F/B takes precedence over TRANSP.
            if (IcalXmlStrMap.FBTYPE_FREE.equals(fb))
                newInv.setTransparency(IcalXmlStrMap.TRANSP_TRANSPARENT);
            else
                newInv.setTransparency(IcalXmlStrMap.TRANSP_OPAQUE);
        } else {
            // TRANSP is examined only when intended F/B is not supplied.
            String transp = element.getAttribute(MailConstants.A_APPT_TRANSPARENCY, IcalXmlStrMap.TRANSP_OPAQUE);
            validateAttr(IcalXmlStrMap.sTranspMap, MailConstants.A_APPT_TRANSPARENCY, transp);
            newInv.setTransparency(transp);
            // If opaque, don't set intended f/b because there are multiple possibilities.
            if (newInv.isTransparent())
                newInv.setFreeBusy(IcalXmlStrMap.FBTYPE_FREE);
        }
    }
    if (newInv.isTodo()) {
        // PERCENT-COMPLETE
        String pctComplete = element.getAttribute(MailConstants.A_TASK_PERCENT_COMPLETE, null);
        newInv.setPercentComplete(pctComplete);
        // COMPLETED
        String completed = element.getAttribute(MailConstants.A_TASK_COMPLETED, null);
        if (completed != null) {
            try {
                ParsedDateTime c = ParsedDateTime.parseUtcOnly(completed);
                newInv.setCompleted(c.getUtcTime());
            } catch (ParseException e) {
                throw ServiceException.INVALID_REQUEST("Invalid COMPLETED value: " + completed, e);
            }
        } else if (status.equals(IcalXmlStrMap.STATUS_COMPLETED)) {
            newInv.setCompleted(System.currentTimeMillis());
        } else {
            newInv.setCompleted(0);
        }
    }
    // ATTENDEEs
    boolean hasAttendees = false;
    for (Iterator<Element> iter = element.elementIterator(MailConstants.E_CAL_ATTENDEE); iter.hasNext(); ) {
        ZAttendee at = ZAttendee.parse(iter.next());
        newInv.addAttendee(at);
        hasAttendees = true;
    }
    if (hasAttendees && newInv.getMethod().equals(ICalTok.PUBLISH.toString())) {
        newInv.setMethod(ICalTok.REQUEST.toString());
    }
    // ORGANIZER
    Element orgElt = element.getOptionalElement(MailConstants.E_CAL_ORGANIZER);
    if (orgElt != null) {
        ZOrganizer org = ZOrganizer.parse(orgElt);
        newInv.setOrganizer(org);
    }
    // Once we have organizer and attendee information, we can tell if this account is the
    // organizer in this invite or not.
    newInv.setIsOrganizer(account);
    if (!newInv.isCancel()) {
        // draft flag
        // True means invite has changes that haven't been sent to attendees.
        boolean draft = element.getAttributeBool(MailConstants.A_CAL_DRAFT, false);
        newInv.setDraft(draft);
        // neverSent flag
        // True means attendees have never been notified for this invite.
        boolean neverSent = element.getAttributeBool(MailConstants.A_CAL_NEVER_SENT, false);
        newInv.setNeverSent(neverSent);
    }
    // RECUR
    Element recur = element.getOptionalElement(MailConstants.A_CAL_RECUR);
    if (recur != null) {
        if (!recurAllowed) {
            throw ServiceException.INVALID_REQUEST("No <recur> allowed in an exception", null);
        }
        // Ensure DTSTART is set if doing recurrence.
        ParsedDateTime st = newInv.getStartTime();
        if (st == null) {
            ParsedDateTime et = newInv.getEndTime();
            if (et != null) {
                if (et.hasTime())
                    st = et.add(ParsedDuration.NEGATIVE_ONE_SECOND);
                else
                    st = et.add(ParsedDuration.NEGATIVE_ONE_DAY);
                newInv.setDtStart(st);
            } else {
                // Both DTSTART and DTEND are unspecified.  Recurrence makes no sense!
                throw ServiceException.INVALID_REQUEST("recurrence used without DTSTART", null);
            }
        }
        Recurrence.IRecurrence recurrence = parseRecur(recur, tzMap, newInv.getStartTime(), newInv.getEndTime(), newInv.getDuration(), newInv.getRecurId());
        newInv.setRecurrence(recurrence);
    }
    // VALARMs
    Iterator<Element> alarmsIter = element.elementIterator(MailConstants.E_CAL_ALARM);
    while (alarmsIter.hasNext()) {
        Alarm alarm = Alarm.parse(alarmsIter.next());
        if (alarm != null)
            newInv.addAlarm(alarm);
    }
    List<ZProperty> xprops = parseXProps(element);
    for (ZProperty prop : xprops) newInv.addXProp(prop);
    newInv.validateDuration();
    //zdsync: must set this only after recur is processed
    if (invId != null) {
        try {
            int invIdInt = Integer.parseInt(invId);
            newInv.setInviteId(invIdInt);
        } catch (NumberFormatException e) {
        // ignore if invId is not a number, e.g. refers to a remote account
        }
    }
    if (dts != null) {
        newInv.setDtStamp(Long.parseLong(dts));
    }
    Element fragment = element.getOptionalElement(MailConstants.E_FRAG);
    if (fragment != null) {
        newInv.setFragment(fragment.getText());
    }
}
Also used : IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) Recurrence(com.zimbra.cs.mailbox.calendar.Recurrence) ParsedDuration(com.zimbra.common.calendar.ParsedDuration) IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) Element(com.zimbra.common.soap.Element) ZOrganizer(com.zimbra.cs.mailbox.calendar.ZOrganizer) RecurId(com.zimbra.cs.mailbox.calendar.RecurId) Geo(com.zimbra.common.calendar.Geo) ZAttendee(com.zimbra.cs.mailbox.calendar.ZAttendee) Alarm(com.zimbra.cs.mailbox.calendar.Alarm) TimeZoneMap(com.zimbra.common.calendar.TimeZoneMap) ZProperty(com.zimbra.common.calendar.ZCalendar.ZProperty) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) ParseException(java.text.ParseException)

Example 2 with Recurrence

use of com.zimbra.cs.mailbox.calendar.Recurrence in project zm-mailbox by Zimbra.

the class CalendarUtils method parseRecur.

static Recurrence.IRecurrence parseRecur(Element recurElt, TimeZoneMap invTzMap, ParsedDateTime dtStart, ParsedDateTime dtEnd, ParsedDuration dur, RecurId recurId) throws ServiceException {
    if (dur == null && dtStart != null && dtEnd != null)
        dur = dtEnd.difference(dtStart);
    ArrayList<IRecurrence> addRules = new ArrayList<IRecurrence>();
    ArrayList<IRecurrence> subRules = new ArrayList<IRecurrence>();
    for (Iterator iter = recurElt.elementIterator(); iter.hasNext(); ) {
        Element e = (Element) iter.next();
        boolean exclude = false;
        if (e.getName().equals(MailConstants.E_CAL_EXCLUDE)) {
            exclude = true;
        } else {
            if (!e.getName().equals(MailConstants.E_CAL_ADD)) {
                continue;
            }
        }
        for (Iterator intIter = e.elementIterator(); intIter.hasNext(); ) {
            Element intElt = (Element) intIter.next();
            if (intElt.getName().equals(MailConstants.E_CAL_DATES)) {
                // handle RDATE or EXDATE
                String tzid = intElt.getAttribute(MailConstants.A_CAL_TIMEZONE, null);
                ICalTimeZone tz = tzid != null ? invTzMap.lookupAndAdd(tzid) : null;
                RdateExdate rexdate = new RdateExdate(exclude ? ICalTok.EXDATE : ICalTok.RDATE, tz);
                ICalTok valueType = null;
                for (Iterator<Element> dtvalIter = intElt.elementIterator(MailConstants.E_CAL_DATE_VAL); dtvalIter.hasNext(); ) {
                    ICalTok dtvalValueType = null;
                    Element dtvalElem = dtvalIter.next();
                    Element dtvalStartElem = dtvalElem.getElement(MailConstants.E_CAL_START_TIME);
                    String dtvalStartDateStr = dtvalStartElem.getAttribute(MailConstants.A_CAL_DATETIME);
                    ParsedDateTime dtvalStart = parseDateTime(dtvalElem.getName(), dtvalStartDateStr, tzid, invTzMap);
                    Element dtvalEndElem = dtvalElem.getOptionalElement(MailConstants.E_CAL_END_TIME);
                    Element dtvalDurElem = dtvalElem.getOptionalElement(MailConstants.E_CAL_DURATION);
                    if (dtvalEndElem == null && dtvalDurElem == null) {
                        if (dtvalStart.hasTime())
                            dtvalValueType = ICalTok.DATE_TIME;
                        else
                            dtvalValueType = ICalTok.DATE;
                        rexdate.addValue(dtvalStart);
                    } else {
                        dtvalValueType = ICalTok.PERIOD;
                        if (dtvalEndElem != null) {
                            String dtvalEndDateStr = dtvalEndElem.getAttribute(MailConstants.A_CAL_DATETIME);
                            ParsedDateTime dtvalEnd = parseDateTime(dtvalElem.getName(), dtvalEndDateStr, tzid, invTzMap);
                            Period p = new Period(dtvalStart, dtvalEnd);
                            rexdate.addValue(p);
                        } else {
                            ParsedDuration d = ParsedDuration.parse(dtvalDurElem);
                            Period p = new Period(dtvalStart, d);
                            rexdate.addValue(p);
                        }
                    }
                    if (valueType == null) {
                        valueType = dtvalValueType;
                        rexdate.setValueType(valueType);
                    } else if (valueType != dtvalValueType)
                        throw ServiceException.INVALID_REQUEST("Cannot mix different value types in a single <" + intElt.getName() + "> element", null);
                }
                Recurrence.SingleDates sd = new Recurrence.SingleDates(rexdate, dur);
                if (exclude)
                    subRules.add(sd);
                else
                    addRules.add(sd);
            } else if (intElt.getName().equals(MailConstants.E_CAL_RULE)) {
                // handle RRULE or EXRULE
                // Turn XML into iCal RECUR string, which will then be
                // parsed by ical4j Recur object.
                StringBuilder recurBuf = new StringBuilder(100);
                String freq = IcalXmlStrMap.sFreqMap.toIcal(intElt.getAttribute(MailConstants.A_CAL_RULE_FREQ));
                recurBuf.append("FREQ=").append(freq);
                for (Iterator ruleIter = intElt.elementIterator(); ruleIter.hasNext(); ) {
                    Element ruleElt = (Element) ruleIter.next();
                    String ruleEltName = ruleElt.getName();
                    if (ruleEltName.equals(MailConstants.E_CAL_RULE_UNTIL)) {
                        recurBuf.append(";UNTIL=");
                        String d = ruleElt.getAttribute(MailConstants.A_CAL_DATETIME);
                        recurBuf.append(d);
                        // (RFC2445 Section 4.3.10 Recurrence Rule)
                        if (d.indexOf("T") >= 0)
                            if (d.indexOf("Z") < 0)
                                recurBuf.append('Z');
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_COUNT)) {
                        int num = (int) ruleElt.getAttributeLong(MailConstants.A_CAL_RULE_COUNT_NUM, -1);
                        if (num > 0) {
                            recurBuf.append(";COUNT=").append(num);
                        } else {
                            throw ServiceException.INVALID_REQUEST("Expected positive num attribute in <recur> <rule> <count>", null);
                        }
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_INTERVAL)) {
                        String ival = ruleElt.getAttribute(MailConstants.A_CAL_RULE_INTERVAL_IVAL);
                        recurBuf.append(";INTERVAL=").append(ival);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYSECOND)) {
                        String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYSECOND_SECLIST);
                        recurBuf.append(";BYSECOND=").append(list);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYMINUTE)) {
                        String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYMINUTE_MINLIST);
                        recurBuf.append(";BYMINUTE=").append(list);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYHOUR)) {
                        String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYHOUR_HRLIST);
                        recurBuf.append(";BYHOUR=").append(list);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYDAY)) {
                        recurBuf.append(";BYDAY=");
                        int pos = 0;
                        for (Iterator bydayIter = ruleElt.elementIterator(MailConstants.E_CAL_RULE_BYDAY_WKDAY); bydayIter.hasNext(); pos++) {
                            Element wkdayElt = (Element) bydayIter.next();
                            if (pos > 0)
                                recurBuf.append(",");
                            String ordwk = wkdayElt.getAttribute(MailConstants.A_CAL_RULE_BYDAY_WKDAY_ORDWK, null);
                            if (ordwk != null)
                                recurBuf.append(ordwk);
                            String day = wkdayElt.getAttribute(MailConstants.A_CAL_RULE_DAY);
                            if (day == null || day.length() == 0)
                                throw ServiceException.INVALID_REQUEST("Missing " + MailConstants.A_CAL_RULE_DAY + " in <" + ruleEltName + ">", null);
                            recurBuf.append(day);
                        }
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYMONTHDAY)) {
                        String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYMONTHDAY_MODAYLIST);
                        recurBuf.append(";BYMONTHDAY=").append(list);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYYEARDAY)) {
                        String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYYEARDAY_YRDAYLIST);
                        recurBuf.append(";BYYEARDAY=").append(list);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYWEEKNO)) {
                        String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYWEEKNO_WKLIST);
                        recurBuf.append(";BYWEEKNO=").append(list);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYMONTH)) {
                        String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYMONTH_MOLIST);
                        recurBuf.append(";BYMONTH=").append(list);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_BYSETPOS)) {
                        String list = ruleElt.getAttribute(MailConstants.A_CAL_RULE_BYSETPOS_POSLIST);
                        recurBuf.append(";BYSETPOS=").append(list);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_WKST)) {
                        String day = ruleElt.getAttribute(MailConstants.A_CAL_RULE_DAY);
                        recurBuf.append(";WKST=").append(day);
                    } else if (ruleEltName.equals(MailConstants.E_CAL_RULE_XNAME)) {
                        String name = ruleElt.getAttribute(MailConstants.A_CAL_RULE_XNAME_NAME, null);
                        if (name != null) {
                            String value = ruleElt.getAttribute(MailConstants.A_CAL_RULE_XNAME_VALUE, "");
                            // TODO: Escape/unescape value according to
                            // "text" rule.
                            recurBuf.append(";").append(name).append("=").append(value);
                        }
                    }
                }
                try {
                    ZRecur recur = new ZRecur(recurBuf.toString(), invTzMap);
                    if (exclude) {
                        subRules.add(new Recurrence.SimpleRepeatingRule(dtStart, dur, recur, null));
                    } else {
                        addRules.add(new Recurrence.SimpleRepeatingRule(dtStart, dur, recur, null));
                    }
                } catch (ServiceException ex) {
                    throw ServiceException.INVALID_REQUEST("Exception parsing <recur> <rule>", ex);
                }
            } else {
                throw ServiceException.INVALID_REQUEST("Expected <date> or <rule> inside of " + e.getName() + ", got " + intElt.getName(), null);
            }
        }
    // iterate inside <add> or <exclude>
    }
    if (recurId != null) {
        return new Recurrence.ExceptionRule(recurId, dtStart, dur, null, addRules, subRules);
    } else {
        return new Recurrence.RecurrenceRule(dtStart, dur, null, addRules, subRules);
    }
}
Also used : IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) Recurrence(com.zimbra.cs.mailbox.calendar.Recurrence) RdateExdate(com.zimbra.cs.mailbox.calendar.RdateExdate) ParsedDuration(com.zimbra.common.calendar.ParsedDuration) Element(com.zimbra.common.soap.Element) ArrayList(java.util.ArrayList) Period(com.zimbra.cs.mailbox.calendar.Period) ICalTok(com.zimbra.common.calendar.ZCalendar.ICalTok) IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) ZRecur(com.zimbra.cs.mailbox.calendar.ZRecur) ServiceException(com.zimbra.common.service.ServiceException) Iterator(java.util.Iterator) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) ICalTimeZone(com.zimbra.common.calendar.ICalTimeZone)

Example 3 with Recurrence

use of com.zimbra.cs.mailbox.calendar.Recurrence in project zm-mailbox by Zimbra.

the class CalendarItem method updateRecurrence.

private boolean updateRecurrence(long nextAlarm) throws ServiceException {
    long startTime, endTime;
    // update our recurrence rule, start with the initial rule
    Invite firstInv = getDefaultInviteOrNull();
    if (firstInv == null) {
        return false;
    }
    IRecurrence recur = firstInv.getRecurrence();
    if (recur instanceof Recurrence.RecurrenceRule) {
        mRecurrence = (IRecurrence) recur.clone();
        // now, go through the list of invites and find all the exceptions
        for (Invite cur : mInvites) {
            if (cur != firstInv) {
                String method = cur.getMethod();
                if (cur.isCancel()) {
                    assert (cur.hasRecurId());
                    if (cur.hasRecurId()) {
                        checkExdateIsSensible(cur.getRecurId());
                        Recurrence.CancellationRule cancelRule = new Recurrence.CancellationRule(cur.getRecurId());
                        ((Recurrence.RecurrenceRule) mRecurrence).addException(cancelRule);
                    }
                } else if (method.equals(ICalTok.REQUEST.toString()) || method.equals(ICalTok.PUBLISH.toString())) {
                    assert (cur.hasRecurId());
                    if (cur.hasRecurId() && cur.getStartTime() != null) {
                        checkRecurIdIsSensible(cur.getRecurId());
                        Recurrence.ExceptionRule exceptRule = null;
                        IRecurrence curRule = cur.getRecurrence();
                        if (curRule != null && curRule instanceof Recurrence.ExceptionRule) {
                            exceptRule = (Recurrence.ExceptionRule) curRule.clone();
                        } else {
                            // create a fake ExceptionRule wrapper around the single-instance
                            exceptRule = new Recurrence.ExceptionRule(cur.getRecurId(), cur.getStartTime(), cur.getEffectiveDuration(), new InviteInfo(cur));
                        }
                        ((Recurrence.RecurrenceRule) mRecurrence).addException(exceptRule);
                    } else {
                        sLog.debug("Got second invite with no RecurID: " + cur.toString());
                    }
                }
            }
        }
        // Find the earliest DTSTART and latest DTEND.  We're just looking for the bounds, so we won't worry
        // about cancelled instances.
        ParsedDateTime earliestStart = null;
        ParsedDateTime latestEnd = null;
        for (Invite cur : mInvites) {
            if (!cur.isCancel()) {
                ParsedDateTime start = cur.getStartTime();
                if (earliestStart == null)
                    earliestStart = start;
                else if (start != null && start.compareTo(earliestStart) < 0)
                    earliestStart = start;
                ParsedDateTime end = cur.getEffectiveEndTime();
                if (latestEnd == null)
                    latestEnd = end;
                else if (end != null && end.compareTo(latestEnd) > 0)
                    latestEnd = end;
            }
        }
        // Take the later of latestEnd and recurrence's end time.
        ParsedDateTime recurEnd = mRecurrence.getEndTime();
        if (latestEnd == null)
            latestEnd = recurEnd;
        else if (recurEnd != null && recurEnd.compareTo(latestEnd) > 0)
            latestEnd = recurEnd;
        // update the start and end time in the CalendarItem table if
        // necessary
        startTime = earliestStart != null ? earliestStart.getUtcTime() : 0;
        endTime = latestEnd != null ? latestEnd.getUtcTime() : 0;
    } else {
        mRecurrence = null;
        startTime = 0;
        endTime = 0;
        for (Invite inv : mInvites) {
            if (!inv.isCancel()) {
                ParsedDateTime dtStart = inv.getStartTime();
                long st = dtStart != null ? dtStart.getUtcTime() : 0;
                if (st != 0 && (st < startTime || startTime == 0))
                    startTime = st;
                ParsedDateTime dtEnd = inv.getEffectiveEndTime();
                long et = dtEnd != null ? dtEnd.getUtcTime() : 0;
                if (et != 0 && et > endTime)
                    endTime = et;
            }
        }
    }
    // Adjust start/end times before recomputing alarm because alarm computation depends on those times.
    boolean timesChanged = false;
    if (mStartTime != startTime || mEndTime != endTime) {
        timesChanged = true;
        mStartTime = startTime;
        mEndTime = endTime;
    }
    // Recompute next alarm.  Bring appointment start time forward to the alarm time,
    // if the next alarm is before the first instance.
    recomputeNextAlarm(nextAlarm, false, false);
    if (mAlarmData != null) {
        long newNextAlarm = mAlarmData.getNextAtBase();
        if (newNextAlarm > 0 && newNextAlarm < startTime && mStartTime != startTime) {
            timesChanged = true;
            mStartTime = newNextAlarm;
        }
    }
    if (timesChanged) {
        if (ZimbraLog.calendar.isDebugEnabled()) {
            ZimbraLog.calendar.debug("Updating recurrence for %s.  nextAlarm=%d.", getMailopContext(this), nextAlarm);
        }
        DbMailItem.updateInCalendarItemTable(this);
    }
    return true;
}
Also used : RecurrenceRule(com.zimbra.cs.mailbox.calendar.Recurrence.RecurrenceRule) IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) IRecurrence(com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence) Recurrence(com.zimbra.cs.mailbox.calendar.Recurrence) InviteInfo(com.zimbra.cs.mailbox.calendar.InviteInfo) ParsedDateTime(com.zimbra.common.calendar.ParsedDateTime) Invite(com.zimbra.cs.mailbox.calendar.Invite)

Aggregations

ParsedDateTime (com.zimbra.common.calendar.ParsedDateTime)3 Recurrence (com.zimbra.cs.mailbox.calendar.Recurrence)3 IRecurrence (com.zimbra.cs.mailbox.calendar.Recurrence.IRecurrence)3 ParsedDuration (com.zimbra.common.calendar.ParsedDuration)2 Element (com.zimbra.common.soap.Element)2 Geo (com.zimbra.common.calendar.Geo)1 ICalTimeZone (com.zimbra.common.calendar.ICalTimeZone)1 TimeZoneMap (com.zimbra.common.calendar.TimeZoneMap)1 ICalTok (com.zimbra.common.calendar.ZCalendar.ICalTok)1 ZProperty (com.zimbra.common.calendar.ZCalendar.ZProperty)1 ServiceException (com.zimbra.common.service.ServiceException)1 Alarm (com.zimbra.cs.mailbox.calendar.Alarm)1 Invite (com.zimbra.cs.mailbox.calendar.Invite)1 InviteInfo (com.zimbra.cs.mailbox.calendar.InviteInfo)1 Period (com.zimbra.cs.mailbox.calendar.Period)1 RdateExdate (com.zimbra.cs.mailbox.calendar.RdateExdate)1 RecurId (com.zimbra.cs.mailbox.calendar.RecurId)1 RecurrenceRule (com.zimbra.cs.mailbox.calendar.Recurrence.RecurrenceRule)1 ZAttendee (com.zimbra.cs.mailbox.calendar.ZAttendee)1 ZOrganizer (com.zimbra.cs.mailbox.calendar.ZOrganizer)1