protected void SetParameter()

in src/log4net/Repository/Hierarchy/XmlHierarchyConfigurator.cs [570:836]


		protected void SetParameter(XmlElement element, object target) 
		{
			// Get the property name
			string name = element.GetAttribute(NAME_ATTR);

			// If the name attribute does not exist then use the name of the element
			if (element.LocalName != PARAM_TAG || name == null || name.Length == 0)
			{
				name = element.LocalName;
			}

			// Look for the property on the target object
			Type targetType = target.GetType();
			Type propertyType = null;

			PropertyInfo propInfo = null;
			MethodInfo methInfo = null;

			// Try to find a writable property
			propInfo = targetType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase);
			if (propInfo != null && propInfo.CanWrite)
			{
				// found a property
				propertyType = propInfo.PropertyType;
			}
			else
			{
				propInfo = null;

				// look for a method with the signature Add<property>(type)
				methInfo = FindMethodInfo(targetType, name);

				if (methInfo != null)
				{
					propertyType = methInfo.GetParameters()[0].ParameterType;
				}
			}

			if (propertyType == null)
			{
				LogLog.Error(declaringType, "XmlHierarchyConfigurator: Cannot find Property [" + name + "] to set object on [" + target.ToString() + "]");
			}
			else
			{
				string propertyValue = null;

				if (element.GetAttributeNode(VALUE_ATTR) != null)
				{
					propertyValue = element.GetAttribute(VALUE_ATTR);
				}
				else if (element.HasChildNodes)
				{
					// Concatenate the CDATA and Text nodes together
					foreach(XmlNode childNode in element.ChildNodes)
					{
						if (childNode.NodeType == XmlNodeType.CDATA || childNode.NodeType == XmlNodeType.Text)
						{
							if (propertyValue == null)
							{
								propertyValue = childNode.InnerText;
							}
							else
							{
								propertyValue += childNode.InnerText;
							}
						}
					}
				}

				if(propertyValue != null)
				{
#if !NETCF && !NETSTANDARD1_3 // NETSTANDARD1_3: System.Runtime.InteropServices.RuntimeInformation not available on desktop 4.6
					try
					{
						// Expand environment variables in the string.
					    IDictionary environmentVariables = Environment.GetEnvironmentVariables();
					    if (HasCaseInsensitiveEnvironment) {
						environmentVariables = CreateCaseInsensitiveWrapper(environmentVariables);
					    }
						propertyValue = OptionConverter.SubstituteVariables(propertyValue, environmentVariables);
					}
					catch(System.Security.SecurityException)
					{
						// This security exception will occur if the caller does not have 
						// unrestricted environment permission. If this occurs the expansion 
						// will be skipped with the following warning message.
						LogLog.Debug(declaringType, "Security exception while trying to expand environment variables. Error Ignored. No Expansion.");
					}
#endif

					Type parsedObjectConversionTargetType = null;

					// Check if a specific subtype is specified on the element using the 'type' attribute
					string subTypeString = element.GetAttribute(TYPE_ATTR);
					if (subTypeString != null && subTypeString.Length > 0)
					{
						// Read the explicit subtype
						try
						{
#if NETSTANDARD1_3
							Type subType = SystemInfo.GetTypeFromString(this.GetType().GetTypeInfo().Assembly, subTypeString, true, true);
#else
							Type subType = SystemInfo.GetTypeFromString(subTypeString, true, true);
#endif

							LogLog.Debug(declaringType, "Parameter ["+name+"] specified subtype ["+subType.FullName+"]");

							if (!propertyType.IsAssignableFrom(subType))
							{
								// Check if there is an appropriate type converter
								if (OptionConverter.CanConvertTypeTo(subType, propertyType))
								{
									// Must re-convert to the real property type
									parsedObjectConversionTargetType = propertyType;

									// Use sub type as intermediary type
									propertyType = subType;
								}
								else
								{
									LogLog.Error(declaringType, "subtype ["+subType.FullName+"] set on ["+name+"] is not a subclass of property type ["+propertyType.FullName+"] and there are no acceptable type conversions.");
								}
							}
							else
							{
								// The subtype specified is found and is actually a subtype of the property
								// type, therefore we can switch to using this type.
								propertyType = subType;
							}
						}
						catch(Exception ex)
						{
							LogLog.Error(declaringType, "Failed to find type ["+subTypeString+"] set on ["+name+"]", ex);
						}
					}

					// Now try to convert the string value to an acceptable type
					// to pass to this property.

					object convertedValue = ConvertStringTo(propertyType, propertyValue);
					
					// Check if we need to do an additional conversion
					if (convertedValue != null && parsedObjectConversionTargetType != null)
					{
						LogLog.Debug(declaringType, "Performing additional conversion of value from [" + convertedValue.GetType().Name + "] to [" + parsedObjectConversionTargetType.Name + "]");
						convertedValue = OptionConverter.ConvertTypeTo(convertedValue, parsedObjectConversionTargetType);
					}

					if (convertedValue != null)
					{
						if (propInfo != null)
						{
							// Got a converted result
							LogLog.Debug(declaringType, "Setting Property [" + propInfo.Name + "] to " + convertedValue.GetType().Name + " value [" + convertedValue.ToString() + "]");

							try
							{
								// Pass to the property
#if NETSTANDARD1_3 // TODO BindingFlags is available for netstandard1.5
								propInfo.SetValue(target, convertedValue, null);
#else
								propInfo.SetValue(target, convertedValue, BindingFlags.SetProperty, null, null, CultureInfo.InvariantCulture);
#endif
							}
							catch(TargetInvocationException targetInvocationEx)
							{
								LogLog.Error(declaringType, "Failed to set parameter [" + propInfo.Name + "] on object [" + target + "] using value [" + convertedValue + "]", targetInvocationEx.InnerException);
							}
						}
						else if (methInfo != null)
						{
							// Got a converted result
							LogLog.Debug(declaringType, "Setting Collection Property [" + methInfo.Name + "] to " + convertedValue.GetType().Name + " value [" + convertedValue.ToString() + "]");

							try
							{
								// Pass to the property
#if NETSTANDARD1_3 // TODO BindingFlags is available for netstandard1.5
								methInfo.Invoke(target, new[] { convertedValue });
#else
								methInfo.Invoke(target, BindingFlags.InvokeMethod, null, new object[] {convertedValue}, CultureInfo.InvariantCulture);
#endif
							}
							catch(TargetInvocationException targetInvocationEx)
							{
								LogLog.Error(declaringType, "Failed to set parameter [" + name + "] on object [" + target + "] using value [" + convertedValue + "]", targetInvocationEx.InnerException);
							}
						}
					}
					else
					{
						LogLog.Warn(declaringType, "Unable to set property [" + name + "] on object [" + target + "] using value [" + propertyValue + "] (with acceptable conversion types)");
					}
				}
				else
				{
					object createdObject = null;

					if (propertyType == typeof(string) && !HasAttributesOrElements(element))
					{
						// If the property is a string and the element is empty (no attributes
						// or child elements) then we special case the object value to an empty string.
						// This is necessary because while the String is a class it does not have
						// a default constructor that creates an empty string, which is the behavior
						// we are trying to simulate and would be expected from CreateObjectFromXml
						createdObject = "";
					}
					else
					{
						// No value specified
						Type defaultObjectType = null;
						if (IsTypeConstructible(propertyType))
						{
							defaultObjectType = propertyType;
						}

						createdObject = CreateObjectFromXml(element, defaultObjectType, propertyType);
					}

					if (createdObject == null)
					{
						LogLog.Error(declaringType, "Failed to create object to set param: "+name);
					}
					else
					{
						if (propInfo != null)
						{
							// Got a converted result
							LogLog.Debug(declaringType, "Setting Property ["+ propInfo.Name +"] to object ["+ createdObject +"]");

							try
							{
								// Pass to the property
#if NETSTANDARD1_3 // TODO BindingFlags is available for netstandard1.5
								propInfo.SetValue(target, createdObject, null);
#else
								propInfo.SetValue(target, createdObject, BindingFlags.SetProperty, null, null, CultureInfo.InvariantCulture);
#endif
							}
							catch(TargetInvocationException targetInvocationEx)
							{
								LogLog.Error(declaringType, "Failed to set parameter [" + propInfo.Name + "] on object [" + target + "] using value [" + createdObject + "]", targetInvocationEx.InnerException);
							}
						}
						else if (methInfo != null)
						{
							// Got a converted result
							LogLog.Debug(declaringType, "Setting Collection Property ["+ methInfo.Name +"] to object ["+ createdObject +"]");

							try
							{
								// Pass to the property
#if NETSTANDARD1_3 // TODO BindingFlags is available for netstandard1.5
								methInfo.Invoke(target, new[] { createdObject });
#else
								methInfo.Invoke(target, BindingFlags.InvokeMethod, null, new object[] {createdObject}, CultureInfo.InvariantCulture);
#endif
							}
							catch(TargetInvocationException targetInvocationEx)
							{
								LogLog.Error(declaringType, "Failed to set parameter [" + methInfo.Name + "] on object [" + target + "] using value [" + createdObject + "]", targetInvocationEx.InnerException);
							}
						}
					}
				}
			}
		}