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: lu4242 $)
36   * @version $Revision: 982465 $ $Date: 2010-08-04 23:39:32 -0500 (MiƩ, 04 Ago 2010) $
37   */
38  public abstract class AbstractThreadSafeAttributeMap<V> extends AbstractMap<String, V>
39  {
40      private Set<String> _keySet;
41      private Collection<V> _values;
42      private Set<Entry<String, V>> _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 != null) ? _entrySet : (_entrySet = new 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 != null) ? _keySet : (_keySet = new 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 key_ = key.toString();
126         final V retval = getAttribute(key_);
127         removeAttribute(key_);
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 != null) ? _values : (_values = new 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             return getValue(_currentKey = _i.next());
228         }
229 
230         protected abstract E getValue(String attributeName);
231     }
232 
233     private final class KeyIterator extends AbstractAttributeIterator<String>
234     {
235         @Override
236         protected String getValue(final String attributeName)
237         {
238             return attributeName;
239         }
240     }
241 
242     private class Values extends AbstractAttributeSet<V>
243     {
244         @Override
245         public Iterator<V> iterator()
246         {
247             return new ValuesIterator();
248         }
249 
250         @Override
251         public boolean contains(final Object o)
252         {
253             if (o == null)
254             {
255                 return false;
256             }
257 
258             for (final Iterator<V> it = iterator(); it.hasNext();)
259             {
260                 if (o.equals(it.next()))
261                 {
262                     return true;
263                 }
264             }
265 
266             return false;
267         }
268 
269         @Override
270         public boolean remove(final Object o)
271         {
272             if (o == null)
273             {
274                 return false;
275             }
276 
277             for (final Iterator<V> it = iterator(); it.hasNext();)
278             {
279                 if (o.equals(it.next()))
280                 {
281                     it.remove();
282                     return true;
283                 }
284             }
285 
286             return false;
287         }
288     }
289 
290     private class ValuesIterator extends AbstractAttributeIterator<V>
291     {
292         @Override
293         protected V getValue(final String attributeName)
294         {
295             return AbstractThreadSafeAttributeMap.this.get(attributeName);
296         }
297     }
298 
299     private final class EntrySet extends AbstractAttributeSet<Entry<String, V>>
300     {
301         @Override
302         public Iterator<Entry<String, V>> iterator()
303         {
304             return new EntryIterator();
305         }
306 
307         @SuppressWarnings("unchecked")
308         @Override
309         public boolean contains(final Object o)
310         {
311             if (!(o instanceof Entry))
312             {
313                 return false;
314             }
315 
316             final Entry<String, V> entry = (Entry<String, V>)o;
317             final Object key = entry.getKey();
318             final Object value = entry.getValue();
319             if (key == null || value == null)
320             {
321                 return false;
322             }
323 
324             return value.equals(AbstractThreadSafeAttributeMap.this.get(key));
325         }
326 
327         @SuppressWarnings("unchecked")
328         @Override
329         public boolean remove(final Object o)
330         {
331             if (!(o instanceof Entry))
332             {
333                 return false;
334             }
335 
336             final Entry<String, V> entry = (Entry<String, V>)o;
337             final Object key = entry.getKey();
338             final Object value = entry.getValue();
339             if (key == null || value == null || !value.equals(AbstractThreadSafeAttributeMap.this.get(key)))
340             {
341                 return false;
342             }
343 
344             return AbstractThreadSafeAttributeMap.this.remove(((Entry<String, V>)o).getKey()) != null;
345         }
346     }
347 
348     /**
349      * Not very efficient since it generates a new instance of <code>Entry</code> for each element and still internaly
350      * uses the <code>KeyIterator</code>. It is more efficient to use the <code>KeyIterator</code> directly.
351      */
352     private final class EntryIterator extends AbstractAttributeIterator<Entry<String, V>>
353     {
354         @Override
355         protected Entry<String, V> getValue(final String attributeName)
356         {
357             // Must create new Entry every time--value of the entry must stay
358             // linked to the same attribute name
359             return new EntrySetEntry(attributeName);
360         }
361     }
362 
363     private final class EntrySetEntry implements Entry<String, V>
364     {
365         private final String _currentKey;
366 
367         public EntrySetEntry(final String currentKey)
368         {
369             _currentKey = currentKey;
370         }
371 
372         public String getKey()
373         {
374             return _currentKey;
375         }
376 
377         public V getValue()
378         {
379             return AbstractThreadSafeAttributeMap.this.get(_currentKey);
380         }
381 
382         public V setValue(final V value)
383         {
384             return AbstractThreadSafeAttributeMap.this.put(_currentKey, value);
385         }
386 
387         @Override
388         public int hashCode()
389         {
390             int result = 1;
391             result = 31 * result + ((_currentKey == null) ? 0 : _currentKey.hashCode());
392             return result;
393         }
394 
395         @Override
396         public boolean equals(final Object obj)
397         {
398             if (this == obj)
399                 return true;
400             if (obj == null)
401                 return false;
402             if (getClass() != obj.getClass())
403                 return false;
404             final EntrySetEntry other = (EntrySetEntry)obj;
405             if (_currentKey == null)
406             {
407                 if (other._currentKey != null)
408                     return false;
409             }
410             else if (!_currentKey.equals(other._currentKey))
411                 return false;
412             return true;
413         }
414 
415     }
416 }