/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.component.mail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import com.sun.mail.imap.SortTerm;
/**
* Utility class for sorting of mail messages
*/
public final class MailSorter {
/**
* No instances
*/
private MailSorter() {
}
/**
* Sort the messages. This emulates sorting the messages on the server if the server doesn't have the sorting
* capability. See RFC 5256
* Does not support complex sorting like in the RFC (with Base Subject or other similar stuff), just simple
* comparisons.
*
* @param messages Messages to sort. Are sorted in place
* @param sortTerm Sort term
*/
public static void sortMessages(Message[] messages, final SortTerm[] sortTerm) {
final List<SortTermWithDescending> sortTermsWithDescending = getSortTermsWithDescending(sortTerm);
sortMessages(messages, sortTermsWithDescending);
}
/**
* Compute the potentially descending sort terms from the input list
*
* @param sortTerm Input list
* @return Sort terms list including if the respective sort should be sorted in descending order
*/
private static List<SortTermWithDescending> getSortTermsWithDescending(SortTerm[] sortTerm) {
// List of reversable sort terms. If the boolean is true the respective sort term is descending
final List<SortTermWithDescending> sortTermsWithDescending = new ArrayList<SortTermWithDescending>(sortTerm.length);
// Descending next item in input because the last item was a "descending"
boolean descendingNext = false;
for (SortTerm term : sortTerm) {
if (term.equals(SortTerm.REVERSE)) {
if (descendingNext) {
throw new IllegalArgumentException("Double reverse in sort term is not allowed");
}
descendingNext = true;
} else {
sortTermsWithDescending.add(new SortTermWithDescending(term, descendingNext));
descendingNext = false;
}
}
return sortTermsWithDescending;
}
/**
* Sort messages using the list of properties
*
* @param messages Messages to sort. Are sorted in place
* @param sortTermsWithDescending Sort terms list including if the respective sort should be sorted in descending order
*/
private static void sortMessages(Message[] messages, final List<SortTermWithDescending> sortTermsWithDescending) {
Arrays.sort(messages, new Comparator<Message>() {
@Override
public int compare(Message m1, Message m2) {
try {
for (SortTermWithDescending reversableTerm : sortTermsWithDescending) {
int comparison = compareMessageProperty(m1, m2, reversableTerm.getTerm());
// Descending
if (reversableTerm.isDescending()) {
comparison = -comparison;
}
// Abort on first non-equal
if (comparison != 0) {
return comparison;
}
}
// Equal
return 0;
} catch (MessagingException e) {
throw new IllegalArgumentException(e);
}
}
});
}
/**
* Compare the value of the property of the two messages.
*
* @param msg1 Message 1
* @param msg2 Message 2
* @param property Property to compare
* @return msg1.property.compareTo(msg2.property)
* @throws javax.mail.MessagingException If message data could not be read.
*/
private static int compareMessageProperty(Message msg1, Message msg2, SortTerm property) throws MessagingException {
if (property.equals(SortTerm.TO)) {
InternetAddress addr1 = (InternetAddress) msg1.getRecipients(Message.RecipientType.TO)[0];
InternetAddress addr2 = (InternetAddress) msg2.getRecipients(Message.RecipientType.TO)[0];
return addr1.getAddress().compareTo(addr2.getAddress());
} else if (property.equals(SortTerm.CC)) {
InternetAddress addr1 = (InternetAddress) msg1.getRecipients(Message.RecipientType.CC)[0];
InternetAddress addr2 = (InternetAddress) msg2.getRecipients(Message.RecipientType.CC)[0];
return addr1.getAddress().compareTo(addr2.getAddress());
} else if (property.equals(SortTerm.FROM)) {
InternetAddress addr1 = (InternetAddress) msg1.getFrom()[0];
InternetAddress addr2 = (InternetAddress) msg2.getFrom()[0];
return addr1.getAddress().compareTo(addr2.getAddress());
} else if (property.equals(SortTerm.ARRIVAL)) {
Date arr1 = msg1.getReceivedDate();
Date arr2 = msg2.getReceivedDate();
return arr1.compareTo(arr2);
} else if (property.equals(SortTerm.DATE)) {
Date sent1 = msg1.getSentDate();
Date sent2 = msg2.getSentDate();
return sent1.compareTo(sent2);
} else if (property.equals(SortTerm.SIZE)) {
int size1 = msg1.getSize();
int size2 = msg2.getSize();
return Integer.compare(size1, size2);
} else if (property.equals(SortTerm.SUBJECT)) {
String sub1 = msg1.getSubject();
String sub2 = msg2.getSubject();
return sub1.compareTo(sub2);
}
throw new IllegalArgumentException(String.format("Unknown sort term: %s", property.toString()));
}
/**
* A sort term with a bit indicating if sorting should be descending for this term
*/
private static final class SortTermWithDescending {
private SortTerm term;
private boolean descending;
private SortTermWithDescending(SortTerm term, boolean descending) {
this.term = term;
this.descending = descending;
}
/**
* @return Actual search term
*/
public SortTerm getTerm() {
return term;
}
/**
* @return true if sorting should be descending, false if it should be ascending
*/
public boolean isDescending() {
return descending;
}
}
}