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.ArrayList;
24  import java.util.Collection;
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  /**
34   * Helper Map implementation for use with different Attribute Maps.
35   * 
36   * @author Anton Koinov (latest modification by $Author: lu4242 $)
37   * @version $Revision: 982465 $ $Date: 2010-08-04 23:39:32 -0500 (MiƩ, 04 Ago 2010) $
38   */
39  public abstract class AbstractThreadSafeAttributeMap extends AbstractMap
40  {
41      private Set              _keySet;
42      private Collection       _values;
43      private Set              _entrySet;
44  
45      public void clear()
46      {
47          final List names = _list(getAttributeNames());
48  
49          for (Iterator it = names.iterator(); it.hasNext();)
50          {
51              removeAttribute((String) it.next());
52          }
53      }
54  
55      public boolean containsKey(Object key)
56      {
57          return getAttribute(key.toString()) != null;
58      }
59  
60      public boolean containsValue(Object findValue)
61      {
62          if (findValue == null)
63          {
64              return false;
65          }
66  
67          for (Enumeration e = getAttributeNames(); e.hasMoreElements();)
68          {
69              Object value = getAttribute((String) e.nextElement());
70              if (findValue.equals(value))
71              {
72                  return true;
73              }
74          }
75  
76          return false;
77      }
78  
79      public Set entrySet()
80      {
81          return (_entrySet != null) ? _entrySet : (_entrySet = new EntrySet());
82      }
83  
84      public Object get(Object key)
85      {
86          return getAttribute(key.toString());
87      }
88  
89      public boolean isEmpty()
90      {
91          return !getAttributeNames().hasMoreElements();
92      }
93  
94      public Set keySet()
95      {
96          return (_keySet != null) ? _keySet : (_keySet = new KeySet());
97      }
98  
99      public Object put(Object key, Object value)
100     {
101         String key_ = key.toString();
102         Object retval = getAttribute(key_);
103         setAttribute(key_, value);
104         return retval;
105     }
106 
107     public void putAll(Map t)
108     {
109         for (Iterator it = t.entrySet().iterator(); it.hasNext();)
110         {
111             Entry entry = (Entry) it.next();
112             setAttribute(entry.getKey().toString(), entry.getValue());
113         }
114     }
115 
116     public Object remove(Object key)
117     {
118         String key_ = key.toString();
119         Object retval = getAttribute(key_);
120         removeAttribute(key_);
121         return retval;
122     }
123 
124     public int size()
125     {
126         int size = 0;
127         for (Enumeration e = getAttributeNames(); e.hasMoreElements();)
128         {
129             size++;
130             e.nextElement();
131         }
132         return size;
133     }
134 
135     public Collection values()
136     {
137         return (_values != null) ? _values : (_values = new Values());
138     }
139     
140     /**
141      * Collections.list() from JDK 1.4
142      */
143     private ArrayList _list(Enumeration e) {
144         ArrayList l = new ArrayList();
145         while (e.hasMoreElements())
146         {
147             l.add(e.nextElement());
148         }
149         return l;
150     }
151     
152 
153     abstract protected Object getAttribute(String key);
154 
155     abstract protected void setAttribute(String key, Object value);
156 
157     abstract protected void removeAttribute(String key);
158 
159     abstract protected Enumeration getAttributeNames();
160 
161 
162     private class KeySet extends AbstractSet
163     {
164         public Iterator iterator()
165         {
166             return new KeyIterator();
167         }
168 
169         public boolean isEmpty()
170         {
171             return AbstractThreadSafeAttributeMap.this.isEmpty();
172         }
173 
174         public int size()
175         {
176             return AbstractThreadSafeAttributeMap.this.size();
177         }
178 
179         public boolean contains(Object o)
180         {
181             return AbstractThreadSafeAttributeMap.this.containsKey(o);
182         }
183 
184         public boolean remove(Object o)
185         {
186             return AbstractThreadSafeAttributeMap.this.remove(o) != null;
187         }
188 
189         public void clear()
190         {
191             AbstractThreadSafeAttributeMap.this.clear();
192         }
193     }
194 
195     private class KeyIterator
196         implements Iterator
197     {
198         // We use a copied version of the Enumeration from getAttributeNames()
199         // here, because directly using it might cause a ConcurrentModificationException
200         // when performing remove(). Note that we can do this since the Enumeration
201         // from getAttributeNames() will contain exactly the attribute names from the time
202         // getAttributeNames() was called and it will not be updated if attributes are 
203         // removed or added.
204         protected final Iterator _i = _list(getAttributeNames()).iterator();
205         protected Object _currentKey;
206 
207         public void remove()
208         {
209             if (_currentKey == null)
210             {
211                 throw new NoSuchElementException(
212                     "You must call next() at least once");
213             }
214             AbstractThreadSafeAttributeMap.this.remove(_currentKey);
215         }
216 
217         public boolean hasNext()
218         {
219             return _i.hasNext();
220         }
221 
222         public Object next()
223         {
224             return _currentKey = _i.next();
225         }
226     }
227 
228     private class Values extends KeySet
229     {
230         public Iterator iterator()
231         {
232             return new ValuesIterator();
233         }
234 
235         public boolean contains(Object o)
236         {
237             return AbstractThreadSafeAttributeMap.this.containsValue(o);
238         }
239 
240         public boolean remove(Object o)
241         {
242             if (o == null)
243             {
244                 return false;
245             }
246 
247             for (Iterator it = iterator(); it.hasNext();)
248             {
249                 if (o.equals(it.next()))
250                 {
251                     it.remove();
252                     return true;
253                 }
254             }
255 
256             return false;
257         }
258     }
259 
260     private class ValuesIterator extends KeyIterator
261     {
262         public Object next()
263         {
264             super.next();
265             return AbstractThreadSafeAttributeMap.this.get(_currentKey);
266         }
267     }
268 
269     private class EntrySet extends KeySet
270     {
271         public Iterator iterator() {
272             return new EntryIterator();
273         }
274 
275         public boolean contains(Object o) {
276             if (!(o instanceof Entry))
277             {
278                 return false;
279             }
280 
281             Entry entry = (Entry) o;
282             Object key = entry.getKey();
283             Object value = entry.getValue();
284             if (key == null || value == null)
285             {
286                 return false;
287             }
288 
289             return value.equals(AbstractThreadSafeAttributeMap.this.get(key));
290         }
291 
292         public boolean remove(Object o) {
293             if (!(o instanceof Entry))
294             {
295                 return false;
296             }
297 
298             Entry entry = (Entry) o;
299             Object key = entry.getKey();
300             Object value = entry.getValue();
301             if (key == null || value == null
302                 || !value.equals(AbstractThreadSafeAttributeMap.this.get(key)))
303             {
304                 return false;
305             }
306 
307             return AbstractThreadSafeAttributeMap.this.remove(((Entry) o).getKey()) != null;
308         }
309     }
310 
311     /**
312      * Not very efficient since it generates a new instance of <code>Entry</code>
313      * for each element and still internaly uses the <code>KeyIterator</code>.
314      * It is more efficient to use the <code>KeyIterator</code> directly.
315      */
316     private class EntryIterator extends KeyIterator
317     {
318         public Object next()
319         {
320             super.next();
321             // Must create new Entry every time--value of the entry must stay
322             // linked to the same attribute name
323             return new EntrySetEntry(_currentKey);
324         }
325     }
326 
327     private class EntrySetEntry implements Entry
328     {
329         private final Object _currentKey;
330 
331         public EntrySetEntry(Object currentKey)
332         {
333             _currentKey = currentKey;
334         }
335 
336         public Object getKey()
337         {
338             return _currentKey;
339         }
340 
341         public Object getValue()
342         {
343             return AbstractThreadSafeAttributeMap.this.get(_currentKey);
344         }
345 
346         public Object setValue(Object value)
347         {
348             return AbstractThreadSafeAttributeMap.this.put(_currentKey, value);
349         }
350 
351         public int hashCode() {
352             return _currentKey == null ? 0 : _currentKey.hashCode();
353         }
354 
355         public boolean equals(Object obj) {
356             if (!(obj instanceof EntrySetEntry))
357                 return false;
358             return _currentKey != null && _currentKey.equals(obj);
359         }
360     }
361 }