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