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