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    }