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   
7   import org.djutils.event.EventInterface;
8   import org.djutils.event.EventListenerInterface;
9   import org.djutils.event.EventProducer;
10  import org.djutils.event.EventType;
11  import org.djutils.event.IdProvider;
12  import org.djutils.event.ref.ReferenceType;
13  import org.djutils.exceptions.Throw;
14  
15  /**
16   * The Event producing collection provides a set to which one can subscribe interest in entry changes. This class does not keep
17   * track of changes which take place indirectly. One is for example not notified on <code>map.iterator.remove()</code>. A
18   * listener must subscribe to the iterator individually.
19   * <p>
20   * Copyright (c) 2002-2020 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 <T> The type t od the eventproducing list.
30   */
31  public class EventProducingCollection<T> extends EventProducer implements EventListenerInterface, Collection<T>
32  {
33      /** The default serial version UID for serializable classes. */
34      private static final long serialVersionUID = 20191230L;
35  
36      /** OBJECT_ADDED_EVENT is fired on new entries. */
37      public static final EventType>EventType OBJECT_ADDED_EVENT = new EventType("OBJECT_ADDED_EVENT");
38  
39      /** OBJECT_REMOVED_EVENT is fired on removel of entries. */
40      public static final EventTypeventType OBJECT_REMOVED_EVENT = new EventType("OBJECT_REMOVED_EVENT");
41  
42      /** OBJECT_CHANGED_EVENT is fired on change of one or more entries. */
43      public static final EventTypeventType OBJECT_CHANGED_EVENT = new EventType("OBJECT_CHANGED_EVENT");
44  
45      /** the parent collection. */
46      private Collection<T> parent = null;
47  
48      /** the function that produces the id by which the EventProducer can be identified. */
49      private final IdProvider sourceIdProvider;
50  
51      /**
52       * constructs a new EventProducingCollectiont.
53       * @param parent Collection&lt;T&gt;; the parent collection.
54       * @param sourceId Serializable; the id by which the EventProducer can be identified by the EventListener
55       */
56      public EventProducingCollection(final Collection<T> parent, final Serializable sourceId)
57      {
58          this(parent, new IdProvider()
59          {
60              /** */
61              private static final long serialVersionUID = 20200119L;
62  
63              @Override
64              public Serializable id()
65              {
66                  return sourceId;
67              }
68          });
69      }
70  
71      /**
72       * Constructs a new EventProducingCollection.
73       * @param parent Collection&lt;T&gt;; the parent set.
74       * @param sourceIdProvider IdProvider; the function that produces the id by which the EventProducer can be identified by the
75       *            EventListener
76       */
77      public EventProducingCollection(final Collection<T> parent, final IdProvider sourceIdProvider)
78      {
79          Throw.whenNull(parent, "parent cannot be null");
80          Throw.whenNull(sourceIdProvider, "sourceIdprovider cannot be null");
81          this.parent = parent;
82          this.sourceIdProvider = sourceIdProvider;
83      }
84  
85      /** {@inheritDoc} */
86      @Override
87      public Serializable getSourceId()
88      {
89          return this.sourceIdProvider.id();
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      public int size()
95      {
96          return this.parent.size();
97      }
98  
99      /** {@inheritDoc} */
100     @Override
101     public boolean isEmpty()
102     {
103         return this.parent.isEmpty();
104     }
105 
106     /** {@inheritDoc} */
107     @Override
108     public void clear()
109     {
110         int nr = this.parent.size();
111         this.parent.clear();
112         if (nr != this.parent.size())
113         {
114             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
115         }
116     }
117 
118     /** {@inheritDoc} */
119     @Override
120     public boolean add(final T o)
121     {
122         boolean changed = this.parent.add(o);
123         if (changed)
124         {
125             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
126         }
127         else
128         {
129             this.fireEvent(OBJECT_CHANGED_EVENT, this.parent.size());
130         }
131         return changed;
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     public boolean addAll(final Collection<? extends T> c)
137     {
138         boolean changed = this.parent.addAll(c);
139         if (changed)
140         {
141             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
142         }
143         else
144         {
145             if (!c.isEmpty())
146             {
147                 this.fireEvent(OBJECT_CHANGED_EVENT, this.parent.size());
148             }
149         }
150         return changed;
151     }
152 
153     /** {@inheritDoc} */
154     @Override
155     public boolean contains(final Object o)
156     {
157         return this.parent.contains(o);
158     }
159 
160     /** {@inheritDoc} */
161     @Override
162     public boolean containsAll(final Collection<?> c)
163     {
164         return this.parent.containsAll(c);
165     }
166 
167     /** {@inheritDoc} */
168     @Override
169     public EventProducingIterator<T> iterator()
170     {
171         EventProducingIterator<T> iterator = new EventProducingIterator<T>(this.parent.iterator(), this.sourceIdProvider);
172         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
173         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
174         return iterator;
175     }
176 
177     /** {@inheritDoc} */
178     @Override
179     public void notify(final EventInterface 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             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
185         }
186     }
187 
188     /** {@inheritDoc} */
189     @Override
190     public boolean remove(final Object o)
191     {
192         boolean changed = this.parent.remove(o);
193         if (changed)
194         {
195             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
196         }
197         return changed;
198     }
199 
200     /** {@inheritDoc} */
201     @Override
202     public boolean removeAll(final Collection<?> c)
203     {
204         boolean changed = this.parent.removeAll(c);
205         if (changed)
206         {
207             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
208         }
209         return changed;
210     }
211 
212     /** {@inheritDoc} */
213     @Override
214     public boolean retainAll(final Collection<?> c)
215     {
216         boolean changed = this.parent.retainAll(c);
217         if (changed)
218         {
219             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
220         }
221         return changed;
222     }
223 
224     /** {@inheritDoc} */
225     @Override
226     public Object[] toArray()
227     {
228         return this.parent.toArray();
229     }
230 
231     /** {@inheritDoc} */
232     @Override
233     public <E> E[] toArray(final E[] a)
234     {
235         return this.parent.toArray(a);
236     }
237 }