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.Set;
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 set provides a set 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 set
33   */
34  public class EventProducingSet<E> extends EventProducer implements EventListenerInterface, Set<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 set after add", "Size of the set",
42                      new ObjectDescriptor("Size of the set after add", "Size of the set", 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 set after remove", "Size of the set",
47                      new ObjectDescriptor("Size of the set after remove", "Size of the set", 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 set after change", "Size of the set",
52                      new ObjectDescriptor("Size of the set after change", "Size of the set", Integer.class)));
53  
54      /** the parent set. */
55      private Set<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 EventProducingSet.
62       * @param parent Set&lt;E&gt;; the parent set.
63       * @param sourceId Serializable; the id by which the EventProducer can be identified by the EventListener
64       */
65      public EventProducingSet(final Set<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 EventProducingSet.
82       * @param parent Set&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 EventProducingSet(final Set<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 boolean add(final E o)
130     {
131         boolean changed = this.parent.add(o);
132         if (changed)
133         {
134             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
135         }
136         else
137         {
138             this.fireEvent(OBJECT_CHANGED_EVENT, this.parent.size());
139         }
140         return changed;
141     }
142 
143     /** {@inheritDoc} */
144     @Override
145     public boolean addAll(final Collection<? extends E> c)
146     {
147         boolean changed = this.parent.addAll(c);
148         if (changed)
149         {
150             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
151         }
152         else
153         {
154             if (!c.isEmpty())
155             {
156                 this.fireEvent(OBJECT_CHANGED_EVENT, this.parent.size());
157             }
158         }
159         return changed;
160     }
161 
162     /** {@inheritDoc} */
163     @Override
164     public boolean contains(final Object o)
165     {
166         return this.parent.contains(o);
167     }
168 
169     /** {@inheritDoc} */
170     @Override
171     public boolean containsAll(final Collection<?> c)
172     {
173         return this.parent.containsAll(c);
174     }
175 
176     /** {@inheritDoc} */
177     @Override
178     public EventProducingIterator<E> iterator()
179     {
180         EventProducingIterator<E> iterator = new EventProducingIterator<E>(this.parent.iterator(), this.sourceIdProvider);
181         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
182         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
183         return iterator;
184     }
185 
186     /** {@inheritDoc} */
187     @Override
188     public void notify(final EventInterface event) throws RemoteException
189     {
190         // pass through the OBJECT_REMOVED_EVENT from the iterator
191         if (event.getType().equals(EventProducingIterator.OBJECT_REMOVED_EVENT))
192         {
193             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
194         }
195     }
196 
197     /** {@inheritDoc} */
198     @Override
199     public boolean remove(final Object o)
200     {
201         boolean changed = this.parent.remove(o);
202         if (changed)
203         {
204             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
205         }
206         return changed;
207     }
208 
209     /** {@inheritDoc} */
210     @Override
211     public boolean removeAll(final Collection<?> c)
212     {
213         boolean changed = this.parent.removeAll(c);
214         if (changed)
215         {
216             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
217         }
218         return changed;
219     }
220 
221     /** {@inheritDoc} */
222     @Override
223     public boolean retainAll(final Collection<?> c)
224     {
225         boolean changed = this.parent.retainAll(c);
226         if (changed)
227         {
228             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
229         }
230         return changed;
231     }
232 
233     /** {@inheritDoc} */
234     @Override
235     public Object[] toArray()
236     {
237         return this.parent.toArray();
238     }
239 
240     /** {@inheritDoc} */
241     @Override
242     public <T> T[] toArray(final T[] a)
243     {
244         return this.parent.toArray(a);
245     }
246 }