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); } }
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); }
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); } }
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); } }
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); }
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); } } }
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); }
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); }
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; }
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); } }
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); } } }
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); } } }
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; }
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; }
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); } } }
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); } }
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 (); }