1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.properties.internal

File DefaultBeanDescriptor.java

 

Coverage histogram

../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

48
98
9
1
344
215
41
0.42
10.89
9
4.56

Classes

Class Line # Actions
DefaultBeanDescriptor 60 98 0% 41 5
0.967741996.8%
 

Contributing tests

This file is covered by 377 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.properties.internal;
21   
22    import java.beans.BeanInfo;
23    import java.beans.Introspector;
24    import java.lang.annotation.Annotation;
25    import java.lang.reflect.Field;
26    import java.lang.reflect.Method;
27    import java.lang.reflect.Modifier;
28    import java.lang.reflect.Type;
29    import java.text.MessageFormat;
30    import java.util.Arrays;
31    import java.util.Collection;
32    import java.util.HashMap;
33    import java.util.LinkedHashMap;
34    import java.util.List;
35    import java.util.Map;
36   
37    import org.apache.commons.lang3.ArrayUtils;
38    import org.slf4j.Logger;
39    import org.slf4j.LoggerFactory;
40    import org.xwiki.component.util.DefaultParameterizedType;
41    import org.xwiki.properties.BeanDescriptor;
42    import org.xwiki.properties.PropertyDescriptor;
43    import org.xwiki.properties.PropertyGroupDescriptor;
44    import org.xwiki.properties.annotation.PropertyAdvanced;
45    import org.xwiki.properties.annotation.PropertyDescription;
46    import org.xwiki.properties.annotation.PropertyDisplayType;
47    import org.xwiki.properties.annotation.PropertyFeature;
48    import org.xwiki.properties.annotation.PropertyGroup;
49    import org.xwiki.properties.annotation.PropertyHidden;
50    import org.xwiki.properties.annotation.PropertyId;
51    import org.xwiki.properties.annotation.PropertyMandatory;
52    import org.xwiki.properties.annotation.PropertyName;
53   
54    /**
55    * Default implementation for BeanDescriptor.
56    *
57    * @version $Id: 2b4fdf1728a3062c06856ee36b5917cfa42ad857 $
58    * @since 2.0M2
59    */
 
60    public class DefaultBeanDescriptor implements BeanDescriptor
61    {
62    /**
63    * The logger to use to log.
64    */
65    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBeanDescriptor.class);
66   
67    private static final List<Class<? extends Annotation>> COMMON_ANNOTATION_CLASSES = Arrays.asList(
68    PropertyMandatory.class, Deprecated.class, PropertyAdvanced.class, PropertyGroup.class,
69    PropertyFeature.class, PropertyDisplayType.class);
70   
71    /**
72    * @see #getBeanClass()
73    */
74    private Class<?> beanClass;
75   
76    /**
77    * The properties of the bean.
78    */
79    private Map<String, PropertyDescriptor> parameterDescriptorMap = new LinkedHashMap<>();
80   
81    private Map<PropertyGroup, PropertyGroupDescriptor> groups = new HashMap<>();
82   
83    /**
84    * @param beanClass the class of the JAVA bean.
85    */
 
86  1180 toggle public DefaultBeanDescriptor(Class<?> beanClass)
87    {
88  1180 this.beanClass = beanClass;
89   
90  1180 extractBeanDescriptor();
91    }
92   
93    /**
94    * Extract informations form the bean.
95    */
 
96  1180 toggle protected void extractBeanDescriptor()
97    {
98  1180 Object defaultInstance = null;
99   
100  1180 try {
101  1180 defaultInstance = getBeanClass().newInstance();
102    } catch (Exception e) {
103  46 LOGGER.debug("Failed to create a new default instance for class " + this.beanClass
104    + ". The BeanDescriptor will not contains any default value information.", e);
105    }
106   
107  1180 try {
108    // Get public fields
109  5211 for (Class<?> currentClass = this.beanClass; currentClass != null; currentClass =
110    currentClass.getSuperclass()) {
111  4031 Field[] fields = currentClass.getFields();
112  4031 for (Field field : fields) {
113  5891 if (!Modifier.isStatic(field.getModifiers())) {
114  236 extractPropertyDescriptor(field, defaultInstance);
115    }
116    }
117    }
118   
119    // Get getter/setter based properties
120  1180 BeanInfo beanInfo = Introspector.getBeanInfo(this.beanClass);
121  1180 java.beans.PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
122  1180 if (propertyDescriptors != null) {
123  1180 for (java.beans.PropertyDescriptor propertyDescriptor : propertyDescriptors) {
124  8342 if (propertyDescriptor != null) {
125  8342 extractPropertyDescriptor(propertyDescriptor, defaultInstance);
126    }
127    }
128    }
129    } catch (Exception e) {
130  1 LOGGER.error("Failed to load bean descriptor for class " + this.beanClass, e);
131    }
132    }
133   
134    /**
135    * Extract provided properties information and insert it in {@link #parameterDescriptorMap}.
136    *
137    * @param propertyDescriptor the JAVA bean property descriptor.
138    * @param defaultInstance the default instance of bean class.
139    */
 
