[tasque/transition: 202/213] [libtasque] Implement BackendTaskCollection to save memory



commit ae1b41f530842f6654f517f6b88ea6f23d7d613b
Author: Antonius Riha <antoniusriha gmail com>
Date:   Sun Aug 19 21:43:56 2012 +0200

    [libtasque] Implement BackendTaskCollection to save memory
    
    BackendTaskCollection exposes the tasks that are entailed in the backend's
    categories as a ReadOnlySortedNotifyCollection<Task>.

 src/libtasque/Backend.cs                        |   94 ++---------------
 src/libtasque/BackendTaskCollection.cs          |  124 +++++++++++++++++++++++
 src/libtasque/ReadOnlySortedNotifyCollection.cs |  103 ++++++++++++++-----
 src/libtasque/libtasque.csproj                  |    1 +
 4 files changed, 214 insertions(+), 108 deletions(-)
---
diff --git a/src/libtasque/Backend.cs b/src/libtasque/Backend.cs
index 172c7c3..6e7d26f 100644
--- a/src/libtasque/Backend.cs
+++ b/src/libtasque/Backend.cs
@@ -26,8 +26,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
 
 namespace Tasque
 {
@@ -43,14 +41,9 @@ namespace Tasque
 				throw new ArgumentNullException ("name");
 
 			Name = name;
-
-			tasks = new SortedNotifyCollection<Task> ();
-			Tasks = new ReadOnlySortedNotifyCollection<Task> (tasks);
-
-			categoriesChangedSources = new List<INotifyCollectionChanged> ();
-			Categories = new SortedNotifyCollection<Category> ();
 			
-			Categories.CollectionChanged += HandleCategoriesChanged;
+			Categories = new SortedNotifyCollection<Category> ();
+			Tasks = new BackendTaskCollection (Categories);
 		}
 
 		#region Properties
@@ -110,12 +103,20 @@ namespace Tasque
 			GC.SuppressFinalize (this);
 		}
 		
-		protected virtual void Dispose (bool disposing) {}
+		protected virtual void Dispose (bool disposing)
+		{
+			if (!disposed && disposing) {
+				Tasks.Dispose ();
+				disposed = true;
+			}
+		}
 		
 		~Backend ()
 		{
 			Dispose (false);
 		}
+		
+		bool disposed;
 		#endregion
 		
 		#region Methods
@@ -167,79 +168,6 @@ namespace Tasque
 		public event EventHandler BackendSyncFinished;
 		public event EventHandler BackendSyncStarted;
 		
-		void HandleCategoriesChanged (object sender, NotifyCollectionChangedEventArgs e)
-		{
-			switch (e.Action) {
-			case NotifyCollectionChangedAction.Add: {
-				var cat = (Category)e.NewItems [0];
-				RegisterCategoriesChanged (cat);
-				foreach (var item in cat)
-					tasks.Add (item);
-				break;
-			}
-			case NotifyCollectionChangedAction.Remove: {
-				var cat = (Category)e.OldItems [0];
-				UnRegisterCategoriesChanged (cat);
-				RemoveCategoryContent (cat);
-				break;
-			}
-			case NotifyCollectionChangedAction.Reset:
-				for (int i = 0; i < categoriesChangedSources.Count; i++) {
-					UnRegisterCategoriesChanged (categoriesChangedSources [0]);
-				}
-				tasks.Clear ();
-				break;
-			}
-		}
-
-		void HandleTaskCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
-		{
-			switch (e.Action) {
-			case NotifyCollectionChangedAction.Add:
-				tasks.Add ((Task)e.NewItems [0]);
-				break;
-			case NotifyCollectionChangedAction.Remove:
-				var task = (Task)e.OldItems [0];
-				if (IsSoleOccurenceInCategory ((Category)sender, task))
-					tasks.Remove (task);
-				break;
-			case NotifyCollectionChangedAction.Reset:
-				RemoveCategoryContent ((Category)sender);
-				break;
-			}
-		}
-
-		bool IsSoleOccurenceInCategory (Category cat, Task task)
-		{
-			foreach (var item in Categories) {
-				if (cat != item && cat.Contains (task))
-					return false;
-			}
-			return true;
-		}
-
-		void RemoveCategoryContent (Category cat)
-		{
-			foreach (var item in cat) {
-				if (IsSoleOccurenceInCategory (cat, item))
-					tasks.Remove (item);
-			}
-		}
-
-		void RegisterCategoriesChanged (INotifyCollectionChanged source)
-		{
-			categoriesChangedSources.Add (source);
-			source.CollectionChanged += HandleTaskCollectionChanged;
-		}
-
-		void UnRegisterCategoriesChanged (INotifyCollectionChanged source)
-		{
-			source.CollectionChanged -= HandleTaskCollectionChanged;
-			categoriesChangedSources.Remove (source);
-		}
-
-		List<INotifyCollectionChanged> categoriesChangedSources;
-		SortedNotifyCollection<Task> tasks;
 		bool initialized;
 	}
 }
