View Javadoc

1   /*
2    * Copyright 2009-2010 Steve Chaloner
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package be.objectify.led;
17  
18  import be.objectify.led.util.ContractUtils;
19  import be.objectify.led.util.StringUtils;
20  import be.objectify.led.validation.ValidationFunction;
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  
24  import java.lang.reflect.AccessibleObject;
25  import java.lang.reflect.Field;
26  import java.lang.reflect.Modifier;
27  
28  /**
29   * @author Steve Chaloner
30   */
31  public class PropertySetter
32  {
33      private static final Logger LOGGER = LoggerFactory.getLogger(PropertySetter.class);
34  
35      private final FactoryResolver factoryResolver;
36  
37      private final PropertyContext propertyContext;
38  
39      private final PropertySetterConfiguration configuration;
40  
41      /**
42       * Initialises a new instance with a default {@link PropertyContext} and {@link FactoryResolver}.
43       */
44      public PropertySetter()
45      {
46          this(new DefaultFactoryResolver(),
47               new DefaultPropertyContext());
48      }
49  
50      /**
51       * Initialses a new instance with the given {@link PropertyContext} and default {@link FactoryResolver}.
52       *
53       * @param propertyContext the property context.  Must not be null
54       */
55      public PropertySetter(PropertyContext propertyContext)
56      {
57          this(new DefaultFactoryResolver(),
58               propertyContext);
59      }
60  
61      /**
62       * Initialises a new instance with the given property context and a object factory registry, and a default type factory registry.
63       *
64       * @param factoryResolver the object factory resolver.  Must not be null
65       * @param propertyContext the property context.  Must not be null
66       */
67      public PropertySetter(FactoryResolver factoryResolver,
68                            PropertyContext propertyContext)
69      {
70          ContractUtils.notNull(factoryResolver, "factoryResolver");
71          ContractUtils.notNull(propertyContext, "propertyContext");
72  
73          this.factoryResolver = factoryResolver;
74          this.propertyContext = propertyContext;
75          this.configuration = new PropertySetterConfiguration();
76      }
77  
78      /**
79       * Processes any {@link Property} annotations in the given class.  This
80       * will not affect any instance members.
81       *
82       * @param target the class to check
83       * @param validationFunctions validation checks
84       */
85      public void process(Class target,
86                          ValidationFunction... validationFunctions)
87      {
88          Field[] fields = target.getDeclaredFields();
89          for (Field field : fields)
90          {
91              if (field.isAnnotationPresent(Property.class) &&
92                  Modifier.isStatic(field.getModifiers()))
93              {
94                  setProperty(target,
95                              field,
96                              validationFunctions);
97              }
98          }
99      }
100 
101     /**
102      * Processes any {@link Property} annotations in the given object, including
103      * static members.
104      *
105      * @param target the object to check
106      * @param validationFunctions validation checks
107      */
108     public void process(Object target,
109                         ValidationFunction... validationFunctions)
110     {
111         Field[] fields = target.getClass().getDeclaredFields();
112         for (Field field : fields)
113         {
114             if (field.isAnnotationPresent(Property.class))
115             {
116                 setProperty(target,
117                             field,
118                             validationFunctions);
119             }
120         }
121     }
122 
123     /**
124      * Sets any system property defined for the field.
125      *
126      * @param target the instance to work on
127      * @param field  the field to use to set the property
128      * @param validationFunctions validation checks
129      */
130     private void setProperty(Object target,
131                              Field field,
132                              ValidationFunction... validationFunctions)
133     {
134         String propertyValue = getProperty(field);
135         boolean finalField = Modifier.isFinal(field.getModifiers());
136         if (!StringUtils.isEmpty(propertyValue) &&
137             (!finalField || configuration.isAllowFinalSetting()))
138         {
139             Class<?> type = field.getType();
140             ObjectFactory objectFactory = factoryResolver.resolveFactory(type,
141                                                                          field);
142             if (objectFactory != null)
143             {
144                 String fieldName = field.getAnnotation(Property.class).value();
145                 objectFactory.validate(fieldName,
146                                        propertyValue,
147                                        validationFunctions);
148                 setValue(target,
149                          field,
150                          objectFactory.createObject(fieldName,
151                                                     propertyValue));
152             }
153             else
154             {
155                 LOGGER.info("No factory available for type [{}]",
156                             type);
157             }
158         }
159     }
160 
161     /**
162      * Sets the value on the target.
163      *
164      * @param target the target object/class
165      * @param field  the field to set
166      * @param value  the value to set
167      */
168     private void setValue(Object target,
169                           Field field,
170                           Object value)
171     {
172         if (value != null)
173         {
174             try
175             {
176                 boolean accessibleState = field.isAccessible();
177                 if (!accessibleState)
178                 {
179                     field.setAccessible(true);
180                 }
181                 field.set(target,
182                           value);
183                 field.setAccessible(accessibleState);
184             }
185             catch (IllegalAccessException e)
186             {
187                 LOGGER.error("Unable to set property",
188                              e);
189             }
190         }
191     }
192 
193     private String getProperty(AccessibleObject accessibleObject)
194     {
195         Property annotation = accessibleObject.getAnnotation(Property.class);
196         String propertyName = annotation.value();
197         String propertyValue = propertyContext.getValue(propertyName);
198 
199         if (!StringUtils.isEmpty(propertyValue))
200         {
201             LOGGER.debug("Found value [{}] for system property [{}] on [{}]",
202                          new Object[] {
203                                  propertyValue,
204                                  propertyName,
205                                  accessibleObject.toString()
206                          });
207         }
208 
209         return propertyValue;
210     }
211 
212     public PropertySetterConfiguration getConfiguration()
213     {
214         return configuration;
215     }
216 }