FormDetail.java

/*
 * Copyright (C) 2000 - 2024 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception. You should have recieved a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.components.formsonline.model;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.silverpeas.components.formsonline.model.DefaultFormsOnlineService.HierarchicalValidatorCacheManager;
import org.silverpeas.core.admin.service.OrganizationController;
import org.silverpeas.core.admin.user.model.Group;
import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.util.MemoizedSupplier;
import org.silverpeas.core.util.StringUtil;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;
import static org.silverpeas.components.formsonline.model.FormInstanceValidationType.*;
import static org.silverpeas.core.util.CollectionUtil.isNotEmpty;
import static org.silverpeas.core.util.StringUtil.defaultStringIfNotDefined;

public class FormDetail {
  public static final int STATE_NOT_YET_PUBLISHED = 0;
  public static final int STATE_PUBLISHED = 1;
  public static final int STATE_UNPUBLISHED = 2;

  public static final int VALIDATOR_OK = 0;
  public static final int VALIDATOR_UNDEFINED = 1;
  public static final int VALIDATOR_NOT_ALLOWED = 2;

  public static final String SENDERS_TYPE = "S";
  public static final String RECEIVERS_TYPE_INTERMEDIATE = "I";
  public static final String RECEIVERS_TYPE_FINAL = "R";
  public static final List<String> ALL_RIGHT_TYPES = unmodifiableList(
      asList(SENDERS_TYPE, RECEIVERS_TYPE_INTERMEDIATE, RECEIVERS_TYPE_FINAL));
  public static final List<String> ALL_RECEIVER_TYPES = unmodifiableList(
      asList(RECEIVERS_TYPE_INTERMEDIATE, RECEIVERS_TYPE_FINAL));

  private int id = -1;
  private String xmlFormName = null;
  private String name = "";
  private String description = "";
  private String title = "";
  private String creatorId = null;
  private Date creationDate = new Date();
  private String instanceId = null;
  private int state = STATE_NOT_YET_PUBLISHED;
  private boolean hierarchicalValidation = false;
  private String requestExchangeReceiver = null;
  private boolean deleteAfterRequestExchange = false;

  private boolean sendable = true;
  private int nbRequests = 0;

  private String hierarchicalValidatorOfCurrentUser;
  private Map<FormInstanceValidationType, Function<FormInstance, Supplier<List<User>>>> possibleValidationTypes;
  private HierarchicalValidatorCacheManager hvManager;

  private List<User> sendersAsUsers;
  private List<Group> sendersAsGroups;

  private List<User> intermediateReceiversAsUsers;
  private List<Group> intermediateReceiversAsGroups;
  private List<User> receiversAsUsers;
  private List<Group> receiversAsGroups;

  /**
   * @return the id
   */
  public int getId() {
    return id;
  }

  /**
   * @return the title
   */
  public String getTitle() {
    return title;
  }

  /**
   * @param title the title to set
   */
  public void setTitle(String title) {
    this.title = title;
  }

  /**
   * @return the state
   */
  public int getState() {
    return state;
  }

  /**
   * @param state the state to set
   */
  public void setState(int state) {
    this.state = state;
  }

  /**
   * @return the creationDate
   */
  public Date getCreationDate() {
    return creationDate != null ? (Date) creationDate.clone() : null;
  }

  /**
   * @param creationDate the creationDate to set
   */
  public void setCreationDate(Date creationDate) {
    this.creationDate = (creationDate != null ? (Date) creationDate.clone() : null);
  }

  /**
   * @param id the id to set
   */
  public void setId(int id) {
    this.id = id;
  }

  /**
   * @return the xmlFormName
   */
  public String getXmlFormName() {
    return xmlFormName;
  }

  /**
   * @param xmlFormName the xmlFormName to set
   */
  public void setXmlFormName(String xmlFormName) {
    this.xmlFormName = xmlFormName;
  }

  /**
   * @return the name
   */
  public String getName() {
    return name;
  }

  /**
   * @param name the name to set
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * @return the description
   */
  public String getDescription() {
    return description;
  }

  /**
   * @param description the description to set
   */
  public void setDescription(String description) {
    this.description = description;
  }

  /**
   * @return the creatorId
   */
  public String getCreatorId() {
    return creatorId;
  }

  /**
   * @param creatorId the creatorId to set
   */
  public void setCreatorId(String creatorId) {
    this.creatorId = creatorId;
  }

  /**
   * @return the instanceId
   */
  public String getInstanceId() {
    return instanceId;
  }

  /**
   * @param instanceId the instanceId to set
   */
  public void setInstanceId(String instanceId) {
    this.instanceId = instanceId;
  }

  /**
   * Indicates if the hierarchical validation enabled.
   * @return true if enabled, false othserwise.
   */
  public boolean isHierarchicalValidation() {
    return hierarchicalValidation;
  }

  /**
   * Sets the hierarchical validation flag.
   * @param hierarchicalValidation true to enabled, false otherwise.
   */
  public void setHierarchicalValidation(final boolean hierarchicalValidation) {
    this.hierarchicalValidation = hierarchicalValidation;
  }

  /**
   * Gets the receiver data which permits to exchange the data of a new form request.
   * <p>
   * If filled, just after its creation the new form request is exchanged with the given receiver.
   * </p>
   * <p>
   * For now, the receiver is represented by an e-mail. By this way, the data are sent to the
   * receiver just after a new form request creation.
   * </p>
   * @return an optional receiver data in charge of request exchange processing.
   */
  public Optional<String> getRequestExchangeReceiver() {
    return Optional.ofNullable(requestExchangeReceiver);
  }

  /**
   * Sets the receiver data (an e-mail for now) which permits to perform the exchange of a new
   * form request creation.
   * @param requestExchangeReceiver receiver data (an e-mail for now)
   */
  public void setRequestExchangeReceiver(final String requestExchangeReceiver) {
    this.requestExchangeReceiver = defaultStringIfNotDefined(requestExchangeReceiver, null);
  }

  /**
   * Indicates id the exchanged form request MUST be deleted after the exchange processing.
   * <p>
   * If method {@link #getRequestExchangeReceiver()} returns no receiver data, then no deletion
   * is indicated.
   * </p>
   * @return true if a new form request MUST be deleted after exchange, false otherwise.
   */
  public boolean isDeleteAfterRequestExchange() {
    return getRequestExchangeReceiver().map(r -> deleteAfterRequestExchange).orElse(false);
  }

  /**
   * Sets the behavior about the deletion of a new form request when it has just been exchanged with
   * the receiver procided by {@link #getRequestExchangeReceiver()} method.
   * @param deleteAfterRequestExchange  true if a new form request MUST be deleted after
   * exchange, false otherwise.
   */
  public void setDeleteAfterRequestExchange(final boolean deleteAfterRequestExchange) {
    this.deleteAfterRequestExchange = deleteAfterRequestExchange;
  }

  @Override
  public boolean equals(final Object o) {
    if (this == o) {
      return true;
    }

    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    final FormDetail that = (FormDetail) o;

    return new EqualsBuilder().append(id, that.id).append(state, that.state)
        .append(hierarchicalValidation, that.hierarchicalValidation)
        .append(deleteAfterRequestExchange, that.deleteAfterRequestExchange)
        .append(xmlFormName, that.xmlFormName).append(name, that.name)
        .append(description, that.description).append(title, that.title)
        .append(creatorId, that.creatorId).append(creationDate, that.creationDate)
        .append(instanceId, that.instanceId)
        .append(requestExchangeReceiver, that.requestExchangeReceiver).isEquals();
  }

  @Override
  public int hashCode() {
    return new HashCodeBuilder(17, 37).append(id).append(xmlFormName).append(name)
        .append(description).append(title).append(creatorId).append(creationDate).append(instanceId)
        .append(state).append(hierarchicalValidation).append(requestExchangeReceiver)
        .append(deleteAfterRequestExchange).toHashCode();
  }

  public boolean isPublished() {
    return getState() == STATE_PUBLISHED;
  }

  public boolean isUnpublished() {
    return getState() == STATE_UNPUBLISHED;
  }

  public boolean isNotYetPublished() {
    return getState() == STATE_NOT_YET_PUBLISHED;
  }

  public void setSendable(boolean sendable) {
    this.sendable = sendable;
  }

  public boolean isSendable() {
    return sendable;
  }

  private boolean isSender(String userId) {
    return isInList(userId, getAllSenders());
  }

  public boolean isValidator(String userId) {
    return isInList(userId, getAllReceivers());
  }

  public FormPK getPK() {
    return new FormPK(Integer.toString(getId()), getInstanceId());
  }

  private boolean isInList(String userId, List<User> users) {
    for (User user : users) {
      if (user.getId().equals(userId)) {
        return true;
      }
    }
    return false;
  }

  private List<User> getAllSenders() {
    List<User> senders = getSendersAsUsers();
    senders.addAll(getUsers(getSendersAsGroups()));
    return senders;
  }

  private List<User> getAllReceivers() {
    return Stream.concat(getReceiversAsUsers().stream(),
           Stream.concat(getUsers(getReceiversAsGroups()).stream(),
           Stream.concat(getIntermediateReceiversAsUsers().stream(),
                         getUsers(getIntermediateReceiversAsGroups()).stream())))
           .distinct()
           .collect(Collectors.toList());
  }

  private List<User> getUsers(List<Group> groups) {
    return groups.stream()
        .flatMap(g -> g.getAllUsers().stream())
        .distinct()
        .collect(Collectors.toList());
  }

  public List<User> getSendersAsUsers() {
    if (sendersAsUsers == null) {
      return new ArrayList<>();
    }
    return sendersAsUsers;
  }

  public void setSendersAsUsers(final List<User> sendersAsUsers) {
    this.sendersAsUsers = sendersAsUsers;
  }

  public List<Group> getSendersAsGroups() {
    if (sendersAsGroups == null) {
      return new ArrayList<>();
    }
    return sendersAsGroups;
  }

  public void setSendersAsGroups(final List<Group> sendersAsGroups) {
    this.sendersAsGroups = sendersAsGroups;
  }

  public List<User> getReceiversAsUsers() {
    if (receiversAsUsers == null) {
      return new ArrayList<>();
    }
    return receiversAsUsers;
  }

  public void setReceiversAsUsers(final List<User> receiversAsUsers) {
    this.receiversAsUsers = receiversAsUsers;
  }

  public List<Group> getReceiversAsGroups() {
    if (receiversAsGroups == null) {
      return new ArrayList<>();
    }
    return receiversAsGroups;
  }

  public void setReceiversAsGroups(final List<Group> receiversAsGroups) {
    this.receiversAsGroups = receiversAsGroups;
  }

  protected List<User> getAllFinalReceivers() {
    return Stream.concat(getReceiversAsUsers().stream(), getUsers(getReceiversAsGroups()).stream())
        .distinct()
        .collect(Collectors.toList());
  }

  public List<User> getIntermediateReceiversAsUsers() {
    if (intermediateReceiversAsUsers == null) {
      return new ArrayList<>();
    }
    return intermediateReceiversAsUsers;
  }

  public void setIntermediateReceiversAsUsers(final List<User> receiversAsUsers) {
    this.intermediateReceiversAsUsers = receiversAsUsers;
  }

  public List<Group> getIntermediateReceiversAsGroups() {
    if (intermediateReceiversAsGroups == null) {
      return new ArrayList<>();
    }
    return intermediateReceiversAsGroups;
  }

  public void setIntermediateReceiversAsGroups(final List<Group> receiversAsGroups) {
    this.intermediateReceiversAsGroups = receiversAsGroups;
  }

  protected List<User> getAllIntermediateReceivers() {
    return Stream.concat(getIntermediateReceiversAsUsers().stream(), getUsers(getIntermediateReceiversAsGroups()).stream())
        .distinct()
        .collect(Collectors.toList());
  }

  public boolean isIntermediateValidator(String userId) {
    return isInList(userId, getAllIntermediateReceivers());
  }

  public boolean isFinalValidator(String userId) {
    return isInList(userId, getAllFinalReceivers());
  }

  public boolean isFinalValidation() {
    return isNotEmpty(getReceiversAsUsers()) || isNotEmpty(getReceiversAsGroups());
  }

  public boolean isIntermediateValidation() {
    return isNotEmpty(getIntermediateReceiversAsUsers()) || isNotEmpty(getIntermediateReceiversAsGroups());
  }

  public int getNbRequests() {
    return nbRequests;
  }

  public void setNbRequests(final int nbRequests) {
    this.nbRequests = nbRequests;
  }

  public String getHierarchicalValidatorOfCurrentUser() {
    if (hierarchicalValidatorOfCurrentUser == null) {
      if (this.isHierarchicalValidation()) {
        hierarchicalValidatorOfCurrentUser = getHvManager().getHierarchicalValidatorOf(User.getCurrentRequester().getId());
      } else {
        hierarchicalValidatorOfCurrentUser = StringUtil.EMPTY;
      }
    }
    return hierarchicalValidatorOfCurrentUser;
  }

  HierarchicalValidatorCacheManager getHvManager() {
    if (hvManager == null) {
      hvManager = HierarchicalValidatorCacheManager.get();
    }
    return hvManager;
  }

  public int getHierarchicalValidatorState() {
    if (!isHierarchicalValidation()) {
      return VALIDATOR_OK;
    }
    if (StringUtil.isNotDefined(getHierarchicalValidatorOfCurrentUser())) {
      return VALIDATOR_UNDEFINED;
    } else if (OrganizationController.get()
        .isComponentAvailableToUser(getInstanceId(), getHierarchicalValidatorOfCurrentUser())) {
      return VALIDATOR_OK;
    }
    return VALIDATOR_NOT_ALLOWED;
  }

  /**
   * Gets the possible request validations.
   * <p>
   * For each request validation, a validator provider is provided.
   * </p>
   * <p>
   *   BE CAREFUL of that following methods MUST have been called before using this method:
   *   <ul>
   *     <li>{@link #setIntermediateReceiversAsUsers(List)}</li>
   *     <li>{@link #setIntermediateReceiversAsGroups(List)}</li>
   *     <li>{@link #setReceiversAsUsers(List)}</li>
   *     <li>{@link #setReceiversAsGroups(List)}</li>
   *   </ul>
   * </p>
   * @return a map of validation type associated to a validator list supplier. Keys of map are
   * sorted as the {@link FormInstanceValidationType} enum.
   */
  public Map<FormInstanceValidationType, Function<FormInstance, Supplier<List<User>>>> getPossibleRequestValidations() {
    if (possibleValidationTypes == null) {
      possibleValidationTypes = new LinkedHashMap<>(FormInstanceValidationType.values().length);
      if (isHierarchicalValidation()) {
        possibleValidationTypes.put(HIERARCHICAL, f -> () -> singletonList(User.getById(f.getHierarchicalValidator())));
      }
      if (isIntermediateValidation()) {
        final MemoizedSupplier<List<User>> supplier = new MemoizedSupplier<>(this::getAllIntermediateReceivers);
        possibleValidationTypes.put(INTERMEDIATE, f -> supplier);
      }
      if (isFinalValidation()) {
        final MemoizedSupplier<List<User>> supplier = new MemoizedSupplier<>(this::getAllFinalReceivers);
        possibleValidationTypes.put(FINAL, f -> supplier);
      }
    }
    return possibleValidationTypes;
  }

  public boolean canBeSentBy(final User user) {
    return isPublished() && isSender(user.getId());
  }
}