    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.jxpath;
    018
    019    import java.beans.BeanInfo;
    020    import java.beans.IntrospectionException;
    021    import java.beans.Introspector;
    022    import java.beans.PropertyDescriptor;
    023    import java.util.Arrays;
    024    import java.util.Comparator;
    025    import java.util.HashMap;
    026
    027    /**
    028     * An implementation of JXPathBeanInfo based on JavaBeans' BeanInfo. Properties
    029     * advertised by JXPathBasicBeanInfo are the same as those advertised by
    030     * BeanInfo for the corresponding class.
    031     *
    032     * @see java.beans.BeanInfo
    033     * @see java.beans.Introspector
    034     *
    035     * @author Dmitri Plotnikov
    036     * @version $Revision: 668329 $ $Date: 2008-06-16 16:59:48 -0500 (Mon, 16 Jun 2008) $
    037     */
    038    public class JXPathBasicBeanInfo implements JXPathBeanInfo {
    039        private static final long serialVersionUID = -3863803443111484155L;
    040
    041        private static final Comparator PROPERTY_DESCRIPTOR_COMPARATOR = new Comparator() {
    042            public int compare(Object left, Object right) {
    043                return ((PropertyDescriptor) left).getName().compareTo(
    044                    ((PropertyDescriptor) right).getName());
    045            }
    046        };
    047
    048        private boolean atomic = false;
    049        private Class clazz;
    050        private Class dynamicPropertyHandlerClass;
    051        private transient PropertyDescriptor[] propertyDescriptors;
    052        private transient HashMap propertyDescriptorMap;
    053
    054        /**
    055         * Create a new JXPathBasicBeanInfo.
    056         * @param clazz bean class
    057         */
    058        public JXPathBasicBeanInfo(Class clazz) {
    059            this.clazz = clazz;
    060        }
    061
    062        /**
    063         * Create a new JXPathBasicBeanInfo.
    064         * @param clazz bean class
    065         * @param atomic whether objects of this class are treated as atomic
    066         *               objects which have no properties of their own.
    067         */
    068        public JXPathBasicBeanInfo(Class clazz, boolean atomic) {
    069            this.clazz = clazz;
    070            this.atomic = atomic;
    071        }
    072
    073        /**
    074         * Create a new JXPathBasicBeanInfo.
    075         * @param clazz bean class
    076         * @param dynamicPropertyHandlerClass dynamic property handler class
    077         */
    078        public JXPathBasicBeanInfo(Class clazz, Class dynamicPropertyHandlerClass) {
    079            this.clazz = clazz;
    080            this.atomic = false;
    081            this.dynamicPropertyHandlerClass = dynamicPropertyHandlerClass;
    082        }
    083
    084        /**
    085         * Returns true if objects of this class are treated as atomic
    086         * objects which have no properties of their own.
    087         * @return boolean
    088         */
    089        public boolean isAtomic() {
    090            return atomic;
    091        }
    092
    093        /**
    094         * Return true if the corresponding objects have dynamic properties.
    095         * @return boolean
    096         */
    097        public boolean isDynamic() {
    098            return dynamicPropertyHandlerClass != null;
    099        }
    100
    101        public synchronized PropertyDescriptor[] getPropertyDescriptors() {
    102            if (propertyDescriptors == null) {
    103                if (clazz == Object.class) {
    104                    propertyDescriptors = new PropertyDescriptor[0];
    105                }
    106                else {
    107                    try {
    108                        BeanInfo bi = null;
    109                        if (clazz.isInterface()) {
    110                            bi = Introspector.getBeanInfo(clazz);
    111                        }
    112                        else {
    113                            bi = Introspector.getBeanInfo(clazz, Object.class);
    114                        }
    115                        PropertyDescriptor[] pds = bi.getPropertyDescriptors();
    116                        PropertyDescriptor[] descriptors = new PropertyDescriptor[pds.length];
    117                        System.arraycopy(pds, 0, descriptors, 0, pds.length);
    118                        Arrays.sort(descriptors, PROPERTY_DESCRIPTOR_COMPARATOR);
    119                        propertyDescriptors = descriptors;
    120                    }
    121                    catch (IntrospectionException ex) {
    122                        ex.printStackTrace();
    123                    }
    124                }
    125            }
    126            if (propertyDescriptors.length == 0) {
    127                return propertyDescriptors;
    128            }
    129            PropertyDescriptor[] result = new PropertyDescriptor[propertyDescriptors.length];
    130            System.arraycopy(propertyDescriptors, 0, result, 0, propertyDescriptors.length);
    131            return result;
    132        }
    133
    134        public synchronized PropertyDescriptor getPropertyDescriptor(String propertyName) {
    135            if (propertyDescriptorMap == null) {
    136                propertyDescriptorMap = new HashMap();
    137                PropertyDescriptor[] pds = getPropertyDescriptors();
    138                for (int i = 0; i < pds.length; i++) {
    139                    propertyDescriptorMap.put(pds[i].getName(), pds[i]);
    140                }
    141            }
    142            return (PropertyDescriptor) propertyDescriptorMap.get(propertyName);
    143        }
    144
    145        /**
    146         * For a dynamic class, returns the corresponding DynamicPropertyHandler
    147         * class.
    148         * @return Class
    149         */
    150        public Class getDynamicPropertyHandlerClass() {
    151            return dynamicPropertyHandlerClass;
    152        }
    153
    154        public String toString() {
    155            StringBuffer buffer = new StringBuffer();
    156            buffer.append("BeanInfo [class = ");
    157            buffer.append(clazz.getName());
    158            if (isDynamic()) {
    159                buffer.append(", dynamic");
    160            }
    161            if (isAtomic()) {
    162                buffer.append(", atomic");
    163            }
    164            buffer.append(", properties = ");
    165            PropertyDescriptor[] jpds = getPropertyDescriptors();
    166            for (int i = 0; i < jpds.length; i++) {
    167                buffer.append("\n    ");
    168                buffer.append(jpds[i].getPropertyType());
    169                buffer.append(": ");
    170                buffer.append(jpds[i].getName());
    171            }
    172            buffer.append("]");
    173            return buffer.toString();
    174        }
    175    }