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;
20  
21  import java.lang.reflect.Array;
22  
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Set;
28  
29  import javax.faces.context.FacesContext;
30  import javax.faces.el.ValueBinding;
31  
32  
33  
34  import org.apache.myfaces.trinidad.bean.util.FlaggedPropertyMap;
35  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
36  
37  /**
38   * Base implementation of FacesBean.
39   *
40   */
41  abstract public class FacesBeanImpl implements FacesBean
42  {
43    public FacesBeanImpl()
44    {
45    }
46  
47    /**
48     * Get the type of a FacesBean
49     */
50    // TODO Use auto "TYPE" detection?
51    abstract public Type getType();
52  
53    final public Object getProperty(PropertyKey key)
54    {
55      Object o = getLocalProperty(key);
56      if (o != null)
57        return o;
58  
59      // Look for a binding if and only if the key supports bindings
60      if (key.getSupportsBinding())
61      {
62        ValueBinding binding = getValueBinding(key);
63        if (binding != null)
64        {
65          FacesContext context = FacesContext.getCurrentInstance();
66          return binding.getValue(context);
67        }
68      }
69  
70      return null;
71    }
72  
73  
74    
75    /**
76     * {@inheritDoc}
77     */
78    final public Object getRawProperty(PropertyKey key)
79    {
80      Object local = getLocalProperty(key);
81      if (local != null)
82        return local;
83  
84      // Look for a binding if and only if the key supports bindings
85      return key.getSupportsBinding() ? getValueBinding(key) : null;
86    }
87  
88  
89    // TODO Need *good* way of hooking property-sets;  it's
90    // currently not called from state restoring, so really, it shouldn't
91    // be used as a hook, but EditableValueBase currently
92    // requires hooking this method.
93    public void setProperty(PropertyKey key, Object value)
94    {
95      _checkNotListKey(key);
96      setPropertyImpl(key, value);
97    }
98  
99    final public Object getLocalProperty(PropertyKey key)
100   {
101     _checkNotListKey(key);
102 
103     return getLocalPropertyImpl(key);
104   }
105 
106   final public ValueBinding getValueBinding(PropertyKey key)
107   {
108     _checkNotListKey(key);
109 
110     PropertyMap map = _getBindingsMap(false);
111     if (map == null)
112       return null;
113 
114     return (ValueBinding) map.get(key);
115   }
116 
117   final public void setValueBinding(PropertyKey key, ValueBinding binding)
118   {
119     _checkNotListKey(key);
120 
121     if (!key.getSupportsBinding())
122     {
123       throw new IllegalArgumentException(_LOG.getMessage(
124         "CANNOT_FIND_PROPERTY", key.getName()));
125     }
126 
127     if (binding == null)
128     {
129       PropertyMap map = _getBindingsMap(false);
130       if (map != null)
131         map.remove(key);
132     }
133     else
134     {
135       _getBindingsMap(true).put(key, binding);
136     }
137 
138   }
139 
140 
141   @SuppressWarnings("unchecked")
142   final public void addEntry(PropertyKey listKey, Object value)
143   {
144     _checkListKey(listKey);
145 
146     List<Object> l = (List<Object>) getLocalPropertyImpl(listKey);
147     if (l == null)
148     {
149       l = _createList();
150       setPropertyImpl(listKey, l);
151     }
152 
153     l.add(value);
154 
155     // If we've marked our initial state, forcibly update the property
156     // so we know to write out the change
157     if (_initialStateMarked)
158       setPropertyImpl(listKey, l);
159   }
160 
161   @SuppressWarnings("unchecked")
162   final public void removeEntry(PropertyKey listKey, Object value)
163   {
164     _checkListKey(listKey);
165 
166     List<Object> l = (List<Object>) getLocalPropertyImpl(listKey);
167     if (l != null)
168     {
169       l.remove(value);
170     }
171 
172     // If we've marked our initial state, forcibly update the property
173     // so we know to write out the change
174     if (_initialStateMarked && (l != null))
175       setPropertyImpl(listKey, l);
176   }
177 
178   @SuppressWarnings("unchecked")
179   final public Object[] getEntries(PropertyKey listKey, Class clazz)
180   {
181     _checkListKey(listKey);
182 
183     List<Object> l = (List<Object>) getLocalPropertyImpl(listKey);
184     if (l == null)
185       return (Object[]) Array.newInstance(clazz, 0);
186 
187     int size = l.size();
188     ArrayList<Object> tempList = new ArrayList<Object>(size);
189     for (int i = 0; i < size; i++)
190     {
191       Object o = l.get(i);
192       if (clazz.isInstance(o))
193         tempList.add(o);
194     }
195 
196     return tempList.toArray((Object[]) Array.newInstance(clazz,
197                                                          tempList.size()));
198   }
199 
200   @SuppressWarnings("unchecked")
201   final public boolean containsEntry(PropertyKey listKey, Class<?> clazz)
202   {
203     _checkListKey(listKey);
204 
205     List<Object> l = (List<Object>) getLocalPropertyImpl(listKey);
206     if (l == null)
207       return false;
208 
209     int size = l.size();
210     for (int i = 0; i < size; i++)
211     {
212       Object o = l.get(i);
213       if (clazz.isInstance(o))
214         return true;
215     }
216 
217     return false;
218   }
219 
220   @SuppressWarnings("unchecked")
221   final public Iterator<Object> entries(PropertyKey listKey)
222   {
223     _checkListKey(listKey);
224 
225     List<Object> l = (List<Object>) getLocalPropertyImpl(listKey);
226     if (l == null)
227       return Collections.emptyList().iterator();
228 
229     return l.iterator();
230   }
231 
232   // TODO provide more efficient implementation for copying
233   // from other FacesBeanImpl instances
234   public void addAll(FacesBean from)
235   {
236     if (from == this)
237       return;
238 
239     for(PropertyKey fromKey : from.keySet())
240     {
241       PropertyKey toKey = _convertKey(fromKey);
242       if ((toKey != null) &&
243           _isCompatible(fromKey, toKey))
244       {
245         if (!fromKey.isList())
246         {
247           setProperty(toKey, from.getLocalProperty(fromKey));
248         }
249         else
250         {
251           Iterator<? extends Object> entries = from.entries(fromKey);
252           while (entries.hasNext())
253             addEntry(toKey, entries.next());
254         }
255       }
256     }
257 
258     for(PropertyKey fromKey : from.bindingKeySet())
259     {
260       PropertyKey toKey = _convertKey(fromKey);
261       if (toKey.getSupportsBinding())
262       {
263         setValueBinding(toKey, from.getValueBinding(fromKey));
264       }
265     }
266   }
267 
268   final public Set<PropertyKey> keySet()
269   {
270     if (_properties == null)
271       return Collections.emptySet();
272 
273     return _properties.keySet();
274   }
275 
276   @SuppressWarnings("unchecked")
277   final public Set<PropertyKey> bindingKeySet()
278   {
279     if (_bindings == null)
280       return Collections.emptySet();
281 
282     return _bindings.keySet();
283   }
284 
285 
286   public void markInitialState()
287   {
288     _initialStateMarked = true;
289 
290     if (_properties != null)
291       _properties.markInitialState();
292 
293     if (_bindings != null)
294       _bindings.markInitialState();
295   }
296 
297   public void restoreState(FacesContext context, Object state)
298   {
299     if (_LOG.isFiner())
300     {
301       _LOG.finer("Restoring state into " + this);
302     }
303 
304     if (state instanceof Object[])
305     {
306       Object[] asArray = (Object[]) state;
307       if (asArray.length == 2)
308       {
309         Object propertyState = asArray[0];
310         Object bindingsState = asArray[1];
311         _getPropertyMap().restoreState(context, getType(), propertyState);
312         _getBindingsMap(true).restoreState(context, getType(), bindingsState);
313         return;
314       }
315       else if (asArray.length == 1)
316       {
317         Object propertyState = asArray[0];
318         _getPropertyMap().restoreState(context, getType(), propertyState);
319         return;
320       }
321     }
322 
323     _getPropertyMap().restoreState(context, getType(), state);
324   }
325 
326   public Object saveState(FacesContext context)
327   {
328     if (_LOG.isFiner())
329     {
330       _LOG.finer("Saving state of " + this);
331     }
332 
333 
334     Object propertyState = (_properties == null)
335                             ? null
336                             : _properties.saveState(context);
337     Object bindingsState = (_bindings == null)
338                             ? null
339                             : _bindings.saveState(context);
340 
341     if (bindingsState != null)
342     {
343       return new Object[]{propertyState, bindingsState};
344     }
345 
346     if (propertyState == null)
347       return null;
348 
349     if (propertyState instanceof Object[])
350     {
351       Object[] asArray = (Object[]) propertyState;
352       if (asArray.length <= 2)
353         return new Object[]{propertyState};
354     }
355 
356     return propertyState;
357   }
358 
359   @Override
360   public String toString()
361   {
362     String className = getClass().getName();
363     int lastPeriod = className.lastIndexOf('.');
364     if (lastPeriod < 0)
365       return className;
366 
367     return className.substring(lastPeriod + 1);
368   }
369 
370   protected void setPropertyImpl(PropertyKey key, Object value)
371   {
372     if (value == null)
373       _getPropertyMap().remove(key);
374     else
375       _getPropertyMap().put(key, value);
376   }
377 
378   protected Object getLocalPropertyImpl(PropertyKey key)
379   {
380     return _getPropertyMap().get(key);
381   }
382 
383   protected PropertyMap createPropertyMap()
384   {
385     return new FlaggedPropertyMap();
386   }
387 
388   protected PropertyMap createBindingsMap()
389   {
390     FlaggedPropertyMap bindings = new FlaggedPropertyMap();
391     bindings.setUseStateHolder(true);
392     return bindings;
393   }
394 
395   // "listKey" is unused, but it seems plausible that
396   // if this ever gets converted to a protected hook that
397   // "listKey" may be useful in that context.
398   private List<Object> _createList(/*PropertyKey listKey*/)
399   {
400     return new ArrayList<Object>();
401   }
402 
403   /**
404    * Converts a key from one type to another.
405    */
406   private PropertyKey _convertKey(PropertyKey fromKey)
407   {
408     Type type = getType();
409     // If the "fromKey" has an index, then see if it's exactly
410     // the same key as one of ours.
411     PropertyKey toKey = type.findKey(fromKey.getIndex());
412     if (toKey == fromKey)
413       return toKey;
414 
415     // Otherwise, just look it up by name.
416     String name = fromKey.getName();
417     toKey = type.findKey(name);
418     if (toKey != null)
419       return toKey;
420 
421     // Finally, give up and create a transient key
422     return new PropertyKey(name);
423   }
424 
425   /**
426    * Returns true if two keys are of compatible types.
427    */
428   static private boolean _isCompatible(
429     PropertyKey fromKey, PropertyKey toKey)
430   {
431     return (fromKey.isList() == toKey.isList());
432   }
433 
434 
435   private PropertyMap _getPropertyMap()
436   {
437     if (_properties == null)
438       _properties = createPropertyMap();
439 
440     return _properties;
441   }
442 
443   private PropertyMap _getBindingsMap(boolean createIfNew)
444   {
445     if (_bindings == null)
446     {
447       if (createIfNew)
448       {
449         _bindings = createBindingsMap();
450       }
451     }
452 
453     return _bindings;
454   }
455 
456 
457   static private void _checkListKey(PropertyKey listKey)
458     throws IllegalArgumentException
459   {
460     if (!listKey.isList())
461       throw new IllegalArgumentException(_LOG.getMessage(
462         ">KEY_CANNOT_BE_USED_FOR_LISTS", listKey));
463   }
464 
465   static private void _checkNotListKey(PropertyKey key)
466     throws IllegalArgumentException
467   {
468     if (key.isList())
469       throw new IllegalArgumentException(_LOG.getMessage(
470         "KEY_IS_LIST_KEY", key));
471   }
472 
473   private PropertyMap  _properties;
474   private PropertyMap  _bindings;
475   private transient boolean  _initialStateMarked;
476 
477   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(FacesBeanImpl.class);
478 }