1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.using System;
15 namespace Castle
.MicroKernel
.SubSystems
.Conversion
18 using System
.Reflection
;
19 using Castle
.Core
.Configuration
;
20 using Castle
.MicroKernel
;
21 using Castle
.MicroKernel
.SubSystems
.Conversion
;
24 public class DefaultComplexConverter
: AbstractTypeConverter
26 private IConversionManager _conversionManager
= null;
28 #region ITypeConverter Member
30 public override bool CanHandleType(Type type
)
32 return !type
.IsPrimitive
;
35 public override object PerformConversion(IConfiguration configuration
, Type targetType
)
37 object instance
= CreateInstance(targetType
, configuration
);
38 ConvertPropertyValues(instance
, targetType
, configuration
);
43 public override object PerformConversion(string value, Type targetType
)
45 throw new NotImplementedException();
51 /// Creates the target type instance.
53 /// <param name="type">The type.</param>
54 /// <param name="configuration">The configuration.</param>
55 /// <returns></returns>
56 private object CreateInstance(Type type
, IConfiguration configuration
)
58 type
= ObtainImplementation(type
, configuration
);
60 ConstructorInfo constructor
= ChooseConstructor(type
);
63 if (constructor
!= null)
65 args
= ConvertConstructorParameters(constructor
, configuration
);
68 object instance
= Activator
.CreateInstance(type
, args
);
72 private Type
ObtainImplementation(Type type
, IConfiguration configuration
)
74 String typeNode
= configuration
.Attributes
["type"];
76 if (String
.IsNullOrEmpty(typeNode
))
80 throw new ConverterException("A type attribute must be specified for interfaces");
86 Type implType
= (Type
) Context
.Composition
.PerformConversion(typeNode
, typeof(Type
));
88 if (!type
.IsAssignableFrom(implType
))
90 String message
= String
.Format("Type {0} is not assignable to {1}",
91 implType
.FullName
, type
.FullName
);
93 throw new ConverterException(message
);
100 /// Chooses the first non default constructor. Throws an exception if more than
101 /// one non default constructor is found
103 /// <param name="type"></param>
104 /// <returns>The chosen constructor, or <c>null</c> if none was found</returns>
105 private ConstructorInfo
ChooseConstructor(Type type
)
107 ConstructorInfo chosen
= null;
108 ConstructorInfo
[] constructors
= type
.GetConstructors();
109 foreach(ConstructorInfo candidate
in constructors
)
111 if (candidate
.GetParameters().Length
== 0)
114 throw new ConverterException("Classes with more than one non-default constructor are not supported.");
122 /// Converts the constructor parameters.
124 /// <param name="constructor">The constructor.</param>
125 /// <param name="configuration">The configuration.</param>
126 /// <returns></returns>
127 private object[] ConvertConstructorParameters(ConstructorInfo constructor
, IConfiguration configuration
)
129 IConversionManager conversionManager
= this.ConversionManager
;
131 ParameterInfo
[] parameters
= constructor
.GetParameters();
132 object[] parameterValues
= new object[parameters
.Length
];
134 for(int i
= 0, n
= parameters
.Length
; i
< n
; ++i
)
136 ParameterInfo parameter
= parameters
[i
];
138 IConfiguration paramConfig
= FindChildIgnoreCase(configuration
, parameter
.Name
);
139 if (paramConfig
== null)
140 throw new ConverterException("Child '" + parameter
.Name
+ "' missing in " + configuration
.Name
+ " element.");
142 Type paramType
= parameter
.ParameterType
;
143 if (!this.ConversionManager
.CanHandleType(paramType
))
144 throw new ConverterException("No converter found for child '" + parameter
.Name
+ "' in " + configuration
.Name
+
145 " element (type: " + paramType
.Name
+ ").");
148 parameterValues
[i
] = ConvertChildParameter(paramConfig
, paramType
);
151 return parameterValues
;
155 /// Converts the property values.
157 /// <param name="instance">The instance.</param>
158 /// <param name="type">The type.</param>
159 /// <param name="configuration">The configuration.</param>
160 private void ConvertPropertyValues(object instance
, Type type
, IConfiguration configuration
)
162 IConversionManager conversionManager
= this.ConversionManager
;
164 foreach(IConfiguration propConfig
in configuration
.Children
)
166 PropertyInfo property
=
167 type
.GetProperty(propConfig
.Name
, BindingFlags
.Instance
| BindingFlags
.Public
| BindingFlags
.IgnoreCase
);
168 if (property
== null || !property
.CanWrite
)
171 Type propType
= property
.PropertyType
;
172 if (!this.ConversionManager
.CanHandleType(propType
))
173 throw new ConverterException("No converter found for child '" + property
.Name
+ "' in " + configuration
.Name
+
174 " element (type: " + propType
.Name
+ ").");
176 object val
= ConvertChildParameter(propConfig
, propType
);
177 property
.SetValue(instance
, val
, null);
182 private object ConvertChildParameter(IConfiguration config
, Type type
)
184 if (config
.Value
== null && config
.Children
.Count
!= 0)
186 return this.Context
.Composition
.PerformConversion(config
, type
);
190 return this.Context
.Composition
.PerformConversion(config
.Value
, type
);
195 /// Gets the conversion manager.
197 /// <value>The conversion manager.</value>
198 private IConversionManager ConversionManager
202 if (_conversionManager
== null)
204 _conversionManager
= (IConversionManager
)
205 this.Context
.Kernel
.GetSubSystem(SubSystemConstants
.ConversionManagerKey
);
207 return _conversionManager
;
212 /// Finds the child (case insensitive).
214 /// <param name="config">The config.</param>
215 /// <param name="name">The name.</param>
216 /// <returns></returns>
217 private IConfiguration
FindChildIgnoreCase(IConfiguration config
, string name
)
219 foreach(IConfiguration child
in config
.Children
)
221 if (string.Compare(child
.Name
, name
, true) == 0)