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.trinidad.bean.util;
20  
21  import java.util.AbstractMap;
22  import java.util.AbstractSet;
23  import java.util.Iterator;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import org.apache.myfaces.trinidad.bean.FacesBean;
28  import org.apache.myfaces.trinidad.bean.PropertyKey;
29  import org.apache.myfaces.trinidad.util.CollectionUtils;
30  
31  /**
32   * Map implementation that exposes the properties of a FacesBean
33   * as a Map.  This Map supports Iterator.remove(), treats
34   * putting a null value as identical to removing the value,
35   * but does not support null keys.  The keys may be either
36   * Strings or {@link PropertyKey}s, but all Map.Entry objects
37   * will return String keys.
38   *
39   */
40  public class ValueMap extends AbstractMap<String, Object>
41  {
42    public ValueMap(FacesBean bean)
43    {
44      _bean = bean;
45    }
46  
47    @Override
48    public Object get(Object key)
49    {
50      if (key == null)
51        throw new NullPointerException();
52  
53      PropertyKey propertyKey = _getPropertyKey(key);
54      // Support attribute transparency for list-based
55      // properties
56      if (propertyKey.isList())
57      {
58        Class<?> type = propertyKey.getType();
59        if (type.isArray())
60          type = type.getComponentType();
61  
62        return _bean.getEntries(propertyKey, type);
63      }
64      else
65      {
66        Object val = _bean.getProperty(propertyKey);
67        return (val != null) ? val : propertyKey.getDefault();
68      }
69    }
70  
71    @Override
72    public Object put(String key, Object value)
73    {
74      if (key == null)
75        throw new NullPointerException();
76  
77      return _putInternal(_getPropertyKey(key), value);
78    }
79    
80    // TODO Should remove just remove values, or also remove bindings?
81    @Override
82    public Object remove(Object key)
83    {
84      PropertyKey propertyKey = _getPropertyKey(key);
85      Object oldValue = _bean.getProperty(propertyKey);
86      _bean.setProperty(propertyKey, null);
87  
88      return oldValue;
89    }
90  
91    /**
92     * Override for better performance
93     * @param key
94     * @return
95     */
96    @Override
97    public boolean containsKey(Object key)
98    {
99      if (key == null)
100       throw new NullPointerException();
101 
102     PropertyKey propertyKey = _getPropertyKey(key);
103     
104     if (_bean.keySet().contains(propertyKey))
105       return true;
106     else
107       return _bean.bindingKeySet().contains(propertyKey);    
108   }
109 
110   @Override
111   public Set<Map.Entry<String, Object>> entrySet()
112   {
113     return CollectionUtils.overlappingCompositeSet(
114              new MakeEntries(_bean.keySet()),
115              new MakeEntries(_bean.bindingKeySet()));
116   }
117   
118   private Object _putInternal(PropertyKey propertyKey, Object value)
119   {
120     assert propertyKey != null;
121 
122     Object oldValue = _bean.getProperty(propertyKey);
123     _bean.setProperty(propertyKey, value);
124 
125     return oldValue;
126   }
127 
128   private class MakeEntries extends AbstractSet<Map.Entry<String, Object>>
129   {
130     public MakeEntries(Set<PropertyKey> keys)
131     {
132       _keys = keys;
133     }
134 
135     @Override
136     public int size()
137     {
138       return _keys.size();
139     }
140 
141     @Override
142     public Iterator<Map.Entry<String, Object>> iterator()
143     {
144       final Iterator<PropertyKey> base = _keys.iterator();
145       return new Iterator<Map.Entry<String, Object>>()
146       {
147         public boolean hasNext()
148         {
149           return base.hasNext();
150         }
151 
152         public Map.Entry<String, Object> next()
153         {
154           _lastEntry = new EntryImpl(base.next());
155           return _lastEntry;
156         }
157 
158         public void remove()
159         {
160           if (_lastEntry == null)
161             throw new IllegalStateException();
162           base.remove();
163           _lastEntry = null;
164         }
165         
166         private EntryImpl _lastEntry;
167       };
168     }
169 
170     private final Set<PropertyKey> _keys;
171   }
172 
173   private class EntryImpl implements Entry<String, Object>
174   {
175     public EntryImpl(PropertyKey key)
176     {
177       assert key != null;
178       
179       _key = key;
180     }
181 
182     public String getKey()
183     {
184       return _key.getName();
185     }
186 
187     public Object getValue()
188     {
189       return get(_key);
190     }
191 
192     public Object setValue(Object value)
193     {
194       return _putInternal(_key, value);
195     }
196 
197     @SuppressWarnings("unchecked")
198     @Override
199     public boolean equals(Object o)
200     {
201       if (o == this)
202         return true;
203 
204       if (!(o instanceof Map.Entry))
205         return false;
206 
207       Map.Entry<Object, Object> e = (Map.Entry<Object, Object>)o;
208       Object k1 = getKey();
209       Object k2 = e.getKey();
210       if (k1 == k2 || (k1 != null && k1.equals(k2)))
211       {
212         Object v1 = getValue();
213         Object v2 = e.getValue();
214         if (v1 == v2 || (v1 != null && v1.equals(v2))) 
215           return true;
216       }
217 
218       return false;
219     }
220     
221     @Override
222     public int hashCode()
223     {
224       Object value = getValue();
225       return _key.hashCode() ^ (value==null ? 0 : value.hashCode());
226     }
227 
228     private final PropertyKey _key;
229   }
230 
231   private PropertyKey _getPropertyKey(Object key)
232   {
233     if (key instanceof String)
234     {
235       String keyString = (String) key;
236       PropertyKey propertyKey = _bean.getType().findKey(keyString);
237       if (propertyKey == null)
238         propertyKey = PropertyKey.getDefaultPropertyKey(keyString);
239       
240       return propertyKey;
241     }
242     
243     if (key instanceof PropertyKey)
244     {
245       return (PropertyKey) key;
246     }
247     
248     throw new ClassCastException();
249   }
250 
251   private FacesBean    _bean;
252 }