chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
using System;
using System.Linq;
 
namespace OpenTap
{
    /// <summary>
    /// 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.
    /// </summary>
    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;
        }
    }
}