1. Project Clover database Fri Dec 7 2018 16:32:04 CET
  2. Package org.xwiki.properties.internal

File DefaultBeanDescriptor.java

 

Coverage histogram

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

Code metrics

44
97
9
1
329
203
38
0.39
10.78
9
4.22

Classes

Class Line # Actions
DefaultBeanDescriptor 56 97 0% 38 11
0.926666792.7%
 

Contributing tests

This file is covered by 16 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.Map;
35   
36    import org.slf4j.Logger;
37    import org.slf4j.LoggerFactory;
38    import org.xwiki.properties.BeanDescriptor;
39    import org.xwiki.properties.PropertyDescriptor;
40    import org.xwiki.properties.PropertyGroupDescriptor;
41    import org.xwiki.properties.annotation.PropertyAdvanced;
42    import org.xwiki.properties.annotation.PropertyDescription;
43    import org.xwiki.properties.annotation.PropertyFeature;
44    import org.xwiki.properties.annotation.PropertyGroup;
45    import org.xwiki.properties.annotation.PropertyHidden;
46    import org.xwiki.properties.annotation.PropertyId;
47    import org.xwiki.properties.annotation.PropertyMandatory;
48    import org.xwiki.properties.annotation.PropertyName;
49   
50    /**
51    * Default implementation for BeanDescriptor.
52    *
53    * @version $Id: 282ab6ecdb6ae6883c6ba106a2b280226fab012d $
54    * @since 2.0M2
55    */
 
56    public class DefaultBeanDescriptor implements BeanDescriptor
57    {
58    /**
59    * The logger to use to log.
60    */
61    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultBeanDescriptor.class);
62   
63    /**
64    * @see #getBeanClass()
65    */
66    private Class<?> beanClass;
67   
68    /**
69    * The properties of the bean.
70    */
71    private Map<String, PropertyDescriptor> parameterDescriptorMap = new LinkedHashMap<>();
72   
73    private Map<PropertyGroup, PropertyGroupDescriptor> groups = new HashMap<>();
74   
75    /**
76    * @param beanClass the class of the JAVA bean.
77    */
 
78  17 toggle public DefaultBeanDescriptor(Class<?> beanClass)
79    {
80  17 this.beanClass = beanClass;
81   
82  17 extractBeanDescriptor();
83    }
84   
85    /**
86    * Extract informations form the bean.
87    */
 
88  17 toggle protected void extractBeanDescriptor()
89    {
90  17 Object defaultInstance = null;
91   
92  17 try {
93  17 defaultInstance = getBeanClass().newInstance();
94    } catch (Exception e) {
95  0 LOGGER.debug("Failed to create a new default instance for class " + this.beanClass
96    + ". The BeanDescriptor will not contains any default value information.", e);
97    }
98   
99  17 try {
100    // Get public fields
101  53 for (Class<?> currentClass = this.beanClass; currentClass != null; currentClass =
102    currentClass.getSuperclass()) {
103  36 Field[] fields = currentClass.getFields();
104  36 for (Field field : fields) {
105  91 if (!Modifier.isStatic(field.getModifiers())) {
106  60 extractPropertyDescriptor(field, defaultInstance);
107    }
108    }
109    }
110   
111    // Get getter/setter based properties
112  17 BeanInfo beanInfo = Introspector.getBeanInfo(this.beanClass);
113  17 java.beans.PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
114  17 if (propertyDescriptors != null) {
115  17 for (java.beans.PropertyDescriptor propertyDescriptor : propertyDescriptors) {
116  170 if (propertyDescriptor != null) {
117  170 extractPropertyDescriptor(propertyDescriptor, defaultInstance);
118    }
119    }
120    }
121    } catch (Exception e) {
122  1 LOGGER.error("Failed to load bean descriptor for class " + this.beanClass, e);
123    }
124    }
125   
126    /**
127    * Extract provided properties information and insert it in {@link #parameterDescriptorMap}.
128    *
129    * @param propertyDescriptor the JAVA bean property descriptor.
130    * @param defaultInstance the default instance of bean class.
131    */
 
