2012-10-25 17:14:28 -04:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 The K-9 Dog Walkers
|
|
|
|
* Copyright (C) 2006 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed 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.
|
|
|
|
*/
|
2012-10-17 23:15:40 -04:00
|
|
|
|
2012-10-25 17:14:28 -04:00
|
|
|
package com.fsck.k9.helper;
|
2012-10-17 23:15:40 -04:00
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
import java.util.Comparator;
|
|
|
|
|
2012-10-17 23:15:40 -04:00
|
|
|
import android.content.ContentResolver;
|
|
|
|
import android.database.CharArrayBuffer;
|
|
|
|
import android.database.ContentObserver;
|
|
|
|
import android.database.Cursor;
|
|
|
|
import android.database.DataSetObserver;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.Bundle;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class can be used to combine multiple {@link Cursor}s into one.
|
|
|
|
*/
|
|
|
|
public class MergeCursor implements Cursor {
|
|
|
|
/**
|
|
|
|
* List of the cursors combined in this object.
|
|
|
|
*/
|
2012-10-25 17:14:28 -04:00
|
|
|
protected final Cursor[] mCursors;
|
2012-10-17 23:15:40 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The currently active cursor.
|
|
|
|
*/
|
|
|
|
protected Cursor mActiveCursor;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The index of the currently active cursor in {@link #mCursors}.
|
|
|
|
*
|
|
|
|
* @see #mActiveCursor
|
|
|
|
*/
|
|
|
|
protected int mActiveCursorIndex;
|
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
/**
|
|
|
|
* The cursor's current position.
|
|
|
|
*/
|
2012-10-25 17:14:28 -04:00
|
|
|
protected int mPosition;
|
|
|
|
|
2012-10-17 23:15:40 -04:00
|
|
|
/**
|
2012-10-26 20:15:30 -04:00
|
|
|
* Used to cache the value of {@link #getCount()}.
|
2012-10-17 23:15:40 -04:00
|
|
|
*/
|
|
|
|
private int mCount = -1;
|
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
/**
|
|
|
|
* The comparator that is used to decide how the individual cursors are merged.
|
|
|
|
*/
|
|
|
|
private final Comparator<Cursor> mComparator;
|
|
|
|
|
2012-10-17 23:15:40 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param cursors
|
|
|
|
* The list of cursors this {@code MultiCursor} should combine.
|
2012-10-26 20:15:30 -04:00
|
|
|
* @param comparator
|
|
|
|
* A comparator that is used to decide in what order the individual cursors are merged.
|
2012-10-17 23:15:40 -04:00
|
|
|
*/
|
2012-10-26 20:15:30 -04:00
|
|
|
public MergeCursor(Cursor[] cursors, Comparator<Cursor> comparator) {
|
2012-10-25 17:14:28 -04:00
|
|
|
mCursors = cursors.clone();
|
2012-10-26 20:15:30 -04:00
|
|
|
mComparator = comparator;
|
|
|
|
|
|
|
|
resetCursors();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void resetCursors() {
|
|
|
|
mActiveCursorIndex = -1;
|
|
|
|
mActiveCursor = null;
|
|
|
|
mPosition = -1;
|
2012-10-25 17:14:28 -04:00
|
|
|
|
|
|
|
for (int i = 0, len = mCursors.length; i < len; i++) {
|
2012-10-26 20:15:30 -04:00
|
|
|
Cursor cursor = mCursors[i];
|
|
|
|
if (cursor != null) {
|
|
|
|
cursor.moveToPosition(-1);
|
|
|
|
|
|
|
|
if (mActiveCursor == null) {
|
|
|
|
mActiveCursorIndex = i;
|
|
|
|
mActiveCursor = mCursors[mActiveCursorIndex];
|
|
|
|
}
|
2012-10-25 17:14:28 -04:00
|
|
|
}
|
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void close() {
|
|
|
|
for (Cursor cursor : mCursors) {
|
2012-10-25 17:14:28 -04:00
|
|
|
if (cursor != null) {
|
|
|
|
cursor.close();
|
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
|
|
|
|
mActiveCursor.copyStringToBuffer(columnIndex, buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void deactivate() {
|
|
|
|
for (Cursor cursor : mCursors) {
|
2012-10-25 17:14:28 -04:00
|
|
|
if (cursor != null) {
|
|
|
|
cursor.deactivate();
|
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] getBlob(int columnIndex) {
|
|
|
|
return mActiveCursor.getBlob(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getColumnCount() {
|
|
|
|
return mActiveCursor.getColumnCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getColumnIndex(String columnName) {
|
|
|
|
return mActiveCursor.getColumnIndex(columnName);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
|
|
|
|
return mActiveCursor.getColumnIndexOrThrow(columnName);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getColumnName(int columnIndex) {
|
|
|
|
return mActiveCursor.getColumnName(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String[] getColumnNames() {
|
|
|
|
return mActiveCursor.getColumnNames();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getCount() {
|
|
|
|
// CursorLoaders seem to call getCount() a lot. So we're caching the aggregated count.
|
|
|
|
if (mCount == -1) {
|
|
|
|
int count = 0;
|
|
|
|
for (Cursor cursor : mCursors) {
|
2012-10-25 17:14:28 -04:00
|
|
|
if (cursor != null) {
|
|
|
|
count += cursor.getCount();
|
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
mCount = count;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public double getDouble(int columnIndex) {
|
|
|
|
return mActiveCursor.getDouble(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public float getFloat(int columnIndex) {
|
|
|
|
return mActiveCursor.getFloat(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getInt(int columnIndex) {
|
|
|
|
return mActiveCursor.getInt(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long getLong(int columnIndex) {
|
|
|
|
return mActiveCursor.getLong(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getPosition() {
|
2012-10-25 17:14:28 -04:00
|
|
|
return mPosition;
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public short getShort(int columnIndex) {
|
|
|
|
return mActiveCursor.getShort(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getString(int columnIndex) {
|
|
|
|
return mActiveCursor.getString(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getType(int columnIndex) {
|
|
|
|
return mActiveCursor.getType(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean getWantsAllOnMoveCalls() {
|
|
|
|
return mActiveCursor.getWantsAllOnMoveCalls();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isAfterLast() {
|
2012-10-25 17:14:28 -04:00
|
|
|
int count = getCount();
|
|
|
|
if (count == 0) {
|
|
|
|
return true;
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
2012-10-25 17:14:28 -04:00
|
|
|
return (mPosition == count);
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isBeforeFirst() {
|
2012-10-25 17:14:28 -04:00
|
|
|
if (getCount() == 0) {
|
|
|
|
return true;
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
2012-10-25 17:14:28 -04:00
|
|
|
return (mPosition == -1);
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isClosed() {
|
|
|
|
return mActiveCursor.isClosed();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isFirst() {
|
2012-10-25 17:14:28 -04:00
|
|
|
if (getCount() == 0) {
|
|
|
|
return false;
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
2012-10-25 17:14:28 -04:00
|
|
|
return (mPosition == 0);
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isLast() {
|
2012-10-25 17:14:28 -04:00
|
|
|
int count = getCount();
|
|
|
|
if (count == 0) {
|
|
|
|
return false;
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
2012-10-25 17:14:28 -04:00
|
|
|
return (mPosition == (count - 1));
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isNull(int columnIndex) {
|
|
|
|
return mActiveCursor.isNull(columnIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean move(int offset) {
|
2012-10-25 17:14:28 -04:00
|
|
|
return moveToPosition(mPosition + offset);
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean moveToFirst() {
|
2012-10-25 17:14:28 -04:00
|
|
|
return moveToPosition(0);
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean moveToLast() {
|
2012-10-25 17:14:28 -04:00
|
|
|
return moveToPosition(getCount() - 1);
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean moveToNext() {
|
2012-10-26 20:15:30 -04:00
|
|
|
int count = getCount();
|
|
|
|
if (mPosition == count) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mPosition == count - 1) {
|
|
|
|
mActiveCursor.moveToNext();
|
|
|
|
mPosition++;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int smallest = -1;
|
|
|
|
for (int i = 0, len = mCursors.length; i < len; i++) {
|
2012-10-30 20:06:26 -04:00
|
|
|
if (mCursors[i] == null || mCursors[i].getCount() == 0 || mCursors[i].isLast()) {
|
2012-10-26 20:15:30 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (smallest == -1) {
|
|
|
|
smallest = i;
|
|
|
|
mCursors[smallest].moveToNext();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Cursor left = mCursors[smallest];
|
|
|
|
Cursor right = mCursors[i];
|
|
|
|
|
|
|
|
right.moveToNext();
|
|
|
|
|
|
|
|
int result = mComparator.compare(left, right);
|
|
|
|
if (result > 0) {
|
|
|
|
smallest = i;
|
|
|
|
left.moveToPrevious();
|
|
|
|
} else {
|
|
|
|
right.moveToPrevious();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mPosition++;
|
|
|
|
if (smallest != -1) {
|
|
|
|
mActiveCursorIndex = smallest;
|
|
|
|
mActiveCursor = mCursors[mActiveCursorIndex];
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean moveToPosition(int position) {
|
2012-10-25 17:14:28 -04:00
|
|
|
// Make sure position isn't past the end of the cursor
|
|
|
|
final int count = getCount();
|
|
|
|
if (position >= count) {
|
|
|
|
mPosition = count;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure position isn't before the beginning of the cursor
|
|
|
|
if (position < 0) {
|
|
|
|
mPosition = -1;
|
|
|
|
return false;
|
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
|
2012-10-25 17:14:28 -04:00
|
|
|
// Check for no-op moves, and skip the rest of the work for them
|
|
|
|
if (position == mPosition) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
if (position > mPosition) {
|
|
|
|
for (int i = 0, end = position - mPosition; i < end; i++) {
|
|
|
|
if (!moveToNext()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = 0, end = mPosition - position; i < end; i++) {
|
|
|
|
if (!moveToPrevious()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean moveToPrevious() {
|
|
|
|
if (mPosition < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mActiveCursor.moveToPrevious();
|
|
|
|
|
|
|
|
if (mPosition == 0) {
|
|
|
|
mPosition = -1;
|
|
|
|
return false;
|
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
int greatest = -1;
|
2012-10-25 17:14:28 -04:00
|
|
|
for (int i = 0, len = mCursors.length; i < len; i++) {
|
2012-10-26 20:15:30 -04:00
|
|
|
if (mCursors[i] == null || mCursors[i].isBeforeFirst()) {
|
2012-10-25 17:14:28 -04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
if (greatest == -1) {
|
|
|
|
greatest = i;
|
|
|
|
continue;
|
2012-10-25 17:14:28 -04:00
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
Cursor left = mCursors[greatest];
|
|
|
|
Cursor right = mCursors[i];
|
2012-10-25 17:14:28 -04:00
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
int result = mComparator.compare(left, right);
|
|
|
|
if (result <= 0) {
|
|
|
|
greatest = i;
|
|
|
|
}
|
2012-10-25 17:14:28 -04:00
|
|
|
}
|
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
mPosition--;
|
|
|
|
if (greatest != -1) {
|
|
|
|
mActiveCursorIndex = greatest;
|
|
|
|
mActiveCursor = mCursors[mActiveCursorIndex];
|
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
|
2012-10-26 20:15:30 -04:00
|
|
|
return true;
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void registerContentObserver(ContentObserver observer) {
|
|
|
|
for (Cursor cursor : mCursors) {
|
|
|
|
cursor.registerContentObserver(observer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void registerDataSetObserver(DataSetObserver observer) {
|
|
|
|
for (Cursor cursor : mCursors) {
|
|
|
|
cursor.registerDataSetObserver(observer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Deprecated
|
|
|
|
@Override
|
|
|
|
public boolean requery() {
|
|
|
|
boolean success = true;
|
|
|
|
for (Cursor cursor : mCursors) {
|
|
|
|
success &= cursor.requery();
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setNotificationUri(ContentResolver cr, Uri uri) {
|
|
|
|
for (Cursor cursor : mCursors) {
|
|
|
|
cursor.setNotificationUri(cr, uri);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void unregisterContentObserver(ContentObserver observer) {
|
|
|
|
for (Cursor cursor : mCursors) {
|
|
|
|
cursor.unregisterContentObserver(observer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void unregisterDataSetObserver(DataSetObserver observer) {
|
|
|
|
for (Cursor cursor : mCursors) {
|
|
|
|
cursor.unregisterDataSetObserver(observer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Bundle getExtras() {
|
|
|
|
throw new RuntimeException("Not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Bundle respond(Bundle extras) {
|
|
|
|
throw new RuntimeException("Not implemented");
|
|
|
|
}
|
2014-01-26 09:13:58 -05:00
|
|
|
|
2014-01-26 11:01:07 -05:00
|
|
|
@Override
|
2014-01-26 09:13:58 -05:00
|
|
|
public Uri getNotificationUri() {
|
|
|
|
return null;
|
|
|
|
}
|
2012-10-17 23:15:40 -04:00
|
|
|
}
|