root/chrome/android/java/src/org/chromium/chrome/browser/database/SQLiteCursor.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. create
  2. getCount
  3. getColumnNames
  4. getString
  5. getShort
  6. getInt
  7. getLong
  8. getFloat
  9. getDouble
  10. isNull
  11. close
  12. onMove
  13. getBlob
  14. supportsUpdates
  15. finalize
  16. fillWindow
  17. fillRow
  18. putValue
  19. getColumnType
  20. nativeDestroy
  21. nativeGetCount
  22. nativeGetColumnNames
  23. nativeGetColumnType
  24. nativeGetString
  25. nativeGetBlob
  26. nativeIsNull
  27. nativeGetLong
  28. nativeGetInt
  29. nativeGetDouble
  30. nativeMoveTo

// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.


package org.chromium.chrome.browser.database;

import android.database.AbstractCursor;
import android.database.CursorWindow;
import android.util.Log;

import org.chromium.base.CalledByNative;

import java.sql.Types;

/**
 * This class exposes the query result from native side.
 */
public class SQLiteCursor extends AbstractCursor {
    private static final String TAG = "SQLiteCursor";
    // Used by JNI.
    private long mNativeSQLiteCursor;

    // The count of result rows.
    private int mCount = -1;

    private int[] mColumnTypes;

    private final Object mColumnTypeLock = new Object();
    private final Object mDestoryNativeLock = new Object();

    // The belows are the locks for those methods that need wait for
    // the callback result in native side.
    private final Object mMoveLock = new Object();
    private final Object mGetBlobLock = new Object();

    private SQLiteCursor(long nativeSQLiteCursor) {
        mNativeSQLiteCursor = nativeSQLiteCursor;
    }

    @CalledByNative
    private static SQLiteCursor create(long nativeSQLiteCursor) {
        return new SQLiteCursor(nativeSQLiteCursor);
    }

    @Override
    public int getCount() {
        synchronized (mMoveLock) {
            if (mCount == -1)
                mCount = nativeGetCount(mNativeSQLiteCursor);
        }
        return mCount;
    }

    @Override
    public String[] getColumnNames() {
        return nativeGetColumnNames(mNativeSQLiteCursor);
    }

    @Override
    public String getString(int column) {
        return nativeGetString(mNativeSQLiteCursor, column);
    }

    @Override
    public short getShort(int column) {
        return (short) nativeGetInt(mNativeSQLiteCursor, column);
    }

    @Override
    public int getInt(int column) {
        return nativeGetInt(mNativeSQLiteCursor, column);
    }

    @Override
    public long getLong(int column) {
        return nativeGetLong(mNativeSQLiteCursor, column);
    }

    @Override
    public float getFloat(int column) {
        return (float) nativeGetDouble(mNativeSQLiteCursor, column);
    }

    @Override
    public double getDouble(int column) {
        return nativeGetDouble(mNativeSQLiteCursor, column);
    }

    @Override
    public boolean isNull(int column) {
        return nativeIsNull(mNativeSQLiteCursor, column);
    }

    @Override
    public void close() {
        super.close();
        synchronized (mDestoryNativeLock) {
            if (mNativeSQLiteCursor != 0) {
                nativeDestroy(mNativeSQLiteCursor);
                mNativeSQLiteCursor = 0;
            }
        }
    }

    @Override
    public boolean onMove(int oldPosition, int newPosition) {
        synchronized (mMoveLock) {
            nativeMoveTo(mNativeSQLiteCursor, newPosition);
        }
        return super.onMove(oldPosition, newPosition);
    }

    @Override
    public byte[] getBlob(int column) {
        synchronized (mGetBlobLock) {
            return nativeGetBlob(mNativeSQLiteCursor, column);
        }
    }

    @Deprecated
    public boolean supportsUpdates() {
        return false;
    }

    @Override
    protected void finalize() {
        super.finalize();
        if (!isClosed()) {
            Log.w(TAG, "Cursor hasn't been closed");
            close();
        }
    }

