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