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  
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-2020 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 EventProducer implements EventListenerInterface, 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>EventType OBJECT_ADDED_EVENT = new EventType("OBJECT_ADDED_EVENT");
39  
40      /** OBJECT_REMOVED_EVENT is fired on removal of entries. */
41      public static final EventTypeventType OBJECT_REMOVED_EVENT = new EventType("OBJECT_REMOVED_EVENT");
42  
43      /** OBJECT_CHANGED_EVENT is fired on change of one or more entries. */
44      public static final EventTypeventType OBJECT_CHANGED_EVENT = new EventType("OBJECT_CHANGED_EVENT");
45  
46      /** the parent set. */
47      private Set<E> parent = null;
48  
49      /** the function that produces the id by which the EventProducer can be identified. */
50      private final IdProvider sourceIdProvider;
51  
52      /**
53       * Constructs a new EventProducingSet.
54       * @param parent Set&lt;E&gt;; the parent set.
55       * @param sourceId Serializable; the id by which the EventProducer can be identified by the EventListener
56       */
57      public EventProducingSet(final Set<E> parent, final Serializable sourceId)
58      {
59          this(parent, new IdProvider()
60          {
61              /** */
62              private static final long serialVersionUID = 20200119L;
63  
64              @Override
65              public Serializable id()
66              {
67                  return sourceId;
68              }
69          });
70      }
71  
72      /**
73       * Constructs a new EventProducingSet.
74       * @param parent Set&lt;T&gt;; the parent set.
75       * @param sourceIdProvider IdProvider; the function that produces the id by which the EventProducer can be identified by the
76       *            EventListener
77       */
78      public EventProducingSet(final Set<E> parent, final IdProvider sourceIdProvider)
79      {
80          Throw.whenNull(parent, "parent cannot be null");
81          Throw.whenNull(sourceIdProvider, "sourceIdprovider cannot be null");
82          this.parent = parent;
83          this.sourceIdProvider = sourceIdProvider;
84      }
85  
86      /** {@inheritDoc} */
87      @Override
88      public Serializable getSourceId()
89      {
90          return this.sourceIdProvider.id();
91      }
92  
93      /** {@inheritDoc} */
94      @Override
95      public int size()
96      {
97          return this.parent.size();
98      }
99  
100     /** {@inheritDoc} */
101     @Override
102     public boolean isEmpty()
103     {
104         return this.parent.isEmpty();
105     }
106 
107     /** {@inheritDoc} */
108     @Override
109     public void clear()
110     {
111         int nr = this.parent.size();
112         this.parent.clear();
113         if (nr != this.parent.size())
114         {
115             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
116         }
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public boolean add(final E o)
122     {
123         boolean changed = this.parent.add(o);
124         if (changed)
125         {
126             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
127         }
128         else
129         {
130             this.fireEvent(OBJECT_CHANGED_EVENT, this.parent.size());
131         }
132         return changed;
133     }
134 
135     /** {@inheritDoc} */
136     @Override
137     public boolean addAll(final Collection<? extends E> c)
138     {
139         boolean changed = this.parent.addAll(c);
140         if (changed)
141         {
142             this.fireEvent(OBJECT_ADDED_EVENT, this.parent.size());
143         }
144         else
145         {
146             if (!c.isEmpty())
147             {
148                 this.fireEvent(OBJECT_CHANGED_EVENT, this.parent.size());
149             }
150         }
151         return changed;
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public boolean contains(final Object o)
157     {
158         return this.parent.contains(o);
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     public boolean containsAll(final Collection<?> c)
164     {
165         return this.parent.containsAll(c);
166     }
167 
168     /** {@inheritDoc} */
169     @Override
170     public EventProducingIterator<E> iterator()
171     {
172         EventProducingIterator<E> iterator = new EventProducingIterator<E>(this.parent.iterator(), this.sourceIdProvider);
173         // WEAK reference as an iterator is usually local and should be eligible for garbage collection
174         iterator.addListener(this, EventProducingIterator.OBJECT_REMOVED_EVENT, ReferenceType.WEAK);
175         return iterator;
176     }
177 
178     /** {@inheritDoc} */
179     @Override
180     public void notify(final EventInterface event) throws RemoteException
181     {
182         // pass through the OBJECT_REMOVED_EVENT from the iterator
183         if (event.getType().equals(EventProducingIterator.OBJECT_REMOVED_EVENT))
184         {
185             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
186         }
187     }
188 
189     /** {@inheritDoc} */
190     @Override
191     public boolean remove(final Object o)
192     {
193         boolean changed = this.parent.remove(o);
194         if (changed)
195         {
196             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
197         }
198         return changed;
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public boolean removeAll(final Collection<?> c)
204     {
205         boolean changed = this.parent.removeAll(c);
206         if (changed)
207         {
208             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
209         }
210         return changed;
211     }
212 
213     /** {@inheritDoc} */
214     @Override
215     public boolean retainAll(final Collection<?> c)
216     {
217         boolean changed = this.parent.retainAll(c);
218         if (changed)
219         {
220             this.fireEvent(OBJECT_REMOVED_EVENT, this.parent.size());
221         }
222         return changed;
223     }
224 
225     /** {@inheritDoc} */
226     @Override
227     public Object[] toArray()
228     {
229         return this.parent.toArray();
230     }
231 
232     /** {@inheritDoc} */
233     @Override
234     public <T> T[] toArray(final T[] a)
235     {
236         return this.parent.toArray(a);
237     }
238 }