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-2024 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      @Override
66      public int size()
67      {
68          return this.wrappedList.size();
69      }
70  
71      @Override
72      public boolean isEmpty()
73      {
74          return this.wrappedList.isEmpty();
75      }
76  
77      @Override
78      public void clear()
79      {
80          int nr = this.wrappedList.size();
81          this.wrappedList.clear();
82          if (nr != this.wrappedList.size())
83          {
84              fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
85          }
86      }
87  
88      @Override
89      public void add(final int index, final E element)
90      {
91          this.wrappedList.add(index, element);
92          fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
93      }
94  
95      @Override
96      public boolean add(final E o)
97      {
98          boolean result = this.wrappedList.add(o);
99          if (result)
100         {
101             fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
102         }
103         return result;
104     }
105 
106     @Override
107     public boolean addAll(final Collection<? extends E> c)
108     {
109         boolean result = this.wrappedList.addAll(c);
110         if (result)
111         {
112             fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
113         }
114         return result;
115     }
116 
117     @Override
118     public boolean addAll(final int index, final Collection<? extends E> c)
119     {
120         boolean result = this.wrappedList.addAll(index, c);
121         if (result)
122         {
123             fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
124         }
125         return result;
126     }
127 
128     @Override
129     public boolean contains(final Object o)
130     {
131         return this.wrappedList.contains(o);
132     }
133 
134     @Override
135     public boolean containsAll(final Collection<?> c)
136     {
137         return this.wrappedList.containsAll(c);
138     }
139 
140     @Override
141     public E get(final int index)
142     {
143         return this.wrappedList.get(index);
144     }
145 
146     @Override
147     public int indexOf(final Object o)
148     {
149         return this.wrappedList.indexOf(o);
150     }
151 
152     @Override
153     public EventProducingIterator<E> iterator()
154     {
155         EventProducingIterator<E> iterator = new EventProducingIterator<E>(this.wrappedList.iterator());
156         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
157         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
158         return iterator;
159     }
160 
161     @Override
162     public EventProducingListIterator<E> listIterator()
163     {
164         return listIterator(0);
165     }
166 
167     @Override
168     public EventProducingListIterator<E> listIterator(final int index)
169     {
170         EventProducingListIterator<E> iterator = new EventProducingListIterator<E>(this.wrappedList.listIterator(index));
171         // WEAK references as an iterator is usually local and should be eligible for garbage collection
172         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
173         iterator.addListener(this, EventProducingListIterator.OBJECT_ADDED_EVENT, ReferenceType.WEAK);
174         iterator.addListener(this, EventProducingListIterator.OBJECT_CHANGED_EVENT, ReferenceType.WEAK);
175         return iterator;
176     }
177 
178     @Override
179     public void notify(final Event event) throws RemoteException
180     {
181         // pass through the OBJECT_REMOVED_EVENT from the iterator
182         if (event.getType().equals(EventProducingIterator.OBJECT_REMOVED_EVENT))
183         {
184             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
185         }
186         else if (event.getType().equals(EventProducingListIterator.OBJECT_ADDED_EVENT))
187         {
188             fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
189         }
190         else if (event.getType().equals(EventProducingListIterator.OBJECT_CHANGED_EVENT))
191         {
192             fireEvent(OBJECT_CHANGED_EVENT, this.wrappedList.size());
193         }
194     }
195 
196     @Override
197     public int lastIndexOf(final Object o)
198     {
199         return this.wrappedList.lastIndexOf(o);
200     }
201 
202     @Override
203     public E remove(final int index)
204     {
205         E result = this.wrappedList.remove(index);
206         fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
207         return result;
208     }
209 
210     @Override
211     public boolean remove(final Object o)
212     {
213         boolean result = this.wrappedList.remove(o);
214         if (result)
215         {
216             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
217         }
218         return result;
219     }
220 
221     @Override
222     public boolean removeAll(final Collection<?> c)
223     {
224         boolean result = this.wrappedList.removeAll(c);
225         if (result)
226         {
227             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
228         }
229         return result;
230     }
231 
232     @Override
233     public boolean retainAll(final Collection<?> c)
234     {
235         boolean result = this.wrappedList.retainAll(c);
236         if (result)
237         {
238             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
239         }
240         return result;
241     }
242 
243     @Override
244     public E set(final int index, final E element)
245     {
246         E result = this.wrappedList.set(index, element);
247         fireEvent(OBJECT_CHANGED_EVENT, this.wrappedList.size());
248         return result;
249     }
250 
251     @Override
252     public List<E> subList(final int fromIndex, final int toIndex)
253     {
254         return this.wrappedList.subList(fromIndex, toIndex);
255     }
256 
257     @Override
258     public Object[] toArray()
259     {
260         return this.wrappedList.toArray();
261     }
262 
263     @Override
264     public <T> T[] toArray(final T[] a)
265     {
266         return this.wrappedList.toArray(a);
267     }
268 
269 }