de.steilerdev.myVerein.server.model.Event.java Source code

Java tutorial

Introduction

Here is the source code for de.steilerdev.myVerein.server.model.Event.java

Source

/**
 * Copyright (C) 2014 Frank Steiler <frank@steilerdev.de>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.steilerdev.myVerein.server.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import de.steilerdev.myVerein.server.controller.admin.DivisionManagementController;
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;

import javax.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * This object is representing an entity within the division's collection of the MongoDB. On top of that the class is providing several useful helper methods.
 */
public class Event {
    /**
     * This enum is representing the status of a message sent to a specific receiver.
     */
    public enum EventStatus {
        /**
         * This status is assigned to an event which has not received any answer from a particular user
         */
        PENDING {
            @Override
            public String toString() {
                return "PENDING";
            }
        },
        /**
         * This status is assigned to an event where the user stated he would participate
         */
        GOING {
            @Override
            public String toString() {
                return "GOING";
            }
        },
        /**
         * This status is assigned to an event where the user stated he might participate
         */
        MAYBE {
            @Override
            public String toString() {
                return "MAYBE";
            }
        },
        /**
         * This status is assigned to an event where the user stated he is not participating
         */
        DECLINE {
            @Override
            public String toString() {
                return "DECLINE";
            }
        },
        /**
         * This status is assigned to an event where the user was previously invited, but left the division
         */
        REMOVED {
            @Override
            public String toString() {
                return "REMOVED";
            }
        }
    }

    @Transient
    @JsonIgnore
    private static Logger logger = LoggerFactory.getLogger(Event.class);

    @Id
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String id;

    @NotBlank
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String name;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String description;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String location;
    @JsonInclude(JsonInclude.Include.NON_DEFAULT)
    private double locationLat;
    @JsonInclude(JsonInclude.Include.NON_DEFAULT)
    private double locationLng;

    @JsonIgnore
    private Map<String, EventStatus> invitedUser;

    /**
     * This variable holds the information when this object was last changed in a way that the invited user should update their cached information about the event.
     * This flag is updated every time the content is changed by an administrator or a user is removed (Because of a removed invited division or because the user was un-subscribed from a division)
     */
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private LocalDateTime lastChanged;

    @Indexed
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private LocalDateTime startDateTime;

    @Indexed
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private LocalDateTime endDateTime;

    @JsonInclude(JsonInclude.Include.NON_DEFAULT)
    private boolean multiDate;

    @Transient
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private String administrationNotAllowedMessage;

    @DBRef(lazy = true)
    @NotEmpty
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private List<Division> invitedDivision;

    @DBRef(lazy = true)
    @NotNull
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private User eventAdmin;

    @Transient
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private EventStatus userResponse;

