1 // Copyright 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.
6 package org.chromium.chrome.browser.database;
8 import android.database.AbstractCursor;
9 import android.database.CursorWindow;
10 import android.util.Log;
12 import org.chromium.base.CalledByNative;
14 import java.sql.Types;
17 * This class exposes the query result from native side.
19 public class SQLiteCursor extends AbstractCursor {
20 private static final String TAG = "SQLiteCursor";
22 private long mNativeSQLiteCursor;
24 // The count of result rows.
25 private int mCount = -1;
27 private int[] mColumnTypes;
29 private final Object mColumnTypeLock = new Object();
30 private final Object mDestoryNativeLock = new Object();
32 // The belows are the locks for those methods that need wait for
33 // the callback result in native side.
34 private final Object mMoveLock = new Object();
35 private final Object mGetBlobLock = new Object();
37 private SQLiteCursor(long nativeSQLiteCursor) {
38 mNativeSQLiteCursor = nativeSQLiteCursor;
42 private static SQLiteCursor create(long nativeSQLiteCursor) {
43 return new SQLiteCursor(nativeSQLiteCursor);
47 public int getCount() {
48 synchronized (mMoveLock) {
50 mCount = nativeGetCount(mNativeSQLiteCursor);
56 public String[] getColumnNames() {
57 return nativeGetColumnNames(mNativeSQLiteCursor);
61 public String getString(int column) {
62 return nativeGetString(mNativeSQLiteCursor, column);
66 public short getShort(int column) {
67 return (short) nativeGetInt(mNativeSQLiteCursor, column);
71 public int getInt(int column) {
72 return nativeGetInt(mNativeSQLiteCursor, column);
76 public long getLong(int column) {
77 return nativeGetLong(mNativeSQLiteCursor, column);
81 public float getFloat(int column) {
82 return (float) nativeGetDouble(mNativeSQLiteCursor, column);
86 public double getDouble(int column) {
87 return nativeGetDouble(mNativeSQLiteCursor, column);
91 public boolean isNull(int column) {
92 return nativeIsNull(mNativeSQLiteCursor, column);
98 synchronized (mDestoryNativeLock) {
99 if (mNativeSQLiteCursor != 0) {
100 nativeDestroy(mNativeSQLiteCursor);
101 mNativeSQLiteCursor = 0;
107 public boolean onMove(int oldPosition, int newPosition) {
108 synchronized (mMoveLock) {
109 nativeMoveTo(mNativeSQLiteCursor, newPosition);
111 return super.onMove(oldPosition, newPosition);
115 public byte[] getBlob(int column) {
116 synchronized (mGetBlobLock) {
117 return nativeGetBlob(mNativeSQLiteCursor, column);
122 public boolean supportsUpdates() {
127 protected void finalize() {
130 Log.w(TAG, "Cursor hasn't been closed");
136 public void fillWindow(int position, CursorWindow window) {
137 if (position < 0 || position > getCount()) {
140 window.acquireReference();
145 window.setStartPosition(position);
146 int columnNum = getColumnCount();
147 window.setNumColumns(columnNum);
148 while (moveToNext() && window.allocRow()) {
149 for (int i = 0; i < columnNum; i++) {
150 boolean hasRoom = true;
151 switch (getColumnType(i)) {
153 hasRoom = fillRow(window, Double.valueOf(getDouble(i)), mPos, i);
156 hasRoom = fillRow(window, Long.valueOf(getLong(i)), mPos, i);
159 hasRoom = fillRow(window, getBlob(i), mPos, i);
161 case Types.LONGVARCHAR:
162 hasRoom = fillRow(window, getString(i), mPos, i);
165 hasRoom = fillRow(window, null, mPos, i);
168 // Ignore an unknown type.
176 } catch (IllegalStateException e) {
179 window.releaseReference();
184 * Fill row with the given value. If the value type is other than Long,
185 * String, byte[] or Double, the NULL will be filled.
187 * @return true if succeeded, false if window is full.
189 private boolean fillRow(CursorWindow window, Object value, int pos, int column) {
190 if (putValue(window, value, pos, column)) {
193 window.freeLastRow();
199 * Put the value in given window. If the value type is other than Long,
200 * String, byte[] or Double, the NULL will be filled.
202 * @return true if succeeded.
204 private boolean putValue(CursorWindow window, Object value, int pos, int column) {
206 return window.putNull(pos, column);
207 } else if (value instanceof Long) {
208 return window.putLong((Long) value, pos, column);
209 } else if (value instanceof String) {
210 return window.putString((String) value, pos, column);
211 } else if (value instanceof byte[] && ((byte[]) value).length > 0) {
212 return window.putBlob((byte[]) value, pos, column);
213 } else if (value instanceof Double) {
214 return window.putDouble((Double) value, pos, column);
216 return window.putNull(pos, column);
221 * @param index the column index.
222 * @return the column type from cache or native side.
224 private int getColumnType(int index) {
225 synchronized (mColumnTypeLock) {
226 if (mColumnTypes == null) {
227 int columnCount = getColumnCount();
228 mColumnTypes = new int[columnCount];
229 for (int i = 0; i < columnCount; i++) {
230 mColumnTypes[i] = nativeGetColumnType(mNativeSQLiteCursor, i);
234 return mColumnTypes[index];
237 private native void nativeDestroy(long nativeSQLiteCursor);
238 private native int nativeGetCount(long nativeSQLiteCursor);
239 private native String[] nativeGetColumnNames(long nativeSQLiteCursor);
240 private native int nativeGetColumnType(long nativeSQLiteCursor, int column);
241 private native String nativeGetString(long nativeSQLiteCursor, int column);
242 private native byte[] nativeGetBlob(long nativeSQLiteCursor, int column);
243 private native boolean nativeIsNull(long nativeSQLiteCursor, int column);
244 private native long nativeGetLong(long nativeSQLiteCursor, int column);
245 private native int nativeGetInt(long nativeSQLiteCursor, int column);
246 private native double nativeGetDouble(long nativeSQLiteCursor, int column);
247 private native int nativeMoveTo(long nativeSQLiteCursor, int newPosition);