k-9/k9mail/src/main/java/com/fsck/k9/helper/MergeCursor.java

467 lines
11 KiB
Java

/*
* 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.
*/
package com.fsck.k9.helper;
import java.util.Comparator;
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.
*/
protected final Cursor[] mCursors;
/**
* The currently active cursor.
*/
protected Cursor mActiveCursor;
/**
* The index of the currently active cursor in {@link #mCursors}.
*
* @see #mActiveCursor
*/
protected int mActiveCursorIndex;
/**
* The cursor's current position.
*/
protected int mPosition;
/**
* Used to cache the value of {@link #getCount()}.
*/
private int mCount = -1;
/**
* The comparator that is used to decide how the individual cursors are merged.
*/
private final Comparator<Cursor> mComparator;
/**
* Constructor
*
* @param cursors
* The list of cursors this {@code MultiCursor} should combine.
* @param comparator
* A comparator that is used to decide in what order the individual cursors are merged.
*/
public MergeCursor(Cursor[] cursors, Comparator<Cursor> comparator) {
mCursors = cursors.clone();
mComparator = comparator;
resetCursors();
}
private void resetCursors() {
mActiveCursorIndex = -1;
mActiveCursor = null;
mPosition = -1;
for (int i = 0, len = mCursors.length; i < len; i++) {
Cursor cursor = mCursors[i];
if (cursor != null) {
cursor.moveToPosition(-1);
if (mActiveCursor == null) {
mActiveCursorIndex = i;
mActiveCursor = mCursors[mActiveCursorIndex];
}
}
}
}
@Override
public void close() {
for (Cursor cursor : mCursors) {
if (cursor != null) {
cursor.close();
}
}
}
@Override
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
mActiveCursor.copyStringToBuffer(columnIndex, buffer);
}
@Override
public void deactivate() {
for (Cursor cursor : mCursors) {
if (cursor != null) {
cursor.deactivate();
}
}
}
@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) {
if (cursor != null) {
count += cursor.getCount();
}
}
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() {
return mPosition;
}
@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() {
int count = getCount();
if (count == 0) {
return true;
}
return (mPosition == count);
}
@Override
public boolean isBeforeFirst() {
if (getCount() == 0) {
return true;
}
return (mPosition == -1);
}
@Override
public boolean isClosed() {
return mActiveCursor.isClosed();
}
@Override
public boolean isFirst() {
if (getCount() == 0) {
return false;
}
return (mPosition == 0);
}
@Override
public boolean isLast() {
int count = getCount();
if (count == 0) {
return false;
}
return (mPosition == (count - 1));
}
@Override
public boolean isNull(int columnIndex) {
return mActiveCursor.isNull(columnIndex);
}
@Override
public boolean move(int offset) {
return moveToPosition(mPosition + offset);
}
@Override
public boolean moveToFirst() {
return moveToPosition(0);
}
@Override
public boolean moveToLast() {
return moveToPosition(getCount() - 1);
}
@Override
public boolean moveToNext() {
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++) {
if (mCursors[i] == null || mCursors[i].getCount() == 0 || mCursors[i].isLast()) {
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;
}
@Override
public boolean moveToPosition(int position) {
// 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;
}
// Check for no-op moves, and skip the rest of the work for them
if (position == mPosition) {
return true;
}
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;
}
int greatest = -1;
for (int i = 0, len = mCursors.length; i < len; i++) {
if (mCursors[i] == null || mCursors[i].isBeforeFirst()) {
continue;
}
if (greatest == -1) {
greatest = i;
continue;
}
Cursor left = mCursors[greatest];
Cursor right = mCursors[i];
int result = mComparator.compare(left, right);
if (result <= 0) {
greatest = i;
}
}
mPosition--;
if (greatest != -1) {
mActiveCursorIndex = greatest;
mActiveCursor = mCursors[mActiveCursorIndex];
}
return true;
}
@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");
}
@Override
public Uri getNotificationUri() {
return null;
}
}