001package stdlib;
002import java.util.ArrayList;
003import java.util.List;
004
005/**
006 * A helper class which can be used to manage an object's subscriptions.
007 *
008 * @param <T> the type of the publisher
009 * @param <U> the type of the optional data argument
010 * @see Subscriber
011 */
012public final class Subscriptions<T, U> {
013        private final T publisher;
014        private final List<Subscriber<T, U>> subscribers;
015        private boolean changed = false;
016
017        /**
018         * Initializes a new instance of the {@link Subscriptions} class with
019         * zero subscribers.
020         *
021         * @param publisher the object being publisher
022         * @throws IllegalArgumentException if publisher is <code>null</code>
023         */
024        public Subscriptions(T publisher) {
025                if (publisher == null)
026                        throw new IllegalArgumentException("publisher cannot be null");
027                this.publisher = publisher;
028                this.subscribers = new ArrayList<Subscriber<T, U>>();
029        }
030
031        /**
032         * Adds an subscriber to the set of subscribers for the publisher object,
033         * provided that it is not the same as some subscriber already in the set.
034         *
035         * @param subscriber
036         * @throws IllegalArgumentException if the parameter subscriber is <code>null</code>
037         */
038        public void addSubscriber(Subscriber<T, U> subscriber) {
039                if (subscriber == null)
040                        throw new IllegalArgumentException("subscriber cannot be null");
041                if (!subscribers.contains(subscriber)) {
042                        subscribers.add(subscriber);
043                }
044        }
045
046        /**
047         * Indicates that the publisher object has no longer changed, or that it has
048         * already notified all of its subscribers of its most recent change, so that
049         * {@link #hasChanged()} will now return <code>false</code>. This method
050         * is called automatically by the {@link #notifySubscribers} methods.
051         *
052         */
053        public void clearChanged() {
054                changed = false;
055        }
056
057        /**
058         * Returns the number of subscribers of publisher object.
059         *
060         * @return the number of subscribers of publisher object
061         */
062        public int countSubscribers() {
063                return subscribers.size();
064        }
065
066        /**
067         * Deletes an subscriber from the set of subscribers. Passing <code>null</code>
068         * to this method will have no effect.
069         *
070         * @param subscriber the {@link Subscriber} to be deleted
071         */
072        public void deleteSubscriber(Subscriber<T, U> subscriber) {
073                subscribers.remove(subscriber);
074        }
075
076        /**
077         * Clears the subscriber list so that the publisher object no longer has any
078         * subscribers.
079         */
080        public void deleteSubscribers() {
081                this.subscribers.clear();
082        }
083
084        /**
085         * Returns <code>true</code> if the publisher object has changed;
086         * otherwise, <code>false</code>.
087         *
088         * @return <code>true</code> if the publisher object has changed;
089         *         otherwise, <code>false</code>
090         */
091        public boolean hasChanged() {
092                return changed;
093        }
094
095        /**
096         * If this object has changed, as indicated by the {@link #hasChanged()},
097         * then notify all of its subscribers and then clear the changed state. This
098         * method is equivalent to calling <code>notifySubscribers(null)</code>.
099         */
100        public void notifySubscribers() {
101                notifySubscribers(null);
102        }
103
104        /**
105         * If this object has changed, as indicated by the {@link #hasChanged()},
106         * then notify all of its subscribers and then clear the changed state.
107         *
108         * @param data optional event specific data which will be passed to the subscribers
109         */
110        public void notifySubscribers(U data) {
111                if (hasChanged()) {
112                        for (Subscriber<T, U> subscriber : subscribers) {
113                                subscriber.update(publisher, data);
114                        }
115                        clearChanged();
116                }
117        }
118
119        /**
120         * Flags the publisher object as having changed.
121         */
122        public void setChanged() {
123                changed = true;
124        }
125}