PageRenderTime 74ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/Raven.Abstractions/Json/Linq/DictionaryWithParentSnapshot.cs

http://github.com/ravendb/ravendb
C# | 341 lines | 296 code | 44 blank | 1 comment | 88 complexity | 38046e1dc8df39c2b0a5db9efd2301ba MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Runtime.CompilerServices;
  6. using Raven.Imports.Newtonsoft.Json.Linq;
  7. using System.Linq;
  8. namespace Raven.Json.Linq
  9. {
  10. internal class DictionaryWithParentSnapshot : IDictionary<string, RavenJToken>
  11. {
  12. private readonly IEqualityComparer<string> comparer;
  13. private static readonly RavenJToken DeletedMarker = new RavenJValue("*DeletedMarker*", JTokenType.Null);
  14. private readonly DictionaryWithParentSnapshot parentSnapshot;
  15. private IDictionary<string, RavenJToken> localChanges;
  16. private string snapshotMsg;
  17. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  18. protected void EnsureCanWriteToLocalChanges()
  19. {
  20. if (localChanges == null)
  21. {
  22. localChanges = new Dictionary<string, RavenJToken>(comparer);
  23. }
  24. }
  25. public DictionaryWithParentSnapshot(IEqualityComparer<string> comparer)
  26. {
  27. this.comparer = comparer;
  28. }
  29. private DictionaryWithParentSnapshot(DictionaryWithParentSnapshot previous)
  30. {
  31. comparer = previous.comparer;
  32. if (previous.parentSnapshot != null)
  33. {
  34. if(previous.localChanges != null)
  35. localChanges = new Dictionary<string, RavenJToken>(previous.localChanges, comparer);
  36. parentSnapshot = previous.parentSnapshot;
  37. }
  38. else
  39. {
  40. parentSnapshot = previous;
  41. }
  42. }
  43. #region Dictionary<string,TValue> Members
  44. public void Add(string key, RavenJToken value)
  45. {
  46. Debug.Assert(!String.IsNullOrWhiteSpace(key), "key must _never_ be null/empty/whitespace");
  47. if (IsSnapshot)
  48. throw new InvalidOperationException(snapshotMsg ?? "Cannot modify a snapshot, this is probably a bug");
  49. if (ContainsKey(key))
  50. throw new ArgumentException(string.Format("An item with the same key has already been added: '{0}'", key));
  51. EnsureCanWriteToLocalChanges();
  52. localChanges[key] = value; // we can't use Add, because LocalChanges may contain a DeletedMarker
  53. localCount = -1;
  54. }
  55. public bool ContainsKey(string key)
  56. {
  57. RavenJToken token;
  58. if (localChanges != null && localChanges.TryGetValue(key, out token))
  59. {
  60. if (token == DeletedMarker)
  61. return false;
  62. return true;
  63. }
  64. return (parentSnapshot != null && parentSnapshot.TryGetValue(key, out token) && token != DeletedMarker);
  65. }
  66. public ICollection<string> Keys
  67. {
  68. get
  69. {
  70. if (localChanges == null)
  71. {
  72. if (parentSnapshot != null)
  73. {
  74. return parentSnapshot.Keys;
  75. }
  76. return new HashSet<string>();
  77. }
  78. ICollection<string> ret = new HashSet<string>();
  79. if (parentSnapshot != null)
  80. {
  81. foreach (var key in parentSnapshot.Keys)
  82. {
  83. if (localChanges != null && localChanges.ContainsKey(key))
  84. continue;
  85. ret.Add(key);
  86. }
  87. }
  88. if (localChanges != null)
  89. {
  90. foreach (var key in localChanges.Keys)
  91. {
  92. RavenJToken value;
  93. if (localChanges.TryGetValue(key, out value) == false ||
  94. value == DeletedMarker)
  95. continue;
  96. ret.Add(key);
  97. }
  98. }
  99. return ret;
  100. }
  101. }
  102. public bool Remove(string key)
  103. {
  104. if (IsSnapshot)
  105. throw new InvalidOperationException("Cannot modify a snapshot, this is probably a bug");
  106. RavenJToken parentToken = null;
  107. bool parentHasIt = parentSnapshot != null &&
  108. parentSnapshot.TryGetValue(key, out parentToken);
  109. EnsureCanWriteToLocalChanges();
  110. RavenJToken token = null;
  111. if (localChanges.TryGetValue(key, out token) == false)
  112. {
  113. if (parentHasIt && parentToken != DeletedMarker)
  114. {
  115. localChanges[key] = DeletedMarker;
  116. localCount = -1;
  117. return true;
  118. }
  119. return false;
  120. }
  121. if (token == DeletedMarker)
  122. return false;
  123. if (parentHasIt)
  124. {
  125. localChanges[key] = DeletedMarker;
  126. }
  127. else
  128. {
  129. localChanges.Remove(key);
  130. }
  131. localCount = -1;
  132. return true;
  133. }
  134. public bool TryGetValue(string key, out RavenJToken value)
  135. {
  136. value = null;
  137. RavenJToken unsafeVal;
  138. if (localChanges != null && localChanges.TryGetValue(key, out unsafeVal))
  139. {
  140. if (unsafeVal == DeletedMarker)
  141. return false;
  142. value = unsafeVal;
  143. return true;
  144. }
  145. if (parentSnapshot == null ||
  146. !parentSnapshot.TryGetValue(key, out unsafeVal) ||
  147. unsafeVal == DeletedMarker)
  148. return false;
  149. if (IsSnapshot == false && unsafeVal != null)
  150. {
  151. if (unsafeVal.IsSnapshot == false && unsafeVal.Type != JTokenType.Object)
  152. unsafeVal.EnsureCannotBeChangeAndEnableSnapshotting();
  153. }
  154. value = unsafeVal;
  155. return true;
  156. }
  157. public ICollection<RavenJToken> Values
  158. {
  159. get
  160. {
  161. ICollection<RavenJToken> ret = new HashSet<RavenJToken>();
  162. foreach (var key in Keys)
  163. {
  164. ret.Add(this[key]);
  165. }
  166. return ret;
  167. }
  168. }
  169. public RavenJToken this[string key]
  170. {
  171. get
  172. {
  173. RavenJToken token;
  174. if (TryGetValue(key, out token))
  175. return token;
  176. throw new KeyNotFoundException(key);
  177. }
  178. set
  179. {
  180. if (IsSnapshot)
  181. throw new InvalidOperationException("Cannot modify a snapshot, this is probably a bug");
  182. EnsureCanWriteToLocalChanges();
  183. localChanges[key] = value;
  184. localCount = -1;
  185. }
  186. }
  187. #endregion
  188. public IEnumerator<KeyValuePair<string, RavenJToken>> GetEnumerator()
  189. {
  190. if (parentSnapshot != null)
  191. {
  192. foreach (var item in parentSnapshot)
  193. {
  194. if (item.Key == null)
  195. continue;
  196. if (localChanges != null && localChanges.ContainsKey(item.Key))
  197. continue;
  198. yield return item;
  199. }
  200. }
  201. if (localChanges != null)
  202. {
  203. foreach (var localChange in localChanges)
  204. {
  205. if (localChange.Value == DeletedMarker)
  206. continue;
  207. yield return localChange;
  208. }
  209. }
  210. }
  211. IEnumerator IEnumerable.GetEnumerator()
  212. {
  213. return GetEnumerator();
  214. }
  215. public void Add(KeyValuePair<string, RavenJToken> item)
  216. {
  217. Add(item.Key, item.Value);
  218. }
  219. public void Clear()
  220. {
  221. // we either already have count set to -1, or it will be invalidated by a call to Remove below
  222. foreach (var key in Keys.ToArray()) // clone the values for the iteration
  223. {
  224. Remove(key);
  225. }
  226. }
  227. public bool Contains(KeyValuePair<string, RavenJToken> item)
  228. {
  229. throw new NotImplementedException();
  230. }
  231. public void CopyTo(KeyValuePair<string, RavenJToken>[] array, int arrayIndex)
  232. {
  233. if (parentSnapshot != null)
  234. {
  235. parentSnapshot.CopyTo(array, arrayIndex);
  236. arrayIndex += parentSnapshot.Count;
  237. }
  238. if (localChanges != null)
  239. localChanges.CopyTo(array, arrayIndex);
  240. }
  241. public bool Remove(KeyValuePair<string, RavenJToken> item)
  242. {
  243. throw new NotImplementedException();
  244. }
  245. private int localCount = -1;
  246. public int Count
  247. {
  248. get
  249. {
  250. if (localCount != -1)
  251. return localCount;
  252. localCount = 0;
  253. if (localChanges != null)
  254. {
  255. foreach (var localChange in localChanges)
  256. {
  257. if (ReferenceEquals(localChange.Value, DeletedMarker) == false)
  258. {
  259. localCount++;
  260. }
  261. }
  262. }
  263. if (parentSnapshot != null)
  264. {
  265. if (localChanges != null)
  266. {
  267. foreach (var kvp in parentSnapshot)
  268. {
  269. if (localChanges.ContainsKey(kvp.Key) == false)
  270. localCount++;
  271. }
  272. }
  273. else
  274. localCount = parentSnapshot.Count;
  275. }
  276. return localCount;
  277. }
  278. }
  279. public bool IsReadOnly
  280. {
  281. get { return false; }
  282. }
  283. public bool CaseInsensitivePropertyNames { get; set; }
  284. public bool IsSnapshot { get; private set; }
  285. public DictionaryWithParentSnapshot CreateSnapshot()
  286. {
  287. if (IsSnapshot == false)
  288. throw new InvalidOperationException("Cannot create snapshot without previously calling EnsureSnapShot");
  289. return new DictionaryWithParentSnapshot(this);
  290. }
  291. public void EnsureSnapshot(string msg = null)
  292. {
  293. snapshotMsg = msg;
  294. IsSnapshot = true;
  295. }
  296. }
  297. }