PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/NHibernate/Collection/PersistentList.cs

https://github.com/asbjornu/nhibernate-core
C# | 622 lines | 511 code | 82 blank | 29 comment | 57 complexity | f4181cf7142aded3fccaa70b5b5d4ce1 MD5 | raw file
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using NHibernate.DebugHelpers;
  7. using NHibernate.Engine;
  8. using NHibernate.Loader;
  9. using NHibernate.Persister.Collection;
  10. using NHibernate.Type;
  11. using NHibernate.Util;
  12. namespace NHibernate.Collection
  13. {
  14. /// <summary>
  15. /// A persistent wrapper for an <see cref="IList"/>
  16. /// </summary>
  17. /// <remarks>
  18. /// The underlying collection used in an <see cref="ArrayList"/>.
  19. /// </remarks>
  20. [Serializable]
  21. [DebuggerTypeProxy(typeof (CollectionProxy))]
  22. public class PersistentList : AbstractPersistentCollection, IList
  23. {
  24. protected IList list;
  25. protected virtual object DefaultForType
  26. {
  27. get { return null; }
  28. }
  29. public PersistentList() {}
  30. /// <summary>
  31. /// Initializes an instance of the <see cref="PersistentList"/>
  32. /// in the <paramref name="session"/>.
  33. /// </summary>
  34. /// <param name="session">The <see cref="ISessionImplementor"/> the list is in.</param>
  35. public PersistentList(ISessionImplementor session) : base(session) {}
  36. /// <summary>
  37. /// Initializes an instance of the <see cref="PersistentList"/>
  38. /// that wraps an existing <see cref="IList"/> in the <paramref name="session"/>.
  39. /// </summary>
  40. /// <param name="session">The <see cref="ISessionImplementor"/> the list is in.</param>
  41. /// <param name="list">The <see cref="IList"/> to wrap.</param>
  42. public PersistentList(ISessionImplementor session, IList list) : base(session)
  43. {
  44. this.list = list;
  45. SetInitialized();
  46. IsDirectlyAccessible = true;
  47. }
  48. public override ICollection GetSnapshot(ICollectionPersister persister)
  49. {
  50. EntityMode entityMode = Session.EntityMode;
  51. List<object> clonedList = new List<object>(list.Count);
  52. foreach (object current in list)
  53. {
  54. object deepCopy = persister.ElementType.DeepCopy(current, entityMode, persister.Factory);
  55. clonedList.Add(deepCopy);
  56. }
  57. return clonedList;
  58. }
  59. public override ICollection GetOrphans(object snapshot, string entityName)
  60. {
  61. IList sn = (IList) snapshot;
  62. return GetOrphans(sn, list, entityName, Session);
  63. }
  64. public override bool EqualsSnapshot(ICollectionPersister persister)
  65. {
  66. IType elementType = persister.ElementType;
  67. IList sn = (IList) GetSnapshot();
  68. if (sn.Count != list.Count)
  69. {
  70. return false;
  71. }
  72. for (int i = 0; i < list.Count; i++)
  73. {
  74. if (elementType.IsDirty(list[i], sn[i], Session))
  75. {
  76. return false;
  77. }
  78. }
  79. return true;
  80. }
  81. public override bool IsSnapshotEmpty(object snapshot)
  82. {
  83. return ((ICollection) snapshot).Count == 0;
  84. }
  85. public override void BeforeInitialize(ICollectionPersister persister, int anticipatedSize)
  86. {
  87. list = (IList) persister.CollectionType.Instantiate(anticipatedSize);
  88. }
  89. public override bool IsWrapper(object collection)
  90. {
  91. return list == collection;
  92. }
  93. public override bool Empty
  94. {
  95. get { return (list.Count == 0); }
  96. }
  97. public override string ToString()
  98. {
  99. Read();
  100. return StringHelper.CollectionToString(list);
  101. }
  102. public override object ReadFrom(IDataReader rs, ICollectionPersister role, ICollectionAliases descriptor, object owner)
  103. {
  104. object element = role.ReadElement(rs, owner, descriptor.SuffixedElementAliases, Session);
  105. int index = (int) role.ReadIndex(rs, descriptor.SuffixedIndexAliases, Session);
  106. //pad with nulls from the current last element up to the new index
  107. for (int i = list.Count; i <= index; i++)
  108. {
  109. list.Insert(i, DefaultForType);
  110. }
  111. list[index] = element;
  112. return element;
  113. }
  114. public override IEnumerable Entries(ICollectionPersister persister)
  115. {
  116. return list;
  117. }
  118. /// <summary>
  119. /// Initializes this PersistentList from the cached values.
  120. /// </summary>
  121. /// <param name="persister">The CollectionPersister to use to reassemble the PersistentList.</param>
  122. /// <param name="disassembled">The disassembled PersistentList.</param>
  123. /// <param name="owner">The owner object.</param>
  124. public override void InitializeFromCache(ICollectionPersister persister, object disassembled, object owner)
  125. {
  126. object[] array = (object[]) disassembled;
  127. int size = array.Length;
  128. BeforeInitialize(persister, size);
  129. for (int i = 0; i < size; i++)
  130. {
  131. object element = persister.ElementType.Assemble(array[i], Session, owner);
  132. list.Add(element ?? DefaultForType);
  133. }
  134. }
  135. public override object Disassemble(ICollectionPersister persister)
  136. {
  137. int length = list.Count;
  138. object[] result = new object[length];
  139. for (int i = 0; i < length; i++)
  140. {
  141. result[i] = persister.ElementType.Disassemble(list[i], Session, null);
  142. }
  143. return result;
  144. }
  145. public override IEnumerable GetDeletes(ICollectionPersister persister, bool indexIsFormula)
  146. {
  147. IList deletes = new List<object>();
  148. IList sn = (IList) GetSnapshot();
  149. int end;
  150. if (sn.Count > list.Count)
  151. {
  152. for (int i = list.Count; i < sn.Count; i++)
  153. {
  154. deletes.Add(indexIsFormula ? sn[i] : i);
  155. }
  156. end = list.Count;
  157. }
  158. else
  159. {
  160. end = sn.Count;
  161. }
  162. for (int i = 0; i < end; i++)
  163. {
  164. if (list[i] == null && sn[i] != null)
  165. {
  166. deletes.Add(indexIsFormula ? sn[i] : i);
  167. }
  168. }
  169. return deletes;
  170. }
  171. public override bool NeedsInserting(object entry, int i, IType elemType)
  172. {
  173. IList sn = (IList) GetSnapshot();
  174. return list[i] != null && (i >= sn.Count || sn[i] == null);
  175. }
  176. public override bool NeedsUpdating(object entry, int i, IType elemType)
  177. {
  178. IList sn = (IList) GetSnapshot();
  179. return i < sn.Count && sn[i] != null && list[i] != null && elemType.IsDirty(list[i], sn[i], Session);
  180. }
  181. public override object GetIndex(object entry, int i, ICollectionPersister persister)
  182. {
  183. return i;
  184. }
  185. public override object GetElement(object entry)
  186. {
  187. return entry;
  188. }
  189. public override object GetSnapshotElement(object entry, int i)
  190. {
  191. IList sn = (IList) GetSnapshot();
  192. return sn[i];
  193. }
  194. public override bool EntryExists(object entry, int i)
  195. {
  196. return entry != null;
  197. }
  198. public override bool Equals(object obj)
  199. {
  200. ICollection that = obj as ICollection;
  201. if (that == null)
  202. {
  203. return false;
  204. }
  205. Read();
  206. return CollectionHelper.CollectionEquals(list, that);
  207. }
  208. public override int GetHashCode()
  209. {
  210. Read();
  211. return list.GetHashCode();
  212. }
  213. #region IList Members
  214. public int Add(object value)
  215. {
  216. if (!IsOperationQueueEnabled)
  217. {
  218. Write();
  219. return list.Add(value);
  220. }
  221. else
  222. {
  223. QueueOperation(new SimpleAddDelayedOperation(this, value));
  224. //TODO: take a look at this - I don't like it because it changes the
  225. // meaning of Add - instead of returning the index it was added at
  226. // returns a "fake" index - not consistent with IList interface...
  227. return -1;
  228. }
  229. }
  230. public bool Contains(object value)
  231. {
  232. bool? exists = ReadElementExistence(value);
  233. return !exists.HasValue ? list.Contains(value) : exists.Value;
  234. }
  235. public void Clear()
  236. {
  237. if (ClearQueueEnabled)
  238. {
  239. QueueOperation(new ClearDelayedOperation(this));
  240. }
  241. else
  242. {
  243. Initialize(true);
  244. if (!(list.Count == 0))
  245. {
  246. list.Clear();
  247. Dirty();
  248. }
  249. }
  250. }
  251. public int IndexOf(object value)
  252. {
  253. Read();
  254. return list.IndexOf(value);
  255. }
  256. public void Insert(int index, object value)
  257. {
  258. if (index < 0)
  259. {
  260. throw new IndexOutOfRangeException("negative index");
  261. }
  262. if (!IsOperationQueueEnabled)
  263. {
  264. Write();
  265. list.Insert(index, value);
  266. }
  267. else
  268. {
  269. QueueOperation(new AddDelayedOperation(this, index, value));
  270. }
  271. }
  272. public void Remove(object value)
  273. {
  274. bool? exists = PutQueueEnabled ? ReadElementExistence(value) : null;
  275. if (!exists.HasValue)
  276. {
  277. Initialize(true);
  278. // NH: Different implementation: we use the count to know if the value was removed (better performance)
  279. int contained = list.Count;
  280. list.Remove(value);
  281. if (contained != list.Count)
  282. {
  283. Dirty();
  284. }
  285. }
  286. else if (exists.Value)
  287. {
  288. QueueOperation(new SimpleRemoveDelayedOperation(this, value));
  289. }
  290. }
  291. public void RemoveAt(int index)
  292. {
  293. if (index < 0)
  294. {
  295. throw new IndexOutOfRangeException("negative index");
  296. }
  297. object old = PutQueueEnabled ? ReadElementByIndex(index) : Unknown;
  298. if (old == Unknown)
  299. {
  300. Write();
  301. list.RemoveAt(index);
  302. }
  303. else
  304. {
  305. QueueOperation(new RemoveDelayedOperation(this, index, old == NotFound ? null : old));
  306. }
  307. }
  308. public object this[int index]
  309. {
  310. get
  311. {
  312. if (index < 0)
  313. {
  314. throw new IndexOutOfRangeException("negative index");
  315. }
  316. object result = ReadElementByIndex(index);
  317. if (result == Unknown)
  318. {
  319. return list[index];
  320. }
  321. if(NotFound == result)
  322. {
  323. // check if the index is valid
  324. if(index >= Count)
  325. {
  326. throw new ArgumentOutOfRangeException("index");
  327. }
  328. return null;
  329. }
  330. return result;
  331. }
  332. set
  333. {
  334. if (index < 0)
  335. {
  336. throw new IndexOutOfRangeException("negative index");
  337. }
  338. object old;
  339. if (PutQueueEnabled)
  340. {
  341. old = ReadElementByIndex(index);
  342. if(old == NotFound)
  343. {
  344. old = null;
  345. }
  346. }
  347. else
  348. {
  349. old = Unknown;
  350. }
  351. if (old == Unknown)
  352. {
  353. Write();
  354. list[index] = value;
  355. }
  356. else
  357. {
  358. QueueOperation(new SetDelayedOperation(this, index, value, old));
  359. }
  360. }
  361. }
  362. public bool IsReadOnly
  363. {
  364. get { return false; }
  365. }
  366. public bool IsFixedSize
  367. {
  368. get { return false; }
  369. }
  370. #endregion
  371. #region ICollection Members
  372. public void CopyTo(Array array, int index)
  373. {
  374. for (int i = index; i < Count; i++)
  375. {
  376. array.SetValue(this[i], i);
  377. }
  378. }
  379. public int Count
  380. {
  381. get { return ReadSize() ? CachedSize : list.Count; }
  382. }
  383. public object SyncRoot
  384. {
  385. get { return this; }
  386. }
  387. public bool IsSynchronized
  388. {
  389. get { return false; }
  390. }
  391. #endregion
  392. #region IEnumerable Members
  393. public IEnumerator GetEnumerator()
  394. {
  395. Read();
  396. return list.GetEnumerator();
  397. }
  398. #endregion
  399. #region DelayedOperations
  400. protected sealed class ClearDelayedOperation : IDelayedOperation
  401. {
  402. private readonly PersistentList enclosingInstance;
  403. public ClearDelayedOperation(PersistentList enclosingInstance)
  404. {
  405. this.enclosingInstance = enclosingInstance;
  406. }
  407. public object AddedInstance
  408. {
  409. get { return null; }
  410. }
  411. public object Orphan
  412. {
  413. get { throw new NotSupportedException("queued clear cannot be used with orphan delete"); }
  414. }
  415. public void Operate()
  416. {
  417. enclosingInstance.list.Clear();
  418. }
  419. }
  420. protected sealed class SimpleAddDelayedOperation : IDelayedOperation
  421. {
  422. private readonly PersistentList enclosingInstance;
  423. private readonly object value;
  424. public SimpleAddDelayedOperation(PersistentList enclosingInstance, object value)
  425. {
  426. this.enclosingInstance = enclosingInstance;
  427. this.value = value;
  428. }
  429. public object AddedInstance
  430. {
  431. get { return value; }
  432. }
  433. public object Orphan
  434. {
  435. get { return null; }
  436. }
  437. public void Operate()
  438. {
  439. enclosingInstance.list.Add(value);
  440. }
  441. }
  442. protected sealed class AddDelayedOperation : IDelayedOperation
  443. {
  444. private readonly PersistentList enclosingInstance;
  445. private readonly int index;
  446. private readonly object value;
  447. public AddDelayedOperation(PersistentList enclosingInstance, int index, object value)
  448. {
  449. this.enclosingInstance = enclosingInstance;
  450. this.index = index;
  451. this.value = value;
  452. }
  453. public object AddedInstance
  454. {
  455. get { return value; }
  456. }
  457. public object Orphan
  458. {
  459. get { return null; }
  460. }
  461. public void Operate()
  462. {
  463. enclosingInstance.list.Insert(index, value);
  464. }
  465. }
  466. protected sealed class SetDelayedOperation : IDelayedOperation
  467. {
  468. private readonly PersistentList enclosingInstance;
  469. private readonly int index;
  470. private readonly object value;
  471. private readonly object old;
  472. public SetDelayedOperation(PersistentList enclosingInstance, int index, object value, object old)
  473. {
  474. this.enclosingInstance = enclosingInstance;
  475. this.index = index;
  476. this.value = value;
  477. this.old = old;
  478. }
  479. public object AddedInstance
  480. {
  481. get { return value; }
  482. }
  483. public object Orphan
  484. {
  485. get { return old; }
  486. }
  487. public void Operate()
  488. {
  489. enclosingInstance.list[index] = value;
  490. }
  491. }
  492. protected sealed class RemoveDelayedOperation : IDelayedOperation
  493. {
  494. private readonly PersistentList enclosingInstance;
  495. private readonly int index;
  496. private readonly object old;
  497. public RemoveDelayedOperation(PersistentList enclosingInstance, int index, object old)
  498. {
  499. this.enclosingInstance = enclosingInstance;
  500. this.index = index;
  501. this.old = old;
  502. }
  503. public object AddedInstance
  504. {
  505. get { return null; }
  506. }
  507. public object Orphan
  508. {
  509. get { return old; }
  510. }
  511. public void Operate()
  512. {
  513. enclosingInstance.list.RemoveAt(index);
  514. }
  515. }
  516. protected sealed class SimpleRemoveDelayedOperation : IDelayedOperation
  517. {
  518. private readonly PersistentList enclosingInstance;
  519. private readonly object value;
  520. public SimpleRemoveDelayedOperation(PersistentList enclosingInstance, object value)
  521. {
  522. this.enclosingInstance = enclosingInstance;
  523. this.value = value;
  524. }
  525. public object AddedInstance
  526. {
  527. get { return null; }
  528. }
  529. public object Orphan
  530. {
  531. get { return value; }
  532. }
  533. public void Operate()
  534. {
  535. enclosingInstance.list.Remove(value);
  536. }
  537. }
  538. #endregion
  539. }
  540. }