2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.internal.database;
19 import android.database.AbstractCursor;
20 import android.database.Cursor;
21 import android.database.DataSetObserver;
22 import android.util.Log;
25 * A variant of MergeCursor that sorts the cursors being merged. If decent
26 * performance is ever obtained, it can be put back under android.database.
28 public class SortCursor extends AbstractCursor
30 private static final String TAG = "SortCursor";
31 private Cursor mCursor; // updated in onMove
32 private Cursor[] mCursors;
33 private int [] mSortColumns;
34 private final int ROWCACHESIZE = 64;
35 private int mRowNumCache[] = new int[ROWCACHESIZE];
36 private int mCursorCache[] = new int[ROWCACHESIZE];
37 private int mCurRowNumCache[][];
38 private int mLastCacheHit = -1;
40 private DataSetObserver mObserver = new DataSetObserver() {
43 public void onChanged() {
44 // Reset our position so the optimizations in move-related code
45 // don't screw us over
50 public void onInvalidated() {
55 public SortCursor(Cursor[] cursors, String sortcolumn)
59 int length = mCursors.length;
60 mSortColumns = new int[length];
61 for (int i = 0 ; i < length ; i++) {
62 if (mCursors[i] == null) continue;
64 // Register ourself as a data set observer
65 mCursors[i].registerDataSetObserver(mObserver);
67 mCursors[i].moveToFirst();
69 // We don't catch the exception
70 mSortColumns[i] = mCursors[i].getColumnIndexOrThrow(sortcolumn);
74 for (int j = 0 ; j < length; j++) {
75 if (mCursors[j] == null || mCursors[j].isAfterLast())
77 String current = mCursors[j].getString(mSortColumns[j]);
78 if (mCursor == null || current.compareToIgnoreCase(smallest) < 0) {
80 mCursor = mCursors[j];
84 for (int i = mRowNumCache.length - 1; i >= 0; i--) {
87 mCurRowNumCache = new int[ROWCACHESIZE][length];
94 int length = mCursors.length;
95 for (int i = 0 ; i < length ; i++) {
96 if (mCursors[i] != null) {
97 count += mCursors[i].getCount();
104 public boolean onMove(int oldPosition, int newPosition)
106 if (oldPosition == newPosition)
109 /* Find the right cursor
110 * Because the client of this cursor (the listadapter/view) tends
111 * to jump around in the cursor somewhat, a simple cache strategy
112 * is used to avoid having to search all cursors from the start.
113 * TODO: investigate strategies for optimizing random access and
114 * reverse-order access.
117 int cache_entry = newPosition % ROWCACHESIZE;
119 if (mRowNumCache[cache_entry] == newPosition) {
120 int which = mCursorCache[cache_entry];
121 mCursor = mCursors[which];
122 if (mCursor == null) {
123 Log.w(TAG, "onMove: cache results in a null cursor.");
126 mCursor.moveToPosition(mCurRowNumCache[cache_entry][which]);
127 mLastCacheHit = cache_entry;
132 int length = mCursors.length;
134 if (mLastCacheHit >= 0) {
135 for (int i = 0; i < length; i++) {
136 if (mCursors[i] == null) continue;
137 mCursors[i].moveToPosition(mCurRowNumCache[mLastCacheHit][i]);
141 if (newPosition < oldPosition || oldPosition == -1) {
142 for (int i = 0 ; i < length; i++) {
143 if (mCursors[i] == null) continue;
144 mCursors[i].moveToFirst();
148 if (oldPosition < 0) {
152 // search forward to the new position
153 int smallestIdx = -1;
154 for(int i = oldPosition; i <= newPosition; i++) {
155 String smallest = "";
157 for (int j = 0 ; j < length; j++) {
158 if (mCursors[j] == null || mCursors[j].isAfterLast()) {
161 String current = mCursors[j].getString(mSortColumns[j]);
162 if (smallestIdx < 0 || current.compareToIgnoreCase(smallest) < 0) {
167 if (i == newPosition) break;
168 if (mCursors[smallestIdx] != null) {
169 mCursors[smallestIdx].moveToNext();
172 mCursor = mCursors[smallestIdx];
173 mRowNumCache[cache_entry] = newPosition;
174 mCursorCache[cache_entry] = smallestIdx;
175 for (int i = 0; i < length; i++) {
176 if (mCursors[i] != null) {
177 mCurRowNumCache[cache_entry][i] = mCursors[i].getPosition();
185 public boolean deleteRow()
187 return mCursor.deleteRow();
191 public boolean commitUpdates() {
192 int length = mCursors.length;
193 for (int i = 0 ; i < length ; i++) {
194 if (mCursors[i] != null) {
195 mCursors[i].commitUpdates();
203 public String getString(int column)
205 return mCursor.getString(column);
209 public short getShort(int column)
211 return mCursor.getShort(column);
215 public int getInt(int column)
217 return mCursor.getInt(column);
221 public long getLong(int column)
223 return mCursor.getLong(column);
227 public float getFloat(int column)
229 return mCursor.getFloat(column);
233 public double getDouble(int column)
235 return mCursor.getDouble(column);
239 public boolean isNull(int column)
241 return mCursor.isNull(column);
245 public byte[] getBlob(int column)
247 return mCursor.getBlob(column);
251 public String[] getColumnNames()
253 if (mCursor != null) {
254 return mCursor.getColumnNames();
256 // All of the cursors may be empty, but they can still return
258 int length = mCursors.length;
259 for (int i = 0 ; i < length ; i++) {
260 if (mCursors[i] != null) {
261 return mCursors[i].getColumnNames();
264 throw new IllegalStateException("No cursor that can return names");
269 public void deactivate()
271 int length = mCursors.length;
272 for (int i = 0 ; i < length ; i++) {
273 if (mCursors[i] == null) continue;
274 mCursors[i].deactivate();
279 public void close() {
280 int length = mCursors.length;
281 for (int i = 0 ; i < length ; i++) {
282 if (mCursors[i] == null) continue;
288 public void registerDataSetObserver(DataSetObserver observer) {
289 int length = mCursors.length;
290 for (int i = 0 ; i < length ; i++) {
291 if (mCursors[i] != null) {
292 mCursors[i].registerDataSetObserver(observer);
298 public void unregisterDataSetObserver(DataSetObserver observer) {
299 int length = mCursors.length;
300 for (int i = 0 ; i < length ; i++) {
301 if (mCursors[i] != null) {
302 mCursors[i].unregisterDataSetObserver(observer);
308 public boolean requery()
310 int length = mCursors.length;
311 for (int i = 0 ; i < length ; i++) {
312 if (mCursors[i] == null) continue;
314 if (mCursors[i].requery() == false) {