MailingList.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 received 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.mailinglist.service.model.beans;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Entity
@Table(name = "sc_mailinglist_list")
@NamedQueries({
@NamedQuery(name = "mailinglist.findByComponentId", query = "from MailingList where componentId = " +
":componentId"),
@NamedQuery(name = "mailinglist.findAll", query = "from MailingList")})
public class MailingList extends IdentifiableObject {
private static final long serialVersionUID = 3983404426767796807L;
private String componentId;
@Transient
private String name;
@Transient
private String subscribedAddress;
@Transient
private String description;
@Transient
private boolean open;
@Transient
private boolean moderated;
@Transient
private boolean notify;
@Transient
private boolean supportRSS;
@Transient
private Set<InternalUser> moderators = new HashSet<>();
@Transient
private Set<InternalUser> readers = new HashSet<>();
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "listId", nullable = false)
private Set<ExternalUser> externalSubscribers = new HashSet<>();
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "mailingListId", nullable = false)
private Set<InternalSubscriber> internalSubscribers = new HashSet<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubscribedAddress() {
return subscribedAddress;
}
public void setSubscribedAddress(String subscribedAddress) {
this.subscribedAddress = subscribedAddress;
}
public boolean isOpen() {
return open;
}
public void setOpen(boolean publiclyVisible) {
this.open = publiclyVisible;
}
public boolean isModerated() {
return moderated;
}
public void setModerated(boolean moderated) {
this.moderated = moderated;
}
public Set<InternalUser> getModerators() {
return moderators;
}
public void setModerators(Set<InternalUser> moderators) {
this.moderators = moderators;
}
public Set<InternalUser> getReaders() {
return readers;
}
public void setReaders(Set<InternalUser> readers) {
this.readers = readers;
}
public String getComponentId() {
return componentId;
}
public void setComponentId(String componentId) {
this.componentId = componentId;
}
public Set<ExternalUser> getExternalSubscribers() {
return externalSubscribers;
}
public boolean isNotify() {
return notify;
}
public void setNotify(boolean notify) {
this.notify = notify;
}
public void removeExternalSubscriber(ExternalUser user) {
externalSubscribers.remove(user);
}
public void addExternalSubscriber(ExternalUser user) {
externalSubscribers.add(user);
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isSupportRSS() {
return supportRSS;
}
public void setSupportRSS(boolean supportRSS) {
this.supportRSS = supportRSS;
}
public boolean isEmailAuthorized(String email) {
for (ExternalUser user : externalSubscribers) {
if (user.getEmail() != null && user.getEmail().equalsIgnoreCase(email)) {
return true;
}
}
for (InternalUser user : readers) {
if (user.getEmail() != null && user.getEmail().equalsIgnoreCase(email)) {
return true;
}
}
for (InternalUser user : moderators) {
if (user.getEmail() != null && user.getEmail().equalsIgnoreCase(email)) {
return true;
}
}
return false;
}
public Set<InternalGroupSubscriber> getGroupSubscribers() {
return new InternalSubscriberSet<>(InternalGroupSubscriber.class);
}
public Set<InternalUserSubscriber> getInternalSubscribers() {
return new InternalSubscriberSet<>(InternalUserSubscriber.class);
}
private class InternalSubscriberSet<T extends InternalSubscriber> implements Set<T> {
private Class<T> subscriberType;
InternalSubscriberSet(Class<T> typeOfT) {
subscriberType = typeOfT;
}
private Stream<T> filter() {
return (Stream<T>) internalSubscribers.stream()
.filter(s -> s.getClass().isAssignableFrom(subscriberType));
}
/**
* Returns the number of elements in this set (its cardinality). If this
* set contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
* @return the number of elements in this set (its cardinality)
*/
@Override
public int size() {
return (int) filter().count();
}
/**
* Returns <tt>true</tt> if this set contains no elements.
* @return <tt>true</tt> if this set contains no elements
*/
@Override
public boolean isEmpty() {
return this.size() == 0;
}
/**
* Returns <tt>true</tt> if this set contains the specified element.
* More formally, returns <tt>true</tt> if and only if this set
* contains an element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
* @param o element whose presence in this set is to be tested
* @return <tt>true</tt> if this set contains the specified element
* @throws ClassCastException if the type of the specified element
* is incompatible with this set
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* set does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
@Override
public boolean contains(final Object o) {
return internalSubscribers.contains(o);
}
/**
* Returns an iterator over the elements in this set. The elements are
* returned in no particular order (unless this set is an instance of some
* class that provides a guarantee).
* @return an iterator over the elements in this set
*/
@Override
public Iterator<T> iterator() {
return filter().collect(Collectors.toSet()).iterator();
}
/**
* Returns an array containing all of the elements in this set.
* If this set makes any guarantees as to what order its elements
* are returned by its iterator, this method must return the
* elements in the same order.
* <p>
* <p>The returned array will be "safe" in that no references to it
* are maintained by this set. (In other words, this method must
* allocate a new array even if this set is backed by an array).
* The caller is thus free to modify the returned array.
* <p>
* <p>This method acts as bridge between array-based and collection-based
* APIs.
* @return an array containing all the elements in this set
*/
@Override
public Object[] toArray() {
return filter().collect(Collectors.toSet()).toArray();
}
/**
* Returns an array containing all of the elements in this set; the
* runtime type of the returned array is that of the specified array.
* If the set fits in the specified array, it is returned therein.
* Otherwise, a new array is allocated with the runtime type of the
* specified array and the size of this set.
* <p>
* <p>If this set fits in the specified array with room to spare
* (i.e., the array has more elements than this set), the element in
* the array immediately following the end of the set is set to
* <tt>null</tt>. (This is useful in determining the length of this
* set <i>only</i> if the caller knows that this set does not contain
* any null elements.)
* <p>
* <p>If this set makes any guarantees as to what order its elements
* are returned by its iterator, this method must return the elements
* in the same order.
* <p>
* <p>Like the {@link #toArray()} method, this method acts as bridge between
* array-based and collection-based APIs. Further, this method allows
* precise control over the runtime type of the output array, and may,
* under certain circumstances, be used to save allocation costs.
* <p>
* <p>Suppose <tt>x</tt> is a set known to contain only strings.
* The following code can be used to dump the set into a newly allocated
* array of <tt>String</tt>:
* <p>
* <pre>
* String[] y = x.toArray(new String[0]);</pre>
* <p>
* Note that <tt>toArray(new Object[0])</tt> is identical in function to
* <tt>toArray()</tt>.
* @param a the array into which the elements of this set are to be
* stored, if it is big enough; otherwise, a new array of the same
* runtime type is allocated for this purpose.
* @return an array containing all the elements in this set
* @throws ArrayStoreException if the runtime type of the specified array
* is not a supertype of the runtime type of every element in this
* set
* @throws NullPointerException if the specified array is null
*/
@Override
public <T1> T1[] toArray(final T1[] a) {
return filter().collect(Collectors.toSet()).toArray(a);
}
/**
* Adds the specified element to this set if it is not already present
* (optional operation). More formally, adds the specified element
* <tt>e</tt> to this set if the set contains no element <tt>e2</tt>
* such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>. In combination with the
* restriction on constructors, this ensures that sets never contain
* duplicate elements.
* <p>
* <p>The stipulation above does not imply that sets must accept all
* elements; sets may refuse to add any particular element, including
* <tt>null</tt>, and throw an exception, as described in the
* specification for {@link Collection#add Collection.add}.
* Individual set implementations should clearly document any
* restrictions on the elements that they may contain.
* @param t element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
* @throws UnsupportedOperationException if the <tt>add</tt> operation
* is not supported by this set
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this set
* @throws NullPointerException if the specified element is null and this
* set does not permit null elements
* @throws IllegalArgumentException if some property of the specified element
* prevents it from being added to this set
*/
@Override
public boolean add(final T t) {
return internalSubscribers.add(t);
}
/**
* Removes the specified element from this set if it is present
* (optional operation). More formally, removes an element <tt>e</tt>
* such that
* <tt>(o==null ? e==null : o.equals(e))</tt>, if
* this set contains such an element. Returns <tt>true</tt> if this set
* contained the element (or equivalently, if this set changed as a
* result of the call). (This set will not contain the element once the
* call returns.)
* @param o object to be removed from this set, if present
* @return <tt>true</tt> if this set contained the specified element
* @throws ClassCastException if the type of the specified element
* is incompatible with this set
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* set does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this set
*/
@Override
public boolean remove(final Object o) {
return internalSubscribers.remove(o);
}
/**
* Returns <tt>true</tt> if this set contains all of the elements of the
* specified collection. If the specified collection is also a set, this
* method returns <tt>true</tt> if it is a <i>subset</i> of this set.
* @param c collection to be checked for containment in this set
* @return <tt>true</tt> if this set contains all of the elements of the
* specified collection
* @throws ClassCastException if the types of one or more elements
* in the specified collection are incompatible with this
* set
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified collection contains one
* or more null elements and this set does not permit null
* elements
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #contains(Object)
*/
@Override
public boolean containsAll(final Collection<?> c) {
return internalSubscribers.containsAll(c);
}
/**
* Adds all of the elements in the specified collection to this set if
* they're not already present (optional operation). If the specified
* collection is also a set, the <tt>addAll</tt> operation effectively
* modifies this set so that its value is the <i>union</i> of the two
* sets. The behavior of this operation is undefined if the specified
* collection is modified while the operation is in progress.
* @param c collection containing elements to be added to this set
* @return <tt>true</tt> if this set changed as a result of the call
* @throws UnsupportedOperationException if the <tt>addAll</tt> operation
* is not supported by this set
* @throws ClassCastException if the class of an element of the
* specified collection prevents it from being added to this set
* @throws NullPointerException if the specified collection contains one
* or more null elements and this set does not permit null
* elements, or if the specified collection is null
* @throws IllegalArgumentException if some property of an element of the
* specified collection prevents it from being added to this set
* @see #add(Object)
*/
@Override
public boolean addAll(final Collection<? extends T> c) {
return internalSubscribers.addAll(c);
}
/**
* Retains only the elements in this set that are contained in the
* specified collection (optional operation). In other words, removes
* from this set all of its elements that are not contained in the
* specified collection. If the specified collection is also a set, this
* operation effectively modifies this set so that its value is the
* <i>intersection</i> of the two sets.
* @param c collection containing elements to be retained in this set
* @return <tt>true</tt> if this set changed as a result of the call
* @throws UnsupportedOperationException if the <tt>retainAll</tt> operation
* is not supported by this set
* @throws ClassCastException if the class of an element of this set
* is incompatible with the specified collection
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this set contains a null element and the
* specified collection does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
@Override
public boolean retainAll(final Collection<?> c) {
return internalSubscribers.retainAll(c);
}
/**
* Removes from this set all of its elements that are contained in the
* specified collection (optional operation). If the specified
* collection is also a set, this operation effectively modifies this
* set so that its value is the <i>asymmetric set difference</i> of
* the two sets.
* @param c collection containing elements to be removed from this set
* @return <tt>true</tt> if this set changed as a result of the call
* @throws UnsupportedOperationException if the <tt>removeAll</tt> operation
* is not supported by this set
* @throws ClassCastException if the class of an element of this set
* is incompatible with the specified collection
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this set contains a null element and the
* specified collection does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
* @see #contains(Object)
*/
@Override
public boolean removeAll(final Collection<?> c) {
return internalSubscribers.removeAll(c);
}
/**
* Removes all of the elements from this set (optional operation).
* The set will be empty after this call returns.
* @throws UnsupportedOperationException if the <tt>clear</tt> method
* is not supported by this set
*/
@Override
public void clear() {
Set<T> subscribers = filter().collect(Collectors.toSet());
internalSubscribers.removeAll(subscribers);
}
}
}