Example #1
0
        static void GetThreads(ThreadableNode root, IList <MessageThread> threads, IList <OrderBy> orderBy)
        {
            root.Children.Sort(orderBy);

            for (int i = 0; i < root.Children.Count; i++)
            {
                var message = root.Children[i].Message;
                var thread  = new MessageThread(message);

                GetThreads(root.Children[i], thread.Children, orderBy);
                threads.Add(thread);
            }
        }
Example #2
0
        static ThreadableNode CreateRoot(IDictionary <string, ThreadableNode> ids)
        {
            var root = new ThreadableNode();

            foreach (var message in ids.Values)
            {
                if (message.Parent == null)
                {
                    root.Children.Add(message);
                }
            }

            return(root);
        }
Example #3
0
        static void GetThreads(ThreadableNode root, List <MessageThread> threads, OrderBy[] orderBy)
        {
            var sorted = MessageSorter.Sort(root.Children, orderBy);

            for (int i = 0; i < sorted.Count; i++)
            {
                var      message = sorted[i].Message;
                UniqueId?uid     = null;

                if (message != null)
                {
                    uid = message.ThreadableUniqueId;
                }

                var thread = new MessageThread(uid);
                GetThreads(sorted[i], thread.children, orderBy);
                threads.Add(thread);
            }
        }
Example #4
0
        static void GetThreads(ThreadableNode root, IList <MessageThread> threads, IList <OrderBy> orderBy)
        {
            root.Children.Sort(orderBy);

            for (int i = 0; i < root.Children.Count; i++)
            {
                var      message = root.Children[i].Message;
                UniqueId?uid     = null;

                if (message != null)
                {
                    uid = message.UniqueId;
                }

                var thread = new MessageThread(uid);
                GetThreads(root.Children[i], thread.Children, orderBy);
                threads.Add(thread);
            }
        }
Example #5
0
        static IList <MessageThread> ThreadBySubject(IEnumerable <IMessageSummary> messages, IList <OrderBy> orderBy)
        {
            var threads = new List <MessageThread> ();
            var root    = new ThreadableNode(null);

            foreach (var message in messages)
            {
                if (message.Envelope == null)
                {
                    throw new ArgumentException("One or more messages is missing information needed for threading.", nameof(messages));
                }

                var node = new ThreadableNode(message);

                root.Children.Add(node);
            }

            GroupBySubject(root);

            GetThreads(root, threads, orderBy);

            return(threads);
        }
Example #6
0
        static void PruneEmptyContainers(ThreadableNode root)
        {
            for (int i = 0; i < root.Children.Count; i++)
            {
                var node = root.Children[i];

                if (node.Message == null && node.Children.Count == 0)
                {
                    // this is an empty container with no children, nuke it.
                    root.Children.RemoveAt(i);
                    i--;
                }
                else if (node.Message == null && node.HasChildren && (node.HasParent || node.Children.Count == 1))
                {
                    // If the Container has no Message, but does have children, remove this container but promote
                    // its children to this level (that is, splice them in to the current child list.)
                    //
                    // Do not promote the children if doing so would promote them to the root set -- unless there
                    // is only one child, in which case, do.
                    root.Children.RemoveAt(i);

                    for (int j = 0; j < node.Children.Count; j++)
                    {
                        node.Children[j].Parent = node.Parent;
                        root.Children.Add(node.Children[j]);
                    }

                    node.Children.Clear();
                    i--;
                }
                else if (node.HasChildren)
                {
                    PruneEmptyContainers(node);
                }
            }
        }
Example #7
0
        static IList <MessageThread> ThreadBySubject(IEnumerable <IThreadable> messages, OrderBy[] orderBy)
        {
            var threads = new List <MessageThread> ();
            var root    = new ThreadableNode();

            foreach (var message in messages)
            {
                if (!message.CanThread)
                {
                    throw new ArgumentException("One or more messages is missing information needed for threading.", "messages");
                }

                var container = new ThreadableNode();
                container.Message = message;

                root.Children.Add(container);
            }

            GroupBySubject(root);

            GetThreads(root, threads, orderBy);

            return(threads);
        }
