using System; using System.Linq; namespace OpenTap { /// /// Class for generic cloning of values. /// This is done in multiple ways. /// - First check IStringValueConvert /// - Then check if its ICloneable /// - Finally check if its XML cloneable. /// class ObjectCloner { readonly object value; readonly ITypeData typeOfValue; readonly bool valueType; // the fields below are set up as needed. bool? strConvertSuccess; TapSerializer serializer; string convertString; string xmlString; public ObjectCloner(object value) { this.value = value; typeOfValue = TypeData.GetTypeData(value); if (value == null || value is string || (typeOfValue.AsTypeData()?.IsValueType ?? false)) valueType = true; } public bool CanClone(object context, ITypeData targetType = null) { return TryClone(context, targetType ?? typeOfValue, false, out object _); } public bool TryClone(object context, ITypeData targetType, bool skipIfPossible, out object clone) { if (ReferenceEquals(targetType, typeOfValue) && valueType || value == null) { clone = value; return true; } if (skipIfPossible == false || typeOfValue.DescendsTo(targetType) == false) // let's just set the value on the first property. { try { if (strConvertSuccess == null) strConvertSuccess = StringConvertProvider.TryGetString(value, out convertString); if (strConvertSuccess == true) { clone = StringConvertProvider.FromString(convertString, targetType, context); return true; } if (value is ICloneable cloneable) { clone = cloneable.Clone(); return true; } if (serializer == null) serializer = new TapSerializer {IgnoreErrors = true}; // dont emit errors. if (xmlString == null) xmlString = serializer.SerializeToString(value); if (xmlString != null) { clone = serializer.DeserializeFromString(xmlString, targetType); if (serializer.Errors.Any()) return false; return true; } } catch { // catch any error. this means cloning is not possible. clone = null; return false; } }else if(skipIfPossible && typeOfValue.DescendsTo(targetType)) { clone = value; return true; } clone = value; return false; } public object Clone(bool skipIfPossible, object context, ITypeData targetType) { if (TryClone(context, targetType, skipIfPossible, out var clone) == false) { throw new InvalidCastException($"Failed cloning '{value}' as '{targetType.Name}'."); } return clone; } } }