package jfxtras.scene.control.agenda.icalendar.editors.revisors;
import java.time.DateTimeException;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAmount;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javafx.util.Callback;
import javafx.util.Pair;
import jfxtras.icalendarfx.VCalendar;
import jfxtras.icalendarfx.VChild;
import jfxtras.icalendarfx.components.VDisplayable;
import jfxtras.icalendarfx.properties.VPropertyElement;
import jfxtras.icalendarfx.properties.component.recurrence.RecurrenceRule;
import jfxtras.icalendarfx.properties.component.recurrence.rrule.RecurrenceRuleValue;
import jfxtras.icalendarfx.properties.component.recurrence.rrule.byxxx.ByDay;
import jfxtras.icalendarfx.properties.component.relationship.RecurrenceId;
import jfxtras.icalendarfx.properties.component.relationship.UniqueIdentifier;
import jfxtras.icalendarfx.properties.component.time.DateTimeStart;
import jfxtras.icalendarfx.utilities.DateTimeUtilities;
import jfxtras.scene.control.agenda.icalendar.editors.ChangeDialogOption;
/**
* Handles revising one or all recurrences of a {@link VDisplayable}
*
* @author David Bal
*
* @param <T> concrete implementation of this class
* @param <U> concrete {@link VDisplayable} class
*/
public abstract class ReviserDisplayable<T, U extends VDisplayable<U>> implements Reviser
{
public ReviserDisplayable(U vComponent)
{
setVComponentCopyEdited(vComponent);
}
/*
* VCOMPONENT EDITED
*/
/** Gets the value of the edited {@link VDisplayable} copy. Note: don't pass original or
* the changes will be instantaneous and cancel is not possible. */
public U getVComponentCopyEdited() { return vComponentEditedCopy; }
private U vComponentEditedCopy;
/** Sets the value of the edited {@link VDisplayable} copy. Note: don't pass original or
* the changes will be instantenous and cancel is not possible. */
public void setVComponentCopyEdited(U vComponentEdited) { this.vComponentEditedCopy = vComponentEdited; }
/**
* Sets the value of the edited {@link VDisplayable}
*
* @return - this class for chaining
* @see VCalendar
*/
public T withVComponentCopyEdited(U vComponentEdited) { setVComponentCopyEdited(vComponentEdited); return (T) this; }
/*
* VCOMPONENT ORIGINAL
*/
/** Gets the value of the original {@link VDisplayable} */
public U getVComponentOriginal() { return vComponentOriginal; }
private U vComponentOriginal;
/** Sets the value of the original {@link VDisplayable} */
public void setVComponentOriginal(U vComponentOriginal) { this.vComponentOriginal = vComponentOriginal; }
/**
* Sets the value of the edited {@link VDisplayable}
*
* @return - this class for chaining
* @see VCalendar
*/
public T withVComponentOriginal(U vComponentOriginal) { setVComponentOriginal(vComponentOriginal); return (T) this; }
/*
* START ORIGINAL RECURRENCE
*/
/** Gets the value of the start of the selected recurrence, before changes */
public Temporal getStartOriginalRecurrence() { return startOriginalRecurrence; }
private Temporal startOriginalRecurrence;
/** Sets the value of the start of the selected recurrence, before changes */
public void setStartOriginalRecurrence(Temporal startOriginalRecurrence) { this.startOriginalRecurrence = startOriginalRecurrence; }
/**
* Sets the value of the start of the selected recurrence, before changes
*
* @return - this class for chaining
*/
public T withStartOriginalRecurrence(Temporal startOriginalRecurrence) { setStartOriginalRecurrence(startOriginalRecurrence); return (T) this; }
/*
* START RECURRENCE - NEW VALUE
*/
/** Gets the value of the start of the selected recurrence, after changes */
public Temporal getStartRecurrence() { return startRecurrence; }
private Temporal startRecurrence;
/** Sets the value of the start of the selected recurrence, after changes */
public void setStartRecurrence(Temporal startRecurrence) { this.startRecurrence = startRecurrence; }
/**
* Sets the value of the start of the selected recurrence, after changes
*
* @return - this class for chaining
*/
public T withStartRecurrence(Temporal startRecurrence) { setStartRecurrence(startRecurrence); return (T) this; }
/*
* CHANGE DIALOG CALLBACK
*/
/** Gets the value of the dialog callback to prompt the user to select revision option */
public Callback<Map<ChangeDialogOption, Pair<Temporal,Temporal>>, ChangeDialogOption> getDialogCallback() { return dialogCallback; }
private Callback<Map<ChangeDialogOption, Pair<Temporal,Temporal>>, ChangeDialogOption> dialogCallback;
/** Sets the value of the dialog callback to prompt the user to select revision option */
public void setDialogCallback(Callback<Map<ChangeDialogOption, Pair<Temporal,Temporal>>, ChangeDialogOption> dialogCallback) { this.dialogCallback = dialogCallback; }
/**
* Sets the value of the dialog callback to prompt the user to select revision option
*
* @return - this class for chaining
*/
public T withDialogCallback(Callback<Map<ChangeDialogOption, Pair<Temporal,Temporal>>, ChangeDialogOption> dialogCallback)
{
setDialogCallback(dialogCallback);
return (T) this;
}
/** Tests the object state is valid and revision can proceed. Returns true if valid, false otherwise */
boolean isValid()
{
if (getVComponentCopyEdited() == null)
{
System.out.println("vComponentEdited must not be null");
return false;
}
if (getVComponentOriginal() == null)
{
System.out.println("vComponentOriginal must not be null");
return false;
}
if (getStartOriginalRecurrence() == null)
{
System.out.println("startOriginalRecurrence must not be null");
return false;
}
if (getStartRecurrence() == null)
{
System.out.println("startRecurrence must not be null");
return false;
}
if (getDialogCallback() == null)
{
System.out.println("dialogCallback must not be null");
return false;
}
return true;
}
@Override
public List<VCalendar> revise()
{
if (! isValid())
{
throw new RuntimeException("Invalid parameters for component revision:");
}
U vComponentEditedCopy = getVComponentCopyEdited();
// // Copy edited component for further changes (i.e. UID, date/time)
// U vComponentEditedCopy = null;
// try
// {
// vComponentEditedCopy = (U) getVComponentEdited().getClass().newInstance();
//// vComponentEditedCopy.copyFrom(getVComponentEdited());
// getVComponentEdited().copyInto(vComponentEditedCopy);
// } catch (InstantiationException | IllegalAccessException e)
// {
// e.printStackTrace();
// }
U vComponentOriginal = getVComponentOriginal();
Temporal startRecurrence = getStartRecurrence();
Temporal startOriginalRecurrence = getStartOriginalRecurrence();
if (! vComponentOriginal.isValid())
{
throw new RuntimeException("Can't revise. Original component is invalid:" + System.lineSeparator() +
vComponentEditedCopy.errors().stream().collect(Collectors.joining(System.lineSeparator())) + System.lineSeparator() +
vComponentEditedCopy);
}
List<VCalendar> itipMessages = new ArrayList<>();
validateStartRecurrenceAndDTStart(vComponentEditedCopy, getStartRecurrence());
final RRuleStatus rruleType = RRuleStatus.getRRuleType(vComponentOriginal.getRecurrenceRule(), vComponentEditedCopy.getRecurrenceRule());
boolean incrementSequence = true;
switch (rruleType)
{
case HAD_REPEAT_BECOMING_INDIVIDUAL:
becomeNonRecurring(vComponentEditedCopy);
// fall through
case WITH_NEW_REPEAT: // no dialog
case INDIVIDUAL:
{
VCalendar message;
if (vComponentEditedCopy.getParent() == null)
{
message = Reviser.emptyPublishiTIPMessage();
incrementSequence = false;
} else
{
message = Reviser.emptyRequestiTIPMessage();
}
adjustStartAndEnd(vComponentEditedCopy, vComponentOriginal);
vComponentEditedCopy.setDateTimeStamp(ZonedDateTime.now().withZoneSameInstant(ZoneId.of("Z")));
message.addChild(vComponentEditedCopy);
itipMessages.add(message);
break;
}
case WITH_EXISTING_REPEAT:
// Find which properties changed
Collection<VPropertyElement> changedProperties = findChangedProperties(vComponentEditedCopy, vComponentOriginal);
// System.out.println("changedProperties:" + changedProperties);
/* Note:
* time properties must be checked separately because changes are stored in startRecurrence and endRecurrence,
* not the VComponents DTSTART and DTEND yet. The changes to DTSTART and DTEND are made after the dialog
* question is answered. */
// determine if any changed properties warrant dialog
boolean provideDialog = changedProperties.stream()
.map(p -> dialogRequiredProperties().contains(p))
.anyMatch(b -> b == true);
if (changedProperties.size() > 0) // if changes occurred
{
// List<U> relatedVComponents = Arrays.asList(vComponentEditedCopy); // TODO - possibly support related components
final ChangeDialogOption changeResponse;
if (provideDialog)
{
Map<ChangeDialogOption, Pair<Temporal,Temporal>> choices = ChangeDialogOption.makeDialogChoices(
vComponentOriginal,
vComponentEditedCopy,
startOriginalRecurrence,
changedProperties);
changeResponse = dialogCallback.call(choices);
} else
{
changeResponse = ChangeDialogOption.ALL;
}
switch (changeResponse)
{
case ALL:
{
adjustDateTime(vComponentEditedCopy);
VCalendar requestMessage = Reviser.emptyRequestiTIPMessage();
requestMessage.addChild(vComponentEditedCopy);
itipMessages.add(requestMessage);
// Note: Child recurrences become orphans and get deleted when the iTIP message is processed
break;
}
case ALL_IGNORE_RECURRENCES:
{
adjustDateTime(vComponentEditedCopy);
VCalendar requestMessage = Reviser.emptyRequestiTIPMessage();
requestMessage.addChild(vComponentEditedCopy);
itipMessages.add(requestMessage);
// update child recurrences
VCalendar publishMessage = Reviser.emptyPublishiTIPMessage();
List<VDisplayable<?>> children = adjustRecurrenceChildren(startRecurrence, startOriginalRecurrence);
children.forEach((c) -> publishMessage.addChild(c));
itipMessages.add(publishMessage);
break;
}
case CANCEL:
break;
case THIS_AND_FUTURE:
{
// change edited original and this-and-future components
U thisAndFutureVComponent = editThisAndFuture(vComponentEditedCopy, vComponentOriginal);
// request message to change original component
VCalendar requestMessage = Reviser.emptyRequestiTIPMessage();
requestMessage.addChild(thisAndFutureVComponent);
itipMessages.add(requestMessage);
// publish message to add new the-and-future component
VCalendar publishMessage = Reviser.emptyPublishiTIPMessage();
publishMessage.addChild(vComponentEditedCopy);
thisAndFutureVComponent.incrementSequence();
incrementSequence = false;
itipMessages.add(publishMessage);
// Note: Child recurrences become orphans and get deleted when the iTIP message is processed
break;
}
case THIS_AND_FUTURE_IGNORE_RECURRENCES:
{
// change edited original and this-and-future components
U thisAndFutureVComponent = editThisAndFuture(vComponentEditedCopy, vComponentOriginal);
// request message to change original component
VCalendar requestMessage = Reviser.emptyRequestiTIPMessage();
requestMessage.addChild(thisAndFutureVComponent);
itipMessages.add(requestMessage);
// publish message to add new the-and-future component
VCalendar publishMessage = Reviser.emptyPublishiTIPMessage();
publishMessage.addChild(vComponentEditedCopy);
thisAndFutureVComponent.incrementSequence();
incrementSequence = false;
itipMessages.add(publishMessage);
// process child recurrences (new recurrences made if after future edit)
String uid = vComponentEditedCopy.getUniqueIdentifier().getValue();
List<VDisplayable<?>> children = adjustRecurrenceChildren(startRecurrence, startOriginalRecurrence)
.stream()
.filter(c -> DateTimeUtilities.TEMPORAL_COMPARATOR2.compare(c.getDateTimeStart().getValue(), startOriginalRecurrence) >= 0) // is after or equals
.peek(c ->
{
c.setUniqueIdentifier(uid);
c.setDateTimeStamp(ZonedDateTime.now().withZoneSameInstant(ZoneId.of("Z")));
})
.collect(Collectors.toList());
children.forEach((c) -> publishMessage.addChild(c));
break;
}
case ONE:
{
editOne(vComponentEditedCopy);
VCalendar message = Reviser.emptyPublishiTIPMessage();
message.addChild(vComponentEditedCopy);
itipMessages.add(message);
break;
}
default:
throw new RuntimeException("Unsupprted response:" + changeResponse);
}
}
}
if (incrementSequence)
{
vComponentEditedCopy.incrementSequence();
}
// if (! vComponentEditedCopy.isValid())
// {
// throw new RuntimeException("Invalid component:" + System.lineSeparator() +
// vComponentEditedCopy.errors().stream().collect(Collectors.joining(System.lineSeparator())) + System.lineSeparator() +
// vComponentEditedCopy.toContent());
// }
return itipMessages;
}
// Make a copy of child recurrences, apply time shift, and return them as a List
private List<VDisplayable<?>> adjustRecurrenceChildren(Temporal startRecurrence, Temporal startOriginalRecurrence)
{
return getVComponentOriginal().recurrenceChildren()
.stream()
.map(v ->
{
Period dayShift = Period.between(LocalDate.from(startOriginalRecurrence), (LocalDate.from(v.getRecurrenceId().getValue())));
Temporal newRecurreneId = startRecurrence.plus(dayShift);
try
{
VDisplayable<?> vCopy = v.getClass().newInstance();
v.copyChildrenInto(vCopy);
vCopy.setRecurrenceId(new RecurrenceId(newRecurreneId));
return vCopy;
} catch (Exception e)
{
e.printStackTrace();
return null;
}
})
.collect(Collectors.toList());
}
/** If startRecurrence isn't valid due to a RRULE change, change startRecurrence and
* endRecurrence to closest valid values
*/
// TODO - VERITFY THIS WORKS - changed from old version
void validateStartRecurrenceAndDTStart(U vComponentEditedCopy, Temporal startRecurrence)
{
if (vComponentEditedCopy.getRecurrenceRule() != null)
{
Temporal firstTemporal = vComponentEditedCopy.getRecurrenceRule().getValue()
.streamRecurrences(vComponentEditedCopy.getDateTimeStart().getValue())
.findFirst()
.get();
if (! firstTemporal.equals(vComponentEditedCopy.getDateTimeStart().getValue()))
{
vComponentEditedCopy.setDateTimeStart(firstTemporal);
}
}
}
/** Make changes necessary for making a repeating component into a non-repeating component */
void becomeNonRecurring(U vComponentEditedCopy)
{
vComponentEditedCopy.setRecurrenceRule((RecurrenceRuleValue) null);
vComponentEditedCopy.setRecurrenceDates(null);
vComponentEditedCopy.setExceptionDates(null);
}
/** Adjust start date/time according to changes in selected recurrence */
void adjustDateTime(U vComponentEditedCopy)
{
// TODO - DescriptiveVBox needs to keep zone - what does this mean?
TemporalAmount shiftAmount = DateTimeUtilities.temporalAmountBetween(getStartOriginalRecurrence(), getStartRecurrence());
TemporalAmount amountToStart = DateTimeUtilities.temporalAmountBetween(vComponentEditedCopy.getDateTimeStart().getValue(), getStartRecurrence());
Temporal newStart = getStartRecurrence().minus(amountToStart).plus(shiftAmount);
// handle WEEKLY day of week change
if (vComponentEditedCopy.getRecurrenceRule() != null)
{
ByDay byDay = (ByDay) vComponentEditedCopy.getRecurrenceRule()
.getValue()
.lookupByRule(ByDay.class);
if (byDay != null)
{
DayOfWeek originalDayOfWeek = DayOfWeek.from(vComponentEditedCopy.getDateTimeStart().getValue());
DayOfWeek replacemenDayOfWeekt = DayOfWeek.from(newStart);
if (originalDayOfWeek != replacemenDayOfWeekt)
{ // day shift occurred - change ByDay rule
byDay.replaceDayOfWeek(originalDayOfWeek, replacemenDayOfWeekt);
}
}
}
vComponentEditedCopy.setDateTimeStart(new DateTimeStart(newStart));
vComponentEditedCopy.setDateTimeStamp(ZonedDateTime.now().withZoneSameInstant(ZoneId.of("Z")));
}
/**
* Generates a list of {@link VPropertyElement} that have changed between the original
* and edited VComponents.
*
* @param vComponentEditedCopy edited VComponent
* @param vComponentOriginalCopy original VComponent
* @return list of changed {@link VPropertyElement}
*/
Collection<VPropertyElement> findChangedProperties(U vComponentEditedCopy, U vComponentOriginalCopy)
{
Map<String, VChild> editedMap = vComponentEditedCopy.childrenUnmodifiable()
.stream()
.collect(Collectors.toMap(v -> v.name(), v -> v));
Map<String, VChild> originalMap = vComponentOriginalCopy.childrenUnmodifiable()
.stream()
.collect(Collectors.toMap(v -> v.name(), v -> v));
List<VPropertyElement> c1 = editedMap.entrySet().stream()
.filter(e ->
{
String key = e.getKey();
VChild edited = e.getValue();
VChild original = originalMap.get(key);
// System.out.println("EO1:" + edited + " " + original);
return ! Objects.equals(edited, original);
})
.map(e -> e.getValue())
.map(v -> VPropertyElement.fromClass(v.getClass()))
.collect(Collectors.toList());
List<VPropertyElement> c2 = originalMap.entrySet().stream()
.filter(e ->
{
String key = e.getKey();
VChild original = e.getValue();
VChild edited = editedMap.get(key);
// System.out.println("EO2:" + edited + " " + original);
return ! Objects.equals(edited, original);
})
.map(e -> e.getValue())
.map(v -> VPropertyElement.fromClass(v.getClass()))
.collect(Collectors.toList());
Set<VPropertyElement> changedChildern = new HashSet<>();
changedChildern.addAll(c1);
changedChildern.addAll(c2);
if (! startOriginalRecurrence.equals(startRecurrence))
{
changedChildern.add(VPropertyElement.DATE_TIME_START);
}
// System.out.println("changedChildern:" + changedChildern);
return changedChildern;
}
/**
* Returned list of {@link VPropertyElement} values, that when changed, necessitate a user dialog to determine scope of change.
* If changes do not contain ANY {@link VPropertyElement} in the returned list then changes can proceed automatically
* without a user dialog.
*
* @return {@code List<VPropertyElement>} that when any are changed require a user dialog to request scope of change
* (e.g. ONE, ALL or THIS_AND_FUTURE)
*/
public List<VPropertyElement> dialogRequiredProperties()
{
return new ArrayList<>(Arrays.asList(
VPropertyElement.ATTACHMENT,
VPropertyElement.ATTENDEE,
VPropertyElement.CATEGORIES,
VPropertyElement.COMMENT,
VPropertyElement.CONTACT,
VPropertyElement.DATE_TIME_START,
VPropertyElement.RECURRENCE_RULE,
VPropertyElement.STATUS,
VPropertyElement.SUMMARY,
VPropertyElement.UNIFORM_RESOURCE_LOCATOR
));
}
/**
* ONE
*
* Edit one instance of a VEvent with a RRule. The instance becomes a new VEvent without a RRule
* as with the same UID as the parent and a recurrence-id for the replaced date or date/time.
*
*/
void editOne(U vComponentEditedCopy)
{
vComponentEditedCopy.setRecurrenceRule((RecurrenceRule) null);
vComponentEditedCopy.setDateTimeStart(new DateTimeStart(getStartRecurrence()));
vComponentEditedCopy.setRecurrenceId(startOriginalRecurrence);
vComponentEditedCopy.setDateTimeStamp(ZonedDateTime.now().withZoneSameInstant(ZoneId.of("Z")));
}
/**
* THIS AND FUTURE
*
* Handles changing this-and-future recurrences in a VComponent by ending the original
* VComponent with a UNTIL date or date/time, then starting a new VComponent from
* the selected recurrence. EXDATE, RDATE and RECURRENCES are split between both
* VComponents.
*
* @param vComponentEditedCopy VComponent with changes
* @param vComponentOriginalCopy Unchanged VComponent, except for addition of the UNTIL element.
*/
U editThisAndFuture(U vComponentEditedCopy, U vComponentOriginal)
{
// Copy original component for further changes
U thisAndFutureVComponent = null;
try
{
thisAndFutureVComponent = (U) getVComponentOriginal().getClass().newInstance();
getVComponentOriginal().copyChildrenInto(thisAndFutureVComponent);
} catch (InstantiationException | IllegalAccessException e)
{
e.printStackTrace();
}
// Reset COUNT, set UNTIL
if (thisAndFutureVComponent.getRecurrenceRule().getValue().getCount() != null)
{
thisAndFutureVComponent.getRecurrenceRule().getValue().setCount(null);
}
/*
* Assigning UNTIL must be done before adjusting the start and end or the previousStreamValue
* will not be valid.
*/
final Temporal untilNew;
if (vComponentEditedCopy.isWholeDay())
{
untilNew = vComponentEditedCopy.previousStreamValue(getStartOriginalRecurrence());
} else
{
Temporal previousRecurrence = vComponentEditedCopy.previousStreamValue(getStartOriginalRecurrence());
if (getStartOriginalRecurrence() instanceof LocalDateTime)
{
untilNew = LocalDateTime.from(previousRecurrence).atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("Z"));
} else if (getStartOriginalRecurrence() instanceof ZonedDateTime)
{
untilNew = ((ZonedDateTime) previousRecurrence).withZoneSameInstant(ZoneId.of("Z"));
} else
{
throw new DateTimeException("Unsupported Temporal type:" + previousRecurrence.getClass());
}
}
thisAndFutureVComponent.getRecurrenceRule().getValue().setUntil(untilNew);
// Adjust start and end - set recurrence temporal as start
adjustStartAndEnd(vComponentEditedCopy, thisAndFutureVComponent);
String relatedUID = (thisAndFutureVComponent.getRelatedTo() == null) ?
thisAndFutureVComponent.getUniqueIdentifier().getValue() : thisAndFutureVComponent.getRelatedTo().get(0).getValue();
vComponentEditedCopy.withRelatedTo(relatedUID);
vComponentEditedCopy.setDateTimeStamp(ZonedDateTime.now().withZoneSameInstant(ZoneId.of("Z")));
// remove EXDATEs that are out of bounds
if (vComponentEditedCopy.getExceptionDates() != null)
{
final Iterator<Temporal> exceptionDateIterator = vComponentEditedCopy.getExceptionDates()
.stream()
.flatMap(e -> e.getValue().stream())
.iterator();
while (exceptionDateIterator.hasNext())
{
Temporal t = exceptionDateIterator.next();
int result = DateTimeUtilities.TEMPORAL_COMPARATOR.compare(t, getStartRecurrence());
if (result < 0)
{
exceptionDateIterator.remove();
}
}
}
if (thisAndFutureVComponent.getExceptionDates() != null)
{
final Iterator<Temporal> exceptionDateIterator = thisAndFutureVComponent.getExceptionDates()
.stream()
.flatMap(e -> e.getValue().stream())
.iterator();
while (exceptionDateIterator.hasNext())
{
Temporal t = exceptionDateIterator.next();
int result = DateTimeUtilities.TEMPORAL_COMPARATOR.compare(t, getStartRecurrence());
if (result > 0)
{
exceptionDateIterator.remove();
}
}
}
// remove RDATEs that are out of bounds
if (vComponentEditedCopy.getRecurrenceDates() != null)
{
final Iterator<Temporal> recurrenceDateIterator = vComponentEditedCopy.getRecurrenceDates()
.stream()
.flatMap(e -> e.getValue().stream())
.iterator();
while (recurrenceDateIterator.hasNext())
{
Temporal t = recurrenceDateIterator.next();
int result = DateTimeUtilities.TEMPORAL_COMPARATOR.compare(t, getStartRecurrence());
if (result < 0)
{
recurrenceDateIterator.remove();
}
}
}
if (thisAndFutureVComponent.getRecurrenceDates() != null)
{
final Iterator<Temporal> recurrenceDateIterator = thisAndFutureVComponent.getRecurrenceDates()
.stream()
.flatMap(e -> e.getValue().stream())
.iterator();
while (recurrenceDateIterator.hasNext())
{
Temporal t = recurrenceDateIterator.next();
int result = DateTimeUtilities.TEMPORAL_COMPARATOR.compare(t, getStartRecurrence());
if (result > 0)
{
recurrenceDateIterator.remove();
}
}
}
vComponentEditedCopy.setUniqueIdentifier(); // assign new UID
// Modify COUNT for the edited vEvent
if (vComponentEditedCopy.getRecurrenceRule().getValue().getCount() != null)
{
int countInOrginal = (int) thisAndFutureVComponent.streamRecurrences().count();
int countInNew = vComponentEditedCopy.getRecurrenceRule().getValue().getCount().getValue() - countInOrginal;
vComponentEditedCopy.getRecurrenceRule().getValue().setCount(countInNew);
}
if (! thisAndFutureVComponent.isValid())
{
throw new RuntimeException("Invalid component:" + System.lineSeparator() +
thisAndFutureVComponent.errors().stream().collect(Collectors.joining(System.lineSeparator())) + System.lineSeparator() +
thisAndFutureVComponent.toString());
}
return thisAndFutureVComponent;
}
/** Handle changing the {@link UniqueIdentifier} of recurrence children, if any exist,
* to match the {@link UniqueIdentifier} of the revised VComponent */
@Deprecated
private void thisAndFutureIgnoreRecurrences(List<U> revisedVComponents, U vComponentEditedCopy)
{
List<VDisplayable<?>> recurrenceChildren = getVComponentCopyEdited().recurrenceChildren();
if (! recurrenceChildren.isEmpty())
{
recurrenceChildren.stream().forEach(c ->
{
Temporal t = c.getRecurrenceId().getValue();
boolean isAfterStartRecurrence = DateTimeUtilities.TEMPORAL_COMPARATOR2.compare(t, getStartRecurrence()) > 0;
if (isAfterStartRecurrence)
{ // change UID to match vComponentEditedCopy
// getVComponents().remove(c);
String uniqueIdentifier = vComponentEditedCopy.getUniqueIdentifier().getValue();
c.setUniqueIdentifier(uniqueIdentifier);
revisedVComponents.add((U) c);
}
});
}
}
void adjustStartAndEnd(U vComponentEditedCopy, U vComponentOriginalCopy)
{
// TODO - SHOULD THIS BE MERGED WITH adjustDateTime(U vComponentEditedCopy)?
// no op hook, override in subclasses
}
/**
* Enum that identifies the VComponent's recurrence rule status
*
* @author David Bal
*
*/
enum RRuleStatus
{
INDIVIDUAL ,
WITH_EXISTING_REPEAT ,
WITH_NEW_REPEAT,
HAD_REPEAT_BECOMING_INDIVIDUAL;
public static RRuleStatus getRRuleType(RecurrenceRule rruleEdited, RecurrenceRule rruleOriginal)
{
if (rruleOriginal == null)
{
if (rruleEdited == null)
{ // edited doesn't have repeat or original have repeat either
return RRuleStatus.INDIVIDUAL;
} else {
return RRuleStatus.HAD_REPEAT_BECOMING_INDIVIDUAL;
}
} else
{ // RRule != null
if (rruleEdited == null)
{
return RRuleStatus.WITH_NEW_REPEAT;
} else
{
return RRuleStatus.WITH_EXISTING_REPEAT;
}
}
}
}
}