View Javadoc
1   package org.djutils.event.util;
2   
3   import java.io.Serializable;
4   import java.rmi.RemoteException;
5   import java.util.Collection;
6   
7   import org.djutils.event.EventInterface;
8   import org.djutils.event.EventListenerInterface;
9   import org.djutils.event.EventProducer;
10  import org.djutils.event.EventType;
11  import org.djutils.event.IdProvider;
12  import org.djutils.event.ref.ReferenceType;
13  import org.djutils.exceptions.Throw;
14  import org.djutils.metadata.MetaData;
15  import org.djutils.metadata.ObjectDescriptor;
16  
17  /**
18   * The Event producing collection provides a set to which one can subscribe interest in entry changes. This class does not keep
19   * track of changes which take place indirectly. One is for example not notified on <code>map.iterator.remove()</code>. A
20   * listener must subscribe to the iterator individually.
21   * <p>
22   * Copyright (c) 2002-2021 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
23   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
24   * distributed under a three-clause BSD-style license, which can be found at
25   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. This class was
26   * originally part of the DSOL project, see <a href="https://simulation.tudelft.nl/dsol/manual" target="_blank">
27   * https://simulation.tudelft.nl/dsol/manual</a>.
28   * </p>
29   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
30   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
31   * @param <T> The type of the event producing Collection.
32   */
33  public class EventProducingCollection<T> extends EventProducer implements EventListenerInterface, Collection<T>
34  {
35      /** The default serial version UID for serializable classes. */
36      private static final long serialVersionUID = 20191230L;
37  
38      /** OBJECT_ADDED_EVENT is fired on new entries. */
39      public static final EventType OBJECT_ADDED_EVENT =
40              new EventType("OBJECT_ADDED_EVENT", new MetaData("Size of the collection after add", "Size of the collection",
41                      new ObjectDescriptor("Size of the collection after add", "Size of the collection", Integer.class)));
42  
43      /** OBJECT_REMOVED_EVENT is fired on removal of entries. */
44      public static final EventType OBJECT_REMOVED_EVENT =
45              new EventType("OBJECT_REMOVED_EVENT", new MetaData("Size of the collection after remove", "Size of the collection",
46                      new ObjectDescriptor("Size of the collection after remove", "Size of the collection", Integer.class)));
47  
48      /** OBJECT_CHANGED_EVENT is fired on change of one or more entries. */
49      public static final EventType OBJECT_CHANGED_EVENT =
50              new EventType("OBJECT_CHANGED_EVENT", new MetaData("Size of the collection after change", "Size of the collection",
51                      new ObjectDescriptor("Size of the collection after change", "Size of the collection", Integer.class)));
52  
53      /** the parent collection. */
54      private Collection<T> parent = null;
55  
56      /** the function that produces the id by which the EventProducer can be identified. */
57      private final IdProvider sourceIdProvider;
58  
59      /**
60       * constructs a new EventProducingCollectiont.
61       * @param parent Collection&lt;T&gt;; the parent collection.
62       * @param sourceId Serializable; the id by which the EventProducer can be identified by the EventListener
63       */
64      public EventProducingCollection(final Collection<T> parent, final Serializable sourceId)
65      {
66          this(parent, new IdProvider()
67          {
68              /** */
69              private static final long serialVersionUID = 20200119L;
70  
71              @Override
72              public Serializable id()
73              {
74                  return sourceId;
75              }
76          });
77      }
78  
79      /**
80       * Constructs a new EventProducingCollection.
81       * @param parent Collection&lt;T&gt;; the parent set.
82       * @param sourceIdProvider IdProvider; the function that produces the id by which the EventProducer can be identified by the
83       *            EventListener
84       */
85      public EventProducingCollection(final Collection<T> parent, final IdProvider sourceIdProvider)
86      {
87          Throw.whenNull(parent, "parent cannot be null");
88          Throw.whenNull(sourceIdProvider, "sourceIdprovider cannot be null");
89          this.parent = parent;
90          this.sourceIdProvider = sourceIdProvider;
91      }
92  
93      /** {@inheritDoc} */
94      @Override
95      public Serializable getSourceId()
96      {
97          return this.sourceIdProvider.id();
98      }
99  
100     /** {@inheritDoc} */
101     @Override
102     public int size()
103     {
104         return this.parent.size();
105     }
106 
107     /** {@inheritDoc} */
108     @Override
109     public boolean isEmpty()
110     {
111         return this.parent.isEmpty();
112     }
113 
114     /** {@inheritDoc} */
115     @Override
116     public void clear()
117     {
118         int nr = this.parent.size();
119         this.parent.clear();
120         if (nr != this.parent.size())
121         {
122             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
123         }
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public boolean add(final T o)
129     {
130         boolean changed = this.parent.add(o);
131         if (changed)
132         {
133             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
134         }
135         else
136         {
137             this.fireEvent(OBJECT_CHANGED_EVENT, this.parent.size());
138         }
139         return changed;
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     public boolean addAll(final Collection<? extends T> c)
145     {
146         boolean changed = this.parent.addAll(c);
147         if (changed)
148         {
149             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
150         }
151         else
152         {
153             if (!c.isEmpty())
154             {
155                 this.fireEvent(OBJECT_CHANGED_EVENT, this.parent.size());
156             }
157         }
158         return changed;
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     public boolean contains(final Object o)
164     {
165         return this.parent.contains(o);
166     }
167 
168     /** {@inheritDoc} */
169     @Override
170     public boolean containsAll(final Collection<?> c)
171     {
172         return this.parent.containsAll(c);
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     public EventProducingIterator<T> iterator()
178     {
179         EventProducingIterator<T> iterator = new EventProducingIterator<T>(this.parent.iterator(), this.sourceIdProvider);
180         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
181         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
182         return iterator;
183     }
184 
185     /** {@inheritDoc} */
186     @Override
187     public void notify(final EventInterface event) throws RemoteException
188     {
189         // pass through the OBJECT_REMOVED_EVENT from the iterator
190         if (event.getType().equals(EventProducingIterator.OBJECT_REMOVED_EVENT))
191         {
192             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
193         }
194     }
195 
196     /** {@inheritDoc} */
197     @Override
198     public boolean remove(final Object o)
199     {
200         boolean changed = this.parent.remove(o);
201         if (changed)
202         {
203             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
204         }
205         return changed;
206     }
207 
208     /** {@inheritDoc} */
209     @Override
210     public boolean removeAll(final Collection<?> c)
211     {
212         boolean changed = this.parent.removeAll(c);
213         if (changed)
214         {
215             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
216         }
217         return changed;
218     }
219 
220     /** {@inheritDoc} */
221     @Override
222     public boolean retainAll(final Collection<?> c)
223     {
224         boolean changed = this.parent.retainAll(c);
225         if (changed)
226         {
227             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
228         }
229         return changed;
230     }
231 
232     /** {@inheritDoc} */
233     @Override
234     public Object[] toArray()
235     {
236         return this.parent.toArray();
237     }
238 
239     /** {@inheritDoc} */
240     @Override
241     public <E> E[] toArray(final E[] a)
242     {
243         return this.parent.toArray(a);
244     }
245 }