Removed untyped contructor from ComponentRegistration and add a protected setter.
[castle.git] / InversionOfControl / Castle.MicroKernel / SubSystems / Conversion / Converters / DefaultComplexConverter.cs
blobd2666440237688464c12d712391b40f16dfb32bf
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
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
17 using System;
18 using System.Reflection;
19 using Castle.Core.Configuration;
20 using Castle.MicroKernel;
21 using Castle.MicroKernel.SubSystems.Conversion;
23 [Serializable]
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);
40 return instance;
43 public override object PerformConversion(string value, Type targetType)
45 throw new NotImplementedException();
48 #endregion
50 /// <summary>
51 /// Creates the target type instance.
52 /// </summary>
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);
62 object[] args = null;
63 if (constructor != null)
65 args = ConvertConstructorParameters(constructor, configuration);
68 object instance = Activator.CreateInstance(type, args);
69 return instance;
72 private Type ObtainImplementation(Type type, IConfiguration configuration)
74 String typeNode = configuration.Attributes["type"];
76 if (String.IsNullOrEmpty(typeNode))
78 if (type.IsInterface)
80 throw new ConverterException("A type attribute must be specified for interfaces");
83 return type;
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);
96 return implType;
99 /// <summary>
100 /// Chooses the first non default constructor. Throws an exception if more than
101 /// one non default constructor is found
102 /// </summary>
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)
112 continue;
113 if (chosen != null)
114 throw new ConverterException("Classes with more than one non-default constructor are not supported.");
115 chosen = candidate;
118 return chosen;
121 /// <summary>
122 /// Converts the constructor parameters.
123 /// </summary>
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;
154 /// <summary>
155 /// Converts the property values.
156 /// </summary>
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)
169 continue;
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);
188 else
190 return this.Context.Composition.PerformConversion(config.Value, type);
194 /// <summary>
195 /// Gets the conversion manager.
196 /// </summary>
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;
211 /// <summary>
212 /// Finds the child (case insensitive).
213 /// </summary>
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)
222 return child;
225 return null;