296 lines
9.1 KiB
C#
296 lines
9.1 KiB
C#
//
|
|
// MessageSorter.cs
|
|
//
|
|
// Author: Jeffrey Stedfast <jestedfa@microsoft.com>
|
|
//
|
|
// Copyright (c) 2013-2020 .NET Foundation and Contributors
|
|
//
|
|
// 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.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
using MimeKit;
|
|
using MailKit.Search;
|
|
|
|
namespace MailKit {
|
|
/// <summary>
|
|
/// Routines for sorting messages.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Routines for sorting messages.
|
|
/// </remarks>
|
|
public static class MessageSorter
|
|
{
|
|
class MessageComparer<T> : IComparer<T> where T : IMessageSummary
|
|
{
|
|
readonly IList<OrderBy> orderBy;
|
|
|
|
public MessageComparer (IList<OrderBy> orderBy)
|
|
{
|
|
this.orderBy = orderBy;
|
|
}
|
|
|
|
#region IComparer implementation
|
|
|
|
static int CompareDisplayNames (InternetAddressList list1, InternetAddressList list2)
|
|
{
|
|
var m1 = list1.Mailboxes.GetEnumerator ();
|
|
var m2 = list2.Mailboxes.GetEnumerator ();
|
|
bool n1 = m1.MoveNext ();
|
|
bool n2 = m2.MoveNext ();
|
|
|
|
while (n1 && n2) {
|
|
var name1 = m1.Current.Name ?? string.Empty;
|
|
var name2 = m2.Current.Name ?? string.Empty;
|
|
int cmp;
|
|
|
|
if ((cmp = string.Compare (name1, name2, StringComparison.OrdinalIgnoreCase)) != 0)
|
|
return cmp;
|
|
|
|
n1 = m1.MoveNext ();
|
|
n2 = m2.MoveNext ();
|
|
}
|
|
|
|
return n1 ? 1 : (n2 ? -1 : 0);
|
|
}
|
|
|
|
static int CompareMailboxAddresses (InternetAddressList list1, InternetAddressList list2)
|
|
{
|
|
var m1 = list1.Mailboxes.GetEnumerator ();
|
|
var m2 = list2.Mailboxes.GetEnumerator ();
|
|
bool n1 = m1.MoveNext ();
|
|
bool n2 = m2.MoveNext ();
|
|
|
|
while (n1 && n2) {
|
|
int cmp;
|
|
|
|
if ((cmp = string.Compare (m1.Current.Address, m2.Current.Address, StringComparison.OrdinalIgnoreCase)) != 0)
|
|
return cmp;
|
|
|
|
n1 = m1.MoveNext ();
|
|
n2 = m2.MoveNext ();
|
|
}
|
|
|
|
return n1 ? 1 : (n2 ? -1 : 0);
|
|
}
|
|
|
|
public int Compare (T x, T y)
|
|
{
|
|
int cmp = 0;
|
|
|
|
for (int i = 0; i < orderBy.Count; i++) {
|
|
switch (orderBy[i].Type) {
|
|
case OrderByType.Annotation:
|
|
var annotation = (OrderByAnnotation) orderBy[i];
|
|
|
|
var xannotation = x.Annotations?.FirstOrDefault (a => a.Entry == annotation.Entry);
|
|
var yannotation = y.Annotations?.FirstOrDefault (a => a.Entry == annotation.Entry);
|
|
|
|
var xvalue = xannotation?.Properties[annotation.Attribute] ?? string.Empty;
|
|
var yvalue = yannotation?.Properties[annotation.Attribute] ?? string.Empty;
|
|
|
|
cmp = string.Compare (xvalue, yvalue, StringComparison.OrdinalIgnoreCase);
|
|
break;
|
|
case OrderByType.Arrival:
|
|
cmp = x.Index.CompareTo (y.Index);
|
|
break;
|
|
case OrderByType.Cc:
|
|
cmp = CompareMailboxAddresses (x.Envelope.Cc, y.Envelope.Cc);
|
|
break;
|
|
case OrderByType.Date:
|
|
cmp = x.Date.CompareTo (y.Date);
|
|
break;
|
|
case OrderByType.DisplayFrom:
|
|
cmp = CompareDisplayNames (x.Envelope.From, y.Envelope.From);
|
|
break;
|
|
case OrderByType.From:
|
|
cmp = CompareMailboxAddresses (x.Envelope.From, y.Envelope.From);
|
|
break;
|
|
case OrderByType.ModSeq:
|
|
var xmodseq = x.ModSeq ?? 0;
|
|
var ymodseq = y.ModSeq ?? 0;
|
|
|
|
cmp = xmodseq.CompareTo (ymodseq);
|
|
break;
|
|
case OrderByType.Size:
|
|
var xsize = x.Size ?? 0;
|
|
var ysize = y.Size ?? 0;
|
|
|
|
cmp = xsize.CompareTo (ysize);
|
|
break;
|
|
case OrderByType.Subject:
|
|
var xsubject = x.Envelope.Subject ?? string.Empty;
|
|
var ysubject = y.Envelope.Subject ?? string.Empty;
|
|
|
|
cmp = string.Compare (xsubject, ysubject, StringComparison.OrdinalIgnoreCase);
|
|
break;
|
|
case OrderByType.DisplayTo:
|
|
cmp = CompareDisplayNames (x.Envelope.To, y.Envelope.To);
|
|
break;
|
|
case OrderByType.To:
|
|
cmp = CompareMailboxAddresses (x.Envelope.To, y.Envelope.To);
|
|
break;
|
|
}
|
|
|
|
if (cmp == 0)
|
|
continue;
|
|
|
|
return orderBy[i].Order == SortOrder.Descending ? cmp * -1 : cmp;
|
|
}
|
|
|
|
return cmp;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
static MessageSummaryItems GetMessageSummaryItems (IList<OrderBy> orderBy)
|
|
{
|
|
var items = MessageSummaryItems.None;
|
|
|
|
for (int i = 0; i < orderBy.Count; i++) {
|
|
switch (orderBy[i].Type) {
|
|
case OrderByType.Annotation:
|
|
items |= MessageSummaryItems.Annotations;
|
|
break;
|
|
case OrderByType.Arrival:
|
|
break;
|
|
case OrderByType.Cc:
|
|
case OrderByType.Date:
|
|
case OrderByType.DisplayFrom:
|
|
case OrderByType.DisplayTo:
|
|
case OrderByType.From:
|
|
case OrderByType.Subject:
|
|
case OrderByType.To:
|
|
items |= MessageSummaryItems.Envelope;
|
|
break;
|
|
case OrderByType.ModSeq:
|
|
items |= MessageSummaryItems.ModSeq;
|
|
break;
|
|
case OrderByType.Size:
|
|
items |= MessageSummaryItems.Size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts the messages by the specified ordering.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Sorts the messages by the specified ordering.
|
|
/// </remarks>
|
|
/// <returns>The sorted messages.</returns>
|
|
/// <typeparam name="T">The message items must implement the <see cref="IMessageSummary"/> interface.</typeparam>
|
|
/// <param name="messages">The messages to sort.</param>
|
|
/// <param name="orderBy">The sort ordering.</param>
|
|
/// <exception cref="System.ArgumentNullException">
|
|
/// <para><paramref name="messages"/> is <c>null</c>.</para>
|
|
/// <para>-or-</para>
|
|
/// <para><paramref name="orderBy"/> is <c>null</c>.</para>
|
|
/// </exception>
|
|
/// <exception cref="System.ArgumentException">
|
|
/// <para><paramref name="messages"/> contains one or more items that is missing information needed for sorting.</para>
|
|
/// <para>-or-</para>
|
|
/// <para><paramref name="orderBy"/> is an empty list.</para>
|
|
/// </exception>
|
|
public static IList<T> Sort<T> (this IEnumerable<T> messages, IList<OrderBy> orderBy) where T : IMessageSummary
|
|
{
|
|
if (messages == null)
|
|
throw new ArgumentNullException (nameof (messages));
|
|
|
|
if (orderBy == null)
|
|
throw new ArgumentNullException (nameof (orderBy));
|
|
|
|
if (orderBy.Count == 0)
|
|
throw new ArgumentException ("No sort order provided.", nameof (orderBy));
|
|
|
|
var requiredFields = GetMessageSummaryItems (orderBy);
|
|
var list = new List<T> ();
|
|
|
|
foreach (var message in messages) {
|
|
if ((message.Fields & requiredFields) != requiredFields)
|
|
throw new ArgumentException ("One or more messages is missing information needed for sorting.", nameof (messages));
|
|
|
|
list.Add (message);
|
|
}
|
|
|
|
if (list.Count < 2)
|
|
return list;
|
|
|
|
var comparer = new MessageComparer<T> (orderBy);
|
|
|
|
list.Sort (comparer);
|
|
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts the messages by the specified ordering.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Sorts the messages by the specified ordering.
|
|
/// </remarks>
|
|
/// <returns>The sorted messages.</returns>
|
|
/// <typeparam name="T">The message items must implement the <see cref="IMessageSummary"/> interface.</typeparam>
|
|
/// <param name="messages">The messages to sort.</param>
|
|
/// <param name="orderBy">The sort ordering.</param>
|
|
/// <exception cref="System.ArgumentNullException">
|
|
/// <para><paramref name="messages"/> is <c>null</c>.</para>
|
|
/// <para>-or-</para>
|
|
/// <para><paramref name="orderBy"/> is <c>null</c>.</para>
|
|
/// </exception>
|
|
/// <exception cref="System.ArgumentException">
|
|
/// <para><paramref name="messages"/> contains one or more items that is missing information needed for sorting.</para>
|
|
/// <para>-or-</para>
|
|
/// <para><paramref name="orderBy"/> is an empty list.</para>
|
|
/// </exception>
|
|
public static void Sort<T> (this List<T> messages, IList<OrderBy> orderBy) where T : IMessageSummary
|
|
{
|
|
if (messages == null)
|
|
throw new ArgumentNullException (nameof (messages));
|
|
|
|
if (orderBy == null)
|
|
throw new ArgumentNullException (nameof (orderBy));
|
|
|
|
if (orderBy.Count == 0)
|
|
throw new ArgumentException ("No sort order provided.", nameof (orderBy));
|
|
|
|
var requiredFields = GetMessageSummaryItems (orderBy);
|
|
|
|
for (int i = 0; i < messages.Count; i++) {
|
|
if ((messages[i].Fields & requiredFields) != requiredFields)
|
|
throw new ArgumentException ("One or more messages is missing information needed for sorting.", nameof (messages));
|
|
}
|
|
|
|
if (messages.Count < 2)
|
|
return;
|
|
|
|
var comparer = new MessageComparer<T> (orderBy);
|
|
|
|
messages.Sort (comparer);
|
|
}
|
|
}
|
|
}
|