1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5 6package org.chromium.chrome.browser.database; 7 8import android.database.AbstractCursor; 9import android.database.CursorWindow; 10import android.util.Log; 11 12import org.chromium.base.CalledByNative; 13 14import java.sql.Types; 15 16/** 17 * This class exposes the query result from native side. 18 */ 19public class SQLiteCursor extends AbstractCursor { 20 private static final String TAG = "SQLiteCursor"; 21 // Used by JNI. 22 private int mNativeSQLiteCursor; 23 24 // The count of result rows. 25 private int mCount = -1; 26 27 private int[] mColumnTypes; 28 29 private final Object mColumnTypeLock = new Object(); 30 31 // The belows are the locks for those methods that need wait for 32 // the callback result in native side. 33 private final Object mMoveLock = new Object(); 34 private final Object mGetBlobLock = new Object(); 35 36 private SQLiteCursor(int nativeSQLiteCursor) { 37 mNativeSQLiteCursor = nativeSQLiteCursor; 38 } 39 40 @CalledByNative 41 private static SQLiteCursor create(int nativeSQLiteCursor) { 42 return new SQLiteCursor(nativeSQLiteCursor); 43 } 44 45 @Override 46 public int getCount() { 47 synchronized (mMoveLock) { 48 if (mCount == -1) 49 mCount = nativeGetCount(mNativeSQLiteCursor); 50 } 51 return mCount; 52 } 53 54 @Override 55 public String[] getColumnNames() { 56 return nativeGetColumnNames(mNativeSQLiteCursor); 57 } 58 59 @Override 60 public String getString(int column) { 61 return nativeGetString(mNativeSQLiteCursor, column); 62 } 63 64 @Override 65 public short getShort(int column) { 66 return (short) nativeGetInt(mNativeSQLiteCursor, column); 67 } 68 69 @Override 70 public int getInt(int column) { 71 return nativeGetInt(mNativeSQLiteCursor, column); 72 } 73 74 @Override 75 public long getLong(int column) { 76 return nativeGetLong(mNativeSQLiteCursor, column); 77 } 78 79 @Override 80 public float getFloat(int column) { 81 return (float)nativeGetDouble(mNativeSQLiteCursor, column); 82 } 83 84 @Override 85 public double getDouble(int column) { 86 return nativeGetDouble(mNativeSQLiteCursor, column); 87 } 88 89 @Override 90 public boolean isNull(int column) { 91 return nativeIsNull(mNativeSQLiteCursor, column); 92 } 93 94 @Override 95 public void close() { 96 super.close(); 97 nativeDestroy(mNativeSQLiteCursor); 98 mNativeSQLiteCursor = 0; 99 } 100 101 @Override 102 public boolean onMove(int oldPosition, int newPosition) { 103 synchronized (mMoveLock) { 104 nativeMoveTo(mNativeSQLiteCursor, newPosition); 105 } 106 return super.onMove(oldPosition, newPosition); 107 } 108 109 @Override 110 public byte[] getBlob(int column) { 111 synchronized (mGetBlobLock) { 112 return nativeGetBlob(mNativeSQLiteCursor, column); 113 } 114 } 115 116 @Deprecated 117 public boolean supportsUpdates() { 118 return false; 119 } 120 121 @Override 122 protected void finalize() { 123 super.finalize(); 124 if (!isClosed()) { 125 Log.w(TAG, "Cursor hasn't been closed"); 126 close(); 127 } 128 } 129 130 @Override 131 public void fillWindow(int position, CursorWindow window) { 132 if (position < 0 || position > getCount()) { 133 return; 134 } 135 window.acquireReference(); 136 try { 137 int oldpos = mPos; 138 mPos = position - 1; 139 window.clear(); 140 window.setStartPosition(position); 141 int columnNum = getColumnCount(); 142 window.setNumColumns(columnNum); 143 while (moveToNext() && window.allocRow()) { 144 for (int i = 0; i < columnNum; i++) { 145 boolean hasRoom = true; 146 switch (getColumnType(i)) { 147 case Types.DOUBLE: 148 hasRoom = fillRow(window, Double.valueOf(getDouble(i)), mPos, i); 149 break; 150 case Types.NUMERIC: 151 hasRoom = fillRow(window, Long.valueOf(getLong(i)), mPos, i); 152 break; 153 case Types.BLOB: 154 hasRoom = fillRow(window, getBlob(i), mPos, i); 155 break; 156 case Types.LONGVARCHAR: 157 hasRoom = fillRow(window, getString(i), mPos, i); 158 break; 159 case Types.NULL: 160 hasRoom = fillRow(window, null, mPos, i); 161 break; 162 } 163 if (!hasRoom) { 164 break; 165 } 166 } 167 } 168 mPos = oldpos; 169 } catch (IllegalStateException e) { 170 // simply ignore it 171 } finally { 172 window.releaseReference(); 173 } 174 } 175 176 /** 177 * Fill row with the given value. If the value type is other than Long, 178 * String, byte[] or Double, the NULL will be filled. 179 * 180 * @return true if succeeded, false if window is full. 181 */ 182 private boolean fillRow(CursorWindow window, Object value, int pos, int column) { 183 if (putValue(window, value, pos, column)) { 184 return true; 185 } else { 186 window.freeLastRow(); 187 return false; 188 } 189 } 190 191 /** 192 * Put the value in given window. If the value type is other than Long, 193 * String, byte[] or Double, the NULL will be filled. 194 * 195 * @return true if succeeded. 196 */ 197 private boolean putValue(CursorWindow window, Object value, int pos, int column) { 198 if (value == null) { 199 return window.putNull(pos, column); 200 } else if (value instanceof Long) { 201 return window.putLong((Long) value, pos, column); 202 } else if (value instanceof String) { 203 return window.putString((String) value, pos, column); 204 } else if (value instanceof byte[] && ((byte[]) value).length > 0) { 205 return window.putBlob((byte[]) value, pos, column); 206 } else if (value instanceof Double) { 207 return window.putDouble((Double) value, pos, column); 208 } else { 209 return window.putNull(pos, column); 210 } 211 } 212 213 /** 214 * @param index the column index. 215 * @return the column type from cache or native side. 216 */ 217 private int getColumnType(int index) { 218 synchronized (mColumnTypeLock) { 219 if (mColumnTypes == null) { 220 int columnCount = getColumnCount(); 221 mColumnTypes = new int[columnCount]; 222 for (int i = 0; i < columnCount; i++) { 223 mColumnTypes[i] = nativeGetColumnType(mNativeSQLiteCursor, i); 224 } 225 } 226 } 227 return mColumnTypes[index]; 228 } 229 230 private native void nativeDestroy(int nativeSQLiteCursor); 231 private native int nativeGetCount(int nativeSQLiteCursor); 232 private native String[] nativeGetColumnNames(int nativeSQLiteCursor); 233 private native int nativeGetColumnType(int nativeSQLiteCursor, int column); 234 private native String nativeGetString(int nativeSQLiteCursor, int column); 235 private native byte[] nativeGetBlob(int nativeSQLiteCursor, int column); 236 private native boolean nativeIsNull(int nativeSQLiteCursor, int column); 237 private native long nativeGetLong(int nativeSQLiteCursor, int column); 238 private native int nativeGetInt(int nativeSQLiteCursor, int column); 239 private native double nativeGetDouble(int nativeSQLiteCursor, int column); 240 private native int nativeMoveTo(int nativeSQLiteCursor, int newPosition); 241} 242