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

File DefaultBeanManager.java

 

Coverage histogram

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

Code metrics

22
52
6
1
235
126
21
0.4
8.67
6
3.5

Classes

Class Line # Actions
DefaultBeanManager 62 52 0% 21 5
0.937593.8%
 

Contributing tests

This file is covered by 364 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.HashMap;
26    import java.util.Map;
27    import java.util.Set;
28   
29    import javax.inject.Inject;
30    import javax.inject.Singleton;
31    import javax.validation.ConstraintViolation;
32    import javax.validation.Validation;
33    import javax.validation.ValidationException;
34    import javax.validation.Validator;
35    import javax.validation.ValidatorFactory;
36   
37    import org.slf4j.Logger;
38    import org.xwiki.collection.SoftCache;
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: 3a6fa880f5e1ff9a79d842c4191c87c918f59098 $
58    * @since 2.0M2
59    */
60    @Component
61    @Singleton
 
62    public class DefaultBeanManager implements BeanManager
63    {
64    /**
65    * Cache the already parsed classes. We store weak reference since the classes might come from extensions later
66    * uninstalled/upgraded or from scripts.
67    */
68    private SoftCache<Class<?>, BeanDescriptor> beanDescriptorCache = new SoftCache<>();
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  2721298 toggle public ValidatorFactory getValidatorFactory()
91    {
92  2721883 if (this.validatorFactory == null) {
93  458 try {
94  458 this.validatorFactory = Validation.buildDefaultValidatorFactory();
95    } catch (ValidationException e) {
96  274 this.logger.debug("Unable to find default JSR 303 provider. There will be no Java bean validation.");
97    }
98    }
99   
100  2721764 return this.validatorFactory;
101    }
102   
 
103  1360482 toggle @Override
104    public void populate(Object bean, Map<String, ?> values) throws PropertyException
105    {
106  1360771 Map<String, Object> remainingValues = new HashMap<String, Object>(values);
107   
108    // Populate
109  1361316 populateBean(bean, remainingValues);
110   
111    // If the bean implements RawProperties, inject remaining properties
112  1361518 if (!remainingValues.isEmpty() && bean instanceof RawProperties) {
113  7229 RawProperties rawProperties = (RawProperties) bean;
114  7229 for (Map.Entry<String, Object> entry : remainingValues.entrySet()) {
115  36001 rawProperties.set(entry.getKey(), entry.getValue());
116    }
117    }
118   
119    // Validate
120  1361495 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  1361131 toggle private void populateBean(Object bean, Map<String, Object> values) throws PropertyException
133    {
134  1361431 BeanDescriptor beanDescriptor = getBeanDescriptor(bean.getClass());
135   
136    // Lower case provided properties to easily ignore properties name case
137  1361668 Map<String, String> lowerKeyMap = new HashMap<String, String>(values.size());
138  1361646 for (Map.Entry<String, ?> entry : values.entrySet()) {
139  282403 lowerKeyMap.put(entry.getKey().toLowerCase(), entry.getKey());
140    }
141   
142  1361568 for (PropertyDescriptor propertyDescriptor : beanDescriptor.getProperties()) {
143  4973125 String propertyId = propertyDescriptor.getId();
144  4973107 Object value = values.get(propertyId);
145   
146  4973708 if (value == null) {
147  4727376 propertyId = propertyId.toLowerCase();
148  4727472 value = values.get(lowerKeyMap.get(propertyId));
149    }
150   
151  4973937 if (value != null) {
152  246361 try {
153    // Convert
154  246361 Object convertedValue = this.converterManager.convert(propertyDescriptor.getPropertyType(), value);
155   
156  246357 if (propertyDescriptor.getWriteMethod() != null) {
157  246333 Method writerMethod = propertyDescriptor.getWriteMethod();
158   
159  246336 setAccessibleSafely(writerMethod);
160   
161    // Invoke the method
162  246334 writerMethod.invoke(bean, convertedValue);
163  24 } else if (propertyDescriptor.getField() != null) {
164  24 Field field = propertyDescriptor.getField();
165   
166  24 setAccessibleSafely(field);
167   
168    // Set the field
169  24 field.set(bean, convertedValue);
170    }
171    } catch (Exception e) {
172  2 throw new PropertyException("Failed to populate property [" + propertyId + "]", e);
173    }
174   
175    // "Tick" already populated properties
176  246355 values.remove(propertyId);
177  4727203 } 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  246357 toggle private void setAccessibleSafely(AccessibleObject classMember)
190    {
191  246356 try {
192    // We do this in a try/catch to avoid false positives caused by existing SecurityManagers.
193  246355 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  1360953 toggle private void validateBean(Object bean) throws PropertyException
206    {
207  1361364 if (getValidatorFactory() != null) {
208  1361092 Validator validator = getValidatorFactory().getValidator();
209  1361336 Set<ConstraintViolation<Object>> constraintViolations = validator.validate(bean);
210  1361077 if (!constraintViolations.isEmpty()) {
211  0 throw new PropertyException(
212    "Failed to validate bean: [" + constraintViolations.iterator().next().getMessage() + "]");
213    }
214    }
215    }
216   
 
217  1361939 toggle @Override
218    public BeanDescriptor getBeanDescriptor(Class<?> beanClass)
219    {
220  1362285 BeanDescriptor beanDescriptor = null;
221   
222  1362254 if (beanClass != null) {
223    // Get the bean descriptor from the cache
224  1362747 beanDescriptor = this.beanDescriptorCache.get(beanClass);
225   
226    // Create a new one if none could be found
227  1363373 if (beanDescriptor == null) {
228  1164 beanDescriptor = new DefaultBeanDescriptor(beanClass);
229  1164 this.beanDescriptorCache.put(beanClass, beanDescriptor);
230    }
231    }
232   
233  1363371 return beanDescriptor;
234    }
235    }