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}