    public Event() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description != null && !description.isEmpty() ? description : null;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location != null && !location.isEmpty() ? location : null;
    }

    public double getLocationLat() {
        return locationLat;
    }

    public void setLocationLat(double locationLat) {
        this.locationLat = locationLat;
    }

    public double getLocationLng() {
        return locationLng;
    }

    public void setLocationLng(double locationLng) {
        this.locationLng = locationLng;
    }

    public LocalDateTime getLastChanged() {
        return lastChanged;
    }

    public void setLastChanged(LocalDateTime lastChanged) {
        this.lastChanged = lastChanged;
    }

    /**
     * @return The LocalDateTime object of the start date of the event. The object is computed using the current startDate integers.
     */
    public LocalDateTime getStartDateTime() {

        return startDateTime;
    }

    /**
     * This function is setting the start date time and updates the startDate integers
     * @param startDateTime The new start date time.
     */
    public void setStartDateTime(LocalDateTime startDateTime) {
        this.startDateTime = startDateTime;
    }

    /**
     * @return The LocalDateTime object of the end date of the event. The object is computed using the current endDat integers.
     */
    public LocalDateTime getEndDateTime() {

        return endDateTime;
    }

    /**
     * This function is setting the end date time and updates the endDate integers
     * @param endDateTime The new start date time.
     */
    public void setEndDateTime(LocalDateTime endDateTime) {
        this.endDateTime = endDateTime;
    }

    @Transient
    @JsonIgnore
    public LocalDate getEndDate() {
        return endDateTime.toLocalDate();
    }

    @Transient
    @JsonIgnore
    public LocalDate getStartDate() {
        return startDateTime.toLocalDate();
    }

    public List<Division> getInvitedDivision() {
        return invitedDivision;
    }

    /**
     * The function is setting all invited divisions, but is optimizing the set by eliminating unnecessary divisions.
     * @param invitedDivision
     */
    public void setInvitedDivision(List<Division> invitedDivision) {
        this.invitedDivision = invitedDivision;
    }

    public void addDivision(Division division) {
        if (invitedDivision == null) {
            invitedDivision = new ArrayList<>();
        }
        invitedDivision.add(division);
    }

    public boolean isMultiDate() {
        return multiDate;
    }

    public void setMultiDate(boolean multiDate) {
        this.multiDate = multiDate;
    }

    /**
     * This function updates the multi date flag, depending on the start and end date.
     */
    public void updateMultiDate() {
        multiDate = !startDateTime.toLocalDate().equals(endDateTime.toLocalDate());
    }

    public User getEventAdmin() {
        return eventAdmin;
    }

    public void setEventAdmin(User eventAdmin) {
        this.eventAdmin = eventAdmin;
    }

    public String getAdministrationNotAllowedMessage() {
        return administrationNotAllowedMessage;
    }

    public void setAdministrationNotAllowedMessage(String administrationNotAllowedMessage) {
        this.administrationNotAllowedMessage = administrationNotAllowedMessage;
    }

    public Map<String, EventStatus> getInvitedUser() {
        return invitedUser;
    }

    public void setInvitedUser(Map<String, EventStatus> invitedUser) {
        this.invitedUser = invitedUser;
    }

    public void setGoing(User user) {
        invitedUser.put(user.getId(), EventStatus.GOING);
    }

    public void setDecline(User user) {
        invitedUser.put(user.getId(), EventStatus.DECLINE);
    }

    public void setMaybe(User user) {
        invitedUser.put(user.getId(), EventStatus.MAYBE);
    }

    public void setRemoved(User user) {
        invitedUser.put(user.getId(), EventStatus.REMOVED);
    }

    public EventStatus getUserResponse() {
        return userResponse;
    }

    public void setUserResponse(EventStatus userResponse) {
        this.userResponse = userResponse;
    }

    /**
     * This function is removing unnecessary divisions from the invited division set.
     */
    public void optimizeInvitedDivisionSet(DivisionRepository divisionRepository) {
        if (invitedDivision != null && !invitedDivision.isEmpty()) {
            invitedDivision = Division.getExpandedSetOfDivisions(invitedDivision, divisionRepository);
        }
    }

    /**
     * This function updates the list of invited user of this event
     * @param divisionRepository An active division repository used to expand the division set
     */
    public void updateInvitedUser(DivisionRepository divisionRepository) {
        if (invitedDivision == null || (invitedDivision = Division.getExpandedSetOfDivisions(invitedDivision,
                divisionRepository)) == null) {
            logger.error("Unable to update invited user, because invited divisions are null!");
        } else {
            logger.info("Updating invited user for event " + this);
            Set<String> oldInvitedUser = invitedUser == null ? new HashSet<>() : invitedUser.keySet();
            HashSet<String> newInvitedUser = new HashSet<>();
            invitedDivision.stream().forEach(div -> newInvitedUser.addAll(div.getMemberList()));

            if (oldInvitedUser.isEmpty() || newInvitedUser.isEmpty()) {
                logger.debug("Old set of invited user or new set of invited user is empty");
                invitedUser = new HashMap<>();
                if (oldInvitedUser.isEmpty() && !newInvitedUser.isEmpty()) {
                    logger.debug("Old set of invited user is empty and new set of invited user is not empty");
                    newInvitedUser.stream().forEach(userID -> invitedUser.put(userID, EventStatus.PENDING));
                } else if (newInvitedUser.isEmpty() && !oldInvitedUser.isEmpty()) {
                    logger.debug("New set of invited user is empty and old set of invited user is not empty");
                    oldInvitedUser.stream().forEach(userID -> invitedUser.put(userID, EventStatus.REMOVED));
                    lastChanged = LocalDateTime.now();
                }
            } else {
                logger.debug("Old and new set of invited user is not empty");
                oldInvitedUser.removeAll(newInvitedUser);
                oldInvitedUser.stream().forEach(userID -> invitedUser.put(userID, EventStatus.REMOVED));
                newInvitedUser.stream().forEach(userID -> invitedUser.putIfAbsent(userID, EventStatus.PENDING));
                lastChanged = LocalDateTime.now();
            }
        }
    }

    /**
     * This function creates a new event object and copies only the id of the current message.
     * @return A new message object only containing the id.
     */
    @JsonIgnore
    @Transient
    public Event getSendingObjectOnlyId() {
        Event sendingObject = new Event();
        sendingObject.setId(id);
        return sendingObject;
    }

    /**
     * This function creates a new message object only copies name time and id of the current message.
     * @return A new message object only containing the name, times and id.
     */
    @JsonIgnore
    @Transient
    public Event getSendingObjectOnlyNameTimeId() {
        String[] ignoredProperties = { "invitedDivision", "description", "location", "locationLat", "locationLng",
                "eventAdmin" };
        return getSendingObject(ignoredProperties);
    }

    /**
     * This function removes all fields that the other users of the app are not allowed to see.
     * @param receivingUser The user who is receiving the event.
     * @return A copied message object, without the fields, other users are not allowed to see.
     */
    @JsonIgnore
    @Transient
    public Event getSendingObjectInternalSync(User receivingUser) {
        Event sendingObject = getSendingObject();
        sendingObject.userResponse = invitedUser.get(receivingUser.getId());
        return sendingObject;
    }

    /**
     * This function creates a sending-save object (ensuring there is no infinite loop caused by references)
     * @return A sending-save instance of the object.
     */
    @JsonIgnore
    @Transient
    public Event getSendingObject() {
        Event sendingObject = getSendingObject(new String[0]);

        if (sendingObject.getEventAdmin() != null) {
            sendingObject.setEventAdmin(sendingObject.getEventAdmin().getSendingObjectOnlyId());
        }

        if (sendingObject.getInvitedDivision() != null) {
            sendingObject.getInvitedDivision().replaceAll(Division::getSendingObjectOnlyId);
        }

        sendingObject.setLastChanged(null);

        return sendingObject;
    }

    /**
     * This function copies the current object, ignoring the member fields specified by the ignored properties vararg.
     * @param ignoredProperties The member fields ignored during the copying.
     * @return A copy of the current object, not containing information about the ignored properties.
     */
    @JsonIgnore
    @Transient
    private Event getSendingObject(String... ignoredProperties) {
        Event sendingObject = new Event();
        BeanUtils.copyProperties(this, sendingObject, ignoredProperties);
        return sendingObject;
    }

    @Override
    public int hashCode() {
        return id == null ? 0 : id.hashCode();
    }

    /**
     * Equality is based on the same id String within the database.
     * @param obj The object compared to the current object.
     * @return True if the IDs of the two objects
     */
    @Override
    public boolean equals(Object obj) {
        return obj != null && obj instanceof Event && this.id != null && this.id.equals(((Event) obj).getId());
    }

    @Override
    public String toString() {
        return name != null && !name.isEmpty() ? name : id;
    }

    /**
     * {@link de.steilerdev.myVerein.server.model.EventRepository#findAllByPrefixedInvitedUser(String)} needs a user id, prefixed with "invitedUser.", because a custom query with a fixed prefix is not working. This function creates this prefixed user id.
     * @param user The user, which needs to be prefixed.
     * @return The prefixed user ID.
     */
    public static String prefixedUserIDForUser(User user) {
        return user == null ? null : "invitedUser." + user.getId();
    }
}