1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.properties.internal

File DefaultBeanManager.java

 

Coverage histogram

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

Code metrics

22
53
6
1
236
129
21
0.4
8.83
6
3.5

Classes

Class Line # Actions
DefaultBeanManager 62 53 0% 21 4
0.950617395.1%
 

Contributing tests

This file is covered by 319 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.lang.reflect.AccessibleObject;
23    import java.lang.reflect.Field;
24    import java.lang.reflect.Method;
25    import java.util.Collections;
26    import java.util.HashMap;
27    import java.util.Map;
28    import java.util.Set;
29   
30    import javax.inject.Inject;
31    import javax.inject.Singleton;
32    import javax.validation.ConstraintViolation;
33    import javax.validation.Validation;
34    import javax.validation.ValidationException;
35    import javax.validation.Validator;
36    import javax.validation.ValidatorFactory;
37   
38    import org.slf4j.Logger;
39    import org.xwiki.component.annotation.Component;
40    import org.xwiki.properties.BeanDescriptor;
41    import org.xwiki.properties.BeanManager;
42    import org.xwiki.properties.ConverterManager;
43    import org.xwiki.properties.PropertyDescriptor;
44    import org.xwiki.properties.PropertyException;
45    import org.xwiki.properties.PropertyMandatoryException;
46    import org.xwiki.properties.RawProperties;
47   
48    /**
49    * Default implementation of {@link BeanManager} component.
50    * <ul>
51    * <li>use hibernate-validator implementation of JSR 303</li>
52    * <li>add support for any Enum conversion. See {@link org.xwiki.properties.internal.converter.EnumConverter}.</li>
53    * <li>add support for {@link java.awt.Color} conversion using "r,g,b" or "#xxxxxx" format. See
54    * {@link org.xwiki.properties.internal.converter.ColorConverter}.</li>
55    * </ul>
56    *
57    * @version $Id: 3b930de732eafcda32f812b1cefa382bf4ef7dfc $
58    * @since 2.0M2
59    */
60    @Component
61    @Singleton
 
