View Javadoc

1   /*
2      Copyright 2007 Ramon Servadei
3   
4      Licensed under the Apache License, Version 2.0 (the "License");
5      you may not use this file except in compliance with the License.
6      You may obtain a copy of the License at
7   
8          http://www.apache.org/licenses/LICENSE-2.0
9   
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15   */
16  package fulmine.util.collection;
17  
18  import java.util.Collection;
19  import java.util.Iterator;
20  import java.util.Map;
21  import java.util.Set;
22  import java.util.Map.Entry;
23  
24  import fulmine.IAddressable;
25  import fulmine.util.reference.is;
26  
27  /**
28   * A collection implementation that stores and identifies objects based on their
29   * {@link AbstractCoalescingCollection#getSystemIdentity(Object) identity}
30   * within a {@link Map}. This provides coalescing of objects that are added to
31   * the event store, like a set but based on the system identity of the object
32   * rather than the hashcode.
33   * <p>
34   * This class is not thread aware and is not thread safe.
35   * 
36   * @author Ramon Servadei
37   * 
38   * @param <E>
39   *            the element type this collection operates with, a sub-class of
40   *            {@link IAddressable}
41   * @see System#identityHashCode(Object)
42   */
43  public abstract class AbstractCoalescingCollection<E extends IAddressable>
44      implements Collection<E>
45  {
46  
47      /**
48       * An internal iterator implementation that provides access to the values of
49       * the data map in the order they were inserted. Delegates to a map entry
50       * iterator of the data map but exposes only the value of the map entry the
51       * delegate iterator returns.
52       * 
53       * @author Ramon Servadei
54       * 
55       * @param <E>
56       */
57      private final class DelegateIterator implements Iterator<E>
58      {
59          private final Iterator<Entry<Object, E>> delegate;
60  
61          private DelegateIterator(Iterator<Entry<Object, E>> delegate)
62          {
63              super();
64              this.delegate = delegate;
65          }
66  
67          public boolean hasNext()
68          {
69              return delegate.hasNext();
70          }
71  
72          public E next()
73          {
74              return delegate.next().getValue();
75          }
76  
77          public void remove()
78          {
79              delegate.remove();
80          }
81      }
82  
83      protected final Map<Object, E> data;
84  
85      /**
86       * Constructor to pass in the data map implementation
87       * 
88       * @param data
89       *            the data map implementation backing this collection
90       */
91      protected AbstractCoalescingCollection(Map<Object, E> data)
92      {
93          this.data = data;
94      }
95  
96      public boolean add(E e)
97      {
98          Object sysId = AbstractCoalescingCollection.getSystemIdentity(e);
99          final E value = this.data.put(sysId, e);
100         return value == null || value != e;
101     }
102 
103     public boolean addAll(Collection<? extends E> c)
104     {
105         boolean changed = false;
106         if (c != null)
107         {
108             for (E e : c)
109             {
110                 changed |= add(e);
111             }
112         }
113         return changed;
114     }
115 
116     public void clear()
117     {
118         this.data.clear();
119     }
120 
121     public boolean contains(Object o)
122     {
123         return this.data.containsKey(AbstractCoalescingCollection.getSystemIdentity(o));
124     }
125 
126     public boolean containsAll(Collection<?> c)
127     {
128         return this.data.values().containsAll(c);
129     }
130 
131     public boolean isEmpty()
132     {
133         return this.data.isEmpty();
134     }
135 
136     public Iterator<E> iterator()
137     {
138         return new DelegateIterator(this.data.entrySet().iterator());
139     }
140 
141     public boolean remove(Object o)
142     {
143         return this.data.remove(AbstractCoalescingCollection.getSystemIdentity(o)) != null;
144     }
145 
146     public boolean removeAll(Collection<?> c)
147     {
148         boolean changed = false;
149         if (c != null)
150         {
151             for (Object object : c)
152             {
153                 changed |= remove(object);
154             }
155         }
156         return changed;
157     }
158 
159     /**
160      * Unsupported.
161      * 
162      * @throws UnsupportedOperationException
163      */
164     public boolean retainAll(Collection<?> c)
165     {
166         throw new UnsupportedOperationException();
167     }
168 
169     public int size()
170     {
171         return this.data.size();
172     }
173 
174     public Object[] toArray()
175     {
176         Object[] array = new Object[this.data.size()];
177         final Set<Entry<Object, E>> entrySet = this.data.entrySet();
178         int i = 0;
179         for (Entry<Object, E> entry : entrySet)
180         {
181             array[i++] = entry.getValue();
182         }
183         return array;
184     }
185 
186     @SuppressWarnings("unchecked")
187     public <T> T[] toArray(T[] array)
188     {
189         final Set<Entry<Object, E>> entrySet = this.data.entrySet();
190         int i = 0;
191         for (Entry<Object, E> entry : entrySet)
192         {
193             array[i++] = (T) entry.getValue();
194         }
195         return array;
196     }
197 
198     @Override
199     public final String toString()
200     {
201         return getClass().getSimpleName()
202             + CollectionUtils.toFormattedString(this.data.keySet()).toString();
203     }
204 
205     @Override
206     public final int hashCode()
207     {
208         final int prime = 31;
209         int result = 1;
210         result =
211             prime * result + ((this.data == null) ? 0 : this.data.hashCode());
212         return result;
213     }
214 
215     @SuppressWarnings("unchecked")
216     @Override
217     public final boolean equals(Object obj)
218     {
219         if (is.same(this, obj))
220         {
221             return true;
222         }
223         if (!(obj instanceof AbstractCoalescingCollection))
224         {
225             return false;
226         }
227         final AbstractCoalescingCollection other =
228             (AbstractCoalescingCollection) obj;
229         return is.eq(this.data, other.data);
230     }
231 
232     /**
233      * Get the system identity for the object. If the object implements
234      * {@link IAddressable}, then the identity is formed of the
235      * {@link IAddressable#getIdentity()}, {@link IAddressable#getType()} and
236      * {@link IAddressable#getDomain()}, otherwise a platform dependent system
237      * identifier mechanism is invoked on the object.
238      * 
239      * @param object
240      *            the object to get the system identity for
241      * @return the {@link String} identity representing the unique system
242      *         identifier for the object.
243      */
244     public static final String getSystemIdentity(Object object)
245     {
246         if (object instanceof IAddressable)
247         {
248             final IAddressable addressable = ((IAddressable) object);
249             return addressable.getAddress();
250         }
251         final int identityHashCode = System.identityHashCode(object);
252         return Integer.toString(identityHashCode);
253     }
254 
255 }