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    }