diff --git a/src/libtasque/BackendTaskCollection.cs b/src/libtasque/BackendTaskCollection.cs
new file mode 100644
index 0000000..d9935fd
--- /dev/null
+++ b/src/libtasque/BackendTaskCollection.cs
@@ -0,0 +1,124 @@
+// 
+// BackendTaskCollection.cs
+//  
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+// 
+// Copyright (c) 2012 Antonius Riha
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Linq;
+
+namespace Tasque
+{
+	class BackendTaskCollection : ReadOnlySortedNotifyCollection<Task>
+	{
+		internal BackendTaskCollection (SortedNotifyCollection<Category> categories)
+		{
+			if (categories == null)
+				throw new ArgumentNullException ("source");
+			categories.CollectionChanged += HandleSourceCollectionChanged;
+			this.categories = categories;
+		}
+		
+		protected override void Dispose (bool disposing)
+		{
+			if (!disposed && disposing) {
+				foreach (var item in categories)
+					item.CollectionChanged -= HandleCollectionChanged;
+				disposed = true;
+			}
+		}
+		
+		internal protected override int CountProtected {
+			get { return categories.SelectMany (c => c).Distinct ().Count (); }
+		}
+		
+		internal protected override bool ContainsProtected (Task item)
+		{
+			return categories.Any (c => c.Contains (item));
+		}
+		
+		internal protected override void CopyToProtected (Task[] array, int arrayIndex)
+		{
+			if (array == null)
+				throw new ArgumentNullException ("array");
+			if (arrayIndex < 0)
+				throw new ArgumentOutOfRangeException ("arrayIndex is less than 0.");
+			if (CountProtected > array.Length - arrayIndex)
+				throw new ArgumentException ("There is not enough space for the elements of the" +
+					"source collection in the provided array starting from arrayIndex."
+				);
+			
+			foreach (var item in this)
+				array [arrayIndex++] = item;
+		}
+		
+		internal protected override IEnumerator<Task> GetEnumeratorProtected ()
+		{
+			Task [] tasks = new Task [CountProtected];
+			int i = 0;
+			foreach (var collection in categories) {
+				foreach (var item in collection) {
+					if (tasks.Contains (item))
+						continue;
+					tasks [i++] = item;
+					yield return item;
+				}
+			}
+		}
+		
+		void AddCollection (Category category)
+		{
+			categories.Add (category);
+			if (categories.Contains (category))
+			    category.CollectionChanged += HandleCollectionChanged;
+		}
+		
+		bool RemoveCollection (Category category)
+		{
+			if (categories.Contains (category))
+				category.CollectionChanged -= HandleCollectionChanged;
+			return categories.Remove (category);
+		}
+		
+		void HandleCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
+		{
+			OnCollectionChanged (e);
+		}
+		
+		void HandleSourceCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
+		{
+			switch (e.Action) {
+			case NotifyCollectionChangedAction.Add:
+				AddCollection (e.NewItems [0] as Category);
+				break;
+			case NotifyCollectionChangedAction.Remove:
+				RemoveCollection (e.OldItems [0] as Category);
+				break;
+			}
+		}
+		
+		SortedNotifyCollection<Category> categories;
+		bool disposed;
+	}
+}
diff --git a/src/libtasque/ReadOnlySortedNotifyCollection.cs b/src/libtasque/ReadOnlySortedNotifyCollection.cs
index cf977d8..d06ca82 100644
--- a/src/libtasque/ReadOnlySortedNotifyCollection.cs
+++ b/src/libtasque/ReadOnlySortedNotifyCollection.cs
@@ -31,30 +31,23 @@ using System.ComponentModel;
 
 namespace Tasque
 {
-	public class ReadOnlySortedNotifyCollection<T> : ICollection<T>, INotifyCollectionChanged
+	public class ReadOnlySortedNotifyCollection<T> : ICollection<T>, INotifyCollectionChanged, IDisposable
 		where T : INotifyPropertyChanged, IComparable<T>
 	{
 		public ReadOnlySortedNotifyCollection (SortedNotifyCollection<T> source)
 		{
 			if (source == null)
 				throw new ArgumentNullException ("source");
-
+			
+			source.CollectionChanged += HandleCollectionChanged;
 			items = source;
 		}
-
-		#region IEnumerable implementation
-		IEnumerator IEnumerable.GetEnumerator ()
-		{
-			return GetEnumerator ();
-		}
-
-		public IEnumerator<T> GetEnumerator ()
-		{
-			return items.GetEnumerator ();
-		}
-		#endregion
-
+		
 		#region ICollection implementation
+		public int Count { get { return CountProtected; } }
+		
+		public bool IsReadOnly { get { return true; } }
+		
 		void ICollection<T>.Add (T item)
 		{
 			throw new NotSupportedException ("This collection is read-only.");
@@ -67,28 +60,88 @@ namespace Tasque
 
 		public bool Contains (T item)
 		{
-			return items.Contains (item);
+			return ContainsProtected (item);
 		}
 
 		public void CopyTo (T[] array, int arrayIndex)
 		{
-			items.CopyTo (array, arrayIndex);
+			CopyToProtected (array, arrayIndex);
 		}
 
 		bool ICollection<T>.Remove (T item)
 		{
 			throw new NotSupportedException ("This collection is read-only.");
 		}
-
-		public int Count { get { return items.Count; } }
-
-		public bool IsReadOnly { get { return true; } }
 		#endregion
-
+		
+		#region IEnumerable implementation
+		public IEnumerator<T> GetEnumerator ()
+		{
+			return GetEnumeratorProtected ();
+		}
+		
+		IEnumerator IEnumerable.GetEnumerator ()
+		{
+			return GetEnumerator ();
+		}
+		#endregion
+		
 		#region INotifyCollectionChanged implementation
-		public event NotifyCollectionChangedEventHandler CollectionChanged {
-			add { items.CollectionChanged += value; }
-			remove { items.CollectionChanged -= value; }
+		public event NotifyCollectionChangedEventHandler CollectionChanged;
+		
+		void HandleCollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
+		{
+			OnCollectionChanged (e);
+		}
+		#endregion
+
+		#region IDisposable implementation
+		public void Dispose ()
+		{
+			Dispose (true);
+			GC.SuppressFinalize (this);
+		}
+		
+		protected virtual void Dispose (bool disposing)
+		{
+			if (!disposed && disposing) {
+				items.CollectionChanged -= HandleCollectionChanged;
+				disposed = true;
+			}
+		}
+		
+		~ReadOnlySortedNotifyCollection ()
+		{
+			Dispose (false);
+		}
+		
+		bool disposed;
+		#endregion
+		
+		#region Internal
+		internal protected ReadOnlySortedNotifyCollection () {}
+		
+		internal protected virtual int CountProtected { get { return items.Count; } }
+		
+		internal protected virtual bool ContainsProtected (T item)
+		{
+			return items.Contains (item);
+		}
+		
+		internal protected virtual void CopyToProtected (T[] array, int arrayIndex)
+		{
+			items.CopyTo (array, arrayIndex);
+		}
+		
+		internal protected virtual IEnumerator<T> GetEnumeratorProtected ()
+		{
+			return items.GetEnumerator ();
+		}
+		
+		internal protected void OnCollectionChanged (NotifyCollectionChangedEventArgs e)
+		{
+			if (CollectionChanged != null)
+				CollectionChanged (this, e);
 		}
 		#endregion
 
diff --git a/src/libtasque/libtasque.csproj b/src/libtasque/libtasque.csproj
index 3cdb2c3..643f283 100644
--- a/src/libtasque/libtasque.csproj
+++ b/src/libtasque/libtasque.csproj
@@ -72,6 +72,7 @@
     <Compile Include="CompletedTaskGroupModel.cs" />
     <Compile Include="Application.cs" />
     <Compile Include="Dispatcher.cs" />
+    <Compile Include="BackendTaskCollection.cs" />
   </ItemGroup>
   <ItemGroup>
     <Reference Include="System" />



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]