62    public class DefaultBeanManager implements BeanManager
63    {
64    /**
65    * Cache the already parsed classes.
66    */
67    private Map<Class<?>, BeanDescriptor> beanDescriptorCache =
68    Collections.synchronizedMap(new HashMap<Class<?>, BeanDescriptor>());
69   
70    /**
71    * The logger to use for logging.
72    */
73    @Inject
74    private Logger logger;
75   
76    /**
77    * The {@link ConverterManager} component.
78    */
79    @Inject
80    private ConverterManager converterManager;
81   
82    /**
83    * The factory to use to get new JSR 303 validators.
84    */
85    private ValidatorFactory validatorFactory;
86   
87    /**
88    * @return the factory to use to get new JSR 303 validators.
89    */
 
90  207994 toggle public ValidatorFactory getValidatorFactory()
91    {
92  207993 if (this.validatorFactory == null) {
93  383 try {
94  383 this.validatorFactory = Validation.buildDefaultValidatorFactory();
95    } catch (ValidationException e) {
96  213 this.logger.debug("Unable to find default JSR 303 provider. There will be no Java bean validation.");
97    }
98    }
99   
100  207992 return this.validatorFactory;
101    }
102   
 
103  104123 toggle @Override
104    public void populate(Object bean, Map<String, ?> values) throws PropertyException
105    {
106  104114 Map<String, Object> remainingValues = new HashMap<String, Object>(values);
107   
108    // Populate
109  104120 populateBean(bean, remainingValues);
110   
111    // If the bean implements RawProperties, inject remaining properties
112  104114 if (!remainingValues.isEmpty() && bean instanceof RawProperties) {
113  127 RawProperties rawProperties = (RawProperties) bean;
114  127 for (Map.Entry<String, Object> entry : remainingValues.entrySet()) {
115  598 rawProperties.set(entry.getKey(), entry.getValue());
116    }
117    }
118   
119    // Validate
120  104115 validateBean(bean);
121    }
122   
123    /**
124    * Populate the provided bean with provided values.
125    * <p>
126    * <code>values</code> is "consumed": when method executing is finished it only contains not populated properties.
127    *
128    * @param bean the java bean to populate
129    * @param values the values to convert and inject in the java bean
130    * @throws PropertyException error when populating the bean
131    */
 
132  104109 toggle private void populateBean(Object bean, Map<String, Object> values) throws PropertyException
133    {
134  104110 BeanDescriptor beanDescriptor = getBeanDescriptor(bean.getClass());
135   
136    // Lower case provided properties to easily ignore properties name case
137  104124 Map<String, String> lowerKeyMap = new HashMap<String, String>(values.size());
138  104124 for (Map.Entry<String, ?> entry : values.entrySet()) {
139  6144 lowerKeyMap.put(entry.getKey().toLowerCase(), entry.getKey());
140    }
141   
142  104124 for (PropertyDescriptor propertyDescriptor : beanDescriptor.getProperties()) {
143  400780 String propertyId = propertyDescriptor.getId();
144  400775 Object value = values.get(propertyId);
145   
146  400779 if (value == null) {
147  395262 propertyId = propertyId.toLowerCase();
148  395251 value = values.get(lowerKeyMap.get(propertyId));
149    }
150   
151  400773 if (value != null) {
152  5517 try {
153    // Convert
154  5517 Object convertedValue = this.converterManager.convert(propertyDescriptor.getPropertyType(), value);
155   
156  5517 if (propertyDescriptor.getWriteMethod() != null) {
157  5515 Method writerMethod = propertyDescriptor.getWriteMethod();
158   
159  5515 setAccessibleSafely(writerMethod);
160   
161    // Invoke the method
162  5515 writerMethod.invoke(bean, convertedValue);
163  2 } else if (propertyDescriptor.getField() != null) {
164  2 Field field = propertyDescriptor.getField();
165   
166  2 setAccessibleSafely(field);
167   
168    // Set the field
169  2 field.set(bean, convertedValue);
170    }
171    } catch (Exception e) {
172  0 throw new PropertyException("Failed to populate property [" + propertyId + "]", e);
173    }
174   
175    // "Tick" already populated properties
176  5517 values.remove(propertyId);
177  395263 } else if (propertyDescriptor.isMandatory()) {
178  1 throw new PropertyMandatoryException(propertyId);
179    }
180    }
181    }
182   
183    /**
184    * Support nested private classes with public setters. Workaround for
185    * <a href="http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4533479">java reflections bug JDK-4533479</a>.
186    *
187    * @param classMember the class member to make accessible.
188    */
 
189  5517 toggle private void setAccessibleSafely(AccessibleObject classMember)
190    {
191  5517 try {
192    // We do this in a try/catch to avoid false positives caused by existing SecurityManagers.
193  5517 classMember.setAccessible(true);
194    } catch (SecurityException se) {
195  0 logger.debug("Failed to call setAccessible for [{}]", classMember.toString(), se);
196    }
197    }
198   
199    /**
200    * Validate populated values based on JSR 303.
201    *
202    * @param bean the bean to validate
203    * @throws PropertyException validation error
204    */
 
205  104096 toggle private void validateBean(Object bean) throws PropertyException
206    {
207  104107 if (getValidatorFactory() != null) {
208  103892 Validator validator = getValidatorFactory().getValidator();
209  103906 Set<ConstraintViolation<Object>> constraintViolations = validator.validate(bean);
210  103891 if (!constraintViolations.isEmpty()) {
211  2 throw new PropertyException(
212    "Failed to validate bean: [" + constraintViolations.iterator().next().getMessage() + "]");
213    }
214    }
215    }
216   
 
217  105465 toggle @Override
218    public BeanDescriptor getBeanDescriptor(Class<?> beanClass)
219    {
220  105464 BeanDescriptor beanDescriptor = null;
221   
222  105456 if (beanClass != null) {
223    // Since the bean descriptor are cached, lock based on the class to not generate twice the same bean
224    // descriptor.
225  105462 synchronized (beanClass) {
226  105473 beanDescriptor = this.beanDescriptorCache.get(beanClass);
227  105473 if (beanDescriptor == null) {
228  927 beanDescriptor = new DefaultBeanDescriptor(beanClass);
229  927 this.beanDescriptorCache.put(beanClass, beanDescriptor);
230    }
231    }
232    }
233   
234  105473 return beanDescriptor;
235    }
236    }