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.util.Collections;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import javax.faces.context.FacesContext;
26  
27  import org.apache.myfaces.trinidad.bean.util.StateUtils;
28  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
29  
30  /**
31   * Key for an entry in a FacesBean.
32   */
33  public class PropertyKey
34  {
35    /**
36     * Capability indicating this property does not support bindings.
37     */
38    static public final int CAP_NOT_BOUND = 1;
39  
40    /**
41     * Capability indicating this property is transient.
42     */
43    static public final int CAP_TRANSIENT = 2;
44  
45    /**
46     * Capability indicating this property describes a list.  List
47     * PropertyKeys will automatically be marked as not supporting
48     * bindings.
49     */
50    static public final int CAP_LIST = 4;
51  
52    /**
53     * Capability indicating this property can use the StateHolder API.
54     */
55    static public final int CAP_STATE_HOLDER = 8;
56  
57    /**
58     * Create a named PropertyKey, not attached to any type.
59     */
60    static public PropertyKey createPropertyKey(String name)
61    {
62      return new PropertyKey(name);
63    }
64    
65    //
66    // Constructors, all package-private.  Only the constructor
67    // that takes a simple String could be exposed at all safely;
68    // note that state saving cannot possibly re-create an
69    // anonymous PropertyKey with anything other than default metadata,
70    // so it is only safe to create such a PropertyKey as part
71    // of a FacesBean.Type.
72    //
73    PropertyKey(
74      String name)
75    {
76      this(name, _TYPE_DEFAULT);
77    }
78    
79    PropertyKey(
80      String   name,
81      Class<?> type)
82    {
83      this(name, type, null);
84    }
85  
86    PropertyKey(
87      String   name,
88      Class<?> type,
89      Object   defaultValue)
90    {
91      this(name, type, defaultValue, _CAPS_DEFAULT, -1);
92    }
93  
94    // Needs to be protected for UINodePropertyKey implementation
95    protected PropertyKey(
96      String   name,
97      Class<?> type,
98      Object   defaultValue,
99      int      capabilities,
100     int      index)
101   {
102     if (name == null)
103       throw new NullPointerException();
104 
105     if (type == null)
106       throw new NullPointerException();
107 
108     if (defaultValue != null)
109     {
110       // Ensure that default value is legal for this property type.
111       Class<?> boxedType = _getBoxedType(type);
112       if (!boxedType.isAssignableFrom(defaultValue.getClass()))
113       {
114         throw new IllegalStateException(_LOG.getMessage(
115           "DEFAULT_VALUE_IS_NOT_ASSIGNED_TO_TYPE", new Object[]{defaultValue, type}));
116       }
117     }
118     else
119     {
120       // Default the default value according to Java Language Specification
121       defaultValue = _getJavaDefault(type);
122 
123       // simplify equality testing in .equals()
124       if (defaultValue == null)
125         defaultValue = _OBJECT_NULL;
126     }
127         
128     if ((capabilities & ~_CAPS_ALL) != 0)
129       throw new IllegalStateException(_LOG.getMessage(
130         "CAPABILITY_MASK_NOT_UNDERSTOOD", (capabilities & ~_CAPS_ALL)));
131 
132     // Lists cannot be bound
133     if ((capabilities & CAP_LIST) != 0)
134       capabilities = capabilities | CAP_NOT_BOUND;
135 
136     _name = name;
137     _type = type;
138     _default = defaultValue;
139     _capabilities = capabilities;
140     _index = index;
141     
142     _hashCode = _name.hashCode();
143   }
144 
145   /**
146    * Returns the type of this property.
147    */
148   public Class<?> getType()
149   {
150     return _type;
151   }
152 
153   /**
154    * Returns the default value of this property.
155    */
156   public Object getDefault()
157   {
158     return (_default != _OBJECT_NULL) ? _default : null;
159   }
160 
161   /**
162    * Returns the owning type for this property key.
163    */
164   public FacesBean.Type getOwner()
165   {
166     return _owner;
167   }
168 
169   /**
170    * Returns true if the property supports being bound.
171    */
172   public boolean getSupportsBinding()
173   {
174     return (_capabilities & CAP_NOT_BOUND) == 0;
175   }
176 
177   /**
178    * Returns true if the property is transient.
179    */
180   public boolean isTransient()
181   {
182     return (_capabilities & CAP_TRANSIENT) != 0;
183   }
184 
185   /**
186    * Returns true if the property is used to store a list.
187    */
188   public boolean isList()
189   {
190     return (_capabilities & CAP_LIST) != 0;
191   }
192 
193   /**
194    * Returns the name of this property.
195    */
196   public String getName()
197   {
198     return _name;
199   }
200 
201   /**
202    * Returns the index of this property.
203    */
204   public int getIndex()
205   {
206     return _index;
207   }
208 
209 
210   public Object saveValue(
211     FacesContext context,
212     Object value)
213   {
214     if ((_capabilities & CAP_STATE_HOLDER) != 0)
215       return StateUtils.saveStateHolder(context, value);
216 
217     if (isList())
218       return StateUtils.saveList(context, value);
219 
220     return value;
221   }
222 
223   public Object restoreValue(
224     FacesContext context,
225     Object savedValue)
226   {
227     if ((_capabilities & CAP_STATE_HOLDER) != 0)
228       return StateUtils.restoreStateHolder(context, savedValue);
229 
230     if (isList())
231       return StateUtils.restoreList(context, savedValue);
232 
233     return savedValue;
234   }
235 
236   
237   @Override
238   public boolean equals(Object o)
239   {
240     if (o == this)
241       return true;
242 
243     if (!(o instanceof PropertyKey))
244       return false;
245 
246     PropertyKey that = (PropertyKey)o;
247 
248     // If we're not both in the same Type, we're not equals.
249     if (_owner != that._owner)
250       return false;
251 
252     // If the index is -1, then it might be an anonymous
253     // type, in which case we have to compare names
254     int index = this._index;
255     if (index == -1)
256     {
257       return ((that._index == -1) && 
258               (that._name.equals(_name)) &&
259               (that._type.equals(_type)) &&
260               (that._default.equals(_default)));
261     }
262 
263     // But otherwise, since the types are the same, then the
264     // same index would imply the same instance - which would
265     // have been caught up above.  So, again, this ain't the same equal
266     return false;
267   }
268 
269   @Override
270   public int hashCode()
271   {
272     return _hashCode;
273   }
274 
275   @Override
276   public String toString()
277   {
278     String className = getClass().getName();
279     int lastPeriod = className.lastIndexOf('.');
280     if (lastPeriod >= 0)
281       className = className.substring(lastPeriod + 1);
282 
283     if (_index >= 0)
284       return className + "[" + _name + "," + _index + "]";
285     return className + "[" + _name + "]";
286   }
287 
288   void __setOwner(FacesBean.Type owner)
289   {
290     _owner = owner;
291   }
292 
293   static private Object _getJavaDefault(
294     Class<?> type)
295   {
296     return _PRIMITIVE_DEFAULTS.get(type);
297   }
298   
299   static private Class<?> _getBoxedType(
300     Class<?> type)
301   {
302     Class<?> boxedType = _BOXED_PRIMITIVES.get(type);
303     return (boxedType != null ? boxedType : type);
304   }
305   
306   static private Map<Class<?>, Object> _createPrimitiveDefaults()
307   {
308     Map<Class<?>, Object> map = new HashMap<Class<?>, Object>();
309     map.put(Boolean.TYPE, Boolean.FALSE);
310     map.put(Byte.TYPE, Byte.valueOf((byte)0));
311     map.put(Character.TYPE, Character.valueOf('\0'));
312     map.put(Double.TYPE, Double.valueOf(0.0));
313     map.put(Float.TYPE, Float.valueOf(0.0f));
314     map.put(Integer.TYPE, Integer.valueOf(0));
315     map.put(Long.TYPE, Long.valueOf(0L));
316     map.put(Short.TYPE, Short.valueOf((short)0));
317 
318     return Collections.unmodifiableMap(map);
319   }
320 
321   static private Map<Class<?>, Class<?>> _createBoxedPrimitives()
322   {
323     Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
324     map.put(Boolean.TYPE, Boolean.class);
325     map.put(Byte.TYPE, Byte.class);
326     map.put(Character.TYPE, Character.class);
327     map.put(Double.TYPE, Double.class);
328     map.put(Float.TYPE, Float.class);
329     map.put(Integer.TYPE, Integer.class);
330     map.put(Long.TYPE, Long.class);
331     map.put(Short.TYPE, Short.class);
332 
333     return Collections.unmodifiableMap(map);
334   }
335 
336   static private final Map<Class<?>, Object>   _PRIMITIVE_DEFAULTS = _createPrimitiveDefaults();
337   static private final Map<Class<?>, Class<?>> _BOXED_PRIMITIVES   = _createBoxedPrimitives();
338 
339   private final int     _hashCode;
340   private final String   _name;
341   private final int      _index;
342   private final int      _capabilities;
343   private final Class<?> _type;
344   private final Object   _default;
345   private       FacesBean.Type _owner;
346 
347   static private final int _CAPS_DEFAULT = 
348     0;
349   
350   static private final int _CAPS_ALL = 
351     CAP_NOT_BOUND |
352     CAP_TRANSIENT |
353     CAP_LIST |
354     CAP_STATE_HOLDER;
355 
356   static private final Class<Object> _TYPE_DEFAULT = Object.class;
357 
358   static private final Object _OBJECT_NULL = new Object();
359   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
360     PropertyKey.class);
361 }
362