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 */
017package org.apache.commons.jxpath;
018
019import java.beans.BeanInfo;
020import java.beans.IntrospectionException;
021import java.beans.Introspector;
022import java.beans.PropertyDescriptor;
023import java.util.Arrays;
024import java.util.Comparator;
025import 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: 1190145 $ $Date: 2011-10-28 05:36:24 +0200 (Fr, 28 Okt 2011) $
037 */
038public 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                    return new PropertyDescriptor[0];
124                }
125            }
126        }
127        if (propertyDescriptors.length == 0) {
128            return propertyDescriptors;
129        }
130        PropertyDescriptor[] result = new PropertyDescriptor[propertyDescriptors.length];
131        System.arraycopy(propertyDescriptors, 0, result, 0, propertyDescriptors.length);
132        return result;
133    }
134
135    public synchronized PropertyDescriptor getPropertyDescriptor(String propertyName) {
136        if (propertyDescriptorMap == null) {
137            propertyDescriptorMap = new HashMap();
138            PropertyDescriptor[] pds = getPropertyDescriptors();
139            for (int i = 0; i < pds.length; i++) {
140                propertyDescriptorMap.put(pds[i].getName(), pds[i]);
141            }
142        }
143        return (PropertyDescriptor) propertyDescriptorMap.get(propertyName);
144    }
145
146    /**
147     * For a dynamic class, returns the corresponding DynamicPropertyHandler
148     * class.
149     * @return Class
150     */
151    public Class getDynamicPropertyHandlerClass() {
152        return dynamicPropertyHandlerClass;
153    }
154
155    public String toString() {
156        StringBuffer buffer = new StringBuffer();
157        buffer.append("BeanInfo [class = ");
158        buffer.append(clazz.getName());
159        if (isDynamic()) {
160            buffer.append(", dynamic");
161        }
162        if (isAtomic()) {
163            buffer.append(", atomic");
164        }
165        buffer.append(", properties = ");
166        PropertyDescriptor[] jpds = getPropertyDescriptors();
167        for (int i = 0; i < jpds.length; i++) {
168            buffer.append("\n    ");
169            buffer.append(jpds[i].getPropertyType());
170            buffer.append(": ");
171            buffer.append(jpds[i].getName());
172        }
173        buffer.append("]");
174        return buffer.toString();
175    }
176}