use of org.bedework.calfacade.util.ChangeTable in project bw-calendar-engine by Bedework.
the class CoreEvents method updateRecurrences.
/* XXX This is a bit brute force but it will do for the moment. We have to
* turn a set of rules into a set of changes. If we'd preserved the rules
* prior to this I guess we could figure out the differences without querying
* the db.
*
* For the moment create a whole set of instances and then query the db to see if
* they match.
*/
@SuppressWarnings("unchecked")
private void updateRecurrences(final EventInfo ei, final UpdateEventResult uc, final Collection<BwEventProxy> overrides, final boolean shared) throws CalFacadeException {
final BwEvent val = ei.getEvent();
final ChangeTable changes = val.getChangeset(currentPrincipal());
if (!changes.isEmpty()) {
if (!changes.recurrenceChanged()) {
return;
}
if (!changes.recurrenceRulesChanged()) {
// We can handle exdate and rdate changes.
ChangeTableEntry ent = changes.getEntry(PropertyInfoIndex.EXDATE);
if (ent.getAddedValues() != null) {
// exdates added - remove the instances.
removeInstances(val, uc, overrides, ent.getAddedValues(), shared);
}
if (ent.getRemovedValues() != null) {
// exdates removed - add the instances.
addInstances(val, uc, overrides, ent.getRemovedValues(), shared);
}
ent = changes.getEntry(PropertyInfoIndex.RDATE);
if (ent.getAddedValues() != null) {
// rdates added - add the instances.
addInstances(val, uc, overrides, ent.getAddedValues(), shared);
}
if (ent.getRemovedValues() != null) {
// rdates removed - remove the instances.
removeInstances(val, uc, overrides, ent.getRemovedValues(), shared);
}
return;
}
}
final Map<String, BwRecurrenceInstance> updated = new HashMap<>();
/* Get all the times for this event. - this could be a problem. Need to
limit the number. Should we do this in chunks, stepping through the
whole period?
*/
final RecurPeriods rp = RecurUtil.getPeriods(val, getAuthprops().getMaxYears(), getAuthprops().getMaxInstances());
if (rp.instances.isEmpty()) {
// No instances for an alleged recurring event.
// XXX Mark the master as non-recurring to stop it disappearing
val.setRecurring(false);
// throwException(CalFacadeException.noRecurrenceInstances);
}
final String stzid = val.getDtstart().getTzid();
/* try {
val.setLatestDate(Timezones.getUtc(rp.rangeEnd.toString(), stzid));
} catch (Throwable t) {
throwException(new CalFacadeException(t));
} */
int maxInstances = getAuthprops().getMaxInstances();
final boolean dateOnly = val.getDtstart().getDateType();
for (final Period p : rp.instances) {
String dtval = p.getStart().toString();
if (dateOnly) {
dtval = dtval.substring(0, 8);
}
final BwDateTime rstart = BwDateTime.makeBwDateTime(dateOnly, dtval, stzid);
dtval = p.getEnd().toString();
if (dateOnly) {
dtval = dtval.substring(0, 8);
}
final BwDateTime rend = BwDateTime.makeBwDateTime(dateOnly, dtval, stzid);
final BwRecurrenceInstance ri = new BwRecurrenceInstance();
ri.setDtstart(rstart);
ri.setDtend(rend);
ri.setRecurrenceId(ri.getDtstart().getDate());
ri.setMaster(val);
updated.put(ri.getRecurrenceId(), ri);
maxInstances--;
if (maxInstances == 0) {
// That's all you're getting from me
break;
}
}
final List<BwRecurrenceInstance> current = dao.getInstances(val);
for (final BwRecurrenceInstance ri : current) {
final BwRecurrenceInstance updri = updated.get(ri.getRecurrenceId());
if (updri == null) {
// Not in the new instance set - delete from db
ei.removeOverride(ri.getRecurrenceId());
dao.delete(ri);
uc.addDeleted(ri);
notifyInstanceChange(SysEvent.SysCode.ENTITY_DELETED, val, shared, ri.getRecurrenceId());
continue;
}
/* Found instance with same recurrence id. Is the start and end the same
*/
if (!ri.getDtstart().equals(updri.getDtstart()) || !ri.getDtend().equals(updri.getDtend())) {
ri.setDtstart(updri.getDtstart());
ri.setDtend(updri.getDtend());
dao.update(ri);
uc.addUpdated(ri);
notifyInstanceChange(SysEvent.SysCode.ENTITY_UPDATED, val, shared, ri.getRecurrenceId());
}
// Remove the entry - we've processed it.
updated.remove(ri.getRecurrenceId());
}
for (final BwRecurrenceInstance ri : updated.values()) {
dao.save(ri);
uc.addAdded(ri);
notifyInstanceChange(SysEvent.SysCode.ENTITY_ADDED, val, shared, ri.getRecurrenceId());
}
}
use of org.bedework.calfacade.util.ChangeTable in project bw-calendar-engine by Bedework.
the class InReply method updateOrganizerCopy.
private boolean updateOrganizerCopy(final EventInfo colEi, final EventInfo inBoxEi, final String attUri, final ScheduleResult sr) throws CalFacadeException {
final BwEvent inBoxEv = inBoxEi.getEvent();
final BwEvent calEv = colEi.getEvent();
final ChangeTable chg = calEv.getChangeset(getPrincipalHref());
/* Only set true if the inbox copy needs to stay as notification.
* Do not set true for status updates
*/
boolean changed = false;
if (debug) {
trace("Update for attendee " + attUri);
}
if (inBoxEv.getScheduleMethod() != ScheduleMethods.methodTypeReply) {
sr.errorCode = CalFacadeException.schedulingBadMethod;
return false;
}
/* If the incoming sequence is less than that in the organizers event
* then ignore the incoming reply?
*/
/* Update the participation status from the incoming attendee */
BwAttendee calAtt;
final ChangeTableEntry cte = chg.getEntry(PropertyIndex.PropertyInfoIndex.ATTENDEE);
if (!inBoxEv.getSuppressed()) {
calAtt = calEv.findAttendee(attUri);
if (calAtt == null) {
if (debug) {
trace("Not an attendee of " + calEv);
}
sr.errorCode = CalFacadeException.schedulingUnknownAttendee;
sr.extraInfo = attUri;
return false;
}
// For a recurring instance we replace or we update all recurring instances.
final boolean recurringInstance = (calEv instanceof BwEventProxy);
final BwAttendee att = inBoxEv.findAttendee(attUri);
if (calAtt.changedBy(att)) {
changed = true;
if (recurringInstance) {
calEv.removeAttendee(att);
calAtt = (BwAttendee) att.clone();
} else {
att.copyTo(calAtt);
}
cte.addChangedValue(calAtt);
}
calAtt.setScheduleStatus(getRstat(inBoxEv));
if (recurringInstance) {
calEv.addAttendee(calAtt);
}
// XXX Ensure no name change
if (calEv instanceof BwEventProxy) {
final BwEventProxy pr = (BwEventProxy) calEv;
final BwEventAnnotation ann = pr.getRef();
ann.setName(null);
}
}
/* The above changed the master - now we need to update or add any overrides
*/
if (calEv.getRecurring() && (inBoxEi.getOverrides() != null)) {
for (final EventInfo oei : inBoxEi.getOverrides()) {
final BwEvent oev = oei.getEvent();
final EventInfo cei = colEi.findOverride(oev.getRecurrenceId());
/*
if (cei == null) {
// Organizer must have deleted the override.
if (debug) {
trace("Skipping missing override " + oev.getRecurrenceId());
}
continue;
}*/
final BwEvent ocalEv = cei.getEvent();
if (((BwEventProxy) ocalEv).getRef().unsaved()) {
// New Override
try {
final String rid = oev.getRecurrenceId();
Date dt = new DateTime(rid);
if (calEv.getDtstart().getDateType()) {
// RECUR - fix all day recurrences sometime
if (rid.length() > 8) {
// Try to fix up bad all day recurrence ids. - assume a local timezone
((DateTime) dt).setTimeZone(null);
dt = new Date(dt.toString().substring(0, 8));
}
}
final DtStart st = new DtStart(dt);
final String tzid = calEv.getDtstart().getTzid();
if (tzid != null) {
final TimeZone tz = Timezones.getTz(tzid);
st.setTimeZone(tz);
}
ocalEv.setDtstart(BwDateTime.makeBwDateTime(st));
ocalEv.setDuration(calEv.getDuration());
ocalEv.setDtend(ocalEv.getDtstart().addDur(new Dur(calEv.getDuration())));
} catch (final CalFacadeException cfe) {
throw cfe;
} catch (final Throwable t) {
throw new CalFacadeException(t);
}
}
final BwAttendee ovatt = oev.findAttendee(attUri);
calAtt = ocalEv.findAttendee(attUri);
if (calAtt == null) {
// Organizer must have removed the attendee.
if (debug) {
trace("Skipping override " + attUri + " is not attending");
}
continue;
}
if (calAtt.changedBy(ovatt)) {
changed = true;
ocalEv.removeAttendee(ovatt);
calAtt = (BwAttendee) ovatt.clone();
calAtt.setScheduleStatus(getRstat(oev));
ocalEv.addAttendee(calAtt);
cte.addChangedValue(calAtt);
}
}
}
final boolean noinvites = !changed;
colEi.setReplyUpdate(true);
/* Update the organizer copy. This will broadcast the changes tp all
* attendees
*/
getSvc().getEventsHandler().update(colEi, noinvites, attUri);
return changed;
}
use of org.bedework.calfacade.util.ChangeTable in project bw-calendar-engine by Bedework.
the class InRequest method updateAttendeeFields.
private boolean updateAttendeeFields(final EventInfo ourCopy, final EventInfo inBoxEi, final String attUri) throws CalFacadeException {
BwEvent ourEv = ourCopy.getEvent();
BwEvent inEv = inBoxEi.getEvent();
boolean flagNeedsReply = false;
ChangeTable chg = ourCopy.getChangeset(getPrincipalHref());
for (PropertyInfoIndex ipi : PropertyInfoIndex.values()) {
BwIcalPropertyInfoEntry bipie = BwIcalPropertyInfo.getPinfo(ipi);
if (bipie == null) {
continue;
}
if ((ourEv.getEntityType() == IcalDefs.entityTypeEvent) && !bipie.getEventProperty()) {
continue;
}
if ((ourEv.getEntityType() == IcalDefs.entityTypeTodo) && !bipie.getTodoProperty()) {
continue;
}
switch(ipi) {
case UNKNOWN_PROPERTY:
break;
case CLASS:
if (chg.changed(ipi, ourEv.getClassification(), inEv.getClassification())) {
ourEv.setClassification(inEv.getClassification());
}
break;
case COMPLETED:
/* Todo only */
if (chg.changed(ipi, ourEv.getCompleted(), inEv.getCompleted())) {
ourEv.setCompleted(inEv.getCompleted());
}
break;
case CREATED:
break;
case DESCRIPTION:
/*
for (BwLongString s: inEv.getDescriptions()) {
chg.addValue(Property.DESCRIPTION, s);
}
*/
if (chg.changed(ipi, ourEv.getDescription(), inEv.getDescription())) {
ourEv.setDescription(inEv.getDescription());
}
break;
case DTEND:
/* Event only */
case DUE:
/* Todo only */
BwDateTime dt = inEv.getDtend();
if (!CalFacadeUtil.eqObjval(ourEv.getDtend(), dt)) {
ourEv.setDtend(dt);
chg.changed(ipi, ourEv.getDtend(), dt);
}
char c = inEv.getEndType();
if (c != ourEv.getEndType()) {
ourEv.setEndType(c);
chg.changed(PropertyInfoIndex.END_TYPE, ourEv.getEndType(), c);
}
break;
case DTSTAMP:
break;
case DTSTART:
dt = inEv.getDtstart();
if (!CalFacadeUtil.eqObjval(ourEv.getDtstart(), dt)) {
ourEv.setDtstart(dt);
chg.changed(ipi, ourEv.getDtstart(), dt);
}
break;
case DURATION:
if (chg.changed(ipi, ourEv.getDuration(), inEv.getDuration())) {
ourEv.setDuration(inEv.getDuration());
}
break;
case GEO:
if (chg.changed(ipi, ourEv.getGeo(), inEv.getGeo())) {
ourEv.setGeo(inEv.getGeo());
}
break;
case LAST_MODIFIED:
break;
case LOCATION:
if (chg.changed(ipi, ourEv.getLocation(), inEv.getLocation())) {
ourEv.setLocation((BwLocation) inEv.getLocation().clone());
}
break;
case ORGANIZER:
if (chg.changed(ipi, ourEv.getOrganizer(), inEv.getOrganizer())) {
ourEv.setOrganizer((BwOrganizer) inEv.getOrganizer().clone());
}
break;
case PRIORITY:
if (chg.changed(ipi, ourEv.getPriority(), inEv.getPriority())) {
ourEv.setPriority(inEv.getPriority());
}
break;
case RECURRENCE_ID:
break;
case SEQUENCE:
if (chg.changed(ipi, ourEv.getSequence(), inEv.getSequence())) {
ourEv.setSequence(inEv.getSequence());
}
break;
case STATUS:
if (chg.changed(ipi, ourEv.getStatus(), inEv.getStatus())) {
ourEv.setStatus(inEv.getStatus());
}
break;
case SUMMARY:
/*
for (BwString s: inEv.getSummaries()) {
chg.addValue(Property.SUMMARY, s);
}
*/
if (chg.changed(ipi, ourEv.getSummary(), inEv.getSummary())) {
ourEv.setSummary(inEv.getSummary());
}
break;
case PERCENT_COMPLETE:
/* Todo only */
if (chg.changed(ipi, ourEv.getPercentComplete(), inEv.getPercentComplete())) {
ourEv.setPercentComplete(inEv.getPercentComplete());
}
break;
case UID:
break;
case URL:
if (chg.changed(ipi, ourEv.getLink(), inEv.getLink())) {
ourEv.setLink(inEv.getLink());
}
break;
case TRANSP:
/* Event only - done with attendee */
break;
case ATTACH:
break;
case ATTENDEE:
String transparency = ourEv.getTransparency();
BwAttendee ourAtt = null;
for (BwAttendee inAtt : inEv.getAttendees()) {
BwAttendee att = (BwAttendee) inAtt.clone();
att.setScheduleStatus(null);
String inAttUri = att.getAttendeeUri();
BwAttendee evAtt = ourEv.findAttendee(inAttUri);
if (inAttUri.equals(attUri)) {
// It's ours
ourAtt = att;
if ((att.getPartstat() == null) || att.getPartstat().equals(IcalDefs.partstatValNeedsAction)) {
transparency = IcalDefs.transparencyTransparent;
// Apple ical seems to expect an x-prop.
flagNeedsReply = true;
}
// att.setScheduleStatus(IcalDefs.deliveryStatusSuccess);
}
/* See if it's in the current set and if anything significant changed
for (BwAttendee calAtt: ourEv.getAttendees()) {
if (calAtt.getAttendeeUri().equals(inAttUri)) {
if (calAtt.changedBy(inAtt, false)) {
ourEv.setSignificantChange(true);
}
}
}*/
final ChangeTableEntry cte = chg.getEntry(PropertyInfoIndex.ATTENDEE);
if (evAtt != null) {
cte.addChangedValue(att);
} else {
cte.addAddedValue(att);
}
}
if (ourAtt == null) {
// Error?
if (debug) {
trace("InSchedule - no attendee for " + ourEv.getOwnerHref());
}
return false;
}
if (chg.changed(PropertyInfoIndex.TRANSP, ourEv.getTransparency(), transparency)) {
ourEv.setTransparency(transparency);
}
break;
case CATEGORIES:
if (!Util.isEmpty(inEv.getCategories())) {
for (BwCategory cat : inEv.getCategories()) {
chg.addValue(ipi, cat);
}
}
break;
case COMMENT:
for (BwString s : inEv.getComments()) {
chg.addValue(ipi, s);
}
break;
case CONTACT:
for (final BwContact ct : inEv.getContacts()) {
chg.addValue(ipi, ct.clone());
}
break;
case EXDATE:
// Don't updaye exdate - we add cancelled overrides
break;
case EXRULE:
// Only for master events
if (ourEv instanceof BwEventProxy) {
break;
}
for (final String s : inEv.getExrules()) {
chg.addValue(ipi, s);
}
break;
case REQUEST_STATUS:
break;
case RELATED_TO:
if (chg.changed(ipi, ourEv.getRelatedTo(), inEv.getRelatedTo())) {
ourEv.setRelatedTo(inEv.getRelatedTo());
}
break;
case RESOURCES:
for (BwString bs : inEv.getResources()) {
chg.addValue(ipi, bs);
}
break;
case RDATE:
// Only for master events
if (ourEv instanceof BwEventProxy) {
break;
}
for (BwDateTime bdt : inEv.getRdates()) {
chg.addValue(ipi, bdt);
}
break;
case RRULE:
// Only for master events
if (ourEv instanceof BwEventProxy) {
break;
}
for (String s : inEv.getRrules()) {
chg.addValue(ipi, s);
}
break;
case XPROP:
for (BwXproperty x : inEv.getXproperties()) {
chg.addValue(ipi, x);
}
break;
case FREEBUSY:
case TZID:
case TZNAME:
case TZOFFSETFROM:
case TZOFFSETTO:
case TZURL:
case ACTION:
case REPEAT:
case TRIGGER:
break;
// non ical
case COLLECTION:
// non ical
case COST:
// non ical
case CREATOR:
// non ical
case OWNER:
case // non ical
ENTITY_TYPE:
break;
case // Component
VALARM:
break;
// Param
case LANG:
case // Param
TZIDPAR:
break;
case PUBLISH_URL:
case POLL_ITEM_ID:
case END_TYPE:
case ETAG:
case HREF:
case XBEDEWORK_COST:
case CALSCALE:
case METHOD:
case PRODID:
case VERSION:
case ACL:
case AFFECTS_FREE_BUSY:
case ALIAS_URI:
case ATTENDEE_SCHEDULING_OBJECT:
case CALTYPE:
case COL_PROPERTIES:
case COLPATH:
case CTOKEN:
case DISPLAY:
case DOCTYPE:
case EVENTREG_END:
case EVENTREG_MAX_TICKETS:
case EVENTREG_MAX_TICKETS_PER_USER:
case EVENTREG_START:
case EVENTREG_WAIT_LIST_LIMIT:
case FILTER_EXPR:
case IGNORE_TRANSP:
case IMAGE:
case INDEX_END:
case INDEX_START:
case INSTANCE:
case LAST_REFRESH:
case LAST_REFRESH_STATUS:
case LOCATION_UID:
case LOCATION_STR:
break;
default:
warn("Not handling icalendar property " + ipi);
}
// switch
}
if (chg.changed(PropertyInfoIndex.COST, ourEv.getCost(), inEv.getCost())) {
ourEv.setCost(inEv.getCost());
}
/* Now see if we need to flag a schedule-tag change. We do so only if
* a. A property other than the attendee changed
* b. An attendee was added or removed
*/
Collection<ChangeTableEntry> changes = chg.getEntries();
ChangeTableEntry attChanges = null;
ourEv.setSignificantChange(false);
for (ChangeTableEntry cte : changes) {
if (!cte.getChanged()) {
continue;
}
if (cte.getIndex() == PropertyInfoIndex.ATTENDEE) {
attChanges = cte;
continue;
}
ourEv.setSignificantChange(true);
}
if (debug) {
trace("After change check getSignificantChange=" + ourEv.getSignificantChange());
}
if (flagNeedsReply) {
// Apple ical seems to expect an x-prop.
// chg.addValue(PropertyInfoIndex.XPROP,
// new BwXproperty(BwXproperty.appleNeedsReply,
// null, "TRUE"));
}
chg.processChanges(ourEv, true);
if (debug) {
trace(chg.toString());
}
/* The attendee change entry will now reflect the changes made to the
* attendee list. See if any significant change was made there.
*/
if (attChanges != null) {
if (!Util.isEmpty(attChanges.getAddedValues()) || !Util.isEmpty(attChanges.getRemovedValues())) {
ourEv.setSignificantChange(true);
} else {
/* TODO - go through the changed entries and look for our entry. See
* if we are being asked to reply - this can probably be done earlier.
*/
}
}
if (debug) {
trace("After attendee change check getSignificantChange=" + ourEv.getSignificantChange());
}
return true;
}
use of org.bedework.calfacade.util.ChangeTable in project bw-calendar-engine by Bedework.
the class IcalUtil method setDates.
/**
* Set the dates in an event given a start and one or none of end and
* duration.
*
* @param userHref
* @param ei
* @param dtStart
* @param dtEnd
* @param duration
* @throws CalFacadeException
*/
public static void setDates(final String userHref, final EventInfo ei, DtStart dtStart, DtEnd dtEnd, final Duration duration) throws CalFacadeException {
BwEvent ev = ei.getEvent();
ChangeTable chg = ei.getChangeset(userHref);
boolean scheduleReply = ev.getScheduleMethod() == ScheduleMethods.methodTypeReply;
boolean todo = ev.getEntityType() == IcalDefs.entityTypeTodo;
boolean vpoll = ev.getEntityType() == IcalDefs.entityTypeVpoll;
try {
if (dtStart == null) {
if (!scheduleReply && !todo && !vpoll) {
throw new CalFacadeException("org.bedework.error.nostartdate");
}
/* A task or vpoll can have no date and time. set start to now, end to
* many years from now and the noStart flag.
*
* A todo without dates has to appear only on the current day.
*/
if (dtEnd != null) {
dtStart = new DtStart(dtEnd.getParameters(), dtEnd.getValue());
} else {
Date now = new Date(new java.util.Date().getTime());
dtStart = new DtStart(now);
dtStart.getParameters().add(Value.DATE);
}
ev.setNoStart(true);
} else {
ev.setNoStart(false);
}
if (dtStart != null) {
BwDateTime bwDtStart = BwDateTime.makeBwDateTime(dtStart);
if (!CalFacadeUtil.eqObjval(ev.getDtstart(), bwDtStart)) {
chg.changed(PropertyInfoIndex.DTSTART, ev.getDtstart(), bwDtStart);
ev.setDtstart(bwDtStart);
}
}
char endType = StartEndComponent.endTypeNone;
if (dtEnd != null) {
endType = StartEndComponent.endTypeDate;
} else if (scheduleReply || todo || vpoll) {
// about 10 years
Dur years = new Dur(520);
Date now = new Date(new java.util.Date().getTime());
dtEnd = new DtEnd(new Date(years.getTime(now)));
dtEnd.getParameters().add(Value.DATE);
}
if (dtEnd != null) {
BwDateTime bwDtEnd = BwDateTime.makeBwDateTime(dtEnd);
if (!CalFacadeUtil.eqObjval(ev.getDtend(), bwDtEnd)) {
chg.changed(PropertyInfoIndex.DTEND, ev.getDtend(), bwDtEnd);
ev.setDtend(bwDtEnd);
}
}
/**
* If we were given a duration store it in the event and calculate
* an end to the event - which we should not have been given.
*/
if (duration != null) {
if (endType != StartEndComponent.endTypeNone) {
if (ev.getEntityType() == IcalDefs.entityTypeFreeAndBusy) {
// Apple is sending both - duration indicates the minimum
// freebusy duration. Ignore for now.
} else {
throw new CalFacadeException(CalFacadeException.endAndDuration);
}
}
endType = StartEndComponent.endTypeDuration;
String durVal = duration.getValue();
if (!durVal.equals(ev.getDuration())) {
chg.changed(PropertyInfoIndex.DURATION, ev.getDuration(), durVal);
ev.setDuration(durVal);
}
Dur dur = duration.getDuration();
ev.setDtend(BwDateTime.makeDateTime(dtStart, ev.getDtstart().getDateType(), dur));
} else if (!scheduleReply && (endType == StartEndComponent.endTypeNone) && !todo) {
/* No duration and no end specified.
* Set the end values to the start values + 1 for dates
*/
boolean dateOnly = ev.getDtstart().getDateType();
Dur dur;
if (dateOnly) {
// 1 day
dur = new Dur(1, 0, 0, 0);
} else {
// No duration
dur = new Dur(0, 0, 0, 0);
}
BwDateTime bwDtEnd = BwDateTime.makeDateTime(dtStart, dateOnly, dur);
if (!CalFacadeUtil.eqObjval(ev.getDtend(), bwDtEnd)) {
chg.changed(PropertyInfoIndex.DTEND, ev.getDtend(), bwDtEnd);
ev.setDtend(bwDtEnd);
}
}
if ((endType != StartEndComponent.endTypeDuration) && (ev.getDtstart() != null) && (ev.getDtend() != null)) {
// Calculate a duration
String durVal = BwDateTime.makeDuration(ev.getDtstart(), ev.getDtend()).toString();
if (!durVal.equals(ev.getDuration())) {
chg.changed(PropertyInfoIndex.DURATION, ev.getDuration(), durVal);
ev.setDuration(durVal);
}
}
ev.setEndType(endType);
} catch (CalFacadeException cfe) {
throw cfe;
} catch (Throwable t) {
throw new CalFacadeException(t);
}
}
use of org.bedework.calfacade.util.ChangeTable in project bw-calendar-engine by Bedework.
the class BwEventUtil method toEvent.
/**
* We are going to try to construct a BwEvent object from a VEvent. This
* may represent a new event or an update to a pre-existing event. In any
* case, the VEvent probably has insufficient information to completely
* reconstitute the event object so we'll get the uid first and retrieve
* the event if it exists.
*
* <p>To put it another way we're doing a diff then update.
*
* <p>If it doesn't exist, we'll first fill in the appropriate fields,
* (non-public, creator, created etc) then for both cases update the
* remaining fields from the VEvent.
*
* <p>Recurring events present some challenges. If there is no recurrence
* id the vevent represents the master entity which defines the recurrence
* rules. If a recurrence id is present then the vevent represents a
* recurrence instance override and we should not attempt to retrieve the
* actual object but the referenced instance.
*
* <p>Also, note that we sorted the components first so we get the master
* before any instances.
*
* <p>If DTSTART, RRULE, EXRULE have changed (also RDATE, EXDATE?) then any
* existing overrides are unusable. We should delete all overrides and replace
* with new ones.
*
* <p>For an update we have to keep track of which fields were present in
* the vevent and set all absent fields to null in the BwEvent.
*
* @param cb IcalCallback object
* @param cal Needed so we can retrieve the event.
* @param ical Icalendar we are converting. We check its events for
* overrides.
* @param val VEvent object
* @param diff True if we should assume we are updating existing events.
* @param mergeAttendees True if we should only update our own attendee.
* @return EventInfo object representing new entry or updated entry
* @throws CalFacadeException
*/
public static EventInfo toEvent(final IcalCallback cb, final BwCalendar cal, final Icalendar ical, final Component val, final boolean diff, final boolean mergeAttendees) throws CalFacadeException {
if (val == null) {
return null;
}
String currentPrincipal = null;
final BwPrincipal principal = cb.getPrincipal();
if (principal != null) {
currentPrincipal = principal.getPrincipalRef();
}
final boolean debug = getLog().isDebugEnabled();
@SuppressWarnings("unchecked") final Holder<Boolean> hasXparams = new Holder<Boolean>(Boolean.FALSE);
final int methodType = ical.getMethodType();
String attUri = null;
if (mergeAttendees) {
// We'll need this later.
attUri = cb.getCaladdr(cb.getPrincipal().getPrincipalRef());
}
final String colPath;
if (cal == null) {
colPath = null;
} else {
colPath = cal.getPath();
}
try {
final PropertyList pl = val.getProperties();
boolean vpoll = false;
if (pl == null) {
// Empty component
return null;
}
final int entityType;
if (val instanceof VEvent) {
entityType = IcalDefs.entityTypeEvent;
} else if (val instanceof VToDo) {
entityType = IcalDefs.entityTypeTodo;
} else if (val instanceof VJournal) {
entityType = IcalDefs.entityTypeJournal;
} else if (val instanceof VFreeBusy) {
entityType = IcalDefs.entityTypeFreeAndBusy;
} else if (val instanceof VAvailability) {
entityType = IcalDefs.entityTypeVavailability;
} else if (val instanceof Available) {
entityType = IcalDefs.entityTypeAvailable;
} else if (val instanceof VPoll) {
entityType = IcalDefs.entityTypeVpoll;
vpoll = true;
} else {
throw new CalFacadeException("org.bedework.invalid.component.type", val.getName());
}
Property prop;
// Get the guid from the component
String guid = null;
prop = pl.getProperty(Property.UID);
if (prop != null) {
testXparams(prop, hasXparams);
guid = prop.getValue();
}
if (guid == null) {
/* XXX A guid is required - but are there devices out there without a
* guid - and if so how do we handle it?
*/
throw new CalFacadeException(CalFacadeException.noGuid);
}
/* See if we have a recurrence id */
BwDateTime ridObj = null;
String rid = null;
prop = pl.getProperty(Property.RECURRENCE_ID);
if (prop != null) {
testXparams(prop, hasXparams);
ridObj = BwDateTime.makeBwDateTime((DateProperty) prop);
if (ridObj.getRange() != null) {
/* XXX What do I do with it? */
warn("TRANS-TO_EVENT: Got a recurrence id range");
}
rid = ridObj.getDate();
}
EventInfo masterEI = null;
EventInfo evinfo = null;
BwEvent ev = null;
/* If we have a recurrence id see if we already have the master (we should
* get a master + all its overrides).
*
* If so find the override and use the annnotation or if no override,
* make one.
*
* If no override retrieve the event, add it to our table and then locate the
* annotation.
*
* If there is no annotation, create one.
*
* It's possible we have been sent 'detached' instances of a recurring
* event. This may happen if we are invited to one or more instances of a
* meeting. In this case we try to retrieve the master and if it doesn't
* exist we manufacture one. We consider such an instance an update to
* that instance only and leave the others alone.
*/
/* We need this in a couple of places */
final DtStart dtStart = (DtStart) pl.getProperty(Property.DTSTART);
/* If this is a recurrence instance see if we can find the master
*/
if (rid != null) {
// See if we have a new master event. If so create a proxy to this event.
masterEI = findMaster(guid, ical.getComponents());
if (masterEI != null) {
evinfo = masterEI.findOverride(rid);
}
}
if (diff && (evinfo == null) && (cal != null) && (cal.getCalType() != BwCalendar.calTypeInbox) && (cal.getCalType() != BwCalendar.calTypePendingInbox) && (cal.getCalType() != BwCalendar.calTypeOutbox)) {
if (debug) {
debugMsg("TRANS-TO_EVENT: try to fetch event with guid=" + guid);
}
final Collection<EventInfo> eis = cb.getEvent(colPath, guid);
if (Util.isEmpty(eis)) {
// do nothing
} else if (eis.size() > 1) {
// DORECUR - wrong again
throw new CalFacadeException("More than one event returned for guid.");
} else {
evinfo = eis.iterator().next();
}
if (debug) {
if (evinfo != null) {
debugMsg("TRANS-TO_EVENT: fetched event with guid");
} else {
debugMsg("TRANS-TO_EVENT: did not find event with guid");
}
}
if (evinfo != null) {
if (rid != null) {
// We just retrieved it's master
masterEI = evinfo;
masterEI.setInstanceOnly(true);
evinfo = masterEI.findOverride(rid);
ical.addComponent(masterEI);
} else if (methodType == ScheduleMethods.methodTypeCancel) {
// This should never have an rid for cancel of entire event.
evinfo.setInstanceOnly(evinfo.getEvent().getSuppressed());
} else {
// Presumably sent an update for the entire event. No longer suppressed master
evinfo.getEvent().setSuppressed(false);
}
} else if (rid != null) {
/* Manufacture a master for the instance */
masterEI = makeNewEvent(cb, entityType, guid, colPath);
final BwEvent e = masterEI.getEvent();
// XXX This seems bogus
final DtStart mdtStart;
final String bogusDate = "19980118";
final String bogusTime = "T230000";
final Parameter par = dtStart.getParameter("VALUE");
final boolean isDateType = (par != null) && (par.equals(Value.DATE));
if (isDateType) {
mdtStart = new DtStart(new Date(bogusDate));
} else if (dtStart.isUtc()) {
mdtStart = new DtStart(bogusDate + bogusTime + "Z");
} else if (dtStart.getTimeZone() == null) {
mdtStart = new DtStart(bogusDate + bogusTime);
} else {
mdtStart = new DtStart(bogusDate + bogusTime + "Z", dtStart.getTimeZone());
}
setDates(cb.getPrincipal().getPrincipalRef(), masterEI, mdtStart, null, null);
e.setRecurring(true);
// e.addRdate(ridObj);
e.setSuppressed(true);
ical.addComponent(masterEI);
evinfo = masterEI.findOverride(rid);
masterEI.setInstanceOnly(rid != null);
}
}
if (evinfo == null) {
evinfo = makeNewEvent(cb, entityType, guid, colPath);
} else if (evinfo.getEvent().getEntityType() != entityType) {
throw new CalFacadeException("org.bedework.mismatched.entity.type", val.toString());
}
final ChangeTable chg = evinfo.getChangeset(cb.getPrincipal().getPrincipalRef());
if (rid != null) {
final String evrid = evinfo.getEvent().getRecurrenceId();
if ((evrid == null) || (!evrid.equals(rid))) {
warn("Mismatched rid ev=" + evrid + " expected " + rid);
// XXX spurious???
chg.changed(PropertyInfoIndex.RECURRENCE_ID, evrid, rid);
}
if (masterEI.getEvent().getSuppressed()) {
masterEI.getEvent().addRdate(ridObj);
}
}
ev = evinfo.getEvent();
ev.setScheduleMethod(methodType);
DtEnd dtEnd = null;
if (entityType == IcalDefs.entityTypeTodo) {
final Due due = (Due) pl.getProperty(Property.DUE);
if (due != null) {
dtEnd = new DtEnd(due.getParameters(), due.getValue());
}
} else {
dtEnd = (DtEnd) pl.getProperty(Property.DTEND);
}
final Duration duration = (Duration) pl.getProperty(Property.DURATION);
setDates(cb.getPrincipal().getPrincipalRef(), evinfo, dtStart, dtEnd, duration);
for (final Object aPl : pl) {
prop = (Property) aPl;
testXparams(prop, hasXparams);
// debugMsg("ical prop " + prop.getClass().getName());
String pval = prop.getValue();
if ((pval != null) && (pval.length() == 0)) {
pval = null;
}
final PropertyInfoIndex pi;
if (prop instanceof XProperty) {
pi = PropertyInfoIndex.XPROP;
} else {
pi = PropertyInfoIndex.fromName(prop.getName());
}
if (pi == null) {
debugMsg("Unknown property with name " + prop.getName() + " class " + prop.getClass() + " and value " + pval);
continue;
}
chg.present(pi);
switch(pi) {
case ACCEPT_RESPONSE:
/* ------------------- Accept Response -------------------- */
String sval = ((AcceptResponse) prop).getValue();
if (chg.changed(pi, ev.getPollAcceptResponse(), sval)) {
ev.setPollAcceptResponse(sval);
}
break;
case ATTACH:
/* ------------------- Attachment -------------------- */
chg.addValue(pi, getAttachment((Attach) prop));
break;
case ATTENDEE:
if (methodType == ScheduleMethods.methodTypePublish) {
if (cb.getStrictness() == IcalCallback.conformanceStrict) {
throw new CalFacadeException(CalFacadeException.attendeesInPublish);
}
if (cb.getStrictness() == IcalCallback.conformanceWarn) {
// warn("Had attendees for PUBLISH");
}
}
Attendee attPr = (Attendee) prop;
if (evinfo.getNewEvent() || !mergeAttendees) {
chg.addValue(pi, getAttendee(cb, attPr));
} else {
final String pUri = cb.getCaladdr(attPr.getValue());
if (pUri.equals(attUri)) {
/* Only update for our own attendee
* We're doing a PUT and this must be the attendee updating their
* partstat. We don't allow them to change other attendees
* whatever the PUT content says.
*/
chg.addValue(pi, getAttendee(cb, attPr));
} else {
// Use the value we currently have
boolean found = false;
for (final BwAttendee att : ev.getAttendees()) {
if (pUri.equals(att.getAttendeeUri())) {
chg.addValue(pi, att.clone());
found = true;
break;
}
}
if (!found) {
// An added attendee
final BwAttendee att = getAttendee(cb, attPr);
att.setPartstat(IcalDefs.partstatValNeedsAction);
chg.addValue(pi, att);
}
}
}
break;
case BUSYTYPE:
int ibt = BwEvent.fromBusyTypeString(pval);
if (chg.changed(pi, ev.getBusyType(), ibt)) {
ev.setBusyType(ibt);
}
break;
case CATEGORIES:
/* ------------------- Categories -------------------- */
Categories cats = (Categories) prop;
TextList cl = cats.getCategories();
String lang = getLang(cats);
if (cl != null) {
/* Got some categories */
Iterator cit = cl.iterator();
while (cit.hasNext()) {
String wd = (String) cit.next();
if (wd == null) {
continue;
}
BwString key = new BwString(lang, wd);
BwCategory cat = cb.findCategory(key);
if (cat == null) {
cat = BwCategory.makeCategory();
cat.setWord(key);
cb.addCategory(cat);
}
chg.addValue(pi, cat);
}
}
break;
case CLASS:
if (chg.changed(pi, ev.getClassification(), pval)) {
ev.setClassification(pval);
}
break;
case COMMENT:
/* ------------------- Comment -------------------- */
chg.addValue(pi, new BwString(null, pval));
break;
case COMPLETED:
if (chg.changed(pi, ev.getCompleted(), pval)) {
ev.setCompleted(pval);
}
break;
case CONTACT:
/* ------------------- Contact -------------------- */
String altrep = getAltRepPar(prop);
lang = getLang(prop);
String uid = getUidPar(prop);
BwString nm = new BwString(lang, pval);
BwContact contact = null;
if (uid != null) {
contact = cb.getContact(uid);
}
if (contact == null) {
contact = cb.findContact(nm);
}
if (contact == null) {
contact = BwContact.makeContact();
contact.setCn(nm);
contact.setLink(altrep);
cb.addContact(contact);
} else {
contact.setCn(nm);
contact.setLink(altrep);
}
chg.addValue(pi, contact);
break;
case CREATED:
if (chg.changed(pi, ev.getCreated(), pval)) {
ev.setCreated(pval);
}
break;
case DESCRIPTION:
if (chg.changed(pi, ev.getDescription(), pval)) {
ev.setDescription(pval);
}
break;
case DTEND:
break;
case DTSTAMP:
/* ------------------- DtStamp -------------------- */
ev.setDtstamp(pval);
break;
case DTSTART:
break;
case DURATION:
break;
case EXDATE:
/* ------------------- ExDate -------------------- */
chg.addValues(pi, makeDateTimes((DateListProperty) prop));
break;
case EXRULE:
/* ------------------- ExRule -------------------- */
chg.addValue(pi, pval);
break;
case FREEBUSY:
/* ------------------- freebusy -------------------- */
FreeBusy fbusy = (FreeBusy) prop;
PeriodList perpl = fbusy.getPeriods();
Parameter par = getParameter(fbusy, "FBTYPE");
int fbtype;
if (par == null) {
fbtype = BwFreeBusyComponent.typeBusy;
} else if (par.equals(FbType.BUSY)) {
fbtype = BwFreeBusyComponent.typeBusy;
} else if (par.equals(FbType.BUSY_TENTATIVE)) {
fbtype = BwFreeBusyComponent.typeBusyTentative;
} else if (par.equals(FbType.BUSY_UNAVAILABLE)) {
fbtype = BwFreeBusyComponent.typeBusyUnavailable;
} else if (par.equals(FbType.FREE)) {
fbtype = BwFreeBusyComponent.typeFree;
} else {
if (debug) {
debugMsg("Unsupported parameter " + par.getName());
}
throw new IcalMalformedException("parameter " + par.getName());
}
BwFreeBusyComponent fbc = new BwFreeBusyComponent();
fbc.setType(fbtype);
Iterator perit = perpl.iterator();
while (perit.hasNext()) {
Period per = (Period) perit.next();
fbc.addPeriod(per);
}
ev.addFreeBusyPeriod(fbc);
break;
case GEO:
/* ------------------- Geo -------------------- */
Geo g = (Geo) prop;
BwGeo geo = new BwGeo(g.getLatitude(), g.getLongitude());
if (chg.changed(pi, ev.getGeo(), geo)) {
ev.setGeo(geo);
}
break;
case LAST_MODIFIED:
if (chg.changed(pi, ev.getLastmod(), pval)) {
ev.setLastmod(pval);
}
break;
case LOCATION:
/* ------------------- Location -------------------- */
BwLocation loc = null;
// String uid = getUidPar(prop);
/* At the moment Mozilla lightning is broken and this leads to all
* sorts of problems.
if (uid != null) {
loc = cb.getLocation(uid);
}
*/
lang = getLang(prop);
BwString addr = null;
if (pval != null) {
if (loc == null) {
addr = new BwString(lang, pval);
loc = cb.findLocation(addr);
}
if (loc == null) {
loc = BwLocation.makeLocation();
loc.setAddress(addr);
cb.addLocation(loc);
}
}
BwLocation evloc = ev.getLocation();
if (chg.changed(pi, evloc, loc)) {
// CHGTBL - this only shows that it's a different location object
ev.setLocation(loc);
} else if ((loc != null) && (evloc != null)) {
// See if the value is changed
String evval = evloc.getAddress().getValue();
String inval = loc.getAddress().getValue();
if (!evval.equals(inval)) {
chg.changed(pi, evval, inval);
evloc.getAddress().setValue(inval);
}
}
break;
case ORGANIZER:
/* ------------------- Organizer -------------------- */
final BwOrganizer org = getOrganizer(cb, (Organizer) prop);
final BwOrganizer evorg = ev.getOrganizer();
final BwOrganizer evorgCopy;
if (evorg == null) {
evorgCopy = null;
} else {
evorgCopy = (BwOrganizer) evorg.clone();
}
if (chg.changed(pi, evorgCopy, org)) {
if (evorg == null) {
ev.setOrganizer(org);
} else {
evorg.update(org);
}
}
break;
case PERCENT_COMPLETE:
/* ------------------- PercentComplete -------------------- */
Integer ival = ((PercentComplete) prop).getPercentage();
if (chg.changed(pi, ev.getPercentComplete(), ival)) {
ev.setPercentComplete(ival);
}
break;
case POLL_MODE:
/* ------------------- Poll mode -------------------- */
sval = ((PollMode) prop).getValue();
if (chg.changed(pi, ev.getPollMode(), sval)) {
ev.setPollMode(sval);
}
break;
case POLL_PROPERTIES:
/* ------------------- Poll properties ---------------- */
sval = ((PollProperties) prop).getValue();
if (chg.changed(pi, ev.getPollProperties(), sval)) {
ev.setPollProperties(sval);
}
break;
case POLL_WINNER:
/* ------------------- Poll winner -------------------- */
ival = ((PollWinner) prop).getPollwinner();
if (chg.changed(pi, ev.getPollWinner(), ival)) {
ev.setPollWinner(ival);
}
break;
case PRIORITY:
/* ------------------- Priority -------------------- */
ival = ((Priority) prop).getLevel();
if (chg.changed(pi, ev.getPriority(), ival)) {
ev.setPriority(ival);
}
break;
case RDATE:
/* ------------------- RDate -------------------- */
chg.addValues(pi, makeDateTimes((DateListProperty) prop));
break;
case RECURRENCE_ID:
break;
case RELATED_TO:
/* ------------------- RelatedTo -------------------- */
final RelatedTo irelto = (RelatedTo) prop;
final BwRelatedTo relto = new BwRelatedTo();
final String parval = IcalUtil.getParameterVal(irelto, "RELTYPE");
if (parval != null) {
relto.setRelType(parval);
}
relto.setValue(irelto.getValue());
if (chg.changed(pi, ev.getRelatedTo(), relto)) {
ev.setRelatedTo(relto);
}
break;
case REQUEST_STATUS:
/* ------------------- RequestStatus -------------------- */
final BwRequestStatus rs = BwRequestStatus.fromRequestStatus((RequestStatus) prop);
chg.addValue(pi, rs);
break;
case RESOURCES:
/* ------------------- Resources -------------------- */
final TextList rl = ((Resources) prop).getResources();
if (rl != null) {
/* Got some resources */
lang = getLang(prop);
Iterator rit = rl.iterator();
while (rit.hasNext()) {
BwString rsrc = new BwString(lang, (String) rit.next());
chg.addValue(pi, rsrc);
}
}
break;
case RRULE:
/* ------------------- RRule -------------------- */
chg.addValue(pi, pval);
break;
case SEQUENCE:
/* ------------------- Sequence -------------------- */
int seq = ((Sequence) prop).getSequenceNo();
if (seq != ev.getSequence()) {
chg.changed(pi, ev.getSequence(), seq);
ev.setSequence(seq);
}
break;
case STATUS:
if (chg.changed(pi, ev.getStatus(), pval)) {
ev.setStatus(pval);
}
break;
case SUMMARY:
if (chg.changed(pi, ev.getSummary(), pval)) {
ev.setSummary(pval);
}
break;
case TRANSP:
if (chg.changed(pi, ev.getPeruserTransparency(cb.getPrincipal().getPrincipalRef()), pval)) {
BwXproperty pu = ev.setPeruserTransparency(cb.getPrincipal().getPrincipalRef(), pval);
if (pu != null) {
chg.addValue(PropertyInfoIndex.XPROP, pu);
}
}
break;
case UID:
break;
case URL:
if (chg.changed(pi, ev.getLink(), pval)) {
ev.setLink(pval);
}
break;
case XPROP:
/* ------------------------- x-property --------------------------- */
final String name = prop.getName();
if (name.equalsIgnoreCase(BwXproperty.bedeworkCost)) {
if (chg.changed(PropertyInfoIndex.COST, ev.getCost(), pval)) {
ev.setCost(pval);
}
break;
}
if (name.equalsIgnoreCase(BwXproperty.xBedeworkCategories)) {
if (checkCategory(cb, chg, ev, null, pval)) {
break;
}
}
if (name.equalsIgnoreCase(BwXproperty.xBedeworkLocation)) {
if (checkLocation(cb, chg, ev, prop)) {
break;
}
}
if (name.equalsIgnoreCase(BwXproperty.xBedeworkContact)) {
if (checkContact(cb, chg, ev, null, pval)) {
break;
}
}
/* See if this is an x-category that can be
converted to a real category
*/
final XProperty xp = (XProperty) prop;
chg.addValue(PropertyInfoIndex.XPROP, new BwXproperty(name, xp.getParameters().toString(), pval));
break;
default:
if (debug) {
debugMsg("Unsupported property with index " + pi + "; class " + prop.getClass() + " and value " + pval);
}
}
}
if (val instanceof VAvailability) {
processAvailable(cb, cal, ical, (VAvailability) val, evinfo);
} else if (!(val instanceof Available)) {
VAlarmUtil.processComponentAlarms(cb, val, ev, currentPrincipal, chg);
if (val instanceof VPoll) {
processVvoters((VPoll) val, evinfo, cb, chg, mergeAttendees);
processCandidates((VPoll) val, evinfo, chg);
}
}
/* Fix up timestamps. */
if (ev.getCreated() == null) {
if (ev.getLastmod() != null) {
ev.setCreated(ev.getLastmod());
chg.changed(PropertyInfoIndex.CREATED, null, ev.getCreated());
} else {
ev.updateDtstamp();
chg.changed(PropertyInfoIndex.CREATED, null, ev.getCreated());
chg.changed(PropertyInfoIndex.LAST_MODIFIED, null, ev.getLastmod());
}
}
if (ev.getLastmod() == null) {
// created cannot be null now
ev.setLastmod(ev.getCreated());
chg.changed(PropertyInfoIndex.LAST_MODIFIED, null, ev.getLastmod());
}
processTimezones(ev, ical, chg);
/* Remove any recipients and originator
*/
if (ev.getRecipients() != null) {
ev.getRecipients().clear();
}
ev.setOriginator(null);
if (hasXparams.value) {
/* Save a text copy of the entire event as an x-property */
Component valCopy = val.copy();
/* Remove potentially large values */
prop = valCopy.getProperty(Property.DESCRIPTION);
if (prop != null) {
prop.setValue(null);
}
prop = valCopy.getProperty(Property.ATTACH);
// Don't store the entire attachment - we just need the parameters.
if (prop != null) {
Value v = (Value) prop.getParameter(Parameter.VALUE);
if (v != null) {
prop.setValue(String.valueOf(prop.getValue().hashCode()));
}
}
chg.addValue(PropertyInfoIndex.XPROP, new BwXproperty(BwXproperty.bedeworkIcal, null, valCopy.toString()));
}
chg.processChanges(ev, true);
ev.setRecurring(new Boolean(ev.isRecurringEntity()));
if (debug) {
debugMsg(chg.toString());
debugMsg(ev.toString());
}
if (masterEI != null) {
// Just return null as this event is on its override list
return null;
}
return evinfo;
} catch (CalFacadeException cfe) {
if (debug) {
cfe.printStackTrace();
}
throw cfe;
} catch (Throwable t) {
if (debug) {
t.printStackTrace();
}
throw new CalFacadeException(t);
}
}
Aggregations