ICloneable.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. //#define ASSERT_CLONE_TYPES
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. namespace Animancer
  6. {
  7. /// <summary>An object that can be cloned.</summary>
  8. /// <remarks>See <see cref="Clone(CloneContext)"/> for example usage.</remarks>
  9. /// https://kybernetik.com.au/animancer/api/Animancer/ICloneable_1
  10. public interface ICloneable<out T>
  11. {
  12. /************************************************************************************************************************/
  13. /// <summary>Creates a new object with the same type and values this.</summary>
  14. ///
  15. /// <remarks>
  16. /// Calling this method directly is not generally recommended.
  17. /// Use <see cref="CloneableExtensions.Clone{T}(ICloneable{T})"/> if you don't have a `context`
  18. /// or <see cref="CloneContext.GetOrCreateClone{T}(ICloneable{T})"/> if you do have one.
  19. /// <para></para>
  20. /// <strong>Example:</strong><code>
  21. /// class BaseClass : ICloneable
  22. /// {
  23. /// // Explicit implementation so that the recommended methods will be used instead.
  24. /// object ICloneable.Clone(CloneContext context)
  25. /// {
  26. /// var clone = (BaseClass)MemberwiseClone();
  27. /// clone.CopyFrom(this, context);
  28. /// return clone;
  29. /// }
  30. ///
  31. /// // Protected method which should only be called by Clone.
  32. /// protected virtual void InitializeClone(CloneContext context)
  33. /// {
  34. /// // Alter any necessary BaseClass fields according to the context.
  35. /// }
  36. /// }
  37. ///
  38. /// class DerivedClass : BaseClass
  39. /// {
  40. /// protected override void InitializeClone(CloneContext context)
  41. /// {
  42. /// base.CopyFrom(copyFrom, context);
  43. ///
  44. /// var derived = (DerivedClass)copyFrom;
  45. /// // Alter any necessary DerivedClass fields according to the context.
  46. /// }
  47. /// }
  48. /// </code></remarks>
  49. T Clone(CloneContext context);
  50. /************************************************************************************************************************/
  51. }
  52. /// <summary>Extension methods for <see cref="ICloneable{T}"/>.</summary>
  53. /// https://kybernetik.com.au/animancer/api/Animancer/CloneableExtensions
  54. public static partial class CloneableExtensions
  55. {
  56. /************************************************************************************************************************/
  57. /// <summary>
  58. /// Calls <see cref="ICloneable{T}.Clone"/> using a <see cref="CloneContext"/> from the
  59. /// <see cref="CloneContext.Pool"/> and casts the result.
  60. /// </summary>
  61. /// <remarks>
  62. /// Returns <c>null</c> if the `original` is <c>null</c>.
  63. /// <para></para>
  64. /// Use <see cref="CloneContext.GetOrCreateClone{T}(ICloneable{T})"/>
  65. /// if you already have a <see cref="CloneContext"/>.
  66. /// </remarks>
  67. public static T Clone<T>(this ICloneable<T> original)
  68. {
  69. if (original == null)
  70. return default;
  71. var context = CloneContext.Pool.Instance.Acquire();
  72. var clone = original.Clone(context);
  73. CloneContext.Pool.Instance.Release(context);
  74. return clone;
  75. }
  76. /************************************************************************************************************************/
  77. /// <summary>[Assert-Conditional] Asserts that the `clone` has the same type as the `original`.</summary>
  78. [System.Diagnostics.Conditional(Strings.Assertions)]
  79. internal static void AssertCloneType<T>(this ICloneable<T> original, object clone)
  80. {
  81. #if UNITY_ASSERTIONS
  82. var cloneType = clone.GetType();
  83. var originalType = original.GetType();
  84. if (cloneType != originalType)
  85. Debug.LogError($"Cloned object type ({cloneType.FullName}" +
  86. $" doesn't match original {originalType.FullName}." +
  87. $"\n• Original: {original}" +
  88. $"\n• Clone: {clone}");
  89. #endif
  90. }
  91. /************************************************************************************************************************/
  92. }
  93. /// <summary>A dictionary which maps objects to their copies.</summary>
  94. /// <remarks>
  95. /// This class is used to clone complex object trees with potentially interconnected references so that
  96. /// references to original objects can be replaced with references to their equivalent clones.
  97. /// </remarks>
  98. public class CloneContext : Dictionary<object, object>
  99. {
  100. /************************************************************************************************************************/
  101. /// <summary>Will the <see cref="IUpdatable"/>s be cloned as part of the current operation?</summary>
  102. /// <remarks>
  103. /// This is used to prevent <see cref="AnimancerNode"/>s from cloning their <see cref="FadeGroup"/>s
  104. /// individually while cloning a whole <see cref="AnimancerGraph"/> because it will clone the whole groups
  105. /// after cloning all the nodes.
  106. /// </remarks>
  107. public bool WillCloneUpdatables { get; set; }
  108. /************************************************************************************************************************/
  109. #region Pooling
  110. /************************************************************************************************************************/
  111. /// <summary>An <see cref="ObjectPool{T}"/> for <see cref="CloneContext"/>.</summary>
  112. /// https://kybernetik.com.au/animancer/api/Animancer/Pool
  113. public class Pool : ObjectPool<CloneContext>
  114. {
  115. /************************************************************************************************************************/
  116. /// <summary>Singleton.</summary>
  117. public static Pool Instance = new();
  118. /************************************************************************************************************************/
  119. /// <inheritdoc/>
  120. protected override CloneContext New()
  121. => new();
  122. /************************************************************************************************************************/
  123. /// <inheritdoc/>
  124. public override CloneContext Acquire()
  125. {
  126. var context = base.Acquire();
  127. CollectionPool<KeyValuePair<object, object>, CloneContext>.AssertEmpty(context);
  128. return context;
  129. }
  130. /************************************************************************************************************************/
  131. /// <inheritdoc/>
  132. public override void Release(CloneContext context)
  133. {
  134. context.Clear();
  135. base.Release(context);
  136. }
  137. /************************************************************************************************************************/
  138. }
  139. /************************************************************************************************************************/
  140. #endregion
  141. /************************************************************************************************************************/
  142. /// <summary>
  143. /// Returns the value registered using `original` as its key if there is one.
  144. /// Otherwise, calls <see cref="CloneableExtensions.Clone"/>, adds the clone to this dictionary, and returns it.
  145. /// </summary>
  146. public T GetOrCreateClone<T>(ICloneable<T> original)
  147. {
  148. if (original == null)
  149. return default;
  150. if (TryGetValue(original, out var value))
  151. return (T)value;
  152. return Clone(original);
  153. }
  154. /************************************************************************************************************************/
  155. /// <summary>
  156. /// Returns the value registered using `original` as its key if there is one.
  157. /// Otherwise, if the `original` is <see cref="ICloneable{T}"/> it calls <see cref="CloneableExtensions.Clone"/>,
  158. /// adds the clone to this dictionary, and returns it.
  159. /// Otherwise, just returns the `original`.
  160. /// </summary>
  161. /// <remarks>
  162. /// If <typeparamref name="T"/> is <see cref="ICloneable{T}"/>,
  163. /// use <see cref="GetOrCreateClone{T}(ICloneable{T})"/> instead.
  164. /// </remarks>
  165. public T GetOrCreateCloneOrOriginal<T>(T original)
  166. {
  167. TryGetOrCreateCloneOrOriginal(original, out original);
  168. return original;
  169. }
  170. /// <summary>
  171. /// Returns <c>true</c> if there is a `clone` registered for the `original`.
  172. /// Otherwise, if the `original` is <see cref="ICloneable{T}"/> it calls <see cref="CloneableExtensions.Clone"/>,
  173. /// adds the `clone` to this dictionary, and returns <c>true</c>.
  174. /// Otherwise, outputs the `original` as the `clone` and returns <c>false</c>.
  175. /// </summary>
  176. /// <remarks>Outputs <c>null</c> and returns <c>true</c> if the `original` is <c>null</c>.</remarks>
  177. public bool TryGetOrCreateCloneOrOriginal<T>(T original, out T clone)
  178. {
  179. if (original == null)
  180. {
  181. clone = default;
  182. return true;
  183. }
  184. if (TryGetValue(original, out var value))
  185. {
  186. clone = (T)value;
  187. return true;
  188. }
  189. if (original is ICloneable<T> cloneable)
  190. {
  191. clone = Clone(cloneable);
  192. return true;
  193. }
  194. clone = original;
  195. return false;
  196. }
  197. /************************************************************************************************************************/
  198. /// <summary>Calls <see cref="ICloneable{T}.Clone"/> and registers the clone.</summary>
  199. /// <exception cref="System.ArgumentException">A clone is already registered for the `original`.</exception>
  200. public T Clone<T>(ICloneable<T> original)
  201. {
  202. var clone = original.Clone(this);
  203. if (clone != null)
  204. {
  205. original.AssertCloneType(clone);
  206. Add(original, clone);
  207. }
  208. return clone;
  209. }
  210. /************************************************************************************************************************/
  211. /// <summary>Returns the clone of the `original` if one was registered. Otherwise, throws.</summary>
  212. /// <exception cref="KeyNotFoundException">No clone of the `original` is registered.</exception>
  213. public T GetClone<T>(T original)
  214. => (T)this[original];
  215. /************************************************************************************************************************/
  216. /// <summary>Returns the clone of the `original` if one is registered. Otherwise, returns the `original`.</summary>
  217. public T GetCloneOrOriginal<T>(T original)
  218. => original != null && TryGetValue(original, out var value)
  219. ? (T)value
  220. : original;
  221. /************************************************************************************************************************/
  222. /// <summary>Replaces the `item` with its clone and returns true if one is registered.</summary>
  223. public bool TryGetClone<T>(ref T item)
  224. {
  225. if (item == null)
  226. return false;
  227. if (!TryGetValue(item, out var value) ||
  228. value is not T valueT)
  229. {
  230. item = default;
  231. return false;
  232. }
  233. item = valueT;
  234. return true;
  235. }
  236. /// <summary>Calls <see cref="Dictionary{TKey, TValue}.TryGetValue(TKey, out TValue)"/> and casts the result.</summary>
  237. public bool TryGetClone<T>(T original, out T clone)
  238. {
  239. clone = original;
  240. return TryGetClone(ref clone);
  241. }
  242. /************************************************************************************************************************/
  243. }
  244. }