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: lu4242 $)
36   * @version $Revision: 1000662 $ $Date: 2010-09-23 18:11:42 -0500 (Thu, 23 Sep 2010) $
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          return (_entrySet != null) ? _entrySet : (_entrySet = new EntrySet());
89      }
90  
91      @Override
92      public V get(final Object key)
93      {
94          return getAttribute(key.toString());
95      }
96  
97      @Override
98      public boolean isEmpty()
99      {
100         return !getAttributeNames().hasMoreElements();
101     }
102 
103     @Override
104     public Set<String> keySet()
105     {
106         return (_keySet != null) ? _keySet : (_keySet = new KeySet());
107     }
108 
109     @Override
110     public final V put(final String key, final V value)
111     {
112         final V retval = getAttribute(key);
113         setAttribute(key, value);
114         return retval;
115     }
116 
117     @Override
118     public void putAll(final Map<? extends String, ? extends V> t)
119     {
120         for (final Entry<? extends String, ? extends V> entry : t.entrySet())
121         {
122             setAttribute(entry.getKey(), entry.getValue());
123         }
124     }
125 
126     @Override
127     public final V remove(final Object key)
128     {
129         final String key_ = key.toString();
130         final V retval = getAttribute(key_);
131         removeAttribute(key_);
132         return retval;
133     }
134 
135     @Override
136     public int size()
137     {
138         int size = 0;
139         for (final Enumeration<String> e = getAttributeNames(); e.hasMoreElements();)
140         {
141             size++;
142             e.nextElement();
143         }
144         return size;
145     }
146 
147     @Override
148     public Collection<V> values()
149     {
150         return (_values != null) ? _values : (_values = new Values());
151     }
152 
153     abstract protected V getAttribute(String key);
154 
155     abstract protected void setAttribute(String key, V value);
156 
157     abstract protected void removeAttribute(String key);
158 
159     abstract protected Enumeration<String> getAttributeNames();
160 
161     private abstract class AbstractAttributeSet<E> extends AbstractSet<E>
162     {
163         @Override
164         public boolean isEmpty()
165         {
166             return AbstractAttributeMap.this.isEmpty();
167         }
168 
169         @Override
170         public int size()
171         {
172             return AbstractAttributeMap.this.size();
173         }
174 
175         @Override
176         public void clear()
177         {
178             AbstractAttributeMap.this.clear();
179         }
180     }
181 
182     private final class KeySet extends AbstractAttributeSet<String>
183     {
184         @Override
185         public Iterator<String> iterator()
186         {
187             return new KeyIterator();
188         }
189 
190         @Override
191         public boolean contains(final Object o)
192         {
193             return AbstractAttributeMap.this.containsKey(o);
194         }
195 
196         @Override
197         public boolean remove(final Object o)
198         {
199             return AbstractAttributeMap.this.remove(o) != null;
200         }
201 
202     }
203 
204     private abstract class AbstractAttributeIterator<E> implements Iterator<E>
205     {
206         protected final Enumeration<String> _e = getAttributeNames();
207         protected String _currentKey;
208 
209         public void remove()
210         {
211             // remove() may cause ConcurrentModificationException.
212             // We could throw an exception here, but not throwing an exception
213             // allows one call to remove() to succeed
214             if (_currentKey == null)
215             {
216                 throw new NoSuchElementException("You must call next() at least once");
217             }
218             AbstractAttributeMap.this.remove(_currentKey);
219         }
220 
221         public boolean hasNext()
222         {
223             return _e.hasMoreElements();
224         }
225 
226         public E next()
227         {
228             return getValue(_currentKey = _e.nextElement());
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 AbstractAttributeMap.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(AbstractAttributeMap.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(AbstractAttributeMap.this.get(key)))
341             {
342                 return false;
343             }
344 
345             return AbstractAttributeMap.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 AbstractAttributeMap.this.get(_currentKey);
381         }
382 
383         public V setValue(final V value)
384         {
385             return AbstractAttributeMap.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                 return true;
401             if (obj == null)
402                 return false;
403             if (getClass() != obj.getClass())
404                 return false;
405             final EntrySetEntry other = (EntrySetEntry)obj;
406             if (_currentKey == null)
407             {
408                 if (other._currentKey != null)
409                     return false;
410             }
411             else if (!_currentKey.equals(other._currentKey))
412                 return false;
413             return true;
414         }
415 
416     }
417 }