• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.database;
18 
19 import java.util.ArrayList;
20 
21 /**
22  * A mutable cursor implementation backed by an array of {@code Object}s. Use
23  * {@link #newRow()} to add rows. Automatically expands internal capacity
24  * as needed.
25  */
26 public class MatrixCursor extends AbstractCursor {
27 
28     private final String[] columnNames;
29     private Object[] data;
30     private int rowCount = 0;
31     private final int columnCount;
32 
33     /**
34      * Constructs a new cursor with the given initial capacity.
35      *
36      * @param columnNames names of the columns, the ordering of which
37      *  determines column ordering elsewhere in this cursor
38      * @param initialCapacity in rows
39      */
MatrixCursor(String[] columnNames, int initialCapacity)40     public MatrixCursor(String[] columnNames, int initialCapacity) {
41         this.columnNames = columnNames;
42         this.columnCount = columnNames.length;
43 
44         if (initialCapacity < 1) {
45             initialCapacity = 1;
46         }
47 
48         this.data = new Object[columnCount * initialCapacity];
49     }
50 
51     /**
52      * Constructs a new cursor.
53      *
54      * @param columnNames names of the columns, the ordering of which
55      *  determines column ordering elsewhere in this cursor
56      */
MatrixCursor(String[] columnNames)57     public MatrixCursor(String[] columnNames) {
58         this(columnNames, 16);
59     }
60 
61     /**
62      * Gets value at the given column for the current row.
63      */
get(int column)64     private Object get(int column) {
65         if (column < 0 || column >= columnCount) {
66             throw new CursorIndexOutOfBoundsException("Requested column: "
67                     + column + ", # of columns: " +  columnCount);
68         }
69         if (mPos < 0) {
70             throw new CursorIndexOutOfBoundsException("Before first row.");
71         }
72         if (mPos >= rowCount) {
73             throw new CursorIndexOutOfBoundsException("After last row.");
74         }
75         return data[mPos * columnCount + column];
76     }
77 
78     /**
79      * Adds a new row to the end and returns a builder for that row. Not safe
80      * for concurrent use.
81      *
82      * @return builder which can be used to set the column values for the new
83      *  row
84      */
newRow()85     public RowBuilder newRow() {
86         rowCount++;
87         int endIndex = rowCount * columnCount;
88         ensureCapacity(endIndex);
89         int start = endIndex - columnCount;
90         return new RowBuilder(start, endIndex);
91     }
92 
93     /**
94      * Adds a new row to the end with the given column values. Not safe
95      * for concurrent use.
96      *
97      * @throws IllegalArgumentException if {@code columnValues.length !=
98      *  columnNames.length}
99      * @param columnValues in the same order as the the column names specified
100      *  at cursor construction time
101      */
addRow(Object[] columnValues)102     public void addRow(Object[] columnValues) {
103         if (columnValues.length != columnCount) {
104             throw new IllegalArgumentException("columnNames.length = "
105                     + columnCount + ", columnValues.length = "
106                     + columnValues.length);
107         }
108 
109         int start = rowCount++ * columnCount;
110         ensureCapacity(start + columnCount);
111         System.arraycopy(columnValues, 0, data, start, columnCount);
112     }
113 
114     /**
115      * Adds a new row to the end with the given column values. Not safe
116      * for concurrent use.
117      *
118      * @throws IllegalArgumentException if {@code columnValues.size() !=
119      *  columnNames.length}
120      * @param columnValues in the same order as the the column names specified
121      *  at cursor construction time
122      */
addRow(Iterable<?> columnValues)123     public void addRow(Iterable<?> columnValues) {
124         int start = rowCount * columnCount;
125         int end = start + columnCount;
126         ensureCapacity(end);
127 
128         if (columnValues instanceof ArrayList<?>) {
129             addRow((ArrayList<?>) columnValues, start);
130             return;
131         }
132 
133         int current = start;
134         Object[] localData = data;
135         for (Object columnValue : columnValues) {
136             if (current == end) {
137                 // TODO: null out row?
138                 throw new IllegalArgumentException(
139                         "columnValues.size() > columnNames.length");
140             }
141             localData[current++] = columnValue;
142         }
143 
144         if (current != end) {
145             // TODO: null out row?
146             throw new IllegalArgumentException(
147                     "columnValues.size() < columnNames.length");
148         }
149 
150         // Increase row count here in case we encounter an exception.
151         rowCount++;
152     }
153 
154     /** Optimization for {@link ArrayList}. */
addRow(ArrayList<?> columnValues, int start)155     private void addRow(ArrayList<?> columnValues, int start) {
156         int size = columnValues.size();
157         if (size != columnCount) {
158             throw new IllegalArgumentException("columnNames.length = "
159                     + columnCount + ", columnValues.size() = " + size);
160         }
161 
162         rowCount++;
163         Object[] localData = data;
164         for (int i = 0; i < size; i++) {
165             localData[start + i] = columnValues.get(i);
166         }
167     }
168 
169     /** Ensures that this cursor has enough capacity. */
ensureCapacity(int size)170     private void ensureCapacity(int size) {
171         if (size > data.length) {
172             Object[] oldData = this.data;
173             int newSize = data.length * 2;
174             if (newSize < size) {
175                 newSize = size;
176             }
177             this.data = new Object[newSize];
178             System.arraycopy(oldData, 0, this.data, 0, oldData.length);
179         }
180     }
181 
182     /**
183      * Builds a row, starting from the left-most column and adding one column
184      * value at a time. Follows the same ordering as the column names specified
185      * at cursor construction time.
186      */
187     public class RowBuilder {
188 
189         private int index;
190         private final int endIndex;
191 
RowBuilder(int index, int endIndex)192         RowBuilder(int index, int endIndex) {
193             this.index = index;
194             this.endIndex = endIndex;
195         }
196 
197         /**
198          * Sets the next column value in this row.
199          *
200          * @throws CursorIndexOutOfBoundsException if you try to add too many
201          *  values
202          * @return this builder to support chaining
203          */
add(Object columnValue)204         public RowBuilder add(Object columnValue) {
205             if (index == endIndex) {
206                 throw new CursorIndexOutOfBoundsException(
207                         "No more columns left.");
208             }
209 
210             data[index++] = columnValue;
211             return this;
212         }
213     }
214 
215     // AbstractCursor implementation.
216 
217     @Override
getCount()218     public int getCount() {
219         return rowCount;
220     }
221 
222     @Override
getColumnNames()223     public String[] getColumnNames() {
224         return columnNames;
225     }
226 
227     @Override
getString(int column)228     public String getString(int column) {
229         Object value = get(column);
230         if (value == null) return null;
231         return value.toString();
232     }
233 
234     @Override
getShort(int column)235     public short getShort(int column) {
236         Object value = get(column);
237         if (value == null) return 0;
238         if (value instanceof Number) return ((Number) value).shortValue();
239         return Short.parseShort(value.toString());
240     }
241 
242     @Override
getInt(int column)243     public int getInt(int column) {
244         Object value = get(column);
245         if (value == null) return 0;
246         if (value instanceof Number) return ((Number) value).intValue();
247         return Integer.parseInt(value.toString());
248     }
249 
250     @Override
getLong(int column)251     public long getLong(int column) {
252         Object value = get(column);
253         if (value == null) return 0;
254         if (value instanceof Number) return ((Number) value).longValue();
255         return Long.parseLong(value.toString());
256     }
257 
258     @Override
getFloat(int column)259     public float getFloat(int column) {
260         Object value = get(column);
261         if (value == null) return 0.0f;
262         if (value instanceof Number) return ((Number) value).floatValue();
263         return Float.parseFloat(value.toString());
264     }
265 
266     @Override
getDouble(int column)267     public double getDouble(int column) {
268         Object value = get(column);
269         if (value == null) return 0.0d;
270         if (value instanceof Number) return ((Number) value).doubleValue();
271         return Double.parseDouble(value.toString());
272     }
273 
274     @Override
getBlob(int column)275     public byte[] getBlob(int column) {
276         Object value = get(column);
277         return (byte[]) value;
278     }
279 
280     @Override
getType(int column)281     public int getType(int column) {
282         return DatabaseUtils.getTypeOfObject(get(column));
283     }
284 
285     @Override
isNull(int column)286     public boolean isNull(int column) {
287         return get(column) == null;
288     }
289 }
290