140  8342 toggle protected void extractPropertyDescriptor(java.beans.PropertyDescriptor propertyDescriptor, Object defaultInstance)
141    {
142  8342 DefaultPropertyDescriptor desc = new DefaultPropertyDescriptor();
143   
144  8342 Method writeMethod = propertyDescriptor.getWriteMethod();
145   
146  8342 if (writeMethod != null) {
147  6676 Method readMethod = propertyDescriptor.getReadMethod();
148   
149    // is parameter hidden
150  6676 PropertyHidden parameterHidden = extractPropertyAnnotation(writeMethod, readMethod, PropertyHidden.class);
151   
152  6676 if (parameterHidden == null) {
153    // get parameter id
154  6319 PropertyId propertyId = extractPropertyAnnotation(writeMethod, readMethod, PropertyId.class);
155  6319 desc.setId(propertyId != null ? propertyId.value() : propertyDescriptor.getName());
156   
157    // set parameter type
158  6319 Type propertyType;
159  6319 if (readMethod != null) {
160  6269 propertyType = readMethod.getGenericReturnType();
161    } else {
162  50 propertyType = writeMethod.getGenericParameterTypes()[0];
163    }
164  6319 desc.setPropertyType(propertyType);
165   
166    // get parameter display name
167  6319 PropertyName parameterName = extractPropertyAnnotation(writeMethod, readMethod, PropertyName.class);
168   
169  6319 desc.setName(parameterName != null ? parameterName.value() : desc.getId());
170   
171    // get parameter description
172  6319 PropertyDescription parameterDescription =
173    extractPropertyAnnotation(writeMethod, readMethod, PropertyDescription.class);
174   
175  6319 desc.setDescription(parameterDescription != null ? parameterDescription.value() : propertyDescriptor
176    .getShortDescription());
177   
178  6319 Map<Class, Annotation> annotations = new HashMap<>();
179  6319 COMMON_ANNOTATION_CLASSES.forEach(aClass ->
180    annotations.put(aClass, extractPropertyAnnotation(writeMethod, readMethod, aClass))
181    );
182   
183  6319 setCommonProperties(desc, annotations);
184   
185  6318 if (defaultInstance != null && readMethod != null) {
186    // get default value
187  6184 try {
188  6184 desc.setDefaultValue(readMethod.invoke(defaultInstance));
189    } catch (Exception e) {
190  0 LOGGER.error(MessageFormat.format(
191    "Failed to get default property value from getter {0} in class {1}",
192    readMethod.getName(),
193    this.beanClass), e);
194    }
195    }
196   
197  6318 desc.setWriteMethod(writeMethod);
198   
199  6318 desc.setReadMethod(readMethod);
200   
201  6318 this.parameterDescriptorMap.put(desc.getId(), desc);
202    }
203    }
204    }
205   
206    /**
207    * Extract provided properties informations and insert it in {@link #parameterDescriptorMap}.
208    *
209    * @param field the JAVA bean property descriptor.
210    * @param defaultInstance the default instance of bean class.
211    */
 
212  236 toggle protected void extractPropertyDescriptor(Field field, Object defaultInstance)
213    {
214  236 DefaultPropertyDescriptor desc = new DefaultPropertyDescriptor();
215   
216    // is parameter hidden
217  236 PropertyHidden parameterHidden = field.getAnnotation(PropertyHidden.class);
218   
219  236 if (parameterHidden == null) {
220    // get parameter id
221  236 PropertyId propertyId = field.getAnnotation(PropertyId.class);
222  236 desc.setId(propertyId != null ? propertyId.value() : field.getName());
223   
224    // set parameter type
225  236 desc.setPropertyType(field.getGenericType());
226   
227    // get parameter name
228  236 PropertyName parameterName = field.getAnnotation(PropertyName.class);
229   
230  236 desc.setName(parameterName != null ? parameterName.value() : desc.getId());
231   
232    // get parameter description
233  236 PropertyDescription parameterDescription = field.getAnnotation(PropertyDescription.class);
234   
235  236 desc.setDescription(parameterDescription != null ? parameterDescription.value() : desc.getId());
236   
237  236 Map<Class, Annotation> annotations = new HashMap<>();
238  236 COMMON_ANNOTATION_CLASSES.forEach(aClass ->
239    annotations.put(aClass, field.getAnnotation(aClass))
240    );
241   
242  236 setCommonProperties(desc, annotations);
243   
244  236 if (defaultInstance != null) {
245    // get default value
246  60 try {
247  60 desc.setDefaultValue(field.get(defaultInstance));
248    } catch (Exception e) {
249  0 LOGGER.error(
250    MessageFormat.format("Failed to get default property value from field {0} in class {1}",
251    field.getName(), this.beanClass), e);
252    }
253    }
254   
255  236 desc.setField(field);
256   
257  236 this.parameterDescriptorMap.put(desc.getId(), desc);
258    }
259    }
260   
 