    @Override
    public void fillWindow(int position, CursorWindow window) {
        if (position < 0 || position > getCount()) {
            return;
        }
        window.acquireReference();
        try {
            int oldpos = mPos;
            mPos = position - 1;
            window.clear();
            window.setStartPosition(position);
            int columnNum = getColumnCount();
            window.setNumColumns(columnNum);
            while (moveToNext() && window.allocRow()) {
                for (int i = 0; i < columnNum; i++) {
                    boolean hasRoom = true;
                    switch (getColumnType(i)) {
                        case Types.DOUBLE:
                            hasRoom = fillRow(window, Double.valueOf(getDouble(i)), mPos, i);
                            break;
                        case Types.NUMERIC:
                            hasRoom = fillRow(window, Long.valueOf(getLong(i)), mPos, i);
                            break;
                        case Types.BLOB:
                            hasRoom = fillRow(window, getBlob(i), mPos, i);
                            break;
                        case Types.LONGVARCHAR:
                            hasRoom = fillRow(window, getString(i), mPos, i);
                            break;
                        case Types.NULL:
                            hasRoom = fillRow(window, null, mPos, i);
                            break;
                    }
                    if (!hasRoom) {
                        break;
                    }
                }
            }
            mPos = oldpos;
        } catch (IllegalStateException e) {
            // simply ignore it
        } finally {
            window.releaseReference();
        }
    }

    /**
     * Fill row with the given value. If the value type is other than Long,
     * String, byte[] or Double, the NULL will be filled.
     *
     * @return true if succeeded, false if window is full.
     */
    private boolean fillRow(CursorWindow window, Object value, int pos, int column) {
        if (putValue(window, value, pos, column)) {
            return true;
        } else {
            window.freeLastRow();
            return false;
        }
    }

    /**
     * Put the value in given window. If the value type is other than Long,
     * String, byte[] or Double, the NULL will be filled.
     *
     * @return true if succeeded.
     */
    private boolean putValue(CursorWindow window, Object value, int pos, int column) {
        if (value == null) {
            return window.putNull(pos, column);
        } else if (value instanceof Long) {
            return window.putLong((Long) value, pos, column);
        } else if (value instanceof String) {
            return window.putString((String) value, pos, column);
        } else if (value instanceof byte[] && ((byte[]) value).length > 0) {
            return window.putBlob((byte[]) value, pos, column);
        } else if (value instanceof Double) {
            return window.putDouble((Double) value, pos, column);
        } else {
            return window.putNull(pos, column);
        }
    }

    /**
     * @param index the column index.
     * @return the column type from cache or native side.
     */
    private int getColumnType(int index) {
        synchronized (mColumnTypeLock) {
            if (mColumnTypes == null) {
                int columnCount = getColumnCount();
                mColumnTypes = new int[columnCount];
                for (int i = 0; i < columnCount; i++) {
                    mColumnTypes[i] = nativeGetColumnType(mNativeSQLiteCursor, i);
                }
            }
        }
        return mColumnTypes[index];
    }

    private native void nativeDestroy(long nativeSQLiteCursor);
    private native int nativeGetCount(long nativeSQLiteCursor);
    private native String[] nativeGetColumnNames(long nativeSQLiteCursor);
    private native int nativeGetColumnType(long nativeSQLiteCursor, int column);
    private native String nativeGetString(long nativeSQLiteCursor, int column);
    private native byte[] nativeGetBlob(long nativeSQLiteCursor, int column);
    private native boolean nativeIsNull(long nativeSQLiteCursor, int column);
    private native long nativeGetLong(long nativeSQLiteCursor, int column);
    private native int nativeGetInt(long nativeSQLiteCursor, int column);
    private native double nativeGetDouble(long nativeSQLiteCursor, int column);
    private native int nativeMoveTo(long nativeSQLiteCursor, int newPosition);
}

/* [<][>][^][v][top][bottom][index][help] */