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.HashMap;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import javax.el.ValueExpression;
26  
27  import javax.faces.component.PartialStateHolder;
28  import javax.faces.context.FacesContext;
29  
30  import org.apache.myfaces.trinidad.bean.FacesBean;
31  import org.apache.myfaces.trinidad.bean.PropertyKey;
32  import org.apache.myfaces.trinidad.bean.PropertyMap;
33  
34  
35  public class PropertyHashMap extends HashMap<PropertyKey,Object>
36                               implements PropertyMap
37  {
38    public PropertyHashMap(
39      PropertyMap map)
40    {
41      super(map);
42    }
43  
44    public PropertyHashMap(
45      int   initialCapacity,
46      float loadFactor)
47    {
48      super(initialCapacity, loadFactor);
49   }
50  
51    public PropertyHashMap(
52      int initialCapacity)
53    {
54      super(initialCapacity);
55    }
56  
57    public PropertyHashMap()
58    {
59      super();
60    }
61  
62    @Override
63     public Object put(
64       PropertyKey key,
65       Object      value)
66     {
67       Object retValue = super.put(key, value);
68       if (_createDeltas())
69       {
70         
71         if ( key.getMutable().isAtLeastSometimesMutable() || !_equals(value, retValue))
72           _deltas.put(key, value);
73       }
74       else if (key.getMutable().isAtLeastSometimesMutable() && !(value instanceof ValueExpression))
75       {
76         _getMutableTracker(true).addProperty(key);
77       }
78       
79       if (key.isPartialStateHolder())
80       {
81         _getPartialStateHolderTracker(true).addProperty(key);
82       }
83  
84       return retValue;
85     }
86  
87    @Override
88     public Object remove(
89       Object key)
90     {
91       boolean useDeltas = _createDeltas();
92      
93       if (useDeltas)
94       {
95         if (!super.containsKey(key))
96           return null;
97  
98         // If this key is contained, it certainly must be a PropertyKey!
99         assert(key instanceof PropertyKey);
100        _deltas.put((PropertyKey) key, null);
101      }
102      
103      if (key instanceof PropertyKey)
104      {
105        PropertyKey propKey  = (PropertyKey)key;
106        if (propKey.isPartialStateHolder())
107        {
108          _getPartialStateHolderTracker(true).removeProperty(propKey);
109        }
110        
111        if (!useDeltas &&  propKey.getMutable().isAtLeastSometimesMutable())
112        {
113          PropertyTracker mutableTracker = _getMutableTracker(false);
114          
115          if (mutableTracker != null)
116            mutableTracker.removeProperty(propKey);
117        }
118      }
119 
120      return super.remove(key);
121    }
122 
123   @Override
124   public void putAll(Map<? extends PropertyKey, ? extends Object> t)
125   {
126     boolean useDeltas =_createDeltas();
127     
128     if (useDeltas)
129       _deltas.putAll(t);
130 
131     Set<? extends PropertyKey> keys = t.keySet();
132     for (PropertyKey key: keys)
133     {
134       if (key.isPartialStateHolder())
135       {
136         _getPartialStateHolderTracker(true).addProperty(key);
137       }  
138       
139       if (!useDeltas && key.getMutable().isAtLeastSometimesMutable())
140       {
141         Object value = t.get(key);
142         
143         if (!(value instanceof ValueExpression))
144         {
145           _getMutableTracker(true).addProperty(key);
146         }
147       }
148 
149     }
150 
151     super.putAll(t);
152   }
153 
154   public Object saveState(FacesContext context)
155   {
156     if (_initialStateMarked)
157     {    
158       if (_deltas == null)
159       {
160         // if deltas is null still call _createDeltaPropertyMap but pass in false in case there
161         // are mutable or partialStateHolder attributes
162         _deltas = _createDeltaPropertyMap(false);
163       }
164       
165       if (_deltas == null)
166         return null;
167 
168       return StateUtils.saveState(_deltas, context, getUseStateHolder());
169     }
170     else
171     {
172       return StateUtils.saveState(this, context, getUseStateHolder());
173     }
174   }
175 
176   public void restoreState(
177     FacesContext context,
178     FacesBean.Type type,
179     Object state)
180   {
181     StateUtils.restoreState(this, context, type, state, getUseStateHolder());
182   }
183 
184   protected PropertyMap createDeltaPropertyMap()
185   {
186     return _createDeltaPropertyMap(true);
187   }
188   
189   /**
190    *
191    * @param createAlways if createAlways is true then always create the map. 
192    *        If createAlways is false then only create the map if one of the trackers are empty
193    */
194   private PropertyMap _createDeltaPropertyMap(boolean createAlways)
195   {  
196     PropertyTracker tracker = _getMutableTracker(false);
197     PropertyTracker partialTracker = _getPartialStateHolderTracker(false);    
198    
199     // if createAlways is true then always create the map.
200     // if createAlways is false then only create the map if one of the trackers are empty
201     if (createAlways || tracker != null || partialTracker != null)
202     {
203       PropertyHashMap map = new PropertyHashMap(2);
204       map.setUseStateHolder(getUseStateHolder());
205       map.setType(_type);
206         
207       if (tracker != null)
208       {      
209         for (PropertyKey key: tracker)
210         {
211           Object val = get(key);
212           
213           if (val != null)
214           {
215             map.put(key, val);
216           }
217         }
218         
219         _mutableTracker = null;
220       }
221   
222       
223       if (partialTracker != null)
224       {      
225         for (PropertyKey key: partialTracker)
226         {
227           // the key might have been in the mutable tracker, so check if the map already contains the key          
228           if(!map.containsKey(key))
229           {
230             Object val = get(key);
231             
232             if (val != null)
233             {
234               map.put(key, val);
235             }
236           }
237         }    
238       }
239       
240       return map;
241     }
242     
243     return null;
244   }
245 
246   public boolean getUseStateHolder()
247   {
248     return _useStateHolder;
249   }
250 
251   public void setUseStateHolder(boolean useStateHolder)
252   {
253     _useStateHolder = useStateHolder;
254   }
255 
256   // =-=AEW CLEAR?
257 
258   public void markInitialState()
259   {
260     _initialStateMarked = true;
261     
262     // PropertyTracker uses a bitmask to track properties
263     // We are tracking all properties that have CA_PARTIAL_STATE_HOLDER capability,
264     // so that we do not have to check every property here
265     PropertyTracker tracker = _getPartialStateHolderTracker(false);
266     if (tracker != null)
267     {
268       for (PropertyKey key: tracker)
269       {
270         Object val = get(key);
271         if (val != null)
272         {
273           ((PartialStateHolder)val).markInitialState();
274         }
275       }
276     }
277   }
278 
279   public void clearInitialState()
280   {
281     _initialStateMarked = false;
282     _deltas = null;
283     
284     // PropertyTracker uses a bitmask to track properties
285     // We are tracking all properties that have CA_PARTIAL_STATE_HOLDER capability,
286     // so that we do not have to check every property here
287     PropertyTracker tracker = _getPartialStateHolderTracker(false);
288     if (tracker != null)
289     {
290       for (PropertyKey key: tracker)
291       {
292         Object val = get(key);
293         if (val != null)
294         {
295           ((PartialStateHolder)val).clearInitialState();
296         }
297       }
298     }
299   }
300 
301   public boolean initialStateMarked()
302   {
303     return _initialStateMarked;
304   }
305   
306   /**
307    * Sets the the FacesBean type used by this map's owner bean
308    * @param type FacesBean type
309    */
310   public void setType(FacesBean.Type type)
311   {
312     _type = type;
313   }
314 
315   private boolean _createDeltas()
316   {
317     if (_initialStateMarked)
318     {
319       if (_deltas == null)
320       {
321         _deltas = createDeltaPropertyMap();
322       }
323 
324       return true;
325     }
326 
327     return false;
328   }
329 
330   static private boolean _equals(Object a, Object b)
331   {
332     if (a == b)
333       return true;
334 
335     if (a == null)
336       return false;
337 
338     return a.equals(b);
339   }
340   
341   private PropertyTracker _getPartialStateHolderTracker(boolean create)
342   {
343     if (_tracker == null && create)
344     {
345       if (_type == null)
346       {
347         throw new IllegalStateException("FacesBean.TYPE is required to track properties");
348       }
349       _tracker = new PropertyTracker(_type);
350     }
351     return _tracker;                  
352   }
353   
354   
355   private PropertyTracker _getMutableTracker(boolean create)
356   {
357     if (_mutableTracker == null && create)
358     {
359       if (_type == null)
360       {
361         throw new IllegalStateException("FacesBean.TYPE is required to track properties");
362       }
363       _mutableTracker = new PropertyTracker(_type);
364     }
365     return _mutableTracker;                  
366   }  
367 
368   private transient boolean _initialStateMarked;
369   private transient PropertyMap _deltas;
370   private boolean      _useStateHolder;
371   private transient FacesBean.Type _type;
372   private transient PropertyTracker _tracker;
373   private transient PropertyTracker _mutableTracker;
374 
375   private static final long serialVersionUID = 1L;
376 
377 }