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.view.facelets.tag.composite;
20  
21  import java.beans.BeanDescriptor;
22  import java.beans.PropertyDescriptor;
23  import java.beans.SimpleBeanInfo;
24  import java.io.Externalizable;
25  import java.io.IOException;
26  import java.io.ObjectInput;
27  import java.io.ObjectOutput;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.Enumeration;
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  
37  import javax.faces.view.AttachedObjectTarget;
38  
39  /**
40   * Implementation of BeanInfo object used by composite components.
41   * Instances of this class are found on component attribute map
42   * using the key UIComponent.BEANINFO_KEY.
43   * 
44   * The points to take into account for implement this class are this:
45   * 
46   * - The following tags:
47   * 
48   *    composite:interface
49   *    composite:attribute
50   *    composite:facet
51   *    composite:valueHolder
52   *    composite:editableValueHolder
53   *    composite:actionSource
54   *    composite:extension
55   *    
56   *    must deal with this class, so it is expected methods that manipulate
57   *    data here are called from their tag handlers.
58   *    
59   * - ViewDeclarationLanguage.retargetAttachedObjects and 
60   *   ViewDeclarationLanguage.retargetMethodExpressions read information
61   *   contained here
62   * 
63   * - This object goes on attribute map, so it is necessary that
64   *   this instance should be Serializable. But note that BeanDescriptor
65   *   is not, so the best way is implements Externalizable interface
66   *   and implement its methods. The only information we need to be Serializable
67   *   from this object is the related to BeanDescriptor, but note that
68   *   serialize information used only in build view time ( like
69   *   AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY list) is not required 
70   *   and could cause serialization exceptions. 
71   * 
72   * @author Leonardo Uribe (latest modification by $Author$)
73   * @version $Revision$ $Date$
74   */
75  public class CompositeComponentBeanInfo extends SimpleBeanInfo 
76      implements Externalizable
77  {
78      
79      public static final String PROPERTY_DESCRIPTOR_MAP_KEY = "oam.cc.beanInfo.PDM";
80  
81      /**
82       * Most of the information here are filled on composite:interface tag.
83       * It is also expected that AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY
84       * key is used by other handlers inside the inner map for this BeanDescriptor.
85       */
86      private BeanDescriptor _descriptor;
87      
88      /**
89       * ViewDeclarationLanguage.retargetMethodExpressions algorithm
90       * suggest that every attribute should have a PropertyDescriptor
91       * object defined for it. So, this list is expected to be filled
92       * on composite:attribute tag. This list is used only when
93       * retargetAttachedObjects and retargetAttachedMethodExpressions,
94       * and this methods are called only on buildView time, so
95       * we don't need to Serialize this list. 
96       */
97      private List<PropertyDescriptor> _propertyDescriptors;
98      
99      private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = new PropertyDescriptor[0];
100     
101     private PropertyDescriptor[] _propertyDescriptorsArray;
102     
103     private Map<String, PropertyDescriptor> _propertyDescriptorsMap;
104     
105     /**
106      * Used for Serialization
107      */
108     public CompositeComponentBeanInfo()
109     {
110         super();
111     }
112     
113     public CompositeComponentBeanInfo(BeanDescriptor descriptor)
114     {
115         super();
116         _descriptor = descriptor;
117         getBeanDescriptor().setValue(PROPERTY_DESCRIPTOR_MAP_KEY, new PropertyDescriptorMap());
118     }
119     
120     @Override
121     public BeanDescriptor getBeanDescriptor()
122     {
123         return _descriptor;
124     }
125 
126     @Override
127     public PropertyDescriptor[] getPropertyDescriptors()
128     {
129         if (_propertyDescriptors == null)
130         {
131             return EMPTY_PROPERTY_DESCRIPTOR_ARRAY;
132         }
133         else
134         {
135             if (_propertyDescriptorsArray == null)
136             {
137                 _propertyDescriptorsArray = _propertyDescriptors.toArray(
138                         new PropertyDescriptor[_propertyDescriptors.size()]); 
139             }
140             else if (_propertyDescriptorsArray.length != _propertyDescriptors.size())
141             {
142                 _propertyDescriptorsArray = _propertyDescriptors.toArray(
143                         new PropertyDescriptor[_propertyDescriptors.size()]);
144             }
145             return _propertyDescriptorsArray; 
146         }
147     }
148 
149     public List<PropertyDescriptor> getPropertyDescriptorsList()
150     {
151         if (_propertyDescriptors == null)
152         {
153             _propertyDescriptors = new ArrayList<PropertyDescriptor>();
154         }
155         return _propertyDescriptors;
156     }
157 
158     public void setPropertyDescriptorsList(List<PropertyDescriptor> descriptors)
159     {
160         _propertyDescriptors = descriptors;
161     }
162 
163     public void readExternal(ObjectInput in) throws IOException,
164             ClassNotFoundException
165     {
166         Class beanClass = (Class) in.readObject();
167         Class customizerClass = (Class) in.readObject();
168         if (customizerClass == null)
169         {
170             _descriptor = new BeanDescriptor(beanClass);
171         }
172         else
173         {
174             _descriptor = new BeanDescriptor(beanClass, customizerClass);
175         }
176         _descriptor.setDisplayName((String) in.readObject());
177         _descriptor.setExpert(in.readBoolean());
178         _descriptor.setName((String) in.readObject());
179         _descriptor.setPreferred(in.readBoolean());
180         _descriptor.setShortDescription((String) in.readObject());
181         _descriptor.setValue(PROPERTY_DESCRIPTOR_MAP_KEY, new PropertyDescriptorMap());
182         
183         Map<String,Object> map = (Map) in.readObject();
184         
185         for (Map.Entry<String, Object> entry : map.entrySet())
186         {
187             _descriptor.setValue(entry.getKey(), entry.getValue());
188         }
189         _propertyDescriptors = (List<PropertyDescriptor>) in.readObject();
190     }
191 
192     public void writeExternal(ObjectOutput out) throws IOException
193     {
194         out.writeObject(_descriptor.getBeanClass());
195         out.writeObject(_descriptor.getCustomizerClass());
196         out.writeObject(_descriptor.getDisplayName());
197         out.writeBoolean(_descriptor.isExpert());
198         out.writeObject(_descriptor.getName());
199         out.writeBoolean(_descriptor.isPreferred());
200         out.writeObject(_descriptor.getShortDescription());
201         
202         Map<String,Object> map = new HashMap<String, Object>(4,1);
203         
204         for (Enumeration<String> e = _descriptor.attributeNames(); e.hasMoreElements();)
205         {
206             String name = e.nextElement();
207             
208             // It is not necessary to serialize AttachedObjectTarget list because
209             // we only use it when VDL.retargetAttachedObjects() is called and this only
210             // happen when the view is built. Also, try to serialize this instances could
211             // cause unwanted exceptions.
212             if (!AttachedObjectTarget.ATTACHED_OBJECT_TARGETS_KEY.equals(name) &&
213                 !PROPERTY_DESCRIPTOR_MAP_KEY.equals(name))
214             {
215                 map.put(name, _descriptor.getValue(name));
216             }
217         }
218         out.writeObject(map);
219         out.writeObject(_propertyDescriptors);
220         
221     }
222     
223     public Map<String, PropertyDescriptor> getPropertyDescriptorsMap()
224     {
225         if (_propertyDescriptors == null)
226         {
227             return Collections.emptyMap();
228         }
229         else
230         {
231             if (_propertyDescriptors.isEmpty())
232             {
233                 return Collections.emptyMap();
234             }
235             else if (_propertyDescriptorsMap == null)
236             {
237                 int initCapacity = (_propertyDescriptors.size() * 4 + 3) / 3;
238                 _propertyDescriptorsMap = new HashMap<String, PropertyDescriptor>(initCapacity);
239                 for (PropertyDescriptor p : _propertyDescriptors)
240                 {
241                     if (!_propertyDescriptorsMap.containsKey(p.getName()))
242                     {
243                         _propertyDescriptorsMap.put(p.getName(), p);
244                     }
245                 }
246             }
247             else if (_propertyDescriptorsMap.size() != _propertyDescriptors.size())
248             {
249                 for (PropertyDescriptor p : _propertyDescriptors)
250                 {
251                     if (!_propertyDescriptorsMap.containsKey(p.getName()))
252                     {
253                         _propertyDescriptorsMap.put(p.getName(), p);
254                     }
255                 }
256                 if (_propertyDescriptorsMap.size() != _propertyDescriptors.size())
257                 {
258                     // PropertyDescriptor was removed
259                     _propertyDescriptorsMap.clear();
260                     for (PropertyDescriptor p : _propertyDescriptors)
261                     {
262                         if (!_propertyDescriptorsMap.containsKey(p.getName()))
263                         {
264                             _propertyDescriptorsMap.put(p.getName(), p);
265                         }
266                     }
267                 }
268             }
269             return _propertyDescriptorsMap;
270         }
271     }
272     
273     /**
274      * Read only map for fast access. It works as an indirection over the real list.
275      */
276     public class PropertyDescriptorMap implements Map<String, PropertyDescriptor>
277     {
278         
279         public int size()
280         {
281             return getPropertyDescriptorsMap().size();
282         }
283 
284         public boolean isEmpty()
285         {
286             return getPropertyDescriptorsMap().isEmpty();
287         }
288 
289        
290         public boolean containsKey(Object key)
291         {
292             return getPropertyDescriptorsMap().containsKey(key);
293         }
294 
295         public boolean containsValue(Object value)
296         {
297             return getPropertyDescriptorsMap().containsValue(value);
298         }
299 
300         public PropertyDescriptor get(Object key)
301         {
302             return getPropertyDescriptorsMap().get(key);
303         }
304 
305         public PropertyDescriptor put(String key, PropertyDescriptor value)
306         {
307             throw new UnsupportedOperationException();
308         }
309 
310         public PropertyDescriptor remove(Object key)
311         {
312             throw new UnsupportedOperationException();
313         }
314 
315         public void putAll(Map<? extends String, ? extends PropertyDescriptor> m)
316         {
317             throw new UnsupportedOperationException();
318         }
319 
320         public void clear()
321         {
322             throw new UnsupportedOperationException();
323         }
324 
325         public Set<String> keySet()
326         {
327             return getPropertyDescriptorsMap().keySet();
328         }
329 
330         public Collection<PropertyDescriptor> values()
331         {
332             return getPropertyDescriptorsMap().values();
333         }
334 
335         public Set<Entry<String, PropertyDescriptor>> entrySet()
336         {
337             return getPropertyDescriptorsMap().entrySet();
338         }
339     }
340 }