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.music;
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 String getString(int column)
187 return mCursor.getString(column);
191 public short getShort(int column)
193 return mCursor.getShort(column);
197 public int getInt(int column)
199 return mCursor.getInt(column);
203 public long getLong(int column)
205 return mCursor.getLong(column);
209 public float getFloat(int column)
211 return mCursor.getFloat(column);
215 public double getDouble(int column)
217 return mCursor.getDouble(column);
221 public boolean isNull(int column)
223 return mCursor.isNull(column);
227 public byte[] getBlob(int column)
229 return mCursor.getBlob(column);
233 public String[] getColumnNames()
235 if (mCursor != null) {
236 return mCursor.getColumnNames();
238 // All of the cursors may be empty, but they can still return
240 int length = mCursors.length;
241 for (int i = 0 ; i < length ; i++) {
242 if (mCursors[i] != null) {
243 return mCursors[i].getColumnNames();
246 throw new IllegalStateException("No cursor that can return names");
251 public void deactivate()
253 int length = mCursors.length;
254 for (int i = 0 ; i < length ; i++) {
255 if (mCursors[i] == null) continue;
256 mCursors[i].deactivate();
261 public void close() {
262 int length = mCursors.length;
263 for (int i = 0 ; i < length ; i++) {
264 if (mCursors[i] == null) continue;
270 public void registerDataSetObserver(DataSetObserver observer) {
271 int length = mCursors.length;
272 for (int i = 0 ; i < length ; i++) {
273 if (mCursors[i] != null) {
274 mCursors[i].registerDataSetObserver(observer);
280 public void unregisterDataSetObserver(DataSetObserver observer) {
281 int length = mCursors.length;
282 for (int i = 0 ; i < length ; i++) {
283 if (mCursors[i] != null) {
284 mCursors[i].unregisterDataSetObserver(observer);
290 public boolean requery()
292 int length = mCursors.length;
293 for (int i = 0 ; i < length ; i++) {
294 if (mCursors[i] == null) continue;
296 if (mCursors[i].requery() == false) {