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