12010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan/* 22010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * Copyright (C) 2016 The Android Open Source Project 32010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * 42010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * Licensed under the Apache License, Version 2.0 (the "License"); 52010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * you may not use this file except in compliance with the License. 62010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * You may obtain a copy of the License at 72010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * 82010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * http://www.apache.org/licenses/LICENSE-2.0 92010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * 102010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * Unless required by applicable law or agreed to in writing, software 112010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * distributed under the License is distributed on an "AS IS" BASIS, 122010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * See the License for the specific language governing permissions and 142010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * limitations under the License. 152010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan */ 162010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 172010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanpackage com.android.documentsui.sorting; 182010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 192010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanimport static com.android.documentsui.base.DocumentInfo.getCursorLong; 202010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanimport static com.android.documentsui.base.DocumentInfo.getCursorString; 212010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 222010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanimport android.database.AbstractCursor; 232010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanimport android.database.Cursor; 24b324dfd842a8412509b0e02626e1ebfafd00c44bSteve McKayimport android.os.Bundle; 252010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanimport android.provider.DocumentsContract.Document; 262010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 27b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tanimport com.android.documentsui.base.Lookup; 282010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanimport com.android.documentsui.base.Shared; 292010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanimport com.android.documentsui.sorting.SortModel.SortDimensionId; 302010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 312010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan/** 322010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * Cursor wrapper that presents a sorted view of the underlying cursor. Handles 332010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * common {@link Document} sorting modes, such as ordering directories first. 342010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan */ 352010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tanclass SortingCursorWrapper extends AbstractCursor { 362010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan private final Cursor mCursor; 372010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 382010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan private final int[] mPosition; 392010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 40b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan public SortingCursorWrapper( 41b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan Cursor cursor, SortDimension dimension, Lookup<String, String> fileTypeLookup) { 422010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan mCursor = cursor; 432010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 442010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final int count = cursor.getCount(); 452010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan mPosition = new int[count]; 462010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan boolean[] isDirs = new boolean[count]; 47b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan String[] stringValues = null; 482010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan long[] longValues = null; 4998d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan String[] ids = new String[count]; 502010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 512010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final @SortDimensionId int id = dimension.getId(); 522010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan switch (id) { 532010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_TITLE: 54b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan case SortModel.SORT_DIMENSION_ID_FILE_TYPE: 55b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan stringValues = new String[count]; 562010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 572010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_DATE: 582010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_SIZE: 592010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan longValues = new long[count]; 602010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 612010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 622010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 632010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan cursor.moveToPosition(-1); 642010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan for (int i = 0; i < count; i++) { 652010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan cursor.moveToNext(); 662010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan mPosition[i] = i; 672010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 682010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final String mimeType = getCursorString(mCursor, Document.COLUMN_MIME_TYPE); 692010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan isDirs[i] = Document.MIME_TYPE_DIR.equals(mimeType); 7098d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan ids[i] = getCursorString(mCursor, Document.COLUMN_DOCUMENT_ID); 712010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 722010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan switch(id) { 732010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_TITLE: 742010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final String displayName = getCursorString( 752010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan mCursor, Document.COLUMN_DISPLAY_NAME); 76b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan stringValues[i] = displayName; 77b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan break; 78b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan case SortModel.SORT_DIMENSION_ID_FILE_TYPE: 79b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan stringValues[i] = fileTypeLookup.lookup(mimeType); 802010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 812010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_DATE: 822010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan longValues[i] = getLastModified(mCursor); 832010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 842010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_SIZE: 852010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan longValues[i] = getCursorLong(mCursor, Document.COLUMN_SIZE); 862010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 872010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 882010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 892010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 902010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 912010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan switch (id) { 922010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_TITLE: 93b47b4b560c46336a0e6ba3e86db3f2d76b4b2bc3Garfield Tan case SortModel.SORT_DIMENSION_ID_FILE_TYPE: 9498d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan binarySort(stringValues, isDirs, mPosition, ids, dimension.getSortDirection()); 952010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 962010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_DATE: 972010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortModel.SORT_DIMENSION_ID_SIZE: 982010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan binarySort(longValues, isDirs, mPosition, ids, dimension.getSortDirection()); 992010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 1002010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1012010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1022010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1032010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1042010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1052010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public void close() { 1062010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan super.close(); 1072010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan mCursor.close(); 1082010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1092010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1102010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1112010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public boolean onMove(int oldPosition, int newPosition) { 1122010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.moveToPosition(mPosition[newPosition]); 1132010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1142010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1152010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1162010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public String[] getColumnNames() { 1172010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getColumnNames(); 1182010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1192010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1202010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1212010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public int getCount() { 1222010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getCount(); 1232010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1242010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1252010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1262010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public double getDouble(int column) { 1272010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getDouble(column); 1282010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1292010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1302010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1312010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public float getFloat(int column) { 1322010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getFloat(column); 1332010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1342010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1352010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1362010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public int getInt(int column) { 1372010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getInt(column); 1382010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1392010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1402010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1412010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public long getLong(int column) { 1422010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getLong(column); 1432010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1442010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1452010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1462010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public short getShort(int column) { 1472010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getShort(column); 1482010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1492010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1502010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1512010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public String getString(int column) { 1522010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getString(column); 1532010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1542010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1552010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1562010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public int getType(int column) { 1572010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.getType(column); 1582010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1592010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1602010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @Override 1612010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan public boolean isNull(int column) { 1622010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return mCursor.isNull(column); 1632010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1642010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 165b324dfd842a8412509b0e02626e1ebfafd00c44bSteve McKay @Override 166b324dfd842a8412509b0e02626e1ebfafd00c44bSteve McKay public Bundle getExtras() { 167b324dfd842a8412509b0e02626e1ebfafd00c44bSteve McKay return mCursor.getExtras(); 168b324dfd842a8412509b0e02626e1ebfafd00c44bSteve McKay } 169b324dfd842a8412509b0e02626e1ebfafd00c44bSteve McKay 1702010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan /** 1712010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * @return Timestamp for the given document. Some docs (e.g. active downloads) have a null 1722010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * timestamp - these will be replaced with MAX_LONG so that such files get sorted to the top 1732010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * when sorting descending by date. 1742010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan */ 1752010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan private static long getLastModified(Cursor cursor) { 1762010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan long l = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED); 1772010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan return (l == -1) ? Long.MAX_VALUE : l; 1782010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 1792010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1802010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan /** 1812010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * Borrowed from TimSort.binarySort(), but modified to sort two column 1822010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * dataset. 1832010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan */ 1842010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan private static void binarySort( 1852010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan String[] sortKey, 1862010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan boolean[] isDirs, 1872010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int[] positions, 18898d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan String[] ids, 1892010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @SortDimension.SortDirection int direction) { 1902010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final int count = positions.length; 1912010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan for (int start = 1; start < count; start++) { 1922010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final int pivotPosition = positions[start]; 1932010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final String pivotValue = sortKey[start]; 1942010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final boolean pivotIsDir = isDirs[start]; 19598d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan final String pivotId = ids[start]; 1962010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 1972010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int left = 0; 1982010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int right = start; 1992010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 2002010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan while (left < right) { 2012010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int mid = (left + right) >>> 1; 2022010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 2032010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan // Directories always go in front. 2042010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int compare = 0; 2052010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final boolean rhsIsDir = isDirs[mid]; 2062010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan if (pivotIsDir && !rhsIsDir) { 2072010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = -1; 2082010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } else if (!pivotIsDir && rhsIsDir) { 2092010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = 1; 2102010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } else { 2112010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final String lhs = pivotValue; 2122010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final String rhs = sortKey[mid]; 2132010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan switch (direction) { 2142010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortDimension.SORT_DIRECTION_ASCENDING: 2152010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = Shared.compareToIgnoreCaseNullable(lhs, rhs); 2162010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 2172010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortDimension.SORT_DIRECTION_DESCENDING: 2182010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = -Shared.compareToIgnoreCaseNullable(lhs, rhs); 2192010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 2202010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan default: 2212010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan throw new IllegalArgumentException( 2222010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan "Unknown sorting direction: " + direction); 2232010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 2242010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 2252010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 22698d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan // Use document ID as a tie breaker to achieve stable sort result. 22798d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan if (compare == 0) { 22898d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan compare = pivotId.compareTo(ids[mid]); 22998d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan } 23098d745d90b2bf8232593f2d5f9092536298d1147Garfield Tan 2312010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan if (compare < 0) { 2322010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan right = mid; 2332010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } else { 2342010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan left = mid + 1; 2352010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 2362010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 2372010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 2382010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int n = start - left; 2392010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan switch (n) { 2402010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case 2: 2412010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan positions[left + 2] = positions[left + 1]; 2422010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan sortKey[left + 2] = sortKey[left + 1]; 2432010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan isDirs[left + 2] = isDirs[left + 1]; 2442010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case 1: 2452010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan positions[left + 1] = positions[left]; 2462010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan sortKey[left + 1] = sortKey[left]; 2472010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan isDirs[left + 1] = isDirs[left]; 2482010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 2492010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan default: 2502010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan System.arraycopy(positions, left, positions, left + 1, n); 2512010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan System.arraycopy(sortKey, left, sortKey, left + 1, n); 2522010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan System.arraycopy(isDirs, left, isDirs, left + 1, n); 2532010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 2542010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 2552010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan positions[left] = pivotPosition; 2562010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan sortKey[left] = pivotValue; 2572010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan isDirs[left] = pivotIsDir; 2582010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 2592010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 2602010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 2612010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan /** 2622010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * Borrowed from TimSort.binarySort(), but modified to sort two column 2632010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan * dataset. 2642010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan */ 2652010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan private static void binarySort( 2662010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan long[] sortKey, 2672010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan boolean[] isDirs, 2682010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int[] positions, 2692010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan String[] ids, 2702010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan @SortDimension.SortDirection int direction) { 2712010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final int count = positions.length; 2722010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan for (int start = 1; start < count; start++) { 2732010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final int pivotPosition = positions[start]; 2742010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final long pivotValue = sortKey[start]; 2752010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final boolean pivotIsDir = isDirs[start]; 2762010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final String pivotId = ids[start]; 2772010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 2782010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int left = 0; 2792010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int right = start; 2802010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 2812010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan while (left < right) { 2822010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int mid = ((left + right) >>> 1); 2832010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 2842010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan // Directories always go in front. 2852010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int compare = 0; 2862010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final boolean rhsIsDir = isDirs[mid]; 2872010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan if (pivotIsDir && !rhsIsDir) { 2882010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = -1; 2892010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } else if (!pivotIsDir && rhsIsDir) { 2902010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = 1; 2912010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } else { 2922010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final long lhs = pivotValue; 2932010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan final long rhs = sortKey[mid]; 2942010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan switch (direction) { 2952010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortDimension.SORT_DIRECTION_ASCENDING: 2962010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = Long.compare(lhs, rhs); 2972010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 2982010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case SortDimension.SORT_DIRECTION_DESCENDING: 2992010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = -Long.compare(lhs, rhs); 3002010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 3012010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan default: 3022010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan throw new IllegalArgumentException( 3032010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan "Unknown sorting direction: " + direction); 3042010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 3052010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 3062010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 3072010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan // If numerical comparison yields a tie, use document ID as a tie breaker. This 3082010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan // will yield stable results even if incoming items are continually shuffling and 3092010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan // have identical numerical sort keys. One common example of this scenario is seen 3102010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan // when sorting a set of active downloads by mod time. 3112010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan if (compare == 0) { 3122010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan compare = pivotId.compareTo(ids[mid]); 3132010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 3142010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 3152010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan if (compare < 0) { 3162010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan right = mid; 3172010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } else { 3182010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan left = mid + 1; 3192010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 3202010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 3212010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 3222010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan int n = start - left; 3232010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan switch (n) { 3242010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case 2: 3252010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan positions[left + 2] = positions[left + 1]; 3262010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan sortKey[left + 2] = sortKey[left + 1]; 3272010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan isDirs[left + 2] = isDirs[left + 1]; 3282010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan ids[left + 2] = ids[left + 1]; 3292010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan case 1: 3302010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan positions[left + 1] = positions[left]; 3312010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan sortKey[left + 1] = sortKey[left]; 3322010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan isDirs[left + 1] = isDirs[left]; 3332010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan ids[left + 1] = ids[left]; 3342010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan break; 3352010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan default: 3362010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan System.arraycopy(positions, left, positions, left + 1, n); 3372010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan System.arraycopy(sortKey, left, sortKey, left + 1, n); 3382010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan System.arraycopy(isDirs, left, isDirs, left + 1, n); 3392010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan System.arraycopy(ids, left, ids, left + 1, n); 3402010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 3412010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan 3422010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan positions[left] = pivotPosition; 3432010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan sortKey[left] = pivotValue; 3442010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan isDirs[left] = pivotIsDir; 3452010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan ids[left] = pivotId; 3462010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 3472010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan } 3482010ff7828f2d68de0be24c975f5c48ec3151f96Garfield Tan} 349