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 }