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 org.codehaus.plexus.component.configurator.ComponentConfigurationException;
20  import org.codehaus.plexus.component.configurator.ConfigurationListener;
21  import org.codehaus.plexus.component.configurator.converters.lookup.ConverterLookup;
22  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
23  import org.codehaus.plexus.configuration.PlexusConfiguration;
24  import org.codehaus.plexus.util.ReflectionUtils;
25  
26  import java.lang.reflect.Field;
27  import java.lang.reflect.InvocationTargetException;
28  import java.lang.reflect.Method;
29  
30  
31  /** @author <a href="mailto:kenney@codehaus.org">Kenney Westerhof</a> */
32  public class ComponentValueSetter
33  {
34      private Object object;
35  
36      private String fieldName;
37  
38      private ConverterLookup lookup;
39  
40      private Method setter;
41  
42      private Class setterParamType;
43  
44      private ConfigurationConverter setterTypeConverter;
45  
46      private Field field;
47  
48      private Class fieldType;
49  
50      private ConfigurationConverter fieldTypeConverter;
51  
52      private ConfigurationListener listener;
53  
54      public ComponentValueSetter( String fieldName,
55                                   Object object,
56                                   ConverterLookup lookup )
57          throws ComponentConfigurationException
58      {
59          this( fieldName, object, lookup, null );
60      }
61  
62      public ComponentValueSetter( String fieldName,
63                                   Object object,
64                                   ConverterLookup lookup,
65                                   ConfigurationListener listener )
66          throws ComponentConfigurationException
67      {
68          this.fieldName = fieldName;
69          this.object = object;
70          this.lookup = lookup;
71          this.listener = listener;
72  
73          if ( object == null )
74          {
75              throw new ComponentConfigurationException( "Component is null" );
76          }
77  
78          initSetter();
79  
80          initField();
81  
82          if ( setter == null && field == null )
83          {
84              throw new ComponentConfigurationException(
85                  "Cannot find setter nor field in " + object.getClass().getName() + " for '" + fieldName + "'" );
86          }
87  
88          if ( setterTypeConverter == null && fieldTypeConverter == null )
89          {
90              throw new ComponentConfigurationException(
91                  "Cannot find converter for " + setterParamType.getName() + ( fieldType != null && !fieldType.equals(
92                      setterParamType ) ? " or " + fieldType.getName() : "" ) );
93          }
94      }
95  
96      private void initSetter()
97      {
98          setter = ReflectionUtils.getSetter( fieldName, object.getClass() );
99  
100         if ( setter == null )
101         {
102             return;
103         }
104 
105         setterParamType = setter.getParameterTypes()[0];
106 
107         try
108         {
109             setterTypeConverter = lookup.lookupConverterForType( setterParamType );
110         }
111         catch ( ComponentConfigurationException e )
112         {
113             // ignore, handle later
114         }
115     }
116 
117     private void initField()
118     {
119         field = ReflectionUtils.getFieldByNameIncludingSuperclasses( fieldName, object.getClass() );
120 
121         if ( field == null )
122         {
123             return;
124         }
125 
126         fieldType = field.getType();
127 
128         try
129         {
130             fieldTypeConverter = lookup.lookupConverterForType( fieldType );
131         }
132         catch ( ComponentConfigurationException e )
133         {
134             // ignore, handle later
135         }
136     }
137 
138     private void setValueUsingField( Object value )
139         throws ComponentConfigurationException
140     {
141         try
142         {
143             boolean wasAccessible = field.isAccessible();
144 
145             if ( !wasAccessible )
146             {
147                 field.setAccessible( true );
148             }
149 
150             if ( listener != null )
151             {
152                 listener.notifyFieldChangeUsingReflection( fieldName, value, object );
153             }
154 
155             field.set( object, value );
156 
157             if ( !wasAccessible )
158             {
159                 field.setAccessible( false );
160             }
161         }
162         catch ( IllegalAccessException e )
163         {
164             throw new ComponentConfigurationException( "Cannot access field: " + field, e );
165         }
166         catch ( IllegalArgumentException e )
167         {
168             throw new ComponentConfigurationException( "Cannot assign value '" + value + "' (type: "
169                 + value.getClass() + ") to " + field, e );
170         }
171     }
172 
173     private void setValueUsingSetter( Object value )
174         throws ComponentConfigurationException
175     {
176         if ( setterParamType == null || setter == null )
177         {
178             throw new ComponentConfigurationException( "No setter found" );
179         }
180 
181         String exceptionInfo = object.getClass().getName() + "." + setter.getName() + "( " + setterParamType.getClass().getName() + " )";
182 
183         if ( listener != null )
184         {
185             listener.notifyFieldChangeUsingSetter( fieldName, value, object );
186         }
187 
188         try
189         {
190             setter.invoke( object, new Object[]{value} );
191         }
192         catch ( IllegalAccessException e )
193         {
194             throw new ComponentConfigurationException( "Cannot access method: " + exceptionInfo, e );
195         }
196         catch ( IllegalArgumentException e )
197         {
198             throw new ComponentConfigurationException(
199                 "Invalid parameter supplied while setting '" + value + "' to " + exceptionInfo, e );
200         }
201         catch ( InvocationTargetException e )
202         {
203             throw new ComponentConfigurationException( "Setter " + exceptionInfo +
204                 " threw exception when called with parameter '" + value + "': " + e.getTargetException().getMessage(),
205                 e );
206         }
207     }
208 
209     public void configure( PlexusConfiguration config, ClassLoader classLoader, ExpressionEvaluator evaluator )
210         throws ComponentConfigurationException
211     {
212         Object value = null;
213 
214         // try setter converter + method first
215 
216         if ( setterTypeConverter != null )
217         {
218             try
219             {
220                 value = setterTypeConverter.fromConfiguration( lookup, config, setterParamType, object.getClass(), classLoader, evaluator, listener );
221 
222                 if ( value != null )
223                 {
224                     setValueUsingSetter( value );
225 
226                     return;
227                 }
228             }
229             catch ( ComponentConfigurationException e )
230             {
231                 if ( fieldTypeConverter == null || fieldTypeConverter.getClass().equals( setterTypeConverter.getClass() ) )
232                 {
233                     throw e;
234                 }
235             }
236         }
237 
238         // try setting field using value found with method
239         // converter, if present.
240 
241         ComponentConfigurationException savedEx = null;
242 
243         if ( value != null )
244         {
245             try
246             {
247                 setValueUsingField( value );
248                 return;
249             }
250             catch ( ComponentConfigurationException e )
251             {
252                 savedEx = e;
253             }
254         }
255 
256         // either no value or setting went wrong. Try
257         // new converter.
258 
259         value = fieldTypeConverter.fromConfiguration( lookup, config, fieldType, object.getClass(), classLoader, evaluator, listener );
260 
261         if ( value != null )
262         {
263             setValueUsingField( value );
264         }
265         // FIXME: need this?
266         else if ( savedEx != null )
267         {
268             throw savedEx;
269         }
270     }
271 
272 }