View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.util;
20  
21  import java.util.AbstractMap;
22  import java.util.AbstractSet;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Enumeration;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.NoSuchElementException;
30  import java.util.Set;
31  
32  /**
33   * Helper Map implementation for use with different Attribute Maps.
34   * 
35   * @author Anton Koinov (latest modification by $Author: struberg $)
36   * @version $Revision: 1189926 $ $Date: 2011-10-27 13:36:29 -0500 (Thu, 27 Oct 2011) $
37   */
38  public abstract class AbstractThreadSafeAttributeMap<V> extends AbstractMap<String, V>
39  {
40      private Set<String> _keySet = new KeySet();
41      private Collection<V> _values =  new Values();
42      private Set<Entry<String, V>> _entrySet = new EntrySet();
43  
44      @Override
45      public void clear()
46      {
47          final List<String> names = Collections.list(getAttributeNames());
48  
49          for (String name : names)
50          {
51              removeAttribute(name);
52          }
53      }
54  
55      @Override
56      public final boolean containsKey(final Object key)
57      {
58          return getAttribute(key.toString()) != null;
59      }
60  
61      @Override
62      public boolean containsValue(final Object findValue)
63      {
64          if (findValue == null)
65          {
66              return false;
67          }
68  
69          for (final Enumeration<String> e = getAttributeNames(); e.hasMoreElements();)
70          {
71              final Object value = getAttribute(e.nextElement());
72              if (findValue.equals(value))
73              {
74                  return true;
75              }
76          }
77  
78          return false;
79      }
80  
81      @Override
82      public Set<Entry<String, V>> entrySet()
83      {
84          return _entrySet;
85      }
86  
87      @Override
88      public V get(final Object key)
89      {
90          return getAttribute(key.toString());
91      }
92  
93      @Override
94      public boolean isEmpty()
95      {
96          return !getAttributeNames().hasMoreElements();
97      }
98  
99      @Override
100     public Set<String> keySet()
101     {
102         return _keySet;
103     }
104 
105     @Override
106     public final V put(final String key, final V value)
107     {
108         final V retval = getAttribute(key);
109         setAttribute(key, value);
110         return retval;
111     }
112 
113     @Override
114     public void putAll(final Map<? extends String, ? extends V> t)
115     {
116         for (final Entry<? extends String, ? extends V> entry : t.entrySet())
117         {
118             setAttribute(entry.getKey(), entry.getValue());
119         }
120     }
121 
122     @Override
123     public final V remove(final Object key)
124     {
125         final String keyString = key.toString();
126         final V retval = getAttribute(keyString);
127         removeAttribute(keyString);
128         return retval;
129     }
130 
131     @Override
132     public int size()
133     {
134         int size = 0;
135         for (final Enumeration<String> e = getAttributeNames(); e.hasMoreElements();)
136         {
137             size++;
138             e.nextElement();
139         }
140         return size;
141     }
142 
143     @Override
144     public Collection<V> values()
145     {
146         return _values;
147     }
148 
149     abstract protected V getAttribute(String key);
150 
151     abstract protected void setAttribute(String key, V value);
152 
153     abstract protected void removeAttribute(String key);
154 
155     abstract protected Enumeration<String> getAttributeNames();
156 
157     private abstract class AbstractAttributeSet<E> extends AbstractSet<E>
158     {
159         @Override
160         public boolean isEmpty()
161         {
162             return AbstractThreadSafeAttributeMap.this.isEmpty();
163         }
164 
165         @Override
166         public int size()
167         {
168             return AbstractThreadSafeAttributeMap.this.size();
169         }
170 
171         @Override
172         public void clear()
173         {
174             AbstractThreadSafeAttributeMap.this.clear();
175         }
176     }
177 
178     private final class KeySet extends AbstractAttributeSet<String>
179     {
180         @Override
181         public Iterator<String> iterator()
182         {
183             return new KeyIterator();
184         }
185 
186         @Override
187         public boolean contains(final Object o)
188         {
189             return AbstractThreadSafeAttributeMap.this.containsKey(o);
190         }
191 
192         @Override
193         public boolean remove(final Object o)
194         {
195             return AbstractThreadSafeAttributeMap.this.remove(o) != null;
196         }
197 
198     }
199 
200     private abstract class AbstractAttributeIterator<E> implements Iterator<E>
201     {
202         // We use a copied version of the Enumeration from getAttributeNames()
203         // here, because directly using it might cause a ConcurrentModificationException
204         // when performing remove(). Note that we can do this since the Enumeration
205         // from getAttributeNames() will contain exactly the attribute names from the time
206         // getAttributeNames() was called and it will not be updated if attributes are 
207         // removed or added.
208         protected final Iterator<String> _i = Collections.list(getAttributeNames()).iterator();
209         protected String _currentKey;
210 
211         public void remove()
212         {
213             if (_currentKey == null)
214             {
215                 throw new NoSuchElementException("You must call next() at least once");
216             }
217             AbstractThreadSafeAttributeMap.this.remove(_currentKey);
218         }
219 
220         public boolean hasNext()
221         {
222             return _i.hasNext();
223         }
224 
225         public E next()
226         {
227             _currentKey = _i.next();
228             return getValue(_currentKey);
229         }
230 
231         protected abstract E getValue(String attributeName);
232     }
233 
234     private final class KeyIterator extends AbstractAttributeIterator<String>
235     {
236         @Override
237         protected String getValue(final String attributeName)
238         {
239             return attributeName;
240         }
241     }
242 
243     private class Values extends AbstractAttributeSet<V>
244     {
245         @Override
246         public Iterator<V> iterator()
247         {
248             return new ValuesIterator();
249         }
250 
251         @Override
252         public boolean contains(final Object o)
253         {
254             if (o == null)
255             {
256                 return false;
257             }
258 
259             for (final Iterator<V> it = iterator(); it.hasNext();)
260             {
261                 if (o.equals(it.next()))
262                 {
263                     return true;
264                 }
265             }
266 
267             return false;
268         }
269 
270         @Override
271         public boolean remove(final Object o)
272         {
273             if (o == null)
274             {
275                 return false;
276             }
277 
278             for (final Iterator<V> it = iterator(); it.hasNext();)
279             {
280                 if (o.equals(it.next()))
281                 {
282                     it.remove();
283                     return true;
284                 }
285             }
286 
287             return false;
288         }
289     }
290 
291     private class ValuesIterator extends AbstractAttributeIterator<V>
292     {
293         @Override
294         protected V getValue(final String attributeName)
295         {
296             return AbstractThreadSafeAttributeMap.this.get(attributeName);
297         }
298     }
299 
300     private final class EntrySet extends AbstractAttributeSet<Entry<String, V>>
301     {
302         @Override
303         public Iterator<Entry<String, V>> iterator()
304         {
305             return new EntryIterator();
306         }
307 
308         @SuppressWarnings("unchecked")
309         @Override
310         public boolean contains(final Object o)
311         {
312             if (!(o instanceof Entry))
313             {
314                 return false;
315             }
316 
317             final Entry<String, V> entry = (Entry<String, V>)o;
318             final Object key = entry.getKey();
319             final Object value = entry.getValue();
320             if (key == null || value == null)
321             {
322                 return false;
323             }
324 
325             return value.equals(AbstractThreadSafeAttributeMap.this.get(key));
326         }
327 
328         @SuppressWarnings("unchecked")
329         @Override
330         public boolean remove(final Object o)
331         {
332             if (!(o instanceof Entry))
333             {
334                 return false;
335             }
336 
337             final Entry<String, V> entry = (Entry<String, V>)o;
338             final Object key = entry.getKey();
339             final Object value = entry.getValue();
340             if (key == null || value == null || !value.equals(AbstractThreadSafeAttributeMap.this.get(key)))
341             {
342                 return false;
343             }
344 
345             return AbstractThreadSafeAttributeMap.this.remove(((Entry<String, V>)o).getKey()) != null;
346         }
347     }
348 
349     /**
350      * Not very efficient since it generates a new instance of <code>Entry</code> for each element and still internaly
351      * uses the <code>KeyIterator</code>. It is more efficient to use the <code>KeyIterator</code> directly.
352      */
353     private final class EntryIterator extends AbstractAttributeIterator<Entry<String, V>>
354     {
355         @Override
356         protected Entry<String, V> getValue(final String attributeName)
357         {
358             // Must create new Entry every time--value of the entry must stay
359             // linked to the same attribute name
360             return new EntrySetEntry(attributeName);
361         }
362     }
363 
364     private final class EntrySetEntry implements Entry<String, V>
365     {
366         private final String _currentKey;
367 
368         public EntrySetEntry(final String currentKey)
369         {
370             _currentKey = currentKey;
371         }
372 
373         public String getKey()
374         {
375             return _currentKey;
376         }
377 
378         public V getValue()
379         {
380             return AbstractThreadSafeAttributeMap.this.get(_currentKey);
381         }
382 
383         public V setValue(final V value)
384         {
385             return AbstractThreadSafeAttributeMap.this.put(_currentKey, value);
386         }
387 
388         @Override
389         public int hashCode()
390         {
391             int result = 1;
392             result = 31 * result + ((_currentKey == null) ? 0 : _currentKey.hashCode());
393             return result;
394         }
395 
396         @Override
397         public boolean equals(final Object obj)
398         {
399             if (this == obj)
400             {
401                 return true;
402             }
403             if (obj == null)
404             {
405                 return false;
406             }
407             if (getClass() != obj.getClass())
408             {
409                 return false;
410             }
411             final EntrySetEntry other = (EntrySetEntry)obj;
412             if (_currentKey == null)
413             {
414                 if (other._currentKey != null)
415                 {
416                     return false;
417                 }
418             }
419             else if (!_currentKey.equals(other._currentKey))
420             {
421                 return false;
422             }
423             return true;
424         }
425 
426     }
427 }