View Javadoc
1   package org.djutils.event.collection;
2   
3   import java.util.Collection;
4   import java.util.List;
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 list provides a list to which one can subscribe interest in entry changes. This class does not keep track
17   * of changes which take place indirectly. One is for example not notified on <code>map.iterator.remove()</code>. A listener
18   * must subscribe to the iterator individually.
19   * <p>
20   * Copyright (c) 2002-2025 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 <E> the type of elements in the list
30   */
31  public class EventProducingList<E> extends LocalEventProducer implements EventListener, List<E>
32  {
33      /** OBJECT_ADDED_EVENT is fired on new entries. */
34      public static final EventType OBJECT_ADDED_EVENT =
35              new EventType("OBJECT_ADDED_EVENT", new MetaData("Size of the list after add", "Size of the list",
36                      new ObjectDescriptor("Size of the list after add", "Size of the list", Integer.class)));
37  
38      /** OBJECT_REMOVED_EVENT is fired on removal of entries. */
39      public static final EventType OBJECT_REMOVED_EVENT =
40              new EventType("OBJECT_REMOVED_EVENT", new MetaData("Size of the list after remove", "Size of the list",
41                      new ObjectDescriptor("Size of the list after remove", "Size of the list", Integer.class)));
42  
43      /** OBJECT_CHANGED_EVENT is fired on change of one or more entries. */
44      public static final EventType OBJECT_CHANGED_EVENT =
45              new EventType("OBJECT_CHANGED_EVENT", new MetaData("Size of the list after change", "Size of the list",
46                      new ObjectDescriptor("Size of the list after change", "Size of the list", Integer.class)));
47  
48      /** the wrapped list. */
49      private List<E> wrappedList = null;
50  
51      /**
52       * constructs a new EventProducingList.
53       * @param wrappedList the embedded list.
54       */
55      public EventProducingList(final List<E> wrappedList)
56      {
57          Throw.whenNull(wrappedList, "wrappedList cannot be null");
58          this.wrappedList = wrappedList;
59      }
60  
61      @Override
62      public int size()
63      {
64          return this.wrappedList.size();
65      }
66  
67      @Override
68      public boolean isEmpty()
69      {
70          return this.wrappedList.isEmpty();
71      }
72  
73      @Override
74      public void clear()
75      {
76          int nr = this.wrappedList.size();
77          this.wrappedList.clear();
78          if (nr != this.wrappedList.size())
79          {
80              fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
81          }
82      }
83  
84      @Override
85      public void add(final int index, final E element)
86      {
87          this.wrappedList.add(index, element);
88          fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
89      }
90  
91      @Override
92      public boolean add(final E o)
93      {
94          boolean result = this.wrappedList.add(o);
95          if (result)
96          {
97              fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
98          }
99          return result;
100     }
101 
102     @Override
103     public boolean addAll(final Collection<? extends E> c)
104     {
105         boolean result = this.wrappedList.addAll(c);
106         if (result)
107         {
108             fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
109         }
110         return result;
111     }
112 
113     @Override
114     public boolean addAll(final int index, final Collection<? extends E> c)
115     {
116         boolean result = this.wrappedList.addAll(index, c);
117         if (result)
118         {
119             fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
120         }
121         return result;
122     }
123 
124     @Override
125     public boolean contains(final Object o)
126     {
127         return this.wrappedList.contains(o);
128     }
129 
130     @Override
131     public boolean containsAll(final Collection<?> c)
132     {
133         return this.wrappedList.containsAll(c);
134     }
135 
136     @Override
137     public E get(final int index)
138     {
139         return this.wrappedList.get(index);
140     }
141 
142     @Override
143     public int indexOf(final Object o)
144     {
145         return this.wrappedList.indexOf(o);
146     }
147 
148     @Override
149     public EventProducingIterator<E> iterator()
150     {
151         EventProducingIterator<E> iterator = new EventProducingIterator<E>(this.wrappedList.iterator());
152         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
153         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
154         return iterator;
155     }
156 
157     @Override
158     public EventProducingListIterator<E> listIterator()
159     {
160         return listIterator(0);
161     }
162 
163     @Override
164     public EventProducingListIterator<E> listIterator(final int index)
165     {
166         EventProducingListIterator<E> iterator = new EventProducingListIterator<E>(this.wrappedList.listIterator(index));
167         // WEAK references as an iterator is usually local and should be eligible for garbage collection
168         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
169         iterator.addListener(this, EventProducingListIterator.OBJECT_ADDED_EVENT, ReferenceType.WEAK);
170         iterator.addListener(this, EventProducingListIterator.OBJECT_CHANGED_EVENT, ReferenceType.WEAK);
171         return iterator;
172     }
173 
174     @Override
175     public void notify(final Event event)
176     {
177         // pass through the OBJECT_REMOVED_EVENT from the iterator
178         if (event.getType().equals(EventProducingIterator.OBJECT_REMOVED_EVENT))
179         {
180             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
181         }
182         else if (event.getType().equals(EventProducingListIterator.OBJECT_ADDED_EVENT))
183         {
184             fireEvent(OBJECT_ADDED_EVENT, this.wrappedList.size());
185         }
186         else if (event.getType().equals(EventProducingListIterator.OBJECT_CHANGED_EVENT))
187         {
188             fireEvent(OBJECT_CHANGED_EVENT, this.wrappedList.size());
189         }
190     }
191 
192     @Override
193     public int lastIndexOf(final Object o)
194     {
195         return this.wrappedList.lastIndexOf(o);
196     }
197 
198     @Override
199     public E remove(final int index)
200     {
201         E result = this.wrappedList.remove(index);
202         fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
203         return result;
204     }
205 
206     @Override
207     public boolean remove(final Object o)
208     {
209         boolean result = this.wrappedList.remove(o);
210         if (result)
211         {
212             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
213         }
214         return result;
215     }
216 
217     @Override
218     public boolean removeAll(final Collection<?> c)
219     {
220         boolean result = this.wrappedList.removeAll(c);
221         if (result)
222         {
223             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
224         }
225         return result;
226     }
227 
228     @Override
229     public boolean retainAll(final Collection<?> c)
230     {
231         boolean result = this.wrappedList.retainAll(c);
232         if (result)
233         {
234             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedList.size());
235         }
236         return result;
237     }
238 
239     @Override
240     public E set(final int index, final E element)
241     {
242         E result = this.wrappedList.set(index, element);
243         fireEvent(OBJECT_CHANGED_EVENT, this.wrappedList.size());
244         return result;
245     }
246 
247     @Override
248     public List<E> subList(final int fromIndex, final int toIndex)
249     {
250         return this.wrappedList.subList(fromIndex, toIndex);
251     }
252 
253     @Override
254     public Object[] toArray()
255     {
256         return this.wrappedList.toArray();
257     }
258 
259     @Override
260     public <T> T[] toArray(final T[] a)
261     {
262         return this.wrappedList.toArray(a);
263     }
264 
265 }