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.HashSet;
24  import java.util.Iterator;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.apache.myfaces.trinidad.bean.FacesBean;
29  import org.apache.myfaces.trinidad.bean.PropertyKey;
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      if (_entries != null)
88        _entries._keys.remove(propertyKey);
89  
90      return oldValue;
91    }
92  
93    @Override
94    public Set<Map.Entry<String, Object>> entrySet()
95    {
96      if (_entries == null)
97      {
98        HashSet<PropertyKey> keySet = new HashSet<PropertyKey>();
99        keySet.addAll(_bean.keySet());
100       keySet.addAll(_bean.bindingKeySet());
101       _entries = new MakeEntries(keySet);
102     }
103 
104     return _entries;
105   }
106   
107   private Object _putInternal(PropertyKey propertyKey, Object value)
108   {
109     assert propertyKey != null;
110 
111     Object oldValue = _bean.getProperty(propertyKey);
112     _bean.setProperty(propertyKey, value);
113     if (_entries != null)
114     {
115       if (value == null)
116         _entries._keys.remove(propertyKey);
117       else
118         _entries._keys.add(propertyKey);
119     }
120 
121     return oldValue;
122   }
123 
124   private class MakeEntries extends AbstractSet<Map.Entry<String, Object>>
125   {
126     public MakeEntries(Set<PropertyKey> keys)
127     {
128       _keys = keys;
129     }
130 
131     @Override
132     public int size()
133     {
134       return _keys.size();
135     }
136 
137     @Override
138     public boolean remove(Object o)
139     {
140       if (!(o instanceof EntryImpl))
141         return false;
142 
143       String keyName = ((EntryImpl) o).getKey();
144       return (ValueMap.this.remove(keyName) != null);
145     }
146 
147     @Override
148     public Iterator<Map.Entry<String, Object>> iterator()
149     {
150       final Iterator<PropertyKey> base = _keys.iterator();
151       return new Iterator<Map.Entry<String, Object>>()
152       {
153         public boolean hasNext()
154         {
155           return base.hasNext();
156         }
157 
158         public Map.Entry<String, Object> next()
159         {
160           _lastEntry = new EntryImpl(base.next());
161           return _lastEntry;
162         }
163 
164         public void remove()
165         {
166           if (_lastEntry == null)
167             throw new IllegalStateException();
168           base.remove();
169           ValueMap.this.remove(_lastEntry.getKey());
170           _lastEntry = null;
171           //          throw new UnsupportedOperationException();
172           
173         }
174         
175         private EntryImpl _lastEntry;
176       };
177     }
178 
179     private Set<PropertyKey> _keys;
180   }
181 
182   private class EntryImpl implements Entry<String, Object>
183   {
184     public EntryImpl(PropertyKey key)
185     {
186       assert key != null;
187       
188       _key = key;
189     }
190 
191     public String getKey()
192     {
193       return _key.getName();
194     }
195 
196     public Object getValue()
197     {
198       return get(_key);
199     }
200 
201     public Object setValue(Object value)
202     {
203       return _putInternal(_key, value);
204     }
205 
206     @SuppressWarnings("unchecked")
207     @Override
208     public boolean equals(Object o)
209     {
210       if (o == this)
211         return true;
212 
213       if (!(o instanceof Map.Entry))
214         return false;
215 
216       Map.Entry<Object, Object> e = (Map.Entry<Object, Object>)o;
217       Object k1 = getKey();
218       Object k2 = e.getKey();
219       if (k1 == k2 || (k1 != null && k1.equals(k2)))
220       {
221         Object v1 = getValue();
222         Object v2 = e.getValue();
223         if (v1 == v2 || (v1 != null && v1.equals(v2))) 
224           return true;
225       }
226 
227       return false;
228     }
229     
230     @Override
231     public int hashCode()
232     {
233       Object value = getValue();
234       return _key.hashCode() ^ (value==null ? 0 : value.hashCode());
235     }
236 
237     private final PropertyKey _key;
238   }
239 
240   private PropertyKey _getPropertyKey(Object key)
241   {
242     if (key instanceof String)
243     {
244       String keyString = (String) key;
245       PropertyKey propertyKey = _bean.getType().findKey(keyString);
246       if (propertyKey == null)
247         propertyKey = PropertyKey.createPropertyKey(keyString);
248       
249       return propertyKey;
250     }
251     
252     if (key instanceof PropertyKey)
253     {
254       return (PropertyKey) key;
255     }
256     
257     throw new ClassCastException();
258   }
259 
260   private FacesBean    _bean;
261   private MakeEntries  _entries;
262 }