blob: 050a49ac959eb008b42cd306927ae27c8239eef3 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package android.database;
18
Artur Satayev26958002019-12-10 17:47:52 +000019import android.compat.annotation.UnsupportedAppUsage;
Mathew Inwood31755f92018-12-20 13:53:36 +000020import android.os.Build;
Anton Hansson788ec752019-04-30 15:21:24 +010021
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import java.util.ArrayList;
23
24/**
25 * A mutable cursor implementation backed by an array of {@code Object}s. Use
26 * {@link #newRow()} to add rows. Automatically expands internal capacity
27 * as needed.
28 */
29public class MatrixCursor extends AbstractCursor {
30
31 private final String[] columnNames;
Mathew Inwood31755f92018-12-20 13:53:36 +000032 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033 private Object[] data;
Mathew Inwood31755f92018-12-20 13:53:36 +000034 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035 private int rowCount = 0;
36 private final int columnCount;
37
38 /**
39 * Constructs a new cursor with the given initial capacity.
40 *
41 * @param columnNames names of the columns, the ordering of which
42 * determines column ordering elsewhere in this cursor
43 * @param initialCapacity in rows
44 */
45 public MatrixCursor(String[] columnNames, int initialCapacity) {
46 this.columnNames = columnNames;
47 this.columnCount = columnNames.length;
48
49 if (initialCapacity < 1) {
50 initialCapacity = 1;
51 }
52
53 this.data = new Object[columnCount * initialCapacity];
54 }
55
56 /**
57 * Constructs a new cursor.
58 *
59 * @param columnNames names of the columns, the ordering of which
60 * determines column ordering elsewhere in this cursor
61 */
62 public MatrixCursor(String[] columnNames) {
63 this(columnNames, 16);
64 }
65
66 /**
67 * Gets value at the given column for the current row.
68 */
Mathew Inwood41b31942018-08-10 16:00:53 +010069 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private Object get(int column) {
71 if (column < 0 || column >= columnCount) {
72 throw new CursorIndexOutOfBoundsException("Requested column: "
73 + column + ", # of columns: " + columnCount);
74 }
75 if (mPos < 0) {
76 throw new CursorIndexOutOfBoundsException("Before first row.");
77 }
78 if (mPos >= rowCount) {
79 throw new CursorIndexOutOfBoundsException("After last row.");
80 }
81 return data[mPos * columnCount + column];
82 }
83
84 /**
85 * Adds a new row to the end and returns a builder for that row. Not safe
86 * for concurrent use.
87 *
88 * @return builder which can be used to set the column values for the new
89 * row
90 */
91 public RowBuilder newRow() {
Jeff Sharkey9d0843d2013-05-07 12:41:33 -070092 final int row = rowCount++;
93 final int endIndex = rowCount * columnCount;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 ensureCapacity(endIndex);
Jeff Sharkey9d0843d2013-05-07 12:41:33 -070095 return new RowBuilder(row);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 }
97
98 /**
99 * Adds a new row to the end with the given column values. Not safe
100 * for concurrent use.
101 *
102 * @throws IllegalArgumentException if {@code columnValues.length !=
103 * columnNames.length}
104 * @param columnValues in the same order as the the column names specified
105 * at cursor construction time
106 */
107 public void addRow(Object[] columnValues) {
108 if (columnValues.length != columnCount) {
109 throw new IllegalArgumentException("columnNames.length = "
110 + columnCount + ", columnValues.length = "
111 + columnValues.length);
112 }
113
114 int start = rowCount++ * columnCount;
115 ensureCapacity(start + columnCount);
116 System.arraycopy(columnValues, 0, data, start, columnCount);
117 }
118
119 /**
120 * Adds a new row to the end with the given column values. Not safe
121 * for concurrent use.
122 *
123 * @throws IllegalArgumentException if {@code columnValues.size() !=
124 * columnNames.length}
125 * @param columnValues in the same order as the the column names specified
126 * at cursor construction time
127 */
128 public void addRow(Iterable<?> columnValues) {
129 int start = rowCount * columnCount;
130 int end = start + columnCount;
131 ensureCapacity(end);
132
133 if (columnValues instanceof ArrayList<?>) {
134 addRow((ArrayList<?>) columnValues, start);
135 return;
136 }
137
138 int current = start;
139 Object[] localData = data;
140 for (Object columnValue : columnValues) {
141 if (current == end) {
142 // TODO: null out row?
143 throw new IllegalArgumentException(
144 "columnValues.size() > columnNames.length");
145 }
146 localData[current++] = columnValue;
147 }
148
149 if (current != end) {
150 // TODO: null out row?
151 throw new IllegalArgumentException(
152 "columnValues.size() < columnNames.length");
153 }
154
155 // Increase row count here in case we encounter an exception.
156 rowCount++;
157 }
158
159 /** Optimization for {@link ArrayList}. */
160 private void addRow(ArrayList<?> columnValues, int start) {
161 int size = columnValues.size();
162 if (size != columnCount) {
163 throw new IllegalArgumentException("columnNames.length = "
164 + columnCount + ", columnValues.size() = " + size);
165 }
166
167 rowCount++;
168 Object[] localData = data;
169 for (int i = 0; i < size; i++) {
170 localData[start + i] = columnValues.get(i);
171 }
172 }
173
174 /** Ensures that this cursor has enough capacity. */
175 private void ensureCapacity(int size) {
176 if (size > data.length) {
177 Object[] oldData = this.data;
178 int newSize = data.length * 2;
179 if (newSize < size) {
180 newSize = size;
181 }
182 this.data = new Object[newSize];
183 System.arraycopy(oldData, 0, this.data, 0, oldData.length);
184 }
185 }
186
187 /**
Jeff Sharkey9d0843d2013-05-07 12:41:33 -0700188 * Builds a row of values using either of these approaches:
189 * <ul>
190 * <li>Values can be added with explicit column ordering using
191 * {@link #add(Object)}, which starts from the left-most column and adds one
192 * column value at a time. This follows the same ordering as the column
193 * names specified at cursor construction time.
194 * <li>Column and value pairs can be offered for possible inclusion using
Jeff Sharkeyb7757a62013-09-09 17:46:54 -0700195 * {@link #add(String, Object)}. If the cursor includes the given column,
Jeff Sharkey9d0843d2013-05-07 12:41:33 -0700196 * the value will be set for that column, otherwise the value is ignored.
197 * This approach is useful when matching data to a custom projection.
198 * </ul>
199 * Undefined values are left as {@code null}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 */
201 public class RowBuilder {
Jeff Sharkey9d0843d2013-05-07 12:41:33 -0700202 private final int row;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 private final int endIndex;
204
Jeff Sharkey9d0843d2013-05-07 12:41:33 -0700205 private int index;
206
207 RowBuilder(int row) {
208 this.row = row;
209 this.index = row * columnCount;
210 this.endIndex = index + columnCount;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 }
212
213 /**
214 * Sets the next column value in this row.
215 *
216 * @throws CursorIndexOutOfBoundsException if you try to add too many
217 * values
218 * @return this builder to support chaining
219 */
220 public RowBuilder add(Object columnValue) {
221 if (index == endIndex) {
222 throw new CursorIndexOutOfBoundsException(
223 "No more columns left.");
224 }
225
226 data[index++] = columnValue;
227 return this;
228 }
Jeff Sharkey9d0843d2013-05-07 12:41:33 -0700229
230 /**
231 * Offer value for possible inclusion if this cursor defines the given
232 * column. Columns not defined by the cursor are silently ignored.
233 *
234 * @return this builder to support chaining
235 */
Jeff Sharkeyb7757a62013-09-09 17:46:54 -0700236 public RowBuilder add(String columnName, Object value) {
Jeff Sharkey9d0843d2013-05-07 12:41:33 -0700237 for (int i = 0; i < columnNames.length; i++) {
238 if (columnName.equals(columnNames[i])) {
239 data[(row * columnCount) + i] = value;
240 }
241 }
242 return this;
243 }
Anton Hansson788ec752019-04-30 15:21:24 +0100244
245 /** @hide */
246 public final RowBuilder add(int columnIndex, Object value) {
247 data[(row * columnCount) + columnIndex] = value;
248 return this;
249 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 }
251
252 // AbstractCursor implementation.
253
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100254 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 public int getCount() {
256 return rowCount;
257 }
258
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100259 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 public String[] getColumnNames() {
261 return columnNames;
262 }
263
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100264 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 public String getString(int column) {
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100266 Object value = get(column);
267 if (value == null) return null;
268 return value.toString();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 }
270
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100271 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 public short getShort(int column) {
273 Object value = get(column);
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100274 if (value == null) return 0;
275 if (value instanceof Number) return ((Number) value).shortValue();
276 return Short.parseShort(value.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 }
278
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100279 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 public int getInt(int column) {
281 Object value = get(column);
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100282 if (value == null) return 0;
283 if (value instanceof Number) return ((Number) value).intValue();
284 return Integer.parseInt(value.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 }
286
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100287 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 public long getLong(int column) {
289 Object value = get(column);
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100290 if (value == null) return 0;
291 if (value instanceof Number) return ((Number) value).longValue();
292 return Long.parseLong(value.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 }
294
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100295 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 public float getFloat(int column) {
297 Object value = get(column);
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100298 if (value == null) return 0.0f;
299 if (value instanceof Number) return ((Number) value).floatValue();
300 return Float.parseFloat(value.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 }
302
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100303 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 public double getDouble(int column) {
305 Object value = get(column);
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100306 if (value == null) return 0.0d;
307 if (value instanceof Number) return ((Number) value).doubleValue();
308 return Double.parseDouble(value.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 }
310
Bjorn Bringertf2cec582009-07-22 20:40:04 +0100311 @Override
Todd Kennedy783123f2011-07-01 14:19:03 -0700312 public byte[] getBlob(int column) {
313 Object value = get(column);
314 return (byte[]) value;
315 }
316
317 @Override
Vasu Nori8b0dd7d2010-05-18 11:54:31 -0700318 public int getType(int column) {
319 return DatabaseUtils.getTypeOfObject(get(column));
320 }
321
322 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 public boolean isNull(int column) {
324 return get(column) == null;
325 }
326}