View Javadoc

1   /*
2    * $Id: BeanAdapter.java 483237 2006-12-06 21:22:08Z ddewolf $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.struts2.views.xslt;
22  
23  import java.beans.IntrospectionException;
24  import java.beans.Introspector;
25  import java.beans.PropertyDescriptor;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.struts2.StrutsException;
36  import org.w3c.dom.Node;
37  import org.w3c.dom.NodeList;
38  
39  
40  /***
41   * This class is the most general type of adapter, utilizing reflective introspection to present a DOM view of all of
42   * the public properties of its value.  For example, a property returning a JavaBean such as:
43   *
44   * <pre>
45   * public Person getMyPerson() { ... }
46   * ...
47   * class Person {
48   *      public String getFirstName();
49   *      public String getLastName();
50   * }
51   * </pre>
52   *
53   * would be rendered as: <myPerson> <firstName>...</firstName> <lastName>...</lastName> </myPerson>
54   */
55  public class BeanAdapter extends AbstractAdapterElement {
56      //~ Static fields/initializers /////////////////////////////////////////////
57  
58      private static final Object[] NULLPARAMS = new Object[0];
59  
60      /***
61       * Cache can savely be static because the cached information is the same for all instances of this class.
62       */
63      private static Map<Class, PropertyDescriptor[]> propertyDescriptorCache;
64  
65      //~ Instance fields ////////////////////////////////////////////////////////
66  
67      private Log log = LogFactory.getLog(this.getClass());
68  
69      //~ Constructors ///////////////////////////////////////////////////////////
70  
71      public BeanAdapter() {
72      }
73  
74      public BeanAdapter(
75              AdapterFactory adapterFactory, AdapterNode parent, String propertyName, Object value) {
76          setContext(adapterFactory, parent, propertyName, value);
77      }
78  
79      //~ Methods ////////////////////////////////////////////////////////////////
80  
81      public String getTagName() {
82          return getPropertyName();
83      }
84  
85      public NodeList getChildNodes() {
86          NodeList nl = super.getChildNodes();
87          // Log child nodes for debug:
88          if (log.isDebugEnabled() && nl != null) {
89              log.debug("BeanAdapter getChildNodes for: " + getTagName());
90              log.debug(nl.toString());
91          }
92          return nl;
93      }
94  
95      protected List<Node> buildChildAdapters() {
96          log.debug("BeanAdapter building children.  PropName = " + getPropertyName());
97          List<Node> newAdapters = new ArrayList<Node>();
98          Class type = getPropertyValue().getClass();
99          PropertyDescriptor[] props = getPropertyDescriptors(getPropertyValue());
100 
101         if (props.length > 0) {
102             for (PropertyDescriptor prop : props) {
103                 Method m = prop.getReadMethod();
104 
105                 if (m == null) {
106                     //FIXME: write only property or indexed access
107                     continue;
108                 }
109                 log.debug("Bean reading property method: " + m.getName());
110 
111                 String propertyName = prop.getName();
112                 Object propertyValue;
113 
114                 /*
115                     Unwrap any invocation target exceptions and log them.
116                     We really need a way to control which properties are accessed.
117                     Perhaps with annotations in Java5?
118                 */
119                 try {
120                     propertyValue = m.invoke(getPropertyValue(), NULLPARAMS);
121                 } catch (Exception e) {
122                     if (e instanceof InvocationTargetException)
123                         e = (Exception) ((InvocationTargetException) e).getTargetException();
124                     log.error(e);
125                     continue;
126                 }
127 
128                 Node childAdapter;
129 
130                 if (propertyValue == null) {
131                     childAdapter = getAdapterFactory().adaptNullValue(this, propertyName);
132                 } else {
133                     childAdapter = getAdapterFactory().adaptNode(this, propertyName, propertyValue);
134                 }
135 
136                 if (childAdapter != null)
137                     newAdapters.add(childAdapter);
138 
139                 if (log.isDebugEnabled()) {
140                     log.debug(this + " adding adapter: " + childAdapter);
141                 }
142             }
143         } else {
144             // No properties found
145             log.info(
146                     "Class " + type.getName() + " has no readable properties, " + " trying to adapt " + getPropertyName() + " with StringAdapter...");
147         }
148 
149         return newAdapters;
150     }
151 
152     /***
153      * Caching facade method to Introspector.getBeanInfo(Class, Class).getPropertyDescriptors();
154      */
155     private synchronized PropertyDescriptor[] getPropertyDescriptors(Object bean) {
156         try {
157             if (propertyDescriptorCache == null) {
158                 propertyDescriptorCache = new HashMap<Class, PropertyDescriptor[]>();
159             }
160 
161             PropertyDescriptor[] props = propertyDescriptorCache.get(bean.getClass());
162 
163             if (props == null) {
164                 log.debug("Caching property descriptor for " + bean.getClass().getName());
165                 props = Introspector.getBeanInfo(bean.getClass(), Object.class).getPropertyDescriptors();
166                 propertyDescriptorCache.put(bean.getClass(), props);
167             }
168 
169             return props;
170         } catch (IntrospectionException e) {
171             e.printStackTrace();
172             throw new StrutsException("Error getting property descriptors for " + bean + " : " + e.getMessage());
173         }
174     }
175 }