View Javadoc
1   package org.djutils.event.collection;
2   
3   import java.rmi.RemoteException;
4   import java.util.Collection;
5   import java.util.Set;
6   
7   import org.djutils.event.Event;
8   import org.djutils.event.EventListener;
9   import org.djutils.event.EventType;
10  import org.djutils.event.LocalEventProducer;
11  import org.djutils.event.reference.ReferenceType;
12  import org.djutils.exceptions.Throw;
13  import org.djutils.metadata.MetaData;
14  import org.djutils.metadata.ObjectDescriptor;
15  
16  /**
17   * The Event producing set provides a set 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-2023 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 set
31   */
32  public class EventProducingSet<E> extends LocalEventProducer implements EventListener, Set<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 OBJECT_ADDED_EVENT =
39              new EventType("OBJECT_ADDED_EVENT", new MetaData("Size of the set after add", "Size of the set",
40                      new ObjectDescriptor("Size of the set after add", "Size of the set", Integer.class)));
41  
42      /** OBJECT_REMOVED_EVENT is fired on removal of entries. */
43      public static final EventType OBJECT_REMOVED_EVENT =
44              new EventType("OBJECT_REMOVED_EVENT", new MetaData("Size of the set after remove", "Size of the set",
45                      new ObjectDescriptor("Size of the set after remove", "Size of the set", Integer.class)));
46  
47      /** OBJECT_CHANGED_EVENT is fired on change of one or more entries. */
48      public static final EventType OBJECT_CHANGED_EVENT =
49              new EventType("OBJECT_CHANGED_EVENT", new MetaData("Size of the set after change", "Size of the set",
50                      new ObjectDescriptor("Size of the set after change", "Size of the set", Integer.class)));
51  
52      /** the wrapped set. */
53      private Set<E> wrappedSet = null;
54  
55      /**
56       * Constructs a new EventProducingSet.
57       * @param wrappedSet Set&lt;E&gt;; the embedded set.
58       */
59      public EventProducingSet(final Set<E> wrappedSet)
60      {
61          Throw.whenNull(wrappedSet, "wrappedSet cannot be null");
62          this.wrappedSet = wrappedSet;
63      }
64  
65      /** {@inheritDoc} */
66      @Override
67      public int size()
68      {
69          return this.wrappedSet.size();
70      }
71  
72      /** {@inheritDoc} */
73      @Override
74      public boolean isEmpty()
75      {
76          return this.wrappedSet.isEmpty();
77      }
78  
79      /** {@inheritDoc} */
80      @Override
81      public void clear()
82      {
83          int nr = this.wrappedSet.size();
84          this.wrappedSet.clear();
85          if (nr != this.wrappedSet.size())
86          {
87              fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
88          }
89      }
90  
91      /** {@inheritDoc} */
92      @Override
93      public boolean add(final E o)
94      {
95          boolean changed = this.wrappedSet.add(o);
96          if (changed)
97          {
98              fireEvent(OBJECT_ADDED_EVENT, this.wrappedSet.size());
99          }
100         else
101         {
102             fireEvent(OBJECT_CHANGED_EVENT, this.wrappedSet.size());
103         }
104         return changed;
105     }
106 
107     /** {@inheritDoc} */
108     @Override
109     public boolean addAll(final Collection<? extends E> c)
110     {
111         boolean changed = this.wrappedSet.addAll(c);
112         if (changed)
113         {
114             fireEvent(OBJECT_ADDED_EVENT, this.wrappedSet.size());
115         }
116         else
117         {
118             if (!c.isEmpty())
119             {
120                 fireEvent(OBJECT_CHANGED_EVENT, this.wrappedSet.size());
121             }
122         }
123         return changed;
124     }
125 
126     /** {@inheritDoc} */
127     @Override
128     public boolean contains(final Object o)
129     {
130         return this.wrappedSet.contains(o);
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public boolean containsAll(final Collection<?> c)
136     {
137         return this.wrappedSet.containsAll(c);
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public EventProducingIterator<E> iterator()
143     {
144         EventProducingIterator<E> iterator = new EventProducingIterator<E>(this.wrappedSet.iterator());
145         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
146         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
147         return iterator;
148     }
149 
150     /** {@inheritDoc} */
151     @Override
152     public void notify(final Event event) throws RemoteException
153     {
154         // pass through the OBJECT_REMOVED_EVENT from the iterator
155         if (event.getType().equals(EventProducingIterator.OBJECT_REMOVED_EVENT))
156         {
157             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
158         }
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     public boolean remove(final Object o)
164     {
165         boolean changed = this.wrappedSet.remove(o);
166         if (changed)
167         {
168             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
169         }
170         return changed;
171     }
172 
173     /** {@inheritDoc} */
174     @Override
175     public boolean removeAll(final Collection<?> c)
176     {
177         boolean changed = this.wrappedSet.removeAll(c);
178         if (changed)
179         {
180             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
181         }
182         return changed;
183     }
184 
185     /** {@inheritDoc} */
186     @Override
187     public boolean retainAll(final Collection<?> c)
188     {
189         boolean changed = this.wrappedSet.retainAll(c);
190         if (changed)
191         {
192             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
193         }
194         return changed;
195     }
196 
197     /** {@inheritDoc} */
198     @Override
199     public Object[] toArray()
200     {
201         return this.wrappedSet.toArray();
202     }
203 
204     /** {@inheritDoc} */
205     @Override
206     public <T> T[] toArray(final T[] a)
207     {
208         return this.wrappedSet.toArray(a);
209     }
210 
211 }