mobile/android/base/sqlite/MatrixBlobCursor.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 /*
michael@0 3 * Copyright (C) 2007 The Android Open Source Project
michael@0 4 *
michael@0 5 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 6 * you may not use this file except in compliance with the License.
michael@0 7 * You may obtain a copy of the License at
michael@0 8 *
michael@0 9 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 10 *
michael@0 11 * Unless required by applicable law or agreed to in writing, software
michael@0 12 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 14 * See the License for the specific language governing permissions and
michael@0 15 * limitations under the License.
michael@0 16 */
michael@0 17
michael@0 18 package org.mozilla.gecko.sqlite;
michael@0 19
michael@0 20 import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
michael@0 21
michael@0 22 import android.database.AbstractCursor;
michael@0 23 import android.database.CursorIndexOutOfBoundsException;
michael@0 24
michael@0 25 import java.nio.ByteBuffer;
michael@0 26 import java.util.ArrayList;
michael@0 27
michael@0 28 /*
michael@0 29 * Android's AbstractCursor throws on getBlob()
michael@0 30 * and MatrixCursor forgot to override it. This was fixed
michael@0 31 * at some point but old devices are still SOL.
michael@0 32 * Oh, and everything in MatrixCursor is private instead of
michael@0 33 * protected, so we need to entirely duplicate it here,
michael@0 34 * instad of just being able to add the missing method.
michael@0 35 */
michael@0 36 /**
michael@0 37 * A mutable cursor implementation backed by an array of {@code Object}s. Use
michael@0 38 * {@link #newRow()} to add rows. Automatically expands internal capacity
michael@0 39 * as needed.
michael@0 40 */
michael@0 41 public class MatrixBlobCursor extends AbstractCursor {
michael@0 42
michael@0 43 private final String[] columnNames;
michael@0 44 private Object[] data;
michael@0 45 private int rowCount = 0;
michael@0 46 private final int columnCount;
michael@0 47
michael@0 48 /**
michael@0 49 * Constructs a new cursor with the given initial capacity.
michael@0 50 *
michael@0 51 * @param columnNames names of the columns, the ordering of which
michael@0 52 * determines column ordering elsewhere in this cursor
michael@0 53 * @param initialCapacity in rows
michael@0 54 */
michael@0 55 @WrapElementForJNI
michael@0 56 public MatrixBlobCursor(String[] columnNames, int initialCapacity) {
michael@0 57 this.columnNames = columnNames;
michael@0 58 this.columnCount = columnNames.length;
michael@0 59
michael@0 60 if (initialCapacity < 1) {
michael@0 61 initialCapacity = 1;
michael@0 62 }
michael@0 63
michael@0 64 this.data = new Object[columnCount * initialCapacity];
michael@0 65 }
michael@0 66
michael@0 67 /**
michael@0 68 * Constructs a new cursor.
michael@0 69 *
michael@0 70 * @param columnNames names of the columns, the ordering of which
michael@0 71 * determines column ordering elsewhere in this cursor
michael@0 72 */
michael@0 73 @WrapElementForJNI
michael@0 74 public MatrixBlobCursor(String[] columnNames) {
michael@0 75 this(columnNames, 16);
michael@0 76 }
michael@0 77
michael@0 78 /**
michael@0 79 * Gets value at the given column for the current row.
michael@0 80 */
michael@0 81 protected Object get(int column) {
michael@0 82 if (column < 0 || column >= columnCount) {
michael@0 83 throw new CursorIndexOutOfBoundsException("Requested column: "
michael@0 84 + column + ", # of columns: " + columnCount);
michael@0 85 }
michael@0 86 if (mPos < 0) {
michael@0 87 throw new CursorIndexOutOfBoundsException("Before first row.");
michael@0 88 }
michael@0 89 if (mPos >= rowCount) {
michael@0 90 throw new CursorIndexOutOfBoundsException("After last row.");
michael@0 91 }
michael@0 92 return data[mPos * columnCount + column];
michael@0 93 }
michael@0 94
michael@0 95 /**
michael@0 96 * Adds a new row to the end and returns a builder for that row. Not safe
michael@0 97 * for concurrent use.
michael@0 98 *
michael@0 99 * @return builder which can be used to set the column values for the new
michael@0 100 * row
michael@0 101 */
michael@0 102 public RowBuilder newRow() {
michael@0 103 rowCount++;
michael@0 104 int endIndex = rowCount * columnCount;
michael@0 105 ensureCapacity(endIndex);
michael@0 106 int start = endIndex - columnCount;
michael@0 107 return new RowBuilder(start, endIndex);
michael@0 108 }
michael@0 109
michael@0 110 /**
michael@0 111 * Adds a new row to the end with the given column values. Not safe
michael@0 112 * for concurrent use.
michael@0 113 *
michael@0 114 * @throws IllegalArgumentException if {@code columnValues.length !=
michael@0 115 * columnNames.length}
michael@0 116 * @param columnValues in the same order as the the column names specified
michael@0 117 * at cursor construction time
michael@0 118 */
michael@0 119 @WrapElementForJNI
michael@0 120 public void addRow(Object[] columnValues) {
michael@0 121 if (columnValues.length != columnCount) {
michael@0 122 throw new IllegalArgumentException("columnNames.length = "
michael@0 123 + columnCount + ", columnValues.length = "
michael@0 124 + columnValues.length);
michael@0 125 }
michael@0 126
michael@0 127 int start = rowCount++ * columnCount;
michael@0 128 ensureCapacity(start + columnCount);
michael@0 129 System.arraycopy(columnValues, 0, data, start, columnCount);
michael@0 130 }
michael@0 131
michael@0 132 /**
michael@0 133 * Adds a new row to the end with the given column values. Not safe
michael@0 134 * for concurrent use.
michael@0 135 *
michael@0 136 * @throws IllegalArgumentException if {@code columnValues.size() !=
michael@0 137 * columnNames.length}
michael@0 138 * @param columnValues in the same order as the the column names specified
michael@0 139 * at cursor construction time
michael@0 140 */
michael@0 141 @WrapElementForJNI
michael@0 142 public void addRow(Iterable<?> columnValues) {
michael@0 143 int start = rowCount * columnCount;
michael@0 144 int end = start + columnCount;
michael@0 145 ensureCapacity(end);
michael@0 146
michael@0 147 if (columnValues instanceof ArrayList<?>) {
michael@0 148 addRow((ArrayList<?>) columnValues, start);
michael@0 149 return;
michael@0 150 }
michael@0 151
michael@0 152 int current = start;
michael@0 153 Object[] localData = data;
michael@0 154 for (Object columnValue : columnValues) {
michael@0 155 if (current == end) {
michael@0 156 // TODO: null out row?
michael@0 157 throw new IllegalArgumentException(
michael@0 158 "columnValues.size() > columnNames.length");
michael@0 159 }
michael@0 160 localData[current++] = columnValue;
michael@0 161 }
michael@0 162
michael@0 163 if (current != end) {
michael@0 164 // TODO: null out row?
michael@0 165 throw new IllegalArgumentException(
michael@0 166 "columnValues.size() < columnNames.length");
michael@0 167 }
michael@0 168
michael@0 169 // Increase row count here in case we encounter an exception.
michael@0 170 rowCount++;
michael@0 171 }
michael@0 172
michael@0 173 /** Optimization for {@link ArrayList}. */
michael@0 174 @WrapElementForJNI
michael@0 175 private void addRow(ArrayList<?> columnValues, int start) {
michael@0 176 int size = columnValues.size();
michael@0 177 if (size != columnCount) {
michael@0 178 throw new IllegalArgumentException("columnNames.length = "
michael@0 179 + columnCount + ", columnValues.size() = " + size);
michael@0 180 }
michael@0 181
michael@0 182 rowCount++;
michael@0 183 Object[] localData = data;
michael@0 184 for (int i = 0; i < size; i++) {
michael@0 185 localData[start + i] = columnValues.get(i);
michael@0 186 }
michael@0 187 }
michael@0 188
michael@0 189 /** Ensures that this cursor has enough capacity. */
michael@0 190 private void ensureCapacity(int size) {
michael@0 191 if (size > data.length) {
michael@0 192 Object[] oldData = this.data;
michael@0 193 int newSize = data.length * 2;
michael@0 194 if (newSize < size) {
michael@0 195 newSize = size;
michael@0 196 }
michael@0 197 this.data = new Object[newSize];
michael@0 198 System.arraycopy(oldData, 0, this.data, 0, oldData.length);
michael@0 199 }
michael@0 200 }
michael@0 201
michael@0 202 /**
michael@0 203 * Builds a row, starting from the left-most column and adding one column
michael@0 204 * value at a time. Follows the same ordering as the column names specified
michael@0 205 * at cursor construction time.
michael@0 206 */
michael@0 207 public class RowBuilder {
michael@0 208
michael@0 209 private int index;
michael@0 210 private final int endIndex;
michael@0 211
michael@0 212 RowBuilder(int index, int endIndex) {
michael@0 213 this.index = index;
michael@0 214 this.endIndex = endIndex;
michael@0 215 }
michael@0 216
michael@0 217 /**
michael@0 218 * Sets the next column value in this row.
michael@0 219 *
michael@0 220 * @throws CursorIndexOutOfBoundsException if you try to add too many
michael@0 221 * values
michael@0 222 * @return this builder to support chaining
michael@0 223 */
michael@0 224 public RowBuilder add(Object columnValue) {
michael@0 225 if (index == endIndex) {
michael@0 226 throw new CursorIndexOutOfBoundsException(
michael@0 227 "No more columns left.");
michael@0 228 }
michael@0 229
michael@0 230 data[index++] = columnValue;
michael@0 231 return this;
michael@0 232 }
michael@0 233 }
michael@0 234
michael@0 235 public void set(int column, Object value) {
michael@0 236 if (column < 0 || column >= columnCount) {
michael@0 237 throw new CursorIndexOutOfBoundsException("Requested column: "
michael@0 238 + column + ", # of columns: " + columnCount);
michael@0 239 }
michael@0 240 if (mPos < 0) {
michael@0 241 throw new CursorIndexOutOfBoundsException("Before first row.");
michael@0 242 }
michael@0 243 if (mPos >= rowCount) {
michael@0 244 throw new CursorIndexOutOfBoundsException("After last row.");
michael@0 245 }
michael@0 246 data[mPos * columnCount + column] = value;
michael@0 247 }
michael@0 248
michael@0 249 // AbstractCursor implementation.
michael@0 250 @Override
michael@0 251 public int getCount() {
michael@0 252 return rowCount;
michael@0 253 }
michael@0 254
michael@0 255 @Override
michael@0 256 public String[] getColumnNames() {
michael@0 257 return columnNames;
michael@0 258 }
michael@0 259
michael@0 260 @Override
michael@0 261 public String getString(int column) {
michael@0 262 Object value = get(column);
michael@0 263 if (value == null) return null;
michael@0 264 return value.toString();
michael@0 265 }
michael@0 266
michael@0 267 @Override
michael@0 268 public short getShort(int column) {
michael@0 269 Object value = get(column);
michael@0 270 if (value == null) return 0;
michael@0 271 if (value instanceof Number) return ((Number) value).shortValue();
michael@0 272 return Short.parseShort(value.toString());
michael@0 273 }
michael@0 274
michael@0 275 @Override
michael@0 276 public int getInt(int column) {
michael@0 277 Object value = get(column);
michael@0 278 if (value == null) return 0;
michael@0 279 if (value instanceof Number) return ((Number) value).intValue();
michael@0 280 return Integer.parseInt(value.toString());
michael@0 281 }
michael@0 282
michael@0 283 @Override
michael@0 284 public long getLong(int column) {
michael@0 285 Object value = get(column);
michael@0 286 if (value == null) return 0;
michael@0 287 if (value instanceof Number) return ((Number) value).longValue();
michael@0 288 return Long.parseLong(value.toString());
michael@0 289 }
michael@0 290
michael@0 291 @Override
michael@0 292 public float getFloat(int column) {
michael@0 293 Object value = get(column);
michael@0 294 if (value == null) return 0.0f;
michael@0 295 if (value instanceof Number) return ((Number) value).floatValue();
michael@0 296 return Float.parseFloat(value.toString());
michael@0 297 }
michael@0 298
michael@0 299 @Override
michael@0 300 public double getDouble(int column) {
michael@0 301 Object value = get(column);
michael@0 302 if (value == null) return 0.0d;
michael@0 303 if (value instanceof Number) return ((Number) value).doubleValue();
michael@0 304 return Double.parseDouble(value.toString());
michael@0 305 }
michael@0 306
michael@0 307 @Override
michael@0 308 public byte[] getBlob(int column) {
michael@0 309 Object value = get(column);
michael@0 310 if (value == null) return null;
michael@0 311 if (value instanceof byte[]) {
michael@0 312 return (byte[]) value;
michael@0 313 }
michael@0 314 if (value instanceof ByteBuffer) {
michael@0 315 ByteBuffer data = (ByteBuffer)value;
michael@0 316 byte[] byteArray = new byte[data.remaining()];
michael@0 317 data.get(byteArray);
michael@0 318 return byteArray;
michael@0 319 }
michael@0 320 throw new UnsupportedOperationException("BLOB Object not of known type");
michael@0 321 }
michael@0 322
michael@0 323 @Override
michael@0 324 public boolean isNull(int column) {
michael@0 325 return get(column) == null;
michael@0 326 }
michael@0 327 }

mercurial