001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.betwixt.digester;
018
019 import java.beans.BeanInfo;
020 import java.beans.Introspector;
021 import java.beans.PropertyDescriptor;
022
023 import org.apache.commons.logging.Log;
024 import org.apache.commons.logging.LogFactory;
025
026 /** <p>Factors out common code used by Betwixt rules that access bean properties.
027 * Maybe a lot of this should be moved into <code>BeanUtils</code>.</p>
028 *
029 * @author Robert Burrell Donkin
030 * @since 0.5
031 */
032 public abstract class MappedPropertyRule extends RuleSupport {
033
034 /** Logger */
035 private static final Log log = LogFactory.getLog( MappedPropertyRule.class );
036 /** Base constructor */
037 public MappedPropertyRule() {
038 }
039
040
041
042 // Implementation methods
043 //-------------------------------------------------------------------------
044
045 /**
046 * Returns the property descriptor for the class and property name.
047 * Note that some caching could be used to improve performance of
048 * this method. Or this method could be added to PropertyUtils.
049 *
050 * @param beanClass descriptor for property in this class
051 * @param propertyName descriptor for property with this name
052 * @return property descriptor for the named property in the given class
053 */
054 protected PropertyDescriptor getPropertyDescriptor( Class beanClass,
055 String propertyName ) {
056 if ( beanClass != null && propertyName != null ) {
057 if (log.isTraceEnabled()) {
058 log.trace("Searching for property " + propertyName + " on " + beanClass);
059 }
060 try {
061 // TODO: replace this call to introspector to an object call
062 // which finds all property descriptors for a class
063 // this allows extra property descriptors to be added
064 BeanInfo beanInfo;
065 if( getXMLIntrospector().getConfiguration().ignoreAllBeanInfo() ) {
066 beanInfo = Introspector.getBeanInfo( beanClass, Introspector.IGNORE_ALL_BEANINFO );
067 }
068 else {
069 beanInfo = Introspector.getBeanInfo( beanClass );
070 }
071 PropertyDescriptor[] descriptors =
072 beanInfo.getPropertyDescriptors();
073 if ( descriptors != null ) {
074 for ( int i = 0, size = descriptors.length; i < size; i++ ) {
075 PropertyDescriptor descriptor = descriptors[i];
076 if ( propertyName.equals( descriptor.getName() ) ) {
077 log.trace("Found matching method.");
078 return descriptor;
079 }
080 }
081 }
082 // for interfaces, check all super interfaces
083 if (beanClass.isInterface()) {
084 Class[] superinterfaces = beanClass.getInterfaces();
085 for (int i=0, size=superinterfaces.length; i<size; i++) {
086 PropertyDescriptor descriptor = getPropertyDescriptor(superinterfaces[i], propertyName);
087 if (descriptor != null)
088 {
089 return descriptor;
090 }
091 }
092 }
093
094 log.trace("No match found.");
095 return null;
096 } catch (Exception e) {
097 log.warn( "Caught introspection exception", e );
098 }
099 }
100 return null;
101 }
102
103
104 /**
105 * Gets the type of a property
106 *
107 * @param propertyClassName class name for property type (may be null)
108 * @param beanClass class that has property
109 * @param propertyName the name of the property whose type is to be determined
110 * @return property type
111 */
112 protected Class getPropertyType( String propertyClassName,
113 Class beanClass, String propertyName ) {
114 // XXX: should use a ClassLoader to handle
115 // complex class loading situations
116 if ( propertyClassName != null ) {
117 try {
118 Class answer = loadClass(propertyClassName);
119 if (answer != null) {
120 if (log.isTraceEnabled()) {
121 log.trace("Used specified type " + answer);
122 }
123 return answer;
124 }
125 } catch (Exception e) {
126 log.warn("Cannot load specified type", e);
127 }
128 }
129
130 PropertyDescriptor descriptor =
131 getPropertyDescriptor( beanClass, propertyName );
132 if ( descriptor != null ) {
133 return descriptor.getPropertyType();
134 }
135
136 if (log.isTraceEnabled()) {
137 log.trace("Cannot find property type.");
138 log.trace(" className=" + propertyClassName
139 + " base=" + beanClass + " name=" + propertyName);
140 }
141 return null;
142 }
143 }