001: /*
002: * $Id: BeanAdapter.java 483237 2006-12-06 21:22:08Z ddewolf $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts2.views.xslt;
022:
023: import java.beans.IntrospectionException;
024: import java.beans.Introspector;
025: import java.beans.PropertyDescriptor;
026: import java.lang.reflect.InvocationTargetException;
027: import java.lang.reflect.Method;
028: import java.util.ArrayList;
029: import java.util.HashMap;
030: import java.util.List;
031: import java.util.Map;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.apache.struts2.StrutsException;
036: import org.w3c.dom.Node;
037: import org.w3c.dom.NodeList;
038:
039: /**
040: * This class is the most general type of adapter, utilizing reflective introspection to present a DOM view of all of
041: * the public properties of its value. For example, a property returning a JavaBean such as:
042: *
043: * <pre>
044: * public Person getMyPerson() { ... }
045: * ...
046: * class Person {
047: * public String getFirstName();
048: * public String getLastName();
049: * }
050: * </pre>
051: *
052: * would be rendered as: <myPerson> <firstName>...</firstName> <lastName>...</lastName> </myPerson>
053: */
054: public class BeanAdapter extends AbstractAdapterElement {
055: //~ Static fields/initializers /////////////////////////////////////////////
056:
057: private static final Object[] NULLPARAMS = new Object[0];
058:
059: /**
060: * Cache can savely be static because the cached information is the same for all instances of this class.
061: */
062: private static Map<Class, PropertyDescriptor[]> propertyDescriptorCache;
063:
064: //~ Instance fields ////////////////////////////////////////////////////////
065:
066: private Log log = LogFactory.getLog(this .getClass());
067:
068: //~ Constructors ///////////////////////////////////////////////////////////
069:
070: public BeanAdapter() {
071: }
072:
073: public BeanAdapter(AdapterFactory adapterFactory,
074: AdapterNode parent, String propertyName, Object value) {
075: setContext(adapterFactory, parent, propertyName, value);
076: }
077:
078: //~ Methods ////////////////////////////////////////////////////////////////
079:
080: public String getTagName() {
081: return getPropertyName();
082: }
083:
084: public NodeList getChildNodes() {
085: NodeList nl = super .getChildNodes();
086: // Log child nodes for debug:
087: if (log.isDebugEnabled() && nl != null) {
088: log.debug("BeanAdapter getChildNodes for: " + getTagName());
089: log.debug(nl.toString());
090: }
091: return nl;
092: }
093:
094: protected List<Node> buildChildAdapters() {
095: log.debug("BeanAdapter building children. PropName = "
096: + getPropertyName());
097: List<Node> newAdapters = new ArrayList<Node>();
098: Class type = getPropertyValue().getClass();
099: 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: "
110: + m.getName());
111:
112: String propertyName = prop.getName();
113: Object propertyValue;
114:
115: /*
116: Unwrap any invocation target exceptions and log them.
117: We really need a way to control which properties are accessed.
118: Perhaps with annotations in Java5?
119: */
120: try {
121: propertyValue = m.invoke(getPropertyValue(),
122: NULLPARAMS);
123: } catch (Exception e) {
124: if (e instanceof InvocationTargetException)
125: e = (Exception) ((InvocationTargetException) e)
126: .getTargetException();
127: log.error(e);
128: continue;
129: }
130:
131: Node childAdapter;
132:
133: if (propertyValue == null) {
134: childAdapter = getAdapterFactory().adaptNullValue(
135: this , propertyName);
136: } else {
137: childAdapter = getAdapterFactory().adaptNode(this ,
138: propertyName, propertyValue);
139: }
140:
141: if (childAdapter != null)
142: newAdapters.add(childAdapter);
143:
144: if (log.isDebugEnabled()) {
145: log
146: .debug(this + " adding adapter: "
147: + childAdapter);
148: }
149: }
150: } else {
151: // No properties found
152: log.info("Class " + type.getName()
153: + " has no readable properties, "
154: + " trying to adapt " + getPropertyName()
155: + " with StringAdapter...");
156: }
157:
158: return newAdapters;
159: }
160:
161: /**
162: * Caching facade method to Introspector.getBeanInfo(Class, Class).getPropertyDescriptors();
163: */
164: private synchronized PropertyDescriptor[] getPropertyDescriptors(
165: Object bean) {
166: try {
167: if (propertyDescriptorCache == null) {
168: propertyDescriptorCache = new HashMap<Class, PropertyDescriptor[]>();
169: }
170:
171: PropertyDescriptor[] props = propertyDescriptorCache
172: .get(bean.getClass());
173:
174: if (props == null) {
175: log.debug("Caching property descriptor for "
176: + bean.getClass().getName());
177: props = Introspector.getBeanInfo(bean.getClass(),
178: Object.class).getPropertyDescriptors();
179: propertyDescriptorCache.put(bean.getClass(), props);
180: }
181:
182: return props;
183: } catch (IntrospectionException e) {
184: e.printStackTrace();
185: throw new StrutsException(
186: "Error getting property descriptors for " + bean
187: + " : " + e.getMessage());
188: }
189: }
190: }
|