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  
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-2020 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 EventProducer implements EventListenerInterface, 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>EventType OBJECT_ADDED_EVENT = new EventType("OBJECT_ADDED_EVENT");
39  
40      /** OBJECT_REMOVED_EVENT is fired on removel of entries. */
41      public static final EventTypeventType OBJECT_REMOVED_EVENT = new EventType("OBJECT_REMOVED_EVENT");
42  
43      /** OBJECT_CHANGED_EVENT is fired on change of one or more entries. */
44      public static final EventTypeventType OBJECT_CHANGED_EVENT = new EventType("OBJECT_CHANGED_EVENT");
45  
46      /** the parent list. */
47      private List<E> parent = null;
48  
49      /** the function that produces the id by which the EventProducer can be identified. */
50      private final IdProvider sourceIdProvider;
51  
52      /**
53       * constructs a new EventProducingList.
54       * @param parent List&lt;E&gt;; the parent list.
55       * @param sourceId Serializable; the id by which the EventProducer can be identified by the EventListener
56       */
57      public EventProducingList(final List<E> parent, final Serializable sourceId)
58      {
59          this(parent, new IdProvider()
60          {
61              /** */
62              private static final long serialVersionUID = 20200119L;
63  
64              @Override
65              public Serializable id()
66              {
67                  return sourceId;
68              }
69          });
70      }
71  
72      /**
73       * Constructs a new EventProducingList.
74       * @param parent List&lt;E&gt;; the parent set.
75       * @param sourceIdProvider IdProvider; the function that produces the id by which the EventProducer can be identified by the
76       *            EventListener
77       */
78      public EventProducingList(final List<E> parent, final IdProvider sourceIdProvider)
79      {
80          Throw.whenNull(parent, "parent cannot be null");
81          Throw.whenNull(sourceIdProvider, "sourceIdprovider cannot be null");
82          this.parent = parent;
83          this.sourceIdProvider = sourceIdProvider;
84      }
85  
86      /** {@inheritDoc} */
87      @Override
88      public Serializable getSourceId()
89      {
90          return this.sourceIdProvider.id();
91      }
92  
93      /** {@inheritDoc} */
94      @Override
95      public int size()
96      {
97          return this.parent.size();
98      }
99  
100     /** {@inheritDoc} */
101     @Override
102     public boolean isEmpty()
103     {
104         return this.parent.isEmpty();
105     }
106 
107     /** {@inheritDoc} */
108     @Override
109     public void clear()
110     {
111         int nr = this.parent.size();
112         this.parent.clear();
113         if (nr != this.parent.size())
114         {
115             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
116         }
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public void add(final int index, final E element)
122     {
123         this.parent.add(index, element);
124         this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
125     }
126 
127     /** {@inheritDoc} */
128     @Override
129     public boolean add(final E o)
130     {
131         boolean result = this.parent.add(o);
132         if (result)
133         {
134             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
135         }
136         return result;
137     }
138 
139     /** {@inheritDoc} */
140     @Override
141     public boolean addAll(final Collection<? extends E> c)
142     {
143         boolean result = this.parent.addAll(c);
144         if (result)
145         {
146             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
147         }
148         return result;
149     }
150 
151     /** {@inheritDoc} */
152     @Override
153     public boolean addAll(final int index, final Collection<? extends E> c)
154     {
155         boolean result = this.parent.addAll(index, c);
156         if (result)
157         {
158             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
159         }
160         return result;
161     }
162 
163     /** {@inheritDoc} */
164     @Override
165     public boolean contains(final Object o)
166     {
167         return this.parent.contains(o);
168     }
169 
170     /** {@inheritDoc} */
171     @Override
172     public boolean containsAll(final Collection<?> c)
173     {
174         return this.parent.containsAll(c);
175     }
176 
177     /** {@inheritDoc} */
178     @Override
179     public E get(final int index)
180     {
181         return this.parent.get(index);
182     }
183 
184     /** {@inheritDoc} */
185     @Override
186     public int indexOf(final Object o)
187     {
188         return this.parent.indexOf(o);
189     }
190 
191     /** {@inheritDoc} */
192     @Override
193     public EventProducingIterator<E> iterator()
194     {
195         EventProducingIterator<E> iterator = new EventProducingIterator<E>(this.parent.iterator(), this.sourceIdProvider);
196         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
197         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
198         return iterator;
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public EventProducingListIterator<E> listIterator()
204     {
205         return listIterator(0);
206     }
207 
208     /** {@inheritDoc} */
209     @Override
210     public EventProducingListIterator<E> listIterator(final int index)
211     {
212         EventProducingListIterator<E> iterator =
213                 new EventProducingListIterator<E>(this.parent.listIterator(index), this.sourceIdProvider);
214         // WEAK references as an iterator is usually local and should be eligible for garbage collection
215         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
216         iterator.addListener(this, EventProducingListIterator.OBJECT_ADDED_EVENT, ReferenceType.WEAK);
217         iterator.addListener(this, EventProducingListIterator.OBJECT_CHANGED_EVENT, ReferenceType.WEAK);
218         return iterator;
219     }
220 
221     /** {@inheritDoc} */
222     @Override
223     public void notify(final EventInterface event) throws RemoteException
224     {
225         // pass through the OBJECT_REMOVED_EVENT from the iterator
226         if (event.getType().equals(EventProducingIterator.OBJECT_REMOVED_EVENT))
227         {
228             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
229         }
230         else if (event.getType().equals(EventProducingListIterator.OBJECT_ADDED_EVENT))
231         {
232             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
233         }
234         else if (event.getType().equals(EventProducingListIterator.OBJECT_CHANGED_EVENT))
235         {
236             this.fireEvent(OBJECT_CHANGED_EVENT, null);
237         }
238     }
239 
240     /** {@inheritDoc} */
241     @Override
242     public int lastIndexOf(final Object o)
243     {
244         return this.parent.lastIndexOf(o);
245     }
246 
247     /** {@inheritDoc} */
248     @Override
249     public E remove(final int index)
250     {
251         E result = this.parent.remove(index);
252         this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
253         return result;
254     }
255 
256     /** {@inheritDoc} */
257     @Override
258     public boolean remove(final Object o)
259     {
260         boolean result = this.parent.remove(o);
261         if (result)
262         {
263             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
264         }
265         return result;
266     }
267 
268     /** {@inheritDoc} */
269     @Override
270     public boolean removeAll(final Collection<?> c)
271     {
272         boolean result = this.parent.removeAll(c);
273         if (result)
274         {
275             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
276         }
277         return result;
278     }
279 
280     /** {@inheritDoc} */
281     @Override
282     public boolean retainAll(final Collection<?> c)
283     {
284         boolean result = this.parent.retainAll(c);
285         if (result)
286         {
287             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
288         }
289         return result;
290     }
291 
292     /** {@inheritDoc} */
293     @Override
294     public E set(final int index, final E element)
295     {
296         E result = this.parent.set(index, element);
297         this.fireEvent(OBJECT_CHANGED_EVENT, null);
298         return result;
299     }
300 
301     /** {@inheritDoc} */
302     @Override
303     public List<E> subList(final int fromIndex, final int toIndex)
304     {
305         return this.parent.subList(fromIndex, toIndex);
306     }
307 
308     /** {@inheritDoc} */
309     @Override
310     public Object[] toArray()
311     {
312         return this.parent.toArray();
313     }
314 
315     /** {@inheritDoc} */
316     @Override
317     public <T> T[] toArray(final T[] a)
318     {
319         return this.parent.toArray(a);
320     }
321 }