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   * Helper Map implementation for use with different Attribute Maps.
34   * 
35   * @author Anton Koinov (latest modification by $Author: struberg $)
36   * @version $Revision: 1190015 $ $Date: 2011-10-27 15:45:37 -0500 (Thu, 27 Oct 2011) $
37   */
38  public abstract class AbstractAttributeMap<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 = new ArrayList<String>();
48          for (final Enumeration<String> e = getAttributeNames(); e.hasMoreElements();)
49          {
50              names.add(e.nextElement());
51          }
52  
53          for (String name : names)
54          {
55              removeAttribute(name);
56          }
57      }
58  
59      @Override
60      public final boolean containsKey(final Object key)
61      {
62          return getAttribute(key.toString()) != null;
63      }
64  
65      @Override
66      public boolean containsValue(final Object findValue)
67      {
68          if (findValue == null)
69          {
70              return false;
71          }
72  
73          for (final Enumeration<String> e = getAttributeNames(); e.hasMoreElements();)
74          {
75              final Object value = getAttribute(e.nextElement());
76              if (findValue.equals(value))
77              {
78                  return true;
79              }
80          }
81  
82          return false;
83      }
84  
85      @Override
86      public Set<Entry<String, V>> entrySet()
87      {
88          if (_entrySet == null)
89          {
90              _entrySet = new EntrySet();
91          }
92          return _entrySet;
93      }
94  
95      @Override
96      public V get(final Object key)
97      {
98          return getAttribute(key.toString());
99      }
100 
101     @Override
102     public boolean isEmpty()
103     {
104         return !getAttributeNames().hasMoreElements();
105     }
106 
107     @Override
108     public Set<String> keySet()
109     {
110         if (_keySet == null)
111         {
112             _keySet = new KeySet();
113         }
114         return _keySet;
115     }
116 
117     @Override
118     public final V put(final String key, final V value)
119     {
120         final V retval = getAttribute(key);
121         setAttribute(key, value);
122         return retval;
123     }
124 
125     @Override
126     public void putAll(final Map<? extends String, ? extends V> t)
127     {
128         for (final Entry<? extends String, ? extends V> entry : t.entrySet())
129         {
130             setAttribute(entry.getKey(), entry.getValue());
131         }
132     }
133 
134     @Override
135     public final V remove(final Object key)
136     {
137         final String keyString = key.toString();
138         final V retval = getAttribute(keyString);
139         removeAttribute(keyString);
140         return retval;
141     }
142 
143     @Override
144     public int size()
145     {
146         int size = 0;
147         for (final Enumeration<String> e = getAttributeNames(); e.hasMoreElements();)
148         {
149             size++;
150             e.nextElement();
151         }
152         return size;
153     }
154 
155     @Override
156     public Collection<V> values()
157     {
158         if (_values == null)
159         {
160             _values = new Values();
161         }
162         return _values;
163     }
164 
165     abstract protected V getAttribute(String key);
166 
167     abstract protected void setAttribute(String key, V value);
168 
169     abstract protected void removeAttribute(String key);
170 
171     abstract protected Enumeration<String> getAttributeNames();
172 
173     private abstract class AbstractAttributeSet<E> extends AbstractSet<E>
174     {
175         @Override
176         public boolean isEmpty()
177         {
178             return AbstractAttributeMap.this.isEmpty();
179         }
180 
181         @Override
182         public int size()
183         {
184             return AbstractAttributeMap.this.size();
185         }
186 
187         @Override
188         public void clear()
189         {
190             AbstractAttributeMap.this.clear();
191         }
192     }
193 
194     private final class KeySet extends AbstractAttributeSet<String>
195     {
196         @Override
197         public Iterator<String> iterator()
198         {
199             return new KeyIterator();
200         }
201 
202         @Override
203         public boolean contains(final Object o)
204         {
205             return AbstractAttributeMap.this.containsKey(o);
206         }
207 
208         @Override
209         public boolean remove(final Object o)
210         {
211             return AbstractAttributeMap.this.remove(o) != null;
212         }
213 
214     }
215 
216     private abstract class AbstractAttributeIterator<E> implements Iterator<E>
217     {
218         protected final Enumeration<String> _e = getAttributeNames();
219         protected String _currentKey;
220 
221         public void remove()
222         {
223             // remove() may cause ConcurrentModificationException.
224             // We could throw an exception here, but not throwing an exception
225             // allows one call to remove() to succeed
226             if (_currentKey == null)
227             {
228                 throw new NoSuchElementException("You must call next() at least once");
229             }
230             AbstractAttributeMap.this.remove(_currentKey);
231         }
232 
233         public boolean hasNext()
234         {
235             return _e.hasMoreElements();
236         }
237 
238         public E next()
239         {
240             _currentKey = _e.nextElement();
241             return getValue(_currentKey);
242         }
243 
244         protected abstract E getValue(String attributeName);
245     }
246 
247     private final class KeyIterator extends AbstractAttributeIterator<String>
248     {
249         @Override
250         protected String getValue(final String attributeName)
251         {
252             return attributeName;
253         }
254     }
255 
256     private class Values extends AbstractAttributeSet<V>
257     {
258         @Override
259         public Iterator<V> iterator()
260         {
261             return new ValuesIterator();
262         }
263 
264         @Override
265         public boolean contains(final Object o)
266         {
267             if (o == null)
268             {
269                 return false;
270             }
271 
272             for (final Iterator<V> it = iterator(); it.hasNext();)
273             {
274                 if (o.equals(it.next()))
275                 {
276                     return true;
277                 }
278             }
279 
280             return false;
281         }
282 
283         @Override
284         public boolean remove(final Object o)
285         {
286             if (o == null)
287             {
288                 return false;
289             }
290 
291             for (final Iterator<V> it = iterator(); it.hasNext();)
292             {
293                 if (o.equals(it.next()))
294                 {
295                     it.remove();
296                     return true;
297                 }
298             }
299 
300             return false;
301         }
302     }
303 
304     private class ValuesIterator extends AbstractAttributeIterator<V>
305     {
306         @Override
307         protected V getValue(final String attributeName)
308         {
309             return AbstractAttributeMap.this.get(attributeName);
310         }
311     }
312 
313     private final class EntrySet extends AbstractAttributeSet<Entry<String, V>>
314     {
315         @Override
316         public Iterator<Entry<String, V>> iterator()
317         {
318             return new EntryIterator();
319         }
320 
321         @SuppressWarnings("unchecked")
322         @Override
323         public boolean contains(final Object o)
324         {
325             if (!(o instanceof Entry))
326             {
327                 return false;
328             }
329 
330             final Entry<String, V> entry = (Entry<String, V>)o;
331             final Object key = entry.getKey();
332             final Object value = entry.getValue();
333             if (key == null || value == null)
334             {
335                 return false;
336             }
337 
338             return value.equals(AbstractAttributeMap.this.get(key));
339         }
340 
341         @SuppressWarnings("unchecked")
342         @Override
343         public boolean remove(final Object o)
344         {
345             if (!(o instanceof Entry))
346             {
347                 return false;
348             }
349 
350             final Entry<String, V> entry = (Entry<String, V>)o;
351             final Object key = entry.getKey();
352             final Object value = entry.getValue();
353             if (key == null || value == null || !value.equals(AbstractAttributeMap.this.get(key)))
354             {
355                 return false;
356             }
357 
358             return AbstractAttributeMap.this.remove(((Entry<String, V>)o).getKey()) != null;
359         }
360     }
361 
362     /**
363      * Not very efficient since it generates a new instance of <code>Entry</code> for each element and still internaly
364      * uses the <code>KeyIterator</code>. It is more efficient to use the <code>KeyIterator</code> directly.
365      */
366     private final class EntryIterator extends AbstractAttributeIterator<Entry<String, V>>
367     {
368         @Override
369         protected Entry<String, V> getValue(final String attributeName)
370         {
371             // Must create new Entry every time--value of the entry must stay
372             // linked to the same attribute name
373             return new EntrySetEntry(attributeName);
374         }
375     }
376 
377     private final class EntrySetEntry implements Entry<String, V>
378     {
379         private final String _currentKey;
380 
381         public EntrySetEntry(final String currentKey)
382         {
383             _currentKey = currentKey;
384         }
385 
386         public String getKey()
387         {
388             return _currentKey;
389         }
390 
391         public V getValue()
392         {
393             return AbstractAttributeMap.this.get(_currentKey);
394         }
395 
396         public V setValue(final V value)
397         {
398             return AbstractAttributeMap.this.put(_currentKey, value);
399         }
400 
401         @Override
402         public int hashCode()
403         {
404             int result = 1;
405             result = 31 * result + ((_currentKey == null) ? 0 : _currentKey.hashCode());
406             return result;
407         }
408 
409         @Override
410         public boolean equals(final Object obj)
411         {
412             if (this == obj)
413             {
414                 return true;
415             }
416             if (obj == null)
417             {
418                 return false;
419             }
420             if (getClass() != obj.getClass())
421             {
422                 return false;
423             }
424             final EntrySetEntry other = (EntrySetEntry)obj;
425             if (_currentKey == null)
426             {
427                 if (other._currentKey != null)
428                 {
429                     return false;
430                 }
431             }
432             else if (!_currentKey.equals(other._currentKey))
433             {
434                 return false;
435             }
436             return true;
437         }
438 
439     }
440 }