View Javadoc
1   package org.codehaus.plexus.component.configurator.converters;
2   
3   /*
4    * Copyright 2005-2007 Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.lang.reflect.Field;
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  
23  import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
24  import org.codehaus.plexus.component.configurator.ConfigurationListener;
25  import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup;
26  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
27  import org.codehaus.plexus.configuration.PlexusConfiguration;
28  import org.codehaus.plexus.util.ReflectionUtils;
29  
30  /** @author <a href="mailto:kenney@codehaus.org">Kenney Westerhof</a> */
31  public class ComponentValueSetter {
32      private Object object;
33  
34      private String fieldName;
35  
36      private ConverterLookup lookup;
37  
38      private Method setter;
39  
40      private Class setterParamType;
41  
42      private ConfigurationConverter setterTypeConverter;
43  
44      private Field field;
45  
46      private Class fieldType;
47  
48      private ConfigurationConverter fieldTypeConverter;
49  
50      private ConfigurationListener listener;
51  
52      public ComponentValueSetter(String fieldName, Object object, ConverterLookup lookup)
53              throws ComponentConfigurationException {
54          this(fieldName, object, lookup, null);
55      }
56  
57      public ComponentValueSetter(String fieldName, Object object, ConverterLookup lookup, ConfigurationListener listener)
58              throws ComponentConfigurationException {
59          this.fieldName = fieldName;
60          this.object = object;
61          this.lookup = lookup;
62          this.listener = listener;
63  
64          if (object == null) {
65              throw new ComponentConfigurationException("Component is null");
66          }
67  
68          initSetter();
69  
70          initField();
71  
72          if (setter == null && field == null) {
73              throw new ComponentConfigurationException(
74                      "Cannot find setter nor field in " + object.getClass().getName() + " for '" + fieldName + "'");
75          }
76  
77          if (setterTypeConverter == null && fieldTypeConverter == null) {
78              throw new ComponentConfigurationException("Cannot find converter for " + setterParamType.getName()
79                      + (fieldType != null && !fieldType.equals(setterParamType) ? " or " + fieldType.getName() : ""));
80          }
81      }
82  
83      private void initSetter() {
84          setter = ReflectionUtils.getSetter(fieldName, object.getClass());
85  
86          if (setter == null) {
87              return;
88          }
89  
90          setterParamType = setter.getParameterTypes()[0];
91  
92          try {
93              setterTypeConverter = lookup.lookupConverterForType(setterParamType);
94          } catch (ComponentConfigurationException e) {
95              // ignore, handle later
96          }
97      }
98  
99      private void initField() {
100         field = ReflectionUtils.getFieldByNameIncludingSuperclasses(fieldName, object.getClass());
101 
102         if (field == null) {
103             return;
104         }
105 
106         fieldType = field.getType();
107 
108         try {
109             fieldTypeConverter = lookup.lookupConverterForType(fieldType);
110         } catch (ComponentConfigurationException e) {
111             // ignore, handle later
112         }
113     }
114 
115     private void setValueUsingField(Object value) throws ComponentConfigurationException {
116         try {
117             boolean wasAccessible = field.isAccessible();
118 
119             if (!wasAccessible) {
120                 field.setAccessible(true);
121             }
122 
123             if (listener != null) {
124                 listener.notifyFieldChangeUsingReflection(fieldName, value, object);
125             }
126 
127             field.set(object, value);
128 
129             if (!wasAccessible) {
130                 field.setAccessible(false);
131             }
132         } catch (IllegalAccessException e) {
133             throw new ComponentConfigurationException("Cannot access field: " + field, e);
134         } catch (IllegalArgumentException e) {
135             throw new ComponentConfigurationException(
136                     "Cannot assign value '" + value + "' (type: " + value.getClass() + ") to " + field, e);
137         }
138     }
139 
140     private void setValueUsingSetter(Object value) throws ComponentConfigurationException {
141         if (setterParamType == null || setter == null) {
142             throw new ComponentConfigurationException("No setter found");
143         }
144 
145         String exceptionInfo = object.getClass().getName() + "." + setter.getName() + "( "
146                 + setterParamType.getClass().getName() + " )";
147 
148         if (listener != null) {
149             listener.notifyFieldChangeUsingSetter(fieldName, value, object);
150         }
151 
152         try {
153             setter.invoke(object, new Object[] {value});
154         } catch (IllegalAccessException e) {
155             throw new ComponentConfigurationException("Cannot access method: " + exceptionInfo, e);
156         } catch (IllegalArgumentException e) {
157             throw new ComponentConfigurationException(
158                     "Invalid parameter supplied while setting '" + value + "' to " + exceptionInfo, e);
159         } catch (InvocationTargetException e) {
160             throw new ComponentConfigurationException(
161                     "Setter " + exceptionInfo + " threw exception when called with parameter '" + value + "': "
162                             + e.getTargetException().getMessage(),
163                     e);
164         }
165     }
166 
167     public void configure(PlexusConfiguration config, ClassLoader classLoader, ExpressionEvaluator evaluator)
168             throws ComponentConfigurationException {
169         Object value = null;
170 
171         // try setter converter + method first
172 
173         if (setterTypeConverter != null) {
174             try {
175                 value = setterTypeConverter.fromConfiguration(
176                         lookup, config, setterParamType, object.getClass(), classLoader, evaluator, listener);
177 
178                 if (value != null) {
179                     setValueUsingSetter(value);
180 
181                     return;
182                 }
183             } catch (ComponentConfigurationException e) {
184                 if (fieldTypeConverter == null
185                         || fieldTypeConverter.getClass().equals(setterTypeConverter.getClass())) {
186                     throw e;
187                 }
188             }
189         }
190 
191         // try setting field using value found with method
192         // converter, if present.
193 
194         ComponentConfigurationException savedEx = null;
195 
196         if (value != null) {
197             try {
198                 setValueUsingField(value);
199                 return;
200             } catch (ComponentConfigurationException e) {
201                 savedEx = e;
202             }
203         }
204 
205         // either no value or setting went wrong. Try
206         // new converter.
207 
208         value = fieldTypeConverter.fromConfiguration(
209                 lookup, config, fieldType, object.getClass(), classLoader, evaluator, listener);
210 
211         if (value != null) {
212             setValueUsingField(value);
213         }
214         // FIXME: need this?
215         else if (savedEx != null) {
216             throw savedEx;
217         }
218     }
219 }