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