mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2024-12-24 03:35:50 +01:00
Implement DataHolder compatible with old public API
This commit is contained in:
parent
cfd7c4e70e
commit
4991c5c05b
@ -17,39 +17,57 @@
|
||||
package com.google.android.gms.common.data;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.database.AbstractWindowedCursor;
|
||||
import android.database.CharArrayBuffer;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWindow;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.safeparcel.AutoSafeParcelable;
|
||||
import org.microg.safeparcel.SafeParceled;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class for accessing collections of data, organized into columns. This provides the backing
|
||||
* support for DataBuffer. Much like a cursor, the holder supports the notion of a current
|
||||
* position, and has methods for extracting various types of data from named columns.
|
||||
*/
|
||||
public class DataHolder extends AutoSafeParcelable {
|
||||
@PublicApi(until = "1")
|
||||
public class DataHolder extends AutoSafeParcelable implements Closeable {
|
||||
@SafeParceled(1000)
|
||||
private int versionCode = 1;
|
||||
|
||||
@SafeParceled(1)
|
||||
public final String[] columns;
|
||||
private final String[] columns;
|
||||
|
||||
@SafeParceled(2)
|
||||
public final CursorWindow[] windows;
|
||||
private final CursorWindow[] windows;
|
||||
|
||||
@SafeParceled(3)
|
||||
public final int statusCode;
|
||||
private final int statusCode;
|
||||
|
||||
@SafeParceled(4)
|
||||
public final Bundle metadata;
|
||||
private final Bundle metadata;
|
||||
|
||||
private boolean closed = false;
|
||||
private Map<String, Integer> columnIndizes;
|
||||
|
||||
protected static final int FIELD_TYPE_NULL = 0;
|
||||
protected static final int FIELD_TYPE_INTEGER = 1;
|
||||
protected static final int FIELD_TYPE_FLOAT = 2;
|
||||
protected static final int FIELD_TYPE_STRING = 3;
|
||||
protected static final int FIELD_TYPE_BLOB = 4;
|
||||
|
||||
private DataHolder() {
|
||||
columns = null;
|
||||
@ -58,18 +76,81 @@ public class DataHolder extends AutoSafeParcelable {
|
||||
metadata = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a data holder with the specified data.
|
||||
*
|
||||
* @param columns The column names corresponding to the data in the given windows.
|
||||
* @param windows The {@link CursorWindow} instances holding the data.
|
||||
* @param statusCode The status code of this {@link DataHolder}.
|
||||
* @param metadata The metadata associated with this {@link DataHolder} (may be null).
|
||||
*/
|
||||
public DataHolder(String[] columns, CursorWindow[] windows, int statusCode, Bundle metadata) {
|
||||
this.columns = columns;
|
||||
this.windows = windows;
|
||||
this.statusCode = statusCode;
|
||||
this.metadata = metadata;
|
||||
validateContents();
|
||||
}
|
||||
|
||||
protected static final int FIELD_TYPE_BLOB = 4;
|
||||
protected static final int FIELD_TYPE_FLOAT = 2;
|
||||
protected static final int FIELD_TYPE_INTEGER = 1;
|
||||
protected static final int FIELD_TYPE_NULL = 0;
|
||||
protected static final int FIELD_TYPE_STRING = 3;
|
||||
/**
|
||||
* Creates a data holder wrapping the provided cursor, with provided status code and metadata.
|
||||
*
|
||||
* @param cursor The cursor containing the data.
|
||||
* @param statusCode The status code of this {@link DataHolder}.
|
||||
* @param metadata The metadata associated with this {@link DataHolder} (may be null).
|
||||
*/
|
||||
public DataHolder(AbstractWindowedCursor cursor, int statusCode, Bundle metadata) {
|
||||
this(cursor.getColumnNames(), createCursorWindows(cursor), statusCode, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a data holder wrapping the provided cursor, with provided status code and metadata.
|
||||
*
|
||||
* @param cursor The cursor containing the data.
|
||||
* @param statusCode The status code of this {@link DataHolder}.
|
||||
* @param metadata The metadata associated with this {@link DataHolder} (may be null).
|
||||
*/
|
||||
public DataHolder(Cursor cursor, int statusCode, Bundle metadata) {
|
||||
this(cursor.getColumnNames(), createCursorWindows(cursor), statusCode, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link DataHolder.Builder} to create a new {@link DataHolder} manually.
|
||||
*
|
||||
* @param columns The array of column names that the object supports.
|
||||
* @param uniqueColumn The non-null column name that must contain unique values. New rows added to the builder with the same value in this column will replace any older rows.
|
||||
* @return {@link DataHolder.Builder} object to work with.
|
||||
*/
|
||||
public static Builder builder(String[] columns, String uniqueColumn) {
|
||||
return new Builder(columns, uniqueColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link DataHolder.Builder} to create a new {@link DataHolder} manually.
|
||||
*
|
||||
* @param columns The array of column names that the object supports.
|
||||
* @return {@link DataHolder.Builder} object to work with.
|
||||
*/
|
||||
public static Builder builder(String[] columns) {
|
||||
return builder(columns, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param statusCode The status code of this {@link DataHolder}.
|
||||
* @param metadata The metadata associated with this {@link DataHolder} (may be null).
|
||||
* @return An empty {@link DataHolder} object with the given status and metadata.
|
||||
*/
|
||||
public static DataHolder empty(int statusCode, Bundle metadata) {
|
||||
return new DataHolder(new String[0], new CursorWindow[0], statusCode, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param statusCode The status code of this {@link DataHolder}.
|
||||
* @return An empty {@link DataHolder} object with the given status and null metadata.
|
||||
*/
|
||||
public static DataHolder empty(int statusCode) {
|
||||
return empty(statusCode, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@SuppressLint("NewApi")
|
||||
@ -98,46 +179,166 @@ public class DataHolder extends AutoSafeParcelable {
|
||||
throw new RuntimeException("Unsupported cursor on this platform!");
|
||||
}
|
||||
|
||||
public static DataHolder fromCursor(Cursor cursor, int statusCode, Bundle metadata) {
|
||||
/**
|
||||
* Closes the data holder, releasing all of its resources and making it completely invalid.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (this) {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
for (CursorWindow window : windows) {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the String content in the given column at the provided position into a {@link CharArrayBuffer}.
|
||||
* This will throw an {@link IllegalArgumentException} if the column does not exist, the
|
||||
* position is invalid, or the data holder has been closed.
|
||||
*
|
||||
* @param column The column to retrieve.
|
||||
* @param row The row to retrieve the data from.
|
||||
* @param windowIndex Index of the cursor window to extract the data from.
|
||||
* @param dataOut The {@link CharArrayBuffer} to copy into.
|
||||
*/
|
||||
public void copyToBuffer(String column, int row, int windowIndex, CharArrayBuffer dataOut) {
|
||||
throw new RuntimeException("Not yet available");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static CursorWindow[] createCursorWindows(Builder builder) {
|
||||
if (builder.columns.length == 0) return new CursorWindow[0];
|
||||
List<CursorWindow> windows = new ArrayList<CursorWindow>();
|
||||
CursorWindow cursorWindow = null;
|
||||
int row = 0;
|
||||
try {
|
||||
CursorWindow current = null;
|
||||
for (int rowIndex = 0; rowIndex < builder.rows.size(); rowIndex++) {
|
||||
Map<String, Object> row = builder.rows.get(rowIndex);
|
||||
if (current == null || !current.allocRow()) {
|
||||
current = new CursorWindow(false);
|
||||
current.setStartPosition(rowIndex);
|
||||
current.setNumColumns(builder.columns.length);
|
||||
windows.add(current);
|
||||
if (!current.allocRow()) {
|
||||
windows.remove(current);
|
||||
return windows.toArray(new CursorWindow[windows.size()]);
|
||||
}
|
||||
}
|
||||
for (int columnIndex = 0; columnIndex < builder.columns.length; columnIndex++) {
|
||||
Object val = row.get(builder.columns[columnIndex]);
|
||||
if (val == null) {
|
||||
current.putNull(rowIndex, columnIndex);
|
||||
} else if (val instanceof String) {
|
||||
current.putString((String) val, rowIndex, columnIndex);
|
||||
} else if (val instanceof Long) {
|
||||
current.putLong((Long) val, rowIndex, columnIndex);
|
||||
} else if (val instanceof Integer) {
|
||||
current.putLong((Integer) val, rowIndex, columnIndex);
|
||||
} else if (val instanceof Boolean) {
|
||||
if ((Boolean) val)
|
||||
current.putLong(1, rowIndex, columnIndex);
|
||||
} else if (val instanceof byte[]) {
|
||||
current.putBlob((byte[]) val, rowIndex, columnIndex);
|
||||
} else if (val instanceof Double) {
|
||||
current.putDouble((Double) val, rowIndex, columnIndex);
|
||||
} else if (val instanceof Float) {
|
||||
current.putDouble((Float) val, rowIndex, columnIndex);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported object for column " + columnIndex + ": " + val);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
for (CursorWindow window : windows) {
|
||||
window.close();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return windows.toArray(new CursorWindow[windows.size()]);
|
||||
}
|
||||
|
||||
private static CursorWindow[] createCursorWindows(Cursor cursor) {
|
||||
if (cursor.getColumnCount() == 0) return new CursorWindow[0];
|
||||
List<CursorWindow> windows = new ArrayList<CursorWindow>();
|
||||
CursorWindow current = null;
|
||||
int rowIndex = 0;
|
||||
while (cursor.moveToNext()) {
|
||||
if (cursorWindow == null || !cursorWindow.allocRow()) {
|
||||
cursorWindow = new CursorWindow(false);
|
||||
cursorWindow.setNumColumns(cursor.getColumnCount());
|
||||
windows.add(cursorWindow);
|
||||
if (!cursorWindow.allocRow())
|
||||
throw new RuntimeException("Impossible to store Cursor in CursorWindows");
|
||||
row = 0;
|
||||
if (current == null || !current.allocRow()) {
|
||||
current = new CursorWindow(false);
|
||||
current.setStartPosition(rowIndex);
|
||||
current.setNumColumns(cursor.getColumnCount());
|
||||
windows.add(current);
|
||||
if (!current.allocRow()) {
|
||||
windows.remove(current);
|
||||
return windows.toArray(new CursorWindow[windows.size()]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cursor.getColumnCount(); i++) {
|
||||
switch (getCursorType(cursor, i)) {
|
||||
case FIELD_TYPE_NULL:
|
||||
cursorWindow.putNull(row, i);
|
||||
current.putNull(rowIndex, i);
|
||||
break;
|
||||
case FIELD_TYPE_BLOB:
|
||||
cursorWindow.putBlob(cursor.getBlob(i), row, i);
|
||||
current.putBlob(cursor.getBlob(i), rowIndex, i);
|
||||
break;
|
||||
case FIELD_TYPE_FLOAT:
|
||||
cursorWindow.putDouble(cursor.getDouble(i), row, i);
|
||||
current.putDouble(cursor.getDouble(i), rowIndex, i);
|
||||
break;
|
||||
case FIELD_TYPE_INTEGER:
|
||||
cursorWindow.putLong(cursor.getLong(i), row, i);
|
||||
current.putLong(cursor.getLong(i), rowIndex, i);
|
||||
break;
|
||||
case FIELD_TYPE_STRING:
|
||||
cursorWindow.putString(cursor.getString(i), row, i);
|
||||
current.putString(cursor.getString(i), rowIndex, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
row++;
|
||||
rowIndex++;
|
||||
}
|
||||
DataHolder dataHolder = new DataHolder(cursor.getColumnNames(), windows.toArray(new CursorWindow[windows.size()]), statusCode, metadata);
|
||||
cursor.close();
|
||||
return dataHolder;
|
||||
return windows.toArray(new CursorWindow[windows.size()]);
|
||||
}
|
||||
|
||||
@PublicApi(exclude = true)
|
||||
@Deprecated
|
||||
public static DataHolder fromCursor(Cursor cursor, int statusCode, Bundle metadata) {
|
||||
return new DataHolder(cursor, statusCode, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the boolean value for a given column at the provided position.
|
||||
* This will throw an {@link IllegalArgumentException} if the column does not exist, the
|
||||
* position is invalid, or the data holder has been closed.
|
||||
*
|
||||
* @param column The column to retrieve.
|
||||
* @param row The row to retrieve the data from.
|
||||
* @param windowIndex Index of the cursor window to extract the data from.
|
||||
* @return The boolean value in that column.
|
||||
*/
|
||||
public boolean getBoolean(String column, int row, int windowIndex) {
|
||||
return windows[windowIndex].getLong(row, columnIndizes.get(column)) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the byte array value for a given column at the provided position.
|
||||
* This will throw an {@link IllegalArgumentException} if the column does not exist, the
|
||||
* position is invalid, or the data holder has been closed.
|
||||
*
|
||||
* @param column The column to retrieve.
|
||||
* @param row The row to retrieve the data from.
|
||||
* @param windowIndex Index of the cursor window to extract the data from.
|
||||
* @return The byte array value in that column.
|
||||
*/
|
||||
public byte[] getByteArray(String column, int row, int windowIndex) {
|
||||
return windows[windowIndex].getBlob(row, columnIndizes.get(column));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of rows in the data holder.
|
||||
*
|
||||
* @return the number of rows in the data holder.
|
||||
*/
|
||||
public int getCount() {
|
||||
int c = 0;
|
||||
if (windows != null) {
|
||||
@ -148,6 +349,88 @@ public class DataHolder extends AutoSafeParcelable {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the integer value for a given column at the provided position.
|
||||
* This will throw an {@link IllegalArgumentException} if the column does not exist, the
|
||||
* position is invalid, or the data holder has been closed.
|
||||
*
|
||||
* @param column The column to retrieve.
|
||||
* @param row The row to retrieve the data from.
|
||||
* @param windowIndex Index of the cursor window to extract the data from.
|
||||
* @return The integer value in that column.
|
||||
*/
|
||||
public int getInteger(String column, int row, int windowIndex) {
|
||||
return windows[windowIndex].getInt(row, columnIndizes.get(column));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the long value for a given column at the provided position.
|
||||
* This will throw an {@link IllegalArgumentException} if the column does not exist, the
|
||||
* position is invalid, or the data holder has been closed.
|
||||
*
|
||||
* @param column The column to retrieve.
|
||||
* @param row The row to retrieve the data from.
|
||||
* @param windowIndex Index of the cursor window to extract the data from.
|
||||
* @return The long value in that column.
|
||||
*/
|
||||
public long getLong(String column, int row, int windowIndex) {
|
||||
return windows[windowIndex].getLong(row, columnIndizes.get(column));
|
||||
}
|
||||
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the string value for a given column at the provided position.
|
||||
* This will throw an {@link IllegalArgumentException} if the column does not exist, the
|
||||
* position is invalid, or the data holder has been closed.
|
||||
*
|
||||
* @param column The column to retrieve.
|
||||
* @param row The row to retrieve the data from.
|
||||
* @param windowIndex Index of the cursor window to extract the data from.
|
||||
* @return The string value in that column.
|
||||
*/
|
||||
public String getString(String column, int row, int windowIndex) {
|
||||
return windows[windowIndex].getString(row, columnIndizes.get(column));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given column at the provided position contains null.
|
||||
* This will throw an {@link IllegalArgumentException} if the column does not exist, the
|
||||
* position is invalid, or the data holder has been closed.
|
||||
*
|
||||
* @param column The column to retrieve.
|
||||
* @param row The row to retrieve the data from.
|
||||
* @param windowIndex Index of the cursor window to extract the data from.
|
||||
* @return Whether the column value is null at this position.
|
||||
*/
|
||||
public boolean isNull(String column, int row, int windowIndex) {
|
||||
return windows[windowIndex].isNull(row, columnIndizes.get(column));
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
synchronized (this) {
|
||||
return closed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the column data at the provided position as a URI if possible, checking for null values.
|
||||
* This will throw an {@link IllegalArgumentException} if the column does not exist, the
|
||||
* position is invalid, or the data holder has been closed.
|
||||
*
|
||||
* @param column The column to retrieve.
|
||||
* @param row The row to retrieve the data from.
|
||||
* @param windowIndex Index of the cursor window to extract the data from.
|
||||
* @return The column data as a URI, or null if not present.
|
||||
*/
|
||||
public Uri parseUri(String column, int row, int windowIndex) {
|
||||
String string = getString(column, row, windowIndex);
|
||||
if (string != null) return Uri.parse(string);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataHolder{" +
|
||||
@ -158,5 +441,119 @@ public class DataHolder extends AutoSafeParcelable {
|
||||
'}';
|
||||
}
|
||||
|
||||
public static final Creator<DataHolder> CREATOR = new AutoCreator<DataHolder>(DataHolder.class);
|
||||
public void validateContents() {
|
||||
columnIndizes = new HashMap<String, Integer>();
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
columnIndizes.put(columns[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to build {@link DataHolder} instances containing arbitrary data.
|
||||
* <p/>
|
||||
* Note that the constructor is private; use DataHolder.builder() to create instances of this class.
|
||||
*/
|
||||
public static class Builder {
|
||||
private final String[] columns;
|
||||
private final ArrayList<Map<String, Object>> rows;
|
||||
private final String uniqueColumn;
|
||||
private final Map<Object, Integer> uniqueIndizes;
|
||||
|
||||
private Builder(String[] columns, String uniqueColumn) {
|
||||
this.columns = columns;
|
||||
this.rows = new ArrayList<Map<String, Object>>();
|
||||
this.uniqueColumn = uniqueColumn;
|
||||
this.uniqueIndizes = new HashMap<Object, Integer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate an {@link DataHolder} from this {@link DataHolder.Builder} with the given status code and metadata.
|
||||
*
|
||||
* @param statusCode The status code of this {@link DataHolder}.
|
||||
* @param metadata The metadata associated with this {@link DataHolder} (may be null).
|
||||
* @return {@link DataHolder} representation of this object.
|
||||
*/
|
||||
public DataHolder build(int statusCode, Bundle metadata) {
|
||||
return new DataHolder(columns, createCursorWindows(this), statusCode, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate an {@link DataHolder} from this {@link DataHolder.Builder} with the given status code and null metadata.
|
||||
*
|
||||
* @param statusCode The status code of this {@link DataHolder}.
|
||||
* @return {@link DataHolder} representation of this object.
|
||||
*/
|
||||
public DataHolder build(int statusCode) {
|
||||
return build(statusCode, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of rows that the resulting DataHolder will contain.
|
||||
*/
|
||||
public int getCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the rows in this builder based on the standard data type comparisons for the value in the provided column.
|
||||
* Calling this multiple times with the same column will not change the sort order of the builder.
|
||||
* Note that any data which is added after this call will not be sorted.
|
||||
*
|
||||
* @param sortColumn The column to sort the rows in this builder by.
|
||||
* @return {@link DataHolder.Builder} to continue construction.
|
||||
*/
|
||||
public Builder sort(String sortColumn) {
|
||||
throw new RuntimeException("Not yet implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new row of data to the {@link DataHolder} this {@link DataHolder.Builder} will create. Note that the data must contain an entry for all columns
|
||||
* <p/>
|
||||
* Currently the only supported value types that are supported are String, Long, and Boolean (Integer is also accepted and will be stored as a Long).
|
||||
*
|
||||
* @param values {@link ContentValues} containing row data.
|
||||
* @return {@link DataHolder.Builder} to continue construction.
|
||||
*/
|
||||
public Builder withRow(ContentValues values) {
|
||||
HashMap<String, Object> row = new HashMap<String, Object>();
|
||||
for (Map.Entry<String, Object> entry : values.valueSet()) {
|
||||
row.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return withRow(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new row of data to the {@link DataHolder} this {@link DataHolder.Builder} will create. Note that the data must contain an entry for all columns
|
||||
* <p/>
|
||||
* Currently the only supported value types that are supported are String, Long, and Boolean (Integer is also accepted and will be stored as a Long).
|
||||
*
|
||||
* @param row Map containing row data.
|
||||
* @return {@link DataHolder.Builder} to continue construction.
|
||||
*/
|
||||
public Builder withRow(HashMap<String, Object> row) {
|
||||
if (uniqueColumn != null) {
|
||||
Object val = row.get(uniqueColumn);
|
||||
if (val != null) {
|
||||
Integer old = uniqueIndizes.get(val);
|
||||
if (old != null) {
|
||||
rows.set(old, row);
|
||||
return this;
|
||||
} else {
|
||||
uniqueIndizes.put(val, rows.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
rows.add(row);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<DataHolder> CREATOR = new AutoCreator<DataHolder>(DataHolder.class) {
|
||||
@Override
|
||||
public DataHolder createFromParcel(Parcel parcel) {
|
||||
DataHolder res = super.createFromParcel(parcel);
|
||||
res.validateContents();
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user