View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jxpath;
19  
20  import java.beans.BeanInfo;
21  import java.beans.IntrospectionException;
22  import java.beans.Introspector;
23  import java.beans.PropertyDescriptor;
24  import java.util.Arrays;
25  import java.util.Comparator;
26  import java.util.HashMap;
27  
28  /**
29   * An implementation of JXPathBeanInfo based on JavaBeans' BeanInfo. Properties advertised by JXPathBasicBeanInfo are the same as those advertised by BeanInfo
30   * for the corresponding class.
31   *
32   * @see java.beans.BeanInfo
33   * @see java.beans.Introspector
34   */
35  public class JXPathBasicBeanInfo implements JXPathBeanInfo {
36  
37      private static final long serialVersionUID = -3863803443111484155L;
38      private static final Comparator<PropertyDescriptor> PROPERTY_DESCRIPTOR_COMPARATOR = Comparator.comparing(PropertyDescriptor::getName);
39      /**
40       * Whether objects of this class are treated as atomic objects which have no properties of their own.
41       */
42      private boolean atomic;
43  
44      /**
45       * Bean class.
46       */
47      private final Class clazz;
48  
49      /**
50       * The DynamicPropertyHandler class for a dynamic class.
51       */
52      private Class dynamicPropertyHandlerClass;
53  
54      /**
55       * List of property descriptors for the beans described by this bean info object.
56       */
57      private transient PropertyDescriptor[] propertyDescriptors;
58  
59      /**
60       * PropertyDescriptor for the specified name or null if there is no such property.
61       */
62      private transient HashMap<String, PropertyDescriptor> propertyDescriptorMap;
63  
64      /**
65       * Constructs a new JXPathBasicBeanInfo.
66       *
67       * @param clazz bean class
68       */
69      public JXPathBasicBeanInfo(final Class clazz) {
70          this.clazz = clazz;
71      }
72  
73      /**
74       * Constructs a new JXPathBasicBeanInfo.
75       *
76       * @param clazz  bean class
77       * @param atomic whether objects of this class are treated as atomic objects which have no properties of their own.
78       */
79      public JXPathBasicBeanInfo(final Class clazz, final boolean atomic) {
80          this.clazz = clazz;
81          this.atomic = atomic;
82      }
83  
84      /**
85       * Constructs a new JXPathBasicBeanInfo.
86       *
87       * @param clazz                       bean class
88       * @param dynamicPropertyHandlerClass dynamic property handler class
89       */
90      public JXPathBasicBeanInfo(final Class clazz, final Class dynamicPropertyHandlerClass) {
91          this.clazz = clazz;
92          this.atomic = false;
93          this.dynamicPropertyHandlerClass = dynamicPropertyHandlerClass;
94      }
95  
96      /**
97       * Gets the DynamicPropertyHandler class for a dynamic class.
98       *
99       * @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 }