jfxtras.icalendarfx.itip.ProcessPublish Maven / Gradle / Ivy
package jfxtras.icalendarfx.itip;
import java.time.temporal.Temporal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import jfxtras.icalendarfx.VCalendar;
import jfxtras.icalendarfx.components.VComponent;
import jfxtras.icalendarfx.components.VDisplayable;
import jfxtras.icalendarfx.components.VEvent;
import jfxtras.icalendarfx.components.VTimeZone;
import jfxtras.icalendarfx.properties.calendar.Method.MethodType;
import jfxtras.icalendarfx.properties.component.relationship.Attendee;
import jfxtras.icalendarfx.properties.component.relationship.Organizer;
import jfxtras.icalendarfx.properties.component.relationship.UniqueIdentifier;
/**
*
* 3.2.1. PUBLISH
The {@link MethodType#Publish PUBLISH} method in a {@link VEvent VEVENT} calendar component is an
unsolicited posting of an iCalendar object. Any CU may add published
components to their calendar. The {@link Organizer} MUST be present in a
published iCalendar component. {@link Attendee Attendees} MUST NOT be present. Its
expected usage is for encapsulating an arbitrary event as an
iCalendar object. The {@link Organizer} may subsequently update (with
another {@link MethodType#Publish PUBLISH} method), add instances to (with an {@link MethodType#Add ADD} method),
or cancel (with a {@link MethodType#Cancel CANCEL} method) a previously published {@link VEvent VEVENT}
calendar component.
This method type is an iCalendar object that conforms to the
following property constraints:
+----------------------------------------------+
| Constraints for a METHOD:PUBLISH of a VEVENT |
+----------------------------------------------+
+--------------------+----------+-----------------------------------+
| Component/Property | Presence | Comment |
+--------------------+----------+-----------------------------------+
| METHOD | 1 | MUST equal PUBLISH. |
| | | |
| VEVENT | 1+ | |
| DTSTAMP | 1 | |
| DTSTART | 1 | |
| ORGANIZER | 1 | |
| SUMMARY | 1 | Can be null. |
| UID | 1 | |
| RECURRENCE-ID | 0 or 1 | Only if referring to an instance |
| | | of a recurring calendar |
| | | component. Otherwise, it MUST |
| | | NOT be present. |
| SEQUENCE | 0 or 1 | MUST be present if value is |
| | | greater than 0; MAY be present if |
| | | 0. |
| ATTACH | 0+ | |
| CATEGORIES | 0+ | |
| CLASS | 0 or 1 | |
| COMMENT | 0+ | |
| CONTACT | 0 or 1 | |
| CREATED | 0 or 1 | |
| DESCRIPTION | 0 or 1 | Can be null. |
| DTEND | 0 or 1 | If present, DURATION MUST NOT be |
| | | present. |
| DURATION | 0 or 1 | If present, DTEND MUST NOT be |
| | | present. |
| EXDATE | 0+ | |
| GEO | 0 or 1 | |
| LAST-MODIFIED | 0 or 1 | |
| LOCATION | 0 or 1 | |
| PRIORITY | 0 or 1 | |
| RDATE | 0+ | |
| RELATED-TO | 0+ | |
| RESOURCES | 0+ | |
| RRULE | 0 or 1 | |
| STATUS | 0 or 1 | MAY be one of |
| | | TENTATIVE/CONFIRMED/CANCELLED. |
| TRANSP | 0 or 1 | |
| URL | 0 or 1 | |
| IANA-PROPERTY | 0+ | |
| X-PROPERTY | 0+ | |
| ATTENDEE | 0 | |
| REQUEST-STATUS | 0 | |
| | | |
| VALARM | 0+ | |
| | | |
| VFREEBUSY | 0 | |
| | | |
| VJOURNAL | 0 | |
| | | |
| VTODO | 0 | |
| | | |
| VTIMEZONE | 0+ | MUST be present if any date/time |
| | | refers to a timezone. |
| | | |
| IANA-COMPONENT | 0+ | |
| X-COMPONENT | 0+ | |
+--------------------+----------+-----------------------------------+
* @author David Bal
*/
public class ProcessPublish implements Processable
{
@Override
public List process(VCalendar mainVCalendar, VCalendar iTIPMessage)
{
List log = new ArrayList<>();
iTIPMessage.childrenUnmodifiable()
.stream()
.filter(c -> c instanceof VComponent)
.map(c -> (VComponent) c)
.forEach(c ->
{
if (c instanceof VDisplayable)
{
VDisplayable> vDisplayable = ((VDisplayable>) c);
final boolean hasOrganizer = vDisplayable.getOrganizer() != null;
// TODO - LOG DEFECTS - DON'T THROW EXCEPTIONS
if (! hasOrganizer) log.add("WARNING: According to RFC 5546, a PUBLISH method MUST contain the ORGANIZER property and it's absent. " + c.getClass().getSimpleName() + " with UID:" + vDisplayable.getUniqueIdentifier().getValue() + " is being processed anyway.");
final boolean hasNoAttendees = vDisplayable.getAttendees() == null;
if (! hasNoAttendees) log.add("WARNING: According to RFC 5546, a PUBLISH MUST NOT contain the ATTENDEE property yet it's exists. " + c.getClass().getSimpleName() + " with UID:" + vDisplayable.getUniqueIdentifier().getValue() + " is being processed anyway.");
final int newSequence = (vDisplayable.getSequence() == null) ? 0 : vDisplayable.getSequence().getValue();
boolean isNewSequenceHigher = true;
UniqueIdentifier uid = vDisplayable.getUniqueIdentifier();
final List> relatedVComponents;
List extends VComponent> list = mainVCalendar.getVComponents(vDisplayable);
if (list == null)
{
relatedVComponents = null;
} else
{
relatedVComponents = mainVCalendar.getVComponents(vDisplayable)
.stream()
.filter(v -> v instanceof VDisplayable)
.map(v -> (VDisplayable>) v)
.filter(v -> v.getUniqueIdentifier().equals(uid))
.collect(Collectors.toList());
}
final Temporal recurrenceID = (vDisplayable.getRecurrenceId() != null) ? vDisplayable.getRecurrenceId().getValue() : null;
// check for previous match to remove it
if (relatedVComponents != null)
{
/* if new has recurrence id:
* if old has matching recurrence-id then replace it
* if old doesn't have recurrence-id, add component as new recurrence
* if new doesn't have recurrence id, but match exists, replace match
* If no match then just add - can't have recurrence id
*/
VDisplayable> oldMatchingVComponent = relatedVComponents
.stream()
.filter(v -> {
Temporal mRecurrenceID = (v.getRecurrenceId() != null) ? v.getRecurrenceId().getValue() : null;
return Objects.equals(recurrenceID, mRecurrenceID);
})
.findAny()
.orElseGet(() -> null);
if (oldMatchingVComponent != null)
{
int oldSequence = (oldMatchingVComponent.getSequence() == null) ? 0 : oldMatchingVComponent.getSequence().getValue();
isNewSequenceHigher = newSequence > oldSequence || (newSequence == 0 && oldSequence == 0);
// SEQUENCE INCREASE REQUIREMENT ONLY APPLIES FOR PARENTS - NOT RECURRENCES THAT ONLY HAVE RECURRENCE-ID CHANGED
if (! isNewSequenceHigher) throw new IllegalArgumentException("Can't process PUBLISH method: SEQUENCY property MUST be higher than previously published component (new=" + newSequence + " old=" + oldSequence + ")");
if (isNewSequenceHigher)
{
mainVCalendar.removeChild(oldMatchingVComponent);
}
}
}
mainVCalendar.addChild(c);
log.add("SUCCESS: added " + c.getClass().getSimpleName() + " with UID:" + vDisplayable.getUniqueIdentifier().getValue());
// Remove orphaned recurrence children
// TODO - IS THIS NECESSARY???
List> orphanedChildren = vDisplayable.orphanedRecurrenceChildren();
if (! orphanedChildren.isEmpty())
{
vDisplayable.calendarList().removeAll(orphanedChildren);
}
} else if (c instanceof VTimeZone)
{
mainVCalendar.getVTimeZones().add((VTimeZone) c);
} else
{ // non-displayable VComponents (only VFREEBUSY has UID)
log.add("Can't process non-displayble component method (not implemented):" + System.lineSeparator() + c.toString());
}
});
// System.out.println("log:" + log);
return log;
}
}