261  6555 toggle private void setCommonProperties(DefaultPropertyDescriptor desc, Map<Class, Annotation> annotations)
262    {
263   
264  6555 desc.setMandatory(annotations.get(PropertyMandatory.class) != null);
265  6555 desc.setDeprecated(annotations.get(Deprecated.class) != null);
266  6555 desc.setAdvanced(annotations.get(PropertyAdvanced.class) != null);
267   
268  6555 PropertyGroup parameterGroup = (PropertyGroup) annotations.get(PropertyGroup.class);
269  6555 PropertyGroupDescriptor group = this.groups.get(parameterGroup);
270  6555 if (group == null && parameterGroup != null) {
271  62 group = new PropertyGroupDescriptor(Arrays.asList(parameterGroup.value()));
272  6493 } else if (group == null) {
273  6431 group = new PropertyGroupDescriptor(null);
274    }
275  6555 desc.setGroupDescriptor(group);
276  6555 if (parameterGroup != null) {
277  124 this.groups.put(parameterGroup, group);
278    }
279   
280  6555 PropertyFeature parameterFeature = (PropertyFeature) annotations.get(PropertyFeature.class);
281  6555 if (parameterFeature != null) {
282  118 if (group.getFeature() != null) {
283  1 throw new RuntimeException(
284    "Property [" + desc.getId() + "] has overriden a feature. (previous: [" + group.getFeature()
285    + "], new: [" + parameterFeature.value() + "]");
286    }
287  117 group.setFeature(parameterFeature.value());
288    }
289   
290   
291  6554 PropertyDisplayType displayTypeAnnotation = (PropertyDisplayType) annotations.get(PropertyDisplayType.class);
292  6554 Type displayType;
293  6554 if (displayTypeAnnotation != null && displayTypeAnnotation.value().length > 0) {
294  42 Class[] types = displayTypeAnnotation.value();
295  42 if (types.length > 1) {
296  21 displayType = new DefaultParameterizedType(null, types[0], ArrayUtils.remove(types, 0));
297    } else {
298  21 displayType = types[0];
299    }
300    } else {
301  6512 displayType = desc.getPropertyType();
302    }
303  6554 desc.setDisplayType(displayType);
304    }
305   
306    /**
307    * Get the parameter annotation. Try first on the setter then on the getter if no annotation has been found.
308    *
309    * @param <T> the Class object corresponding to the annotation type.
310    * @param writeMethod the method that should be used to write the property value.
311    * @param readMethod the method that should be used to read the property value.
312    * @param annotationClass the Class object corresponding to the annotation type.
313    * @return this element's annotation for the specified annotation type if present on this element, else null.
314    */
 
315  63547 toggle protected <T extends Annotation> T extractPropertyAnnotation(Method writeMethod, Method readMethod,
316    Class<T> annotationClass)
317    {
318  63547 T parameterDescription = writeMethod.getAnnotation(annotationClass);
319   
320  63547 if (parameterDescription == null && readMethod != null) {
321  61067 parameterDescription = readMethod.getAnnotation(annotationClass);
322    }
323   
324  63547 return parameterDescription;
325    }
326   
 
327  515706 toggle @Override
328    public Class<?> getBeanClass()
329    {
330  515785 return this.beanClass;
331    }
332   
 
333  1363148 toggle @Override
334    public Collection<PropertyDescriptor> getProperties()
335    {
336  1363198 return this.parameterDescriptorMap.values();
337    }
338   
 
339  19 toggle @Override
340    public PropertyDescriptor getProperty(String propertyName)
341    {
342  19 return this.parameterDescriptorMap.get(propertyName);
343    }
344    }