132  170 toggle protected void extractPropertyDescriptor(java.beans.PropertyDescriptor propertyDescriptor, Object defaultInstance)
133    {
134  170 DefaultPropertyDescriptor desc = new DefaultPropertyDescriptor();
135   
136  170 Method writeMethod = propertyDescriptor.getWriteMethod();
137   
138  170 if (writeMethod != null) {
139  152 Method readMethod = propertyDescriptor.getReadMethod();
140   
141    // is parameter hidden
142  152 PropertyHidden parameterHidden = extractPropertyAnnotation(writeMethod, readMethod, PropertyHidden.class);
143   
144  152 if (parameterHidden == null) {
145    // get parameter id
146  137 PropertyId propertyId = extractPropertyAnnotation(writeMethod, readMethod, PropertyId.class);
147  137 desc.setId(propertyId != null ? propertyId.value() : propertyDescriptor.getName());
148   
149    // set parameter type
150  137 Type propertyType;
151  137 if (readMethod != null) {
152  137 propertyType = readMethod.getGenericReturnType();
153    } else {
154  0 propertyType = writeMethod.getGenericParameterTypes()[0];
155    }
156  137 desc.setPropertyType(propertyType);
157   
158    // get parameter display name
159  137 PropertyName parameterName = extractPropertyAnnotation(writeMethod, readMethod, PropertyName.class);
160   
161  137 desc.setName(parameterName != null ? parameterName.value() : desc.getId());
162   
163    // get parameter description
164  137 PropertyDescription parameterDescription =
165    extractPropertyAnnotation(writeMethod, readMethod, PropertyDescription.class);
166   
167  137 desc.setDescription(parameterDescription != null ? parameterDescription.value() : propertyDescriptor
168    .getShortDescription());
169   
170  137 Map<Class, Annotation> annotations = new HashMap<>();
171  137 annotations.put(PropertyMandatory.class,
172    extractPropertyAnnotation(writeMethod, readMethod, PropertyMandatory.class));
173  137 annotations.put(Deprecated.class, extractPropertyAnnotation(writeMethod, readMethod, Deprecated.class));
174  137 annotations.put(PropertyAdvanced.class,
175    extractPropertyAnnotation(writeMethod, readMethod, PropertyAdvanced.class));
176  137 annotations.put(PropertyGroup.class,
177    extractPropertyAnnotation(writeMethod, readMethod, PropertyGroup.class));
178  137 annotations.put(PropertyFeature.class,
179    extractPropertyAnnotation(writeMethod, readMethod, PropertyFeature.class));
180   
181  137 setCommonProperties(desc, annotations);
182   
183  136 if (defaultInstance != null && readMethod != null) {
184    // get default value
185  136 try {
186  136 desc.setDefaultValue(readMethod.invoke(defaultInstance));
187    } catch (Exception e) {
188  0 LOGGER.error(MessageFormat.format(
189    "Failed to get default property value from getter {0} in class {1}",
190    readMethod.getName(),
191    this.beanClass), e);
192    }
193    }
194   
195  136 desc.setWriteMethod(writeMethod);
196   
197  136 desc.setReadMethod(readMethod);
198   
199  136 this.parameterDescriptorMap.put(desc.getId(), desc);
200    }
201    }
202    }
203   
204    /**
205    * Extract provided properties informations and insert it in {@link #parameterDescriptorMap}.
206    *
207    * @param field the JAVA bean property descriptor.
208    * @param defaultInstance the default instance of bean class.
209    */
 
