package com.getbase.android.db.cursors;
import com.getbase.android.db.loaders.LazyCursorList;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.LinkedHashMultimap;
import android.content.ContentResolver;
import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.net.Uri;
import java.util.LinkedHashMap;
import java.util.NoSuchElementException;
/**
* Wrapper for Android {@link android.database.Cursor} providing methods
* for transforming Cursor into other Java types. It also wraps null Cursors
* into valid Cursor instance with no data and no columns.
*/
public class FluentCursor extends CrossProcessCursorWrapper {
public FluentCursor(Cursor cursor) {
super(Cursors.returnSameOrEmptyIfNull(cursor));
}
/**
* Transforms Cursor to FluentIterable of T applying given function
* WARNING: This method closes cursor. Do not use this from onLoadFinished()
*
* @param singleRowTransform Function to apply on every single row of this cursor
* @param <T> Type of Iterable's single element
* @return Transformed iterable
*/
public <T> FluentIterable<T> toFluentIterable(Function<? super Cursor, T> singleRowTransform) {
try {
return Cursors.toFluentIterable(this, singleRowTransform);
} finally {
close();
}
}
/**
* Transforms Cursor to LazyCursorList of T applying given function
* WARNING: This method doesn't close cursor. You are responsible for calling close()
* on returned list or on backing Cursor.
*
* @param singleRowTransform Function to apply on every single row of this cursor
* @param <T> Type of List's single element
* @return Transformed list
*/
public <T> LazyCursorList<T> toLazyCursorList(Function<? super Cursor, T> singleRowTransform) {
return new LazyCursorList<>(this, singleRowTransform);
}
/**
* Transforms Cursor to LinkedHashMultimap<TKey, TValue> by applying given
* functions. The iteration order for the returned map is the same as
* the iteration order over rows of Cursor.
* WARNING: This method closes cursor. Do not use this from onLoadFinished()
*
* @param keyTransform Function to apply on every single row of this cursor
* to get the key of the entry representing this row.
* @param valueTransform Function to apply on every single row of this cursor
* to get the value of the entry representing this row.
* @param <TKey> Type of keys in the returned multimap
* @param <TValue> Type of values in the returned multimap
* @return Transformed map
*/
public <TKey, TValue> LinkedHashMultimap<TKey, TValue> toMultimap(Function<? super Cursor, TKey> keyTransform, Function<? super Cursor, TValue> valueTransform) {
try {
LinkedHashMultimap<TKey, TValue> result = LinkedHashMultimap.create(getCount(), 1);
for (moveToFirst(); !isAfterLast(); moveToNext()) {
result.put(keyTransform.apply(this), valueTransform.apply(this));
}
return result;
} finally {
close();
}
}
/**
* Transforms Cursor to LinkedHashMap<TKey, TValue> by applying given
* functions. The iteration order for the returned map is the same as
* the iteration order over rows of Cursor.
* WARNING: This method closes cursor. Do not use this from onLoadFinished()
*
* @param keyTransform Function to apply on every single row of this cursor
* to get the key of the entry representing this row.
* @param valueTransform Function to apply on every single row of this cursor
* to get the value of the entry representing this row.
* @param <TKey> Type of keys in the returned map
* @param <TValue> Type of values in the returned map
* @return Transformed map
* @throws IllegalArgumentException if Cursor contains duplicate keys
*/
public <TKey, TValue> LinkedHashMap<TKey, TValue> toMap(Function<? super Cursor, TKey> keyTransform, Function<? super Cursor, TValue> valueTransform) {
try {
LinkedHashMap<TKey, TValue> result = new LinkedHashMap<>(getCount(), 1);
for (moveToFirst(); !isAfterLast(); moveToNext()) {
final TKey key = keyTransform.apply(this);
final TValue value = valueTransform.apply(this);
final TValue previousValue = result.put(key, value);
Preconditions.checkArgument(previousValue == null, "Duplicate key %s found on position %s", key, getPosition());
}
return result;
} finally {
close();
}
}
/**
* Returns the only row of this cursor transformed using the given function.
* WARNING: This method closes cursor. Do not use this from onLoadFinished()
*
* @param singleRowTransform Function to apply on the only row of this cursor
* @param <T> Type of returned element
* @return Transformed first row of the cursor. If the cursor is empty,
* NoSuchElementException is thrown. If the cursor contains more than one
* row, IllegalArgumentException is thrown.
*/
public <T> T toOnlyElement(Function<? super Cursor, T> singleRowTransform) {
try {
switch (getCount()) {
case 0:
throw new NoSuchElementException();
case 1:
moveToFirst();
return singleRowTransform.apply(this);
default:
throw new IllegalArgumentException("expected one element but was: " + getCount());
}
} finally {
close();
}
}
/**
* Returns the only row of this cursor transformed using the given function,
* or the supplied default value if cursor is empty.
* WARNING: This method closes cursor. Do not use this from onLoadFinished()
*
* @param singleRowTransform Function to apply on the only row of this cursor
* @param <T> Type of returned element
* @return Transformed first row of the cursor or the supplied default
* value if the cursor is empty. If the cursor contains more than one
* row, IllegalArgumentException is thrown.
*/
public <T> T toOnlyElement(Function<? super Cursor, T> singleRowTransform, T defaultValue) {
if (moveToFirst()) {
return toOnlyElement(singleRowTransform);
} else {
close();
return defaultValue;
}
}
/**
* Returns number of rows in this cursor and closes it.
* WARNING: This method closes cursor. Do not use this from onLoadFinished()
*
* @return Row count from this cursor
*/
public int toRowCount() {
try {
return getCount();
} finally {
close();
}
}
/**
* Sets the notification {@code Uri} on wrapped {@code Cursor}.
*
* @return this {@code FluentCursor}
*/
public FluentCursor withNotificationUri(ContentResolver resolver, Uri uri) {
setNotificationUri(resolver, uri);
return this;
}
}