SusiMail: Sorting cleanups and fixes, only sort when required

This commit is contained in:
zzz
2017-12-16 14:16:56 +00:00
parent c2a1d7956c
commit 0430323d2a
3 changed files with 102 additions and 91 deletions

View File

@ -39,7 +39,7 @@ import java.util.Map;
* paging and sorting.
*
* You create a folder object, set the contents with setElements(),
* add Comparators with addSorter(), choose one with sortBy() and
* add Comparators with addSorter(), choose one with setSortBy() and
* and then fetch the content of the current page with
* currentPageIterator().
*
@ -153,17 +153,20 @@ public class Folder<O extends Object> {
/**
* Sorts the elements according the order given by @link addSorter()
* and @link sortBy().
* and @link setSortBy().
*
* @since public since 0.9.33
*/
private void sort()
public synchronized void sort()
{
if( currentSorter != null )
if (currentSorter != null && elements != null && elements.length > 1)
Arrays.sort( elements, currentSorter );
}
/**
* Set the array of objects the folder should manage.
* Does NOT copy the array.
* Sorts the array if a sorter set.
*
* @param elements Array of Os.
*/
@ -171,8 +174,7 @@ public class Folder<O extends Object> {
{
if (elements.length > 0) {
this.elements = elements;
if( currentSorter != null )
sort();
sort();
} else {
this.elements = null;
}
@ -213,33 +215,49 @@ public class Folder<O extends Object> {
* Add an element only if it does not already exist
*
* @param element to add
* @return true if added
*/
public void addElement(O element) {
addElements(Collections.singleton(element));
public boolean addElement(O element) {
return addElements(Collections.singletonList(element));
}
/**
* Add elements only if it they do not already exist
* Add elements only if they do not already exist
* Re-sorts the array if a sorter is set and any elements are actually added.
*
* @param elems to adde
* @param elems to add
* @return true if any were added
*/
@SuppressWarnings("unchecked")
public synchronized void addElements(Collection<O> elems) {
public synchronized boolean addElements(List<O> elems) {
boolean shouldUpdate = false;
if (elements != null) {
List<O> list = new ArrayList<O>(Arrays.asList(elements));
boolean shouldUpdate = false;
// delay copy until required
List<O> list = null;
for (O e : elems) {
if (!list.contains(e)) {
boolean found = false;
for (int i = 0; i < elements.length; i++) {
if (e.equals(elements[i])) {
found = true;
break;
}
}
if (!found) {
if (list == null) {
list = new ArrayList<O>(Arrays.asList(elements));
shouldUpdate = true;
}
list.add(e);
shouldUpdate = true;
}
}
if (shouldUpdate) {
elements = (O[]) list.toArray(new Object[list.size()]);
sort();
update();
setElements((O[]) list.toArray(new Object[list.size()]));
}
} else if (!elems.isEmpty()) {
setElements((O[]) (elems.toArray(new Object[elems.size()])));
shouldUpdate = true;
}
return shouldUpdate;
}
/**
@ -303,9 +321,9 @@ public class Folder<O extends Object> {
/**
* Adds a new sorter to the folder. You can sort the folder by
* calling sortBy() and choose the given id there.
* calling setSortBy() and choose the given id there.
*
* @param id ID to identify the Comparator with @link sortBy()
* @param id ID to identify the Comparator with @link setSortBy()
* @param sorter a Comparator to sort the Array given by @link setElements()
*/
public synchronized void addSorter( String id, Comparator<O> sorter )
@ -317,16 +335,21 @@ public class Folder<O extends Object> {
* Activates sorting by the choosen Comparator. The id must
* match the one, which the Comparator has been stored in the
* folder with @link addSorter().
* Sets the sorting direction of the folder.
*
* Warning, this does not do the actual sort, only addElements() and setElements() does a sort.
*
* @param id ID to identify the Comparator stored with @link addSorter()
* @param direction UP or DOWN. UP is reverse sort.
*/
public synchronized void sortBy( String id )
public synchronized void setSortBy(String id, SortOrder direction)
{
sortingDirection = direction;
currentSorter = sorter.get( id );
if (currentSorter != null) {
if (sortingDirection == SortOrder.UP)
currentSorter = Collections.reverseOrder(currentSorter);
currentSortID = id;
currentSortID = id;
} else {
currentSortID = null;
}
@ -367,17 +390,6 @@ public class Folder<O extends Object> {
}
****/
/**
* Sets the sorting direction of the folder.
* Does not re-sort. Caller must call sortBy()
*
* @param direction UP or DOWN
*/
public synchronized void setSortingDirection(SortOrder direction)
{
sortingDirection = direction;
}
/**
* Returns the first element of the sorted folder.
*

View File

@ -90,6 +90,7 @@ class MailCache {
* The ones known locally, which will include any known on the server, if connected.
* Will not include any marked for deletion.
*
* @return non-null
* @since 0.9.13
*/
public String[] getUIDLs() {

View File

@ -191,6 +191,7 @@ public class WebMail extends HttpServlet
private static final String SORT_DATE = "date";
private static final String SORT_SIZE = "size";
private static final String SORT_DEFAULT = SORT_DATE;
private static final SortOrder SORT_ORDER_DEFAULT = SortOrder.UP;
// for XSS
private static final List<String> VALID_SORTS = Arrays.asList(new String[] {
SORT_ID, SORT_SENDER, SORT_SUBJECT, SORT_DATE, SORT_SIZE,
@ -480,7 +481,7 @@ public class WebMail extends HttpServlet
Folder<String> f = folder;
if (mc != null && f != null) {
String[] uidls = mc.getUIDLs();
f.setElements(uidls);
f.addElements(Arrays.asList(uidls));
}
}
@ -561,17 +562,18 @@ public class WebMail extends HttpServlet
{
StringBuilder buf = new StringBuilder(128);
buf.append(label).append("&nbsp;&nbsp;");
// UP is reverse sort. DOWN is normal sort.
if (name.equals(currentName) && currentOrder == SortOrder.UP) {
buf.append("<img class=\"sort\" src=\"").append(imgPath).append("3up.png\" border=\"0\" alt=\"^\">\n");
} else {
buf.append("<a class=\"sort\" href=\"").append(myself).append("?page=").append(page).append("&amp;sort=").append(name).append("\">");
buf.append("<a class=\"sort\" href=\"").append(myself).append("?page=").append(page).append("&amp;sort=-").append(name).append("\">");
buf.append("<img class=\"sort\" src=\"").append(imgPath).append("3up.png\" border=\"0\" alt=\"^\" style=\"opacity: 0.4;\">");
buf.append("</a>\n");
}
if (name.equals(currentName) && currentOrder == SortOrder.DOWN) {
buf.append("<img class=\"sort\" src=\"").append(imgPath).append("3down.png\" border=\"0\" alt=\"v\">");
} else {
buf.append("<a class=\"sort\" href=\"").append(myself).append("?page=").append(page).append("&amp;sort=-").append(name).append("\">");
buf.append("<a class=\"sort\" href=\"").append(myself).append("?page=").append(page).append("&amp;sort=").append(name).append("\">");
buf.append("<img class=\"sort\" src=\"").append(imgPath).append("3down.png\" border=\"0\" alt=\"v\" style=\"opacity: 0.4;\">");
buf.append("</a>");
}
@ -863,21 +865,24 @@ public class WebMail extends HttpServlet
sessionObject.folder = new Folder<String>();
if (!offline) {
// prime the cache, request all headers at once
// otherwise they are pulled one at a time by sortBy() below
// otherwise they are pulled one at a time
// during the sort in folder.setElements() below
mc.getMail(MailCache.FetchMode.HEADER);
}
// get through cache so we have the disk-only ones too
String[] uidls = mc.getUIDLs();
sessionObject.folder.setElements(uidls);
// setElements() sorts, so configure the sorting first
//sessionObject.folder.addSorter( SORT_ID, new IDSorter( sessionObject.mailCache ) );
sessionObject.folder.addSorter( SORT_SENDER, new SenderSorter( sessionObject.mailCache ) );
sessionObject.folder.addSorter( SORT_SUBJECT, new SubjectSorter( sessionObject.mailCache ) );
sessionObject.folder.addSorter( SORT_DATE, new DateSorter( sessionObject.mailCache ) );
sessionObject.folder.addSorter( SORT_SIZE, new SizeSorter( sessionObject.mailCache ) );
// reverse sort, latest mail first
sessionObject.folder.setSortingDirection(SortOrder.UP);
sessionObject.folder.sortBy(SORT_DEFAULT);
// TODO get user defaults from config
sessionObject.folder.setSortBy(SORT_DEFAULT, SORT_ORDER_DEFAULT);
// get through cache so we have the disk-only ones too
String[] uidls = mc.getUIDLs();
sessionObject.folder.setElements(uidls);
sessionObject.reallyDelete = false;
if (offline)
Debug.debug(Debug.DEBUG, "OFFLINE MODE");
@ -1237,7 +1242,7 @@ public class WebMail extends HttpServlet
// get through cache so we have the disk-only ones too
String[] uidls = sessionObject.mailCache.getUIDLs();
if (uidls != null)
sessionObject.folder.setElements(uidls);
sessionObject.folder.addElements(Arrays.asList(uidls));
sessionObject.pageChanged = true;
}
return state;
@ -1536,18 +1541,22 @@ public class WebMail extends HttpServlet
*/
private static void processSortingButtons(SessionObject sessionObject, RequestWrapper request)
{
// GET param
String str = request.getParameter(SORT);
// POST param
if (str == null)
str = request.getParameter(CURRENT_SORT);
if (str != null && VALID_SORTS.contains(str)) {
SortOrder order;
// UP is reverse sort. DOWN is normal sort.
if (str.startsWith("-")) {
order = SortOrder.DOWN;
order = SortOrder.UP;
str = str.substring(1);
} else {
order = SortOrder.UP;
order = SortOrder.DOWN;
}
// TODO don't store in session
sessionObject.folder.setSortingDirection(order);
sessionObject.folder.sortBy(str);
// Store in session. processRequest() will re-sort if necessary.
sessionObject.folder.setSortBy(str, order);
}
}
@ -1800,16 +1809,6 @@ public class WebMail extends HttpServlet
return;
}
}
// sort buttons are GETs
processSortingButtons( sessionObject, request );
for( Iterator<String> it = sessionObject.folder.currentPageIterator(); it != null && it.hasNext(); ) {
String uidl = it.next();
Mail mail = sessionObject.mailCache.getMail( uidl, MailCache.FetchMode.HEADER );
if( mail != null && mail.error.length() > 0 ) {
sessionObject.error += mail.error;
mail.error = "";
}
}
}
// ?show= links - this forces State.SHOW
@ -1866,14 +1865,35 @@ public class WebMail extends HttpServlet
/*
* update folder content
* We need a valid and sorted folder for SHOW also, for the previous/next buttons
*/
Folder<String> folder = sessionObject.folder;
if( state == State.LIST ) {
if (state == State.LIST || state == State.SHOW) {
// sort buttons are GETs
String oldSort = folder.getCurrentSortBy();
SortOrder oldOrder = folder.getCurrentSortingDirection();
processSortingButtons( sessionObject, request );
if (state == State.LIST) {
for (Iterator<String> it = sessionObject.folder.currentPageIterator(); it != null && it.hasNext(); ) {
String uidl = it.next();
Mail mail = sessionObject.mailCache.getMail( uidl, MailCache.FetchMode.HEADER );
if( mail != null && mail.error.length() > 0 ) {
sessionObject.error += mail.error;
mail.error = "";
}
}
}
// get through cache so we have the disk-only ones too
String[] uidls = sessionObject.mailCache.getUIDLs();
if (uidls != null) {
// TODO why every time?
folder.setElements(uidls);
if (folder.addElements(Arrays.asList(uidls))) {
// we added elements, so it got sorted
} else {
// check for changed sort
String curSort = folder.getCurrentSortBy();
SortOrder curOrder = folder.getCurrentSortingDirection();
if (oldOrder != curOrder || !oldSort.equals(curSort))
folder.sort();
}
}
@ -1963,10 +1983,13 @@ public class WebMail extends HttpServlet
}
out.println("<input type=\"hidden\" name=\"" + CUR_PAGE + "\" value=\"" + page + "\">");
}
}
if (state == State.SHOW || state == State.NEW || state == State.LIST) {
// Save sort order in case it changes later
String curSort = folder.getCurrentSortBy();
SortOrder curOrder = folder.getCurrentSortingDirection();
String fullSort = curOrder == SortOrder.DOWN ? '-' + curSort : curSort;
// UP is reverse sort. DOWN is normal sort.
String fullSort = curOrder == SortOrder.UP ? '-' + curSort : curSort;
out.println("<input type=\"hidden\" name=\"" + CURRENT_SORT + "\" value=\"" + fullSort + "\">");
}
if( sessionObject.error != null && sessionObject.error.length() > 0 ) {
@ -1996,7 +2019,7 @@ public class WebMail extends HttpServlet
//out.println( "</form><div id=\"footer\"><hr><p class=\"footer\">susimail v0." + version +" " + ( RELEASE ? "release" : "development" ) + " &copy; 2004-2005 <a href=\"mailto:susi23@mail.i2p\">susi</a></div></div></body>\n</html>");
out.println( "</form><div class=\"footer\"><p class=\"footer\">susimail &copy; 2004-2005 susi</p></div></div></body>\n</html>");
out.flush();
}
} // synch sessionObject
}
/**
@ -2449,33 +2472,8 @@ public class WebMail extends HttpServlet
}
out.println("</div>");
SortOrder curOrder;
String curSort = request.getParameter(SORT);
String fullSort;
if (curSort == null)
curSort = request.getParameter(CURRENT_SORT);
if (curSort != null && VALID_SORTS.contains(curSort)) {
fullSort = curSort;
if (curSort.startsWith("-")) {
curSort = curSort.substring(1);
curOrder = SortOrder.DOWN;
} else {
curOrder = SortOrder.UP;
}
// TODO don't store in session
if (curOrder != folder.getCurrentSortingDirection() || !curSort.equals(folder.getCurrentSortBy())) {
folder.setSortingDirection(curOrder);
folder.sortBy(curSort);
}
} else {
curSort = folder.getCurrentSortBy();
curOrder = folder.getCurrentSortingDirection();
fullSort = curOrder == SortOrder.DOWN ? '-' + curSort : curSort;
}
out.println("<input type=\"hidden\" name=\"" + CURRENT_SORT + "\" value=\"" + fullSort + "\">");
if (curSort.startsWith("-"))
curSort = curSort.substring(1);
String curSort = folder.getCurrentSortBy();
SortOrder curOrder = folder.getCurrentSortingDirection();
out.println("<table id=\"mailbox\" cellspacing=\"0\" cellpadding=\"5\">\n" +
"<tr><td colspan=\"9\"><hr></td></tr>\n<tr><th title=\"" + _t("Mark for deletion") + "\">&nbsp;</th>" +
thSpacer + "<th>" + sortHeader(SORT_SENDER, _t("From"), sessionObject.imgPath, curSort, curOrder, page) + "</th>" +