210  60 toggle protected void extractPropertyDescriptor(Field field, Object defaultInstance)
211    {
212  60 DefaultPropertyDescriptor desc = new DefaultPropertyDescriptor();
213   
214    // is parameter hidden
215  60 PropertyHidden parameterHidden = field.getAnnotation(PropertyHidden.class);
216   
217  60 if (parameterHidden == null) {
218    // get parameter id
219  60 PropertyId propertyId = field.getAnnotation(PropertyId.class);
220  60 desc.setId(propertyId != null ? propertyId.value() : field.getName());
221   
222    // set parameter type
223  60 desc.setPropertyType(field.getGenericType());
224   
225    // get parameter name
226  60 PropertyName parameterName = field.getAnnotation(PropertyName.class);
227   
228  60 desc.setName(parameterName != null ? parameterName.value() : desc.getId());
229   
230    // get parameter description
231  60 PropertyDescription parameterDescription = field.getAnnotation(PropertyDescription.class);
232   
233  60 desc.setDescription(parameterDescription != null ? parameterDescription.value() : desc.getId());
234   
235  60 Map<Class, Annotation> annotations = new HashMap<>();
236  60 annotations.put(PropertyMandatory.class, field.getAnnotation(PropertyMandatory.class));
237  60 annotations.put(Deprecated.class, field.getAnnotation(Deprecated.class));
238  60 annotations.put(PropertyAdvanced.class, field.getAnnotation(PropertyAdvanced.class));
239  60 annotations.put(PropertyGroup.class, field.getAnnotation(PropertyGroup.class));
240  60 annotations.put(PropertyFeature.class, field.getAnnotation(PropertyFeature.class));
241   
242  60 setCommonProperties(desc, annotations);
243   
244  60 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  60 desc.setField(field);
256   
257  60 this.parameterDescriptorMap.put(desc.getId(), desc);
258    }
259    }
260   
 
261  197 toggle private void setCommonProperties(DefaultPropertyDescriptor desc, Map<Class, Annotation> annotations)
262    {
263   
264  197 desc.setMandatory(annotations.get(PropertyMandatory.class) != null);
265  197 desc.setDeprecated(annotations.get(Deprecated.class) != null);
266  197 desc.setAdvanced(annotations.get(PropertyAdvanced.class) != null);
267   
268  197 PropertyGroup parameterGroup = (PropertyGroup) annotations.get(PropertyGroup.class);
269  197 PropertyGroupDescriptor group = this.groups.get(parameterGroup);
270  197 if (group == null && parameterGroup != null) {
271  16 group = new PropertyGroupDescriptor(Arrays.asList(parameterGroup.value()));
272  181 } else if (group == null) {
273  165 group = new PropertyGroupDescriptor(null);
274    }
275  197 desc.setGroupDescriptor(group);
276  197 if (parameterGroup != null) {
277  32 this.groups.put(parameterGroup, group);
278    }
279   
280  197 PropertyFeature parameterFeature = (PropertyFeature) annotations.get(PropertyFeature.class);
281  197 if (parameterFeature != null) {
282  32 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  31 group.setFeature(parameterFeature.value());
288    }
289    }
290   
291    /**
292    * Get the parameter annotation. Try first on the setter then on the getter if no annotation has been found.
293    *
294    * @param <T> the Class object corresponding to the annotation type.
295    * @param writeMethod the method that should be used to write the property value.
296    * @param readMethod the method that should be used to read the property value.
297    * @param annotationClass the Class object corresponding to the annotation type.
298    * @return this element's annotation for the specified annotation type if present on this element, else null.
299    */
 
300  1248 toggle protected <T extends Annotation> T extractPropertyAnnotation(Method writeMethod, Method readMethod,
301    Class<T> annotationClass)
302    {
303  1248 T parameterDescription = writeMethod.getAnnotation(annotationClass);
304   
305  1248 if (parameterDescription == null && readMethod != null) {
306  1139 parameterDescription = readMethod.getAnnotation(annotationClass);
307    }
308   
309  1248 return parameterDescription;
310    }
311   
 
312  17 toggle @Override
313    public Class<?> getBeanClass()
314    {
315  17 return this.beanClass;
316    }
317   
 
318  3 toggle @Override
319    public Collection<PropertyDescriptor> getProperties()
320    {
321  3 return this.parameterDescriptorMap.values();
322    }
323   
 
324  17 toggle @Override
325    public PropertyDescriptor getProperty(String propertyName)
326    {
327  17 return this.parameterDescriptorMap.get(propertyName);
328    }
329    }