Example #8
0
        static IDictionary <string, ThreadableNode> CreateIdTable(IEnumerable <IMessageSummary> messages)
        {
            var            ids = new Dictionary <string, ThreadableNode> ();
            ThreadableNode node;

            foreach (var message in messages)
            {
                if (message.Envelope == null)
                {
                    throw new ArgumentException("One or more messages is missing information needed for threading.", nameof(messages));
                }

                var id = message.Envelope.MessageId;

                if (string.IsNullOrEmpty(id))
                {
                    id = MimeUtils.GenerateMessageId();
                }

                if (ids.TryGetValue(id, out node))
                {
                    if (node.Message == null)
                    {
                        // a previously processed message referenced this message
                        node.Message = message;
                    }
                    else
                    {
                        // a duplicate message-id, just create a dummy id and use that
                        id   = MimeUtils.GenerateMessageId();
                        node = null;
                    }
                }

                if (node == null)
                {
                    // create a new ThreadContainer for this message and add it to ids
                    node         = new ThreadableNode();
                    node.Message = message;
                    ids.Add(id, node);
                }

                ThreadableNode parent = null;
                foreach (var reference in message.References)
                {
                    ThreadableNode referenced;

                    if (!ids.TryGetValue(reference, out referenced))
                    {
                        // create a dummy container for the referenced message
                        referenced = new ThreadableNode();
                        ids.Add(reference, referenced);
                    }

                    // chain up the references, disallowing loops
                    if (parent != null && referenced.Parent == null && parent != referenced && !parent.Children.Contains(referenced))
                    {
                        parent.Children.Add(referenced);
                        referenced.Parent = parent;
                    }

                    parent = referenced;
                }

                // don't allow loops
                if (parent != null && (parent == node || node.Children.Contains(parent)))
                {
                    parent = null;
                }

                if (node.HasParent)
                {
                    // unlink from our old parent
                    node.Parent.Children.Remove(node);
                    node.Parent = null;
                }

                if (parent != null)
                {
                    // add it as a child of our new parent
                    parent.Children.Add(node);
                    node.Parent = parent;
                }
            }

            return(ids);
        }
Example #9
0
		static IList<MessageThread> ThreadBySubject (IEnumerable<IMessageSummary> messages, IList<OrderBy> orderBy)
		{
			var threads = new List<MessageThread> ();
			var root = new ThreadableNode ();

			foreach (var message in messages) {
				if (message.Envelope == null)
					throw new ArgumentException ("One or more messages is missing information needed for threading.", "messages");

				var container = new ThreadableNode ();
				container.Message = message;

				root.Children.Add (container);
			}

			GroupBySubject (root);

			GetThreads (root, threads, orderBy);

			return threads;
		}
Example #10
0
		static void GetThreads (ThreadableNode root, List<MessageThread> threads, OrderBy[] orderBy)
		{
			var sorted = MessageSorter.Sort (root.Children, orderBy);

			for (int i = 0; i < sorted.Count; i++) {
				var message = sorted[i].Message;
				UniqueId? uid = null;

				if (message != null)
					uid = message.ThreadableUniqueId;

				var thread = new MessageThread (uid);
				GetThreads (sorted[i], thread.children, orderBy);
				threads.Add (thread);
			}
		}
Example #11
0
		static void GroupBySubject (ThreadableNode root)
		{
			var subjects = new Dictionary<string, ThreadableNode> (StringComparer.OrdinalIgnoreCase);
			ThreadableNode match;
			int count = 0;

			for (int i = 0; i < root.Children.Count; i++) {
				var current = root.Children[i];
				var subject = current.NormalizedSubject;

				// don't thread messages with empty subjects
				if (string.IsNullOrEmpty (subject))
					continue;

				if (!subjects.TryGetValue (subject, out match) ||
					(current.Message == null && match.Message != null) ||
					(match.Message != null && match.Message.IsReply &&
						current.Message != null && !current.Message.IsReply)) {
					subjects[subject] = current;
					count++;
				}
			}

			if (count == 0)
				return;

			for (int i = 0; i < root.Children.Count; i++) {
				var current = root.Children[i];
				var subject = current.NormalizedSubject;

				// don't thread messages with empty subjects
				if (string.IsNullOrEmpty (subject))
					continue;

				match = subjects[subject];

				if (match == current)
					continue;

				// remove the second message with the same subject
				root.Children.RemoveAt (i);

				// group these messages together...
				if (match.Message == null && current.Message == null) {
					// If both messages are dummies, append the current message's children
					// to the children of the message in the subject table (the children of
					// both messages become siblings), and then delete the current message.
					match.Children.AddRange (current.Children);
				} else if (match.Message == null && current.Message != null) {
					// If the message in the subject table is a dummy and the current message
					// is not, make the current message a child of the message in the subject
					// table (a sibling of its children).
					match.Children.Add (current);
				} else if (current.Message.IsReply && !match.Message.IsReply) {
					// If the current message is a reply or forward and the message in the
					// subject table is not, make the current message a child of the message
					// in the subject table (a sibling of its children).
					match.Children.Add (current);
				} else {
					// Otherwise, create a new dummy message and make both the current message
					// and the message in the subject table children of the dummy. Then replace
					// the message in the subject table with the dummy message.

					// Note: if we re-use the node already in the subject table and the root, then
					// we won't have to insert the new dummy node at the matched node's location
					var dummy = match;

					// clone the message already in the subject table
					match = new ThreadableNode ();
					match.Message = dummy.Message;
					match.Children.AddRange (dummy.Children);

					// empty out the old match node (aka the new dummy node)
					dummy.Children.Clear ();
					dummy.Message = null;

					// now add both messages to the dummy
					dummy.Children.Add (match);
					dummy.Children.Add (current);
				}
			}
		}
Example #12
0
		static void PruneEmptyContainers (ThreadableNode root)
		{
			for (int i = 0; i < root.Children.Count; i++) {
				var node = root.Children[i];

				if (node.Message == null && node.Children.Count == 0) {
					// this is an empty container with no children, nuke it.
					root.Children.RemoveAt (i);
					i--;
				} else if (node.Message == null && node.HasChildren && (node.HasParent || node.Children.Count == 1)) {
					// If the Container has no Message, but does have children, remove this container but promote
					// its children to this level (that is, splice them in to the current child list.)
					//
					// Do not promote the children if doing so would promote them to the root set -- unless there
					// is only one child, in which case, do.
					root.Children.RemoveAt (i);

					for (int j = 0; j < node.Children.Count; j++) {
						node.Children[j].Parent = node.Parent;
						root.Children.Add (node.Children[j]);
					}

					node.Children.Clear ();
					i--;
				} else if (node.HasChildren) {
					PruneEmptyContainers (node);
				}
			}
		}
Example #13
0
		static ThreadableNode CreateRoot (IDictionary<string, ThreadableNode> ids)
		{
			var root = new ThreadableNode ();

			foreach (var message in ids.Values) {
				if (message.Parent == null)
					root.Children.Add (message);
			}

			return root;
		}
Example #14
0
		static IDictionary<string, ThreadableNode> CreateIdTable (IEnumerable<IMessageSummary> messages)
		{
			var ids = new Dictionary<string, ThreadableNode> ();
			ThreadableNode node;

			foreach (var message in messages) {
				if (message.Envelope == null)
					throw new ArgumentException ("One or more messages is missing information needed for threading.", "messages");

				var id = message.Envelope.MessageId;

				if (string.IsNullOrEmpty (id))
					id = MimeUtils.GenerateMessageId ();

				if (ids.TryGetValue (id, out node)) {
					if (node.Message == null) {
						// a previously processed message referenced this message
						node.Message = message;
					} else {
						// a duplicate message-id, just create a dummy id and use that
						id = MimeUtils.GenerateMessageId ();
						node = null;
					}
				}

				if (node == null) {
					// create a new ThreadContainer for this message and add it to ids
					node = new ThreadableNode ();
					node.Message = message;
					ids.Add (id, node);
				}

				ThreadableNode parent = null;
				foreach (var reference in message.References) {
					ThreadableNode referenced;

					if (!ids.TryGetValue (reference, out referenced)) {
						// create a dummy container for the referenced message
						referenced = new ThreadableNode ();
						ids.Add (reference, referenced);
					}

					// chain up the references, disallowing loops
					if (parent != null && referenced.Parent == null && parent != referenced && !parent.Children.Contains (referenced)) {
						parent.Children.Add (referenced);
						referenced.Parent = parent;
					}

					parent = referenced;
				}

				// don't allow loops
				if (parent != null && (parent == node || node.Children.Contains (parent)))
					parent = null;

				if (node.HasParent) {
					// unlink from our old parent
					node.Parent.Children.Remove (node);
					node.Parent = null;
				}

				if (parent != null) {
					// add it as a child of our new parent
					parent.Children.Add (node);
					node.Parent = parent;
				}
			}

			return ids;
		}
Example #15
0
        static void GroupBySubject(ThreadableNode root)
        {
            var            subjects = new Dictionary <string, ThreadableNode> (StringComparer.OrdinalIgnoreCase);
            ThreadableNode match;
            int            count = 0;

            for (int i = 0; i < root.Children.Count; i++)
            {
                var current = root.Children[i];
                var subject = current.NormalizedSubject;

                // don't thread messages with empty subjects
                if (string.IsNullOrEmpty(subject))
                {
                    continue;
                }

                if (!subjects.TryGetValue(subject, out match) ||
                    (current.Message == null && match.Message != null) ||
                    (match.Message != null && match.Message.IsReply &&
                     current.Message != null && !current.Message.IsReply))
                {
                    subjects[subject] = current;
                    count++;
                }
            }

            if (count == 0)
            {
                return;
            }

            for (int i = 0; i < root.Children.Count; i++)
            {
                var current = root.Children[i];
                var subject = current.NormalizedSubject;

                // don't thread messages with empty subjects
                if (string.IsNullOrEmpty(subject))
                {
                    continue;
                }

                match = subjects[subject];

                if (match == current)
                {
                    continue;
                }

                // remove the second message with the same subject
                root.Children.RemoveAt(i--);

                // group these messages together...
                if (match.Message == null && current.Message == null)
                {
                    // If both messages are dummies, append the current message's children
                    // to the children of the message in the subject table (the children of
                    // both messages become siblings), and then delete the current message.
                    match.Children.AddRange(current.Children);
                }
                else if (match.Message == null && current.Message != null)
                {
                    // If the message in the subject table is a dummy and the current message
                    // is not, make the current message a child of the message in the subject
                    // table (a sibling of its children).
                    match.Children.Add(current);
                }
                else if (current.Message.IsReply && !match.Message.IsReply)
                {
                    // If the current message is a reply or forward and the message in the
                    // subject table is not, make the current message a child of the message
                    // in the subject table (a sibling of its children).
                    match.Children.Add(current);
                }
                else
                {
                    // Otherwise, create a new dummy message and make both the current message
                    // and the message in the subject table children of the dummy. Then replace
                    // the message in the subject table with the dummy message.

                    // Note: if we re-use the node already in the subject table and the root, then
                    // we won't have to insert the new dummy node at the matched node's location
                    var dummy = match;

                    // clone the message already in the subject table
                    match         = new ThreadableNode();
                    match.Message = dummy.Message;
                    match.Children.AddRange(dummy.Children);

                    // empty out the old match node (aka the new dummy node)
                    dummy.Children.Clear();
                    dummy.Message = null;

                    // now add both messages to the dummy
                    dummy.Children.Add(match);
                    dummy.Children.Add(current);
                }
            }
        }
Example #16
0
		static void GetThreads (ThreadableNode root, IList<MessageThread> threads, IList<OrderBy> orderBy)
		{
			root.Children.Sort (orderBy);

			for (int i = 0; i < root.Children.Count; i++) {
				var message = root.Children[i].Message;
				UniqueId? uid = null;

				if (message != null)
					uid = message.UniqueId;

				var thread = new MessageThread (uid);
				GetThreads (root.Children[i], thread.Children, orderBy);
				threads.Add (thread);
			}
		}
Example #17
0
        static MessageThread[] ThreadBySubject(IEnumerable<IThreadable> messages, OrderBy[] orderBy)
        {
            var threads = new List<MessageThread> ();
            var root = new ThreadableNode ();

            foreach (var message in messages) {
                if (!message.CanThread)
                    throw new ArgumentException ("One or more messages is missing information needed for threading.", "messages");

                var container = new ThreadableNode ();
                container.Message = message;

                root.Children.Add (container);
            }

            GroupBySubject (root);

            GetThreads (root, threads, orderBy);

            return threads.ToArray ();
        }