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-2024 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      @Override
66      public int size()
67      {
68          return this.wrappedSet.size();
69      }
70  
71      @Override
72      public boolean isEmpty()
73      {
74          return this.wrappedSet.isEmpty();
75      }
76  
77      @Override
78      public void clear()
79      {
80          int nr = this.wrappedSet.size();
81          this.wrappedSet.clear();
82          if (nr != this.wrappedSet.size())
83          {
84              fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
85          }
86      }
87  
88      @Override
89      public boolean add(final E o)
90      {
91          boolean changed = this.wrappedSet.add(o);
92          if (changed)
93          {
94              fireEvent(OBJECT_ADDED_EVENT, this.wrappedSet.size());
95          }
96          else
97          {
98              fireEvent(OBJECT_CHANGED_EVENT, this.wrappedSet.size());
99          }
100         return changed;
101     }
102 
103     @Override
104     public boolean addAll(final Collection<? extends E> c)
105     {
106         boolean changed = this.wrappedSet.addAll(c);
107         if (changed)
108         {
109             fireEvent(OBJECT_ADDED_EVENT, this.wrappedSet.size());
110         }
111         else
112         {
113             if (!c.isEmpty())
114             {
115                 fireEvent(OBJECT_CHANGED_EVENT, this.wrappedSet.size());
116             }
117         }
118         return changed;
119     }
120 
121     @Override
122     public boolean contains(final Object o)
123     {
124         return this.wrappedSet.contains(o);
125     }
126 
127     @Override
128     public boolean containsAll(final Collection<?> c)
129     {
130         return this.wrappedSet.containsAll(c);
131     }
132 
133     @Override
134     public EventProducingIterator<E> iterator()
135     {
136         EventProducingIterator<E> iterator = new EventProducingIterator<E>(this.wrappedSet.iterator());
137         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
138         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
139         return iterator;
140     }
141 
142     @Override
143     public void notify(final Event event) throws RemoteException
144     {
145         // pass through the OBJECT_REMOVED_EVENT from the iterator
146         if (event.getType().equals(EventProducingIterator.OBJECT_REMOVED_EVENT))
147         {
148             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
149         }
150     }
151 
152     @Override
153     public boolean remove(final Object o)
154     {
155         boolean changed = this.wrappedSet.remove(o);
156         if (changed)
157         {
158             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
159         }
160         return changed;
161     }
162 
163     @Override
164     public boolean removeAll(final Collection<?> c)
165     {
166         boolean changed = this.wrappedSet.removeAll(c);
167         if (changed)
168         {
169             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
170         }
171         return changed;
172     }
173 
174     @Override
175     public boolean retainAll(final Collection<?> c)
176     {
177         boolean changed = this.wrappedSet.retainAll(c);
178         if (changed)
179         {
180             fireEvent(OBJECT_REMOVED_EVENT, this.wrappedSet.size());
181         }
182         return changed;
183     }
184 
185     @Override
186     public Object[] toArray()
187     {
188         return this.wrappedSet.toArray();
189     }
190 
191     @Override
192     public <T> T[] toArray(final T[] a)
193     {
194         return this.wrappedSet.toArray(a);
195     }
196 
197 }