All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.primefaces.model.timeline.TimelineModel Maven / Gradle / Ivy

There is a newer version: 14.0.0
Show newest version
/*
 * The MIT License
 *
 * Copyright (c) 2009-2023 PrimeTek Informatics
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.primefaces.model.timeline;

import org.primefaces.component.timeline.TimelineUpdater;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.IntStream;

public class TimelineModel implements Serializable {

    private static final long serialVersionUID = 20130316L;

    /**
     * list of events
     */
    private List> events;

    /**
     * list of groups
     */
    private List> groups;

    public TimelineModel() {
        events = new ArrayList<>();
    }

    public TimelineModel(List> events) {
        this.events = new ArrayList<>();

        if (events != null && !events.isEmpty()) {
            for (TimelineEvent event : events) {
                add(event);
            }
        }
    }

    public TimelineModel(List> events, List> groups) {
        this(events);
        this.groups = groups;
    }

    /**
     * Adds a given event to the model without UI update.
     *
     * @param event event to be added
     */
    public void add(TimelineEvent event) {
        events.add(event);
    }

    /**
     * Adds a given group to the model.
     *
     * @param group group to be added
     */
    public void addGroup(TimelineGroup group) {
        if (groups == null) {
            groups = new ArrayList<>();
        }

        groups.add(group);
    }

    /**
     * Adds a given event to the model with UI update.
     *
     * @param event           event to be added
     * @param timelineUpdater TimelineUpdater instance to add the event in UI
     */
    public void add(TimelineEvent event, TimelineUpdater timelineUpdater) {
        events.add(event);

        if (timelineUpdater != null) {
            // update UI
            timelineUpdater.add(event);
        }
    }

    /**
     * Adds all given event to the model without UI update.
     *
     * @param events collection of events to be added
     */
    public void addAll(Collection> events) {
        addAll(events, null);
    }

    /**
     * Adds all given groups to the model.
     *
     * @param groups collection of groups to be added
     */
    public void addAllGroups(Collection> groups) {
        if (this.groups == null) {
            this.groups = new ArrayList<>();
        }

        this.groups.addAll(groups);
    }

    /**
     * Adds all given events to the model with UI update.
     *
     * @param events          collection of events to be added
     * @param timelineUpdater TimelineUpdater instance to add the events in UI
     */
    public void addAll(Collection> events, TimelineUpdater timelineUpdater) {
        if (events != null && !events.isEmpty()) {
            for (TimelineEvent event : events) {
                add(event, timelineUpdater);
            }
        }
    }

    /**
     * Updates a given event in the model without UI update.
     *
     * @param event event to be updated
     */
    public void update(TimelineEvent event) {
        update(event, null);
    }

    /**
     * Updates a given event in the model with UI update.
     *
     * @param event           event to be added
     * @param timelineUpdater TimelineUpdater instance to update the event in UI
     */
    public void update(TimelineEvent event, TimelineUpdater timelineUpdater) {
        // GitHub #8577 must update event
        int index = IntStream.range(0, events.size())
                .filter(i -> event.equals(events.get(i)))
                .findFirst().orElse(-1);

        if (index >= 0) {
            events.set(index, event);

            if (timelineUpdater != null) {
                // update UI
                timelineUpdater.update(event);
            }
        }
    }

    /**
     * Updates all given events in the model without UI update.
     *
     * @param events collection of events to be updated
     */
    public void updateAll(Collection> events) {
        updateAll(events, null);
    }

    /**
     * Updates all given events in the model with UI update.
     *
     * @param events          collection of events to be updated
     * @param timelineUpdater TimelineUpdater instance to update the events in UI
     */
    public void updateAll(Collection> events, TimelineUpdater timelineUpdater) {
        if (events != null && !events.isEmpty()) {
            for (TimelineEvent event : events) {
                update(event, timelineUpdater);
            }
        }
    }

    /**
     * Deletes a given event in the model without UI update.
     *
     * @param event event to be deleted
     */
    public void delete(TimelineEvent event) {
        delete(event, null);
    }

    /**
     * Deletes a given event in the model with UI update.
     *
     * @param event           event to be deleted
     * @param timelineUpdater TimelineUpdater instance to delete the event in UI
     */
    public void delete(TimelineEvent event, TimelineUpdater timelineUpdater) {
        events.remove(event);

        if (timelineUpdater != null) {
            // update UI
            timelineUpdater.delete(event.getId());
        }
    }

    /**
     * Deletes all given events in the model without UI update.
     *
     * @param events collection of events to be deleted
     */
    public void deleteAll(Collection> events) {
        deleteAll(events, null);
    }

    /**
     * Deletes all given events in the model with UI update.
     *
     * @param events          collection of events to be deleted
     * @param timelineUpdater TimelineUpdater instance to delete the events in UI
     */
    public void deleteAll(Collection> events, TimelineUpdater timelineUpdater) {
        if (events != null && !events.isEmpty()) {
            for (TimelineEvent event : events) {
                delete(event, timelineUpdater);
            }
        }
    }

    /**
     * Selects a given event in UI visually. To unselect all events, pass a null as event.
     *
     * @param event           event to be selected
     * @param timelineUpdater TimelineUpdater instance to select the event in UI
     */
    public void select(TimelineEvent event, TimelineUpdater timelineUpdater) {
        if (timelineUpdater != null) {
            // update UI
            timelineUpdater.select(event != null ? event.getId() : null);
        }
    }

    /**
     * Clears the timeline model without UI update (no events are available after that)
     */
    public void clear() {
        events.clear();
    }

    /**
     * Clears the timeline model with UI update (no events are available after that)
     *
     * @param timelineUpdater TimelineUpdater instance to clear the timeline in UI
     */
    public void clear(TimelineUpdater timelineUpdater) {
        events.clear();

        if (timelineUpdater != null) {
            // update UI
            timelineUpdater.clear();
        }
    }

    /**
     * Gets all overlapped events to the given one. The given and overlapped events belong to the same group. Events are ordered
     * by their start dates - first events with more recent start dates and then events with older start dates. If start dates are
     * equal, events will be ordered by their end dates. In this case, if an event has a null end date, it is ordered before the
     * event with a not null end date.
     *
     * @param event given event
     * @return TreeSet<TimelineEvent<E>> ordered overlapped events or null if no overlapping exist
     */
    public Set> getOverlappedEvents(TimelineEvent event) {
        if (event == null) {
            return Collections.emptySet();
        }

        List> overlappedEvents = null;
        for (TimelineEvent e : events) {
            if (e.equals(event)) {
                // given event should not be included
                continue;
            }

            if (event.getGroup() == null && e.getGroup() != null
                    || (event.getGroup() != null && !event.getGroup().equals(e.getGroup()))) {
                // ignore different groups
                continue;
            }

            if (isOverlapping(event, e)) {
                if (overlappedEvents == null) {
                    overlappedEvents = new ArrayList<>();
                }

                overlappedEvents.add(e);
            }
        }

        if (overlappedEvents == null) {
            return Collections.emptySet();
        }

        // order overlapped events according to their start / end dates
        TreeSet> orderedOverlappedEvents = new TreeSet<>(new TimelineEventComparator());
        orderedOverlappedEvents.addAll(overlappedEvents);

        return orderedOverlappedEvents;
    }

    /**
     * Merge the given one event with the given collection of events without UI update. Only events within one group can be
     * merged. Note: after merging, the merged event will get the same properties as the given one event except start and end
     * dates.
     *
     * @param event  given event to be merged with collection of events
     * @param events collection of events
     * @return TimelineEvent result event after merging
     * @throws IllegalStateException thrown if not all events are within the same group
     */
    public TimelineEvent merge(TimelineEvent event, Collection> events) {
        return merge(event, events, null);
    }

    /**
     * Merge the given one event with the given collection of events with UI update. Only events within one group can be merged.
     * Note: after merging, the merged event will get the same properties as the given one event except start and end dates.
     *
     * @param event           given event to be merged with collection of events
     * @param events          collection of events
     * @param timelineUpdater TimelineUpdater instance to update the merged events in UI
     * @return TimelineEvent result event after merging
     * @throws IllegalStateException thrown if not all events are within the same group
     */
    public TimelineEvent merge(TimelineEvent event, Collection> events, TimelineUpdater timelineUpdater) {
        if (event == null) {
            // nothing to merge
            return null;
        }

        if (events == null || events.isEmpty()) {
            // nothing to merge
            return event;
        }

        // check whether all events within the same group
        String group = event.getGroup();
        for (TimelineEvent e : events) {
            if ((group == null && e.getGroup() != null) || (group != null && !group.equals(e.getGroup()))) {
                throw new IllegalStateException("Events to be merged may be only belong to one and the same group!");
            }
        }

        // order events according to their start / end dates
        TreeSet> orderedEvents = new TreeSet<>(new TimelineEventComparator());
        orderedEvents.add(event);
        orderedEvents.addAll(events);

        // find the largest end date
        LocalDateTime endDate = orderedEvents.stream()
                .map(TimelineEvent::getEndDate)
                .filter(Objects::nonNull)
                .max(LocalDateTime::compareTo)
                .orElse(null);

        TimelineEvent mergedEvent = TimelineEvent.builder()
                .data(event.getData())
                .startDate(orderedEvents.first().getStartDate())
                .endDate(endDate)
                .editable(event.isEditable())
                .group(event.getGroup())
                .styleClass(event.getStyleClass())
                .build();

        // merge...
        deleteAll(events, timelineUpdater);
        update(mergedEvent, timelineUpdater);

        return mergedEvent;
    }

    /**
     * Gets all events in this model
     *
     * @return List<TimelineEvent<E>> list of events
     */
    public List> getEvents() {
        return events;
    }

    /**
     * Sets events into this model
     *
     * @param events List<TimelineEvent<E>> list of events
     */
    public void setEvents(List> events) {
        this.events = events;
    }

    /**
     * Gets all groups in this model
     *
     * @return List<TimelineGroup<G>> list of groups
     */
    public List> getGroups() {
        return groups;
    }

    /**
     * Sets groups into this model
     *
     * @param groups List<TimelineGroup<G>> list of groups
     */
    public void setGroups(List> groups) {
        this.groups = groups;
    }

    /**
     * Gets event by its id property.
     *
     * @param id id property of the event
     * @return TimelineEvent found event or null
     */
    public TimelineEvent getEvent(String id) {
        if (id != null) {
            return events.stream()
                    .filter(event -> id.equals(event.getId()))
                    .findAny()
                    .orElse(null);
        }
        return null;
    }

    /**
     * Check if current model has the event passed as parameter.
     * @param event
     * @return
     */
    public boolean hasEvent(TimelineEvent event) {
        return events.contains(event);
    }

    private boolean isOverlapping(TimelineEvent event1, TimelineEvent event2) {
        if (event1.getEndDate() == null && event2.getEndDate() == null) {
            return event1.getStartDate().equals(event2.getStartDate());
        }
        else if (event1.getEndDate() == null && event2.getEndDate() != null) {
            return (event1.getStartDate().equals(event2.getStartDate()) || event1.getStartDate().equals(event2.getEndDate())
                    || (event1.getStartDate().isAfter(event2.getStartDate()) && event1.getStartDate().isBefore(event2.getEndDate())));
        }
        else if (event1.getEndDate() != null && event2.getEndDate() == null) {
            return (event2.getStartDate().equals(event1.getStartDate()) || event2.getStartDate().equals(event1.getEndDate())
                    || (event2.getStartDate().isAfter(event1.getStartDate()) && event2.getStartDate().isBefore(event1.getEndDate())));
        }
        else {
            // check with ODER if
            // 1. start date of the event 1 is within the event 2
            // 2. end date of the event 1 is within the event 2
            // 3. event 2 is completely strong within the event 1
            return (event1.getStartDate().equals(event2.getStartDate()) || event1.getStartDate().equals(event2.getEndDate())
                    || (event1.getStartDate().isAfter(event2.getStartDate()) && event1.getStartDate().isBefore(event2.getEndDate())))
                    || (event1.getEndDate().equals(event2.getStartDate()) || event1.getEndDate().equals(event2.getEndDate())
                    || (event1.getEndDate().isAfter(event2.getStartDate()) && event1.getEndDate().isBefore(event2.getEndDate())))
                    || (event1.getStartDate().isBefore(event2.getStartDate()) && event1.getEndDate().isAfter(event2.getEndDate()));
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy