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