PageRenderTime 28ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/DDay.iCal/General/CalendarPropertyCompositeList.cs

https://bitbucket.org/mdavid/dday.ical
C# | 577 lines | 462 code | 82 blank | 33 comment | 73 complexity | 583e5b3d44e1a101076f4d34db12f01a MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Collections;
  5. namespace DDay.iCal
  6. {
  7. /// <summary>
  8. /// This class takes multiple calendar properties/property values
  9. /// and consolidates them into a single list.
  10. ///
  11. /// <example>
  12. /// Consider the following example:
  13. ///
  14. /// BEGIN:VEVENT
  15. /// CATEGORIES:APPOINTMENT,EDUCATION
  16. /// CATEGORIES:MEETING
  17. /// END:EVENT
  18. /// </example>
  19. ///
  20. /// When we process this event, we don't really care that there
  21. /// are 2 different CATEGORIES properties, no do we care that
  22. /// the first CATEGORIES property has 2 values, whereas the
  23. /// second CATEGORIES property only has 1 value. In the end,
  24. /// we want a list of 3 values: APPOINTMENT, EDUCATION, and MEETING.
  25. ///
  26. /// This class consolidates properties of a given name into a list,
  27. /// and allows you to work with those values directly against the
  28. /// properties themselves. This preserves the notion that our values
  29. /// are still stored directly within the calendar property, but gives
  30. /// us the flexibility to work with multiple properties through a
  31. /// single (composite) list.
  32. /// </summary>
  33. public class CalendarPropertyCompositeList<T> :
  34. ICalendarPropertyCompositeList<T>
  35. {
  36. #region Private Fields
  37. ICalendarPropertyList m_PropertyList;
  38. string m_PropertyName;
  39. #endregion
  40. #region Constructors
  41. public CalendarPropertyCompositeList(ICalendarPropertyList propertyList, string propertyName)
  42. {
  43. m_PropertyList = propertyList;
  44. m_PropertyName = propertyName;
  45. }
  46. #endregion
  47. #region Protected Methods
  48. protected int PropertyValueCount(object value, out bool isList)
  49. {
  50. isList = false;
  51. if (value is IList<object>)
  52. {
  53. isList = true;
  54. int count = 0;
  55. foreach (object obj in (IList<object>)value)
  56. {
  57. if (obj is T)
  58. count++;
  59. }
  60. return count;
  61. }
  62. else if (value != null)
  63. {
  64. return 1;
  65. }
  66. return 0;
  67. }
  68. protected ICalendarProperty PropertyForIndex(int index, out bool isList, out int propertyIndex, out int indexInProperty)
  69. {
  70. int count = 0;
  71. propertyIndex = 0;
  72. isList = false;
  73. // FIXME: this method is messed up I think.
  74. // Search through the properties for the one
  75. // that contains the index in question.
  76. int propertyValueCount = 0;
  77. while (propertyIndex < m_PropertyList.Count)
  78. {
  79. if (string.Equals(m_PropertyList[propertyIndex].Name, m_PropertyName))
  80. {
  81. propertyValueCount = PropertyValueCount(m_PropertyList[propertyIndex].Value, out isList);
  82. if (propertyValueCount > 0) // The value contains items
  83. {
  84. if (count + propertyValueCount > index) // The desired index is in this property
  85. break;
  86. count += propertyValueCount;
  87. }
  88. }
  89. propertyIndex++;
  90. }
  91. if (propertyIndex < m_PropertyList.Count &&
  92. count <= index &&
  93. propertyValueCount > index - count)
  94. {
  95. indexInProperty = index - count;
  96. return m_PropertyList[propertyIndex];
  97. }
  98. propertyIndex = -1;
  99. indexInProperty = -1;
  100. return null;
  101. }
  102. protected ICalendarProperty PropertyForItem(T item, out bool isList, out int itemIndex, out int indexInProperty)
  103. {
  104. itemIndex = 0;
  105. foreach (ICalendarProperty p in m_PropertyList)
  106. {
  107. if (p.Name.Equals(m_PropertyName))
  108. {
  109. if (p.Value is IList<object>)
  110. {
  111. IList<object> list = (IList<object>)p.Value;
  112. indexInProperty = list.IndexOf(item);
  113. if (indexInProperty >= 0)
  114. {
  115. isList = true;
  116. itemIndex += indexInProperty;
  117. return p;
  118. }
  119. else itemIndex += list.Count;
  120. }
  121. else
  122. {
  123. if (object.Equals(item, p.Value))
  124. {
  125. isList = false;
  126. indexInProperty = -1;
  127. return p;
  128. }
  129. else
  130. {
  131. itemIndex++;
  132. }
  133. }
  134. }
  135. }
  136. isList = false;
  137. indexInProperty = -1;
  138. itemIndex = -1;
  139. return null;
  140. }
  141. #endregion
  142. #region IList<T> Members
  143. public int IndexOf(T item)
  144. {
  145. bool isList;
  146. int itemIndex, indexInProperty;
  147. ICalendarProperty p = PropertyForItem(item, out isList, out itemIndex, out indexInProperty);
  148. return itemIndex;
  149. }
  150. public void Insert(int index, T item)
  151. {
  152. if (IsReadOnly)
  153. throw new NotSupportedException();
  154. if (index < 0 || index >= Count)
  155. throw new ArgumentOutOfRangeException("index");
  156. bool isList;
  157. int propertyIndex, indexInProperty;
  158. ICalendarProperty p = PropertyForIndex(index, out isList, out propertyIndex, out indexInProperty);
  159. if (p != null)
  160. {
  161. if (isList)
  162. ((IList<object>)p.Value).Insert(indexInProperty, item);
  163. else
  164. {
  165. ICalendarProperty newProperty = p.Copy<ICalendarProperty>();
  166. newProperty.Value = item;
  167. m_PropertyList.Insert(propertyIndex, newProperty);
  168. }
  169. }
  170. }
  171. public void RemoveAt(int index)
  172. {
  173. if (IsReadOnly)
  174. throw new NotSupportedException();
  175. if (index < 0 || index >= Count)
  176. throw new ArgumentOutOfRangeException("index");
  177. bool isList;
  178. int propertyIndex, indexInProperty;
  179. ICalendarProperty p = PropertyForIndex(index, out isList, out propertyIndex, out indexInProperty);
  180. if (p != null)
  181. {
  182. if (isList)
  183. ((IList<object>)p.Value).RemoveAt(indexInProperty);
  184. else
  185. m_PropertyList.RemoveAt(propertyIndex);
  186. }
  187. }
  188. public T this[int index]
  189. {
  190. get
  191. {
  192. bool isList;
  193. int propertyIndex, indexInProperty;
  194. ICalendarProperty p = PropertyForIndex(index, out isList, out propertyIndex, out indexInProperty);
  195. if (p != null)
  196. {
  197. object value;
  198. if (isList)
  199. value = ((IList<object>)p.Value)[indexInProperty];
  200. else
  201. value = p.Value;
  202. if (value is T)
  203. return (T)value;
  204. }
  205. return default(T);
  206. }
  207. set
  208. {
  209. bool isList;
  210. int propertyIndex, indexInProperty;
  211. ICalendarProperty p = PropertyForIndex(index, out isList, out propertyIndex, out indexInProperty);
  212. if (p != null)
  213. {
  214. object oldValue;
  215. if (isList)
  216. {
  217. oldValue = ((IList<object>)p)[indexInProperty];
  218. ((IList<object>)p)[indexInProperty] = value;
  219. }
  220. else
  221. {
  222. oldValue = p.Value;
  223. p.Value = value;
  224. }
  225. T old = oldValue is T ? (T)oldValue : default(T);
  226. // FIXME: Call OnItemAdded()/OnItemRemoved() here?
  227. }
  228. }
  229. }
  230. #endregion
  231. #region ICollection<T> Members
  232. public void Add(T item)
  233. {
  234. if (IsReadOnly)
  235. throw new NotSupportedException();
  236. // Create a new list to store our item
  237. IList<object> list = new List<object>();
  238. list.Add(item);
  239. // FIXME: Do we always add a property, or should we aggregate to
  240. // another property that is an IList<object>?
  241. CalendarProperty p = new CalendarProperty();
  242. p.Name = m_PropertyName;
  243. p.Value = list;
  244. m_PropertyList.Add(p);
  245. }
  246. public void Clear()
  247. {
  248. for (int i = m_PropertyList.Count - 1; i >= 0; i--)
  249. {
  250. if (string.Equals(m_PropertyList[i].Name, m_PropertyName))
  251. m_PropertyList.RemoveAt(i);
  252. }
  253. }
  254. public bool Contains(T item)
  255. {
  256. return IndexOf(item) >= 0;
  257. }
  258. public void CopyTo(T[] array, int arrayIndex)
  259. {
  260. if (array == null)
  261. throw new ArgumentNullException("array");
  262. if (arrayIndex < 0)
  263. throw new ArgumentOutOfRangeException("arrayIndex");
  264. if (arrayIndex >= array.Length)
  265. throw new ArgumentException("arrayIndex");
  266. int index = 0;
  267. if (array.Length >= Count)
  268. {
  269. foreach (ICalendarProperty p in m_PropertyList)
  270. {
  271. if (string.Equals(p.Name, m_PropertyName))
  272. {
  273. if (p.Value is IList<object>)
  274. {
  275. IList<object> list = (IList<object>)p.Value;
  276. for (int i = 0; i < list.Count; i++)
  277. array.SetValue(list[i], i + index + arrayIndex);
  278. index += list.Count;
  279. }
  280. else if (p.Value is T)
  281. {
  282. array[index + arrayIndex] = (T)p.Value;
  283. index++;
  284. }
  285. }
  286. }
  287. }
  288. }
  289. public int Count
  290. {
  291. get
  292. {
  293. int count = 0;
  294. foreach (ICalendarProperty p in m_PropertyList)
  295. {
  296. if (string.Equals(p.Name, m_PropertyName))
  297. {
  298. if (p.Value is IList<object>)
  299. {
  300. foreach (object obj in (IList<object>)p.Value)
  301. {
  302. if (obj is T)
  303. count++;
  304. }
  305. }
  306. else if (p.Value != null)
  307. count++;
  308. }
  309. }
  310. return count;
  311. }
  312. }
  313. public bool IsReadOnly
  314. {
  315. get { return false; }
  316. }
  317. public bool Remove(T item)
  318. {
  319. bool isList;
  320. int itemIndex, indexInProperty;
  321. ICalendarProperty p = PropertyForItem(item, out isList, out itemIndex, out indexInProperty);
  322. if (p != null)
  323. {
  324. if (isList)
  325. {
  326. ((IList<object>)p.Value).RemoveAt(indexInProperty);
  327. return true;
  328. }
  329. else
  330. {
  331. return m_PropertyList.Remove(p);
  332. }
  333. }
  334. return false;
  335. }
  336. #endregion
  337. #region IEnumerable<T> Members
  338. public IEnumerator<T> GetEnumerator()
  339. {
  340. return new CalendarPropertyCompositeListEnumerator<T>(m_PropertyList, m_PropertyName);
  341. }
  342. #endregion
  343. #region IEnumerable Members
  344. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  345. {
  346. return new CalendarPropertyCompositeListEnumerator<T>(m_PropertyList, m_PropertyName);
  347. }
  348. #endregion
  349. private class SingleValueEnumerator<T> :
  350. IEnumerator<T>
  351. {
  352. private bool m_IsMoved;
  353. private T m_Current;
  354. private T m_Item;
  355. #region Constructors
  356. public SingleValueEnumerator(T item)
  357. {
  358. m_Item = item;
  359. }
  360. #endregion
  361. #region IEnumerator<T> Members
  362. public T Current
  363. {
  364. get { return m_Current; }
  365. }
  366. #endregion
  367. #region IDisposable Members
  368. public void Dispose()
  369. {
  370. }
  371. #endregion
  372. #region IEnumerator Members
  373. object IEnumerator.Current
  374. {
  375. get
  376. {
  377. return m_IsMoved ? (object)m_Current : null;
  378. }
  379. }
  380. public bool MoveNext()
  381. {
  382. if (!m_IsMoved)
  383. {
  384. m_Current = m_Item;
  385. m_IsMoved = true;
  386. return true;
  387. }
  388. else
  389. {
  390. m_Current = default(T);
  391. }
  392. return false;
  393. }
  394. public void Reset()
  395. {
  396. m_IsMoved = false;
  397. m_Current = default(T);
  398. }
  399. #endregion
  400. }
  401. private class CalendarPropertyCompositeListEnumerator<T> :
  402. IEnumerator<T>
  403. {
  404. #region Private Fields
  405. string m_PropertyName;
  406. ICalendarPropertyList m_PropertyList;
  407. IEnumerator m_CurrentListEnumerator;
  408. int m_PropertyIndex = -1;
  409. #endregion
  410. #region Constructors
  411. public CalendarPropertyCompositeListEnumerator(ICalendarPropertyList propertyList, string propertyName)
  412. {
  413. m_PropertyList = propertyList;
  414. m_PropertyName = propertyName;
  415. }
  416. #endregion
  417. #region Private Methods
  418. private void MoveNextProperty()
  419. {
  420. m_CurrentListEnumerator = null;
  421. while (m_PropertyIndex + 1 < m_PropertyList.Count)
  422. {
  423. ICalendarProperty p = m_PropertyList[++m_PropertyIndex];
  424. if (string.Equals(p.Name, m_PropertyName))
  425. {
  426. object value = p.Value;
  427. if (value is IList<object>)
  428. {
  429. IList<T> list = new List<T>();
  430. foreach (object obj in (IList<object>)value)
  431. {
  432. if (obj is T)
  433. list.Add((T)obj);
  434. }
  435. m_CurrentListEnumerator = list.GetEnumerator();
  436. return;
  437. }
  438. else if (value is T)
  439. {
  440. m_CurrentListEnumerator = new SingleValueEnumerator<T>((T)value);
  441. return;
  442. }
  443. }
  444. }
  445. }
  446. #endregion
  447. #region IEnumerator<T> Members
  448. public T Current
  449. {
  450. get
  451. {
  452. if (m_CurrentListEnumerator != null)
  453. return ((IEnumerator<T>)m_CurrentListEnumerator).Current;
  454. return default(T);
  455. }
  456. }
  457. #endregion
  458. #region IDisposable Members
  459. public void Dispose()
  460. {
  461. }
  462. #endregion
  463. #region IEnumerator Members
  464. object System.Collections.IEnumerator.Current
  465. {
  466. get
  467. {
  468. return ((IEnumerator<T>)this).Current;
  469. }
  470. }
  471. public bool MoveNext()
  472. {
  473. if (m_CurrentListEnumerator == null)
  474. MoveNextProperty();
  475. if (m_CurrentListEnumerator != null)
  476. {
  477. if (!m_CurrentListEnumerator.MoveNext())
  478. {
  479. MoveNextProperty();
  480. if (m_CurrentListEnumerator != null)
  481. return m_CurrentListEnumerator.MoveNext();
  482. }
  483. else return true;
  484. }
  485. return false;
  486. }
  487. public void Reset()
  488. {
  489. m_CurrentListEnumerator = null;
  490. m_PropertyIndex = -1;
  491. }
  492. #endregion
  493. }
  494. }
  495. }