Interface AgendaScheduleConflictService


public interface AgendaScheduleConflictService
Finds the clashes on a user's own calendar over a window.

This exists because overlap detection is interval arithmetic, and interval arithmetic belongs in code that can be tested rather than in a caller — an AI prompt, a screen, a report — that has to be talked into getting it right every time. The one error this service exists to make impossible is adjacency read as a clash: a 10:00 - 11:00 meeting followed by an 11:00 - 12:00 meeting is a normal morning, and reporting it as a double booking is how a schedule report loses its reader.

What counts as a conflict

Two events conflict when they strictly overlap: a.start < b.end && b.start < a.end. Touching boundaries do not overlap. Groups are transitive — see ScheduleConflict.

What is deliberately not a conflict

  • An event the user has declined. They told the organiser they are not coming, so the time is theirs; reporting it as a clash is how a report earns the habit of being ignored. This matches free/busy, where a decline does not make a user busy.
  • A cancelled event. It is not happening.
  • An event published as free time (EventAvailability.FREE) — a marker its organiser explicitly said does not occupy anyone.
  • An event with no positive duration, which cannot overlap anything under the strict rule and whose only effect would be to make grouping depend on the order equal keys happen to sort in.
An all-day event is none of those and does clash with a meeting inside its day: it occupies the day, and a user with an all-day offsite and a 10:00 call has something real to sort out. It is reported like any other event, and it is the caller's job to narrate it as what it is rather than as a double booking of the same hour.

Recurring series are seen as their occurrences. The window is read through AgendaEventService.getEvents(org.exoplatform.agenda.model.EventFilter, java.time.ZoneId, long), which materialises each occurrence falling inside it, so the Tuesday instance of a weekly stand-up is what conflicts — not the series. Those occurrences carry no identifier of their own (id is 0 and the series is in parentId), which is why nothing here keys events by their id.

Whose calendar

The asking user's, and only theirs. AgendaEventService.getEvents(org.exoplatform.agenda.model.EventFilter, java.time.ZoneId, long) refuses an attendee filter naming anyone else, so the platform's own calendar ACL is what answers here and this service adds no permission rule of its own. Someone else's schedule is a different question with a different answer — AgendaAvailabilityService, which discloses times and never titles.
  • Method Details

    • getScheduleConflicts

      ScheduleConflicts getScheduleConflicts(ZonedDateTime start, ZonedDateTime end, long userIdentityId) throws IllegalAccessException
      Reports the clashes on the asking user's own calendar over a window.
      Parameters:
      start - window start, mandatory
      end - window end, mandatory, strictly after start
      userIdentityId - identity id of the user whose calendar is read, which is also the user the read is performed as
      Returns:
      the conflict groups, each holding at least two events, ordered by the time they start; never null, and empty when nothing overlaps
      Throws:
      IllegalAccessException - when the user may not read that calendar
      IllegalArgumentException - when the window is missing, inverted or longer than this service will scan in one call