/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.andlib.data;
import java.util.WeakHashMap;
import android.database.Cursor;
import android.database.CursorWrapper;
import com.todoroo.andlib.data.Property.PropertyVisitor;
/**
* AstridCursor wraps a cursor and allows users to query for individual
* {@link Property} types or read an entire {@link AbstractModel} from
* a database row.
*
* @author Tim Su <tim@todoroo.com>
*
* @param <TYPE> a model type that is returned by this cursor
*/
public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
/** Properties read by this cursor */
private final Property<?>[] properties;
/** Weakly cache field name to column id references for this cursor.
* Because it's a weak hash map, entire keys can be discarded by GC */
private final WeakHashMap<String, Integer> columnIndexCache;
/** Property reading visitor */
private static final CursorReadingVisitor reader = new CursorReadingVisitor();
/** Wrapped cursor */
private final Cursor cursor;
/**
* Create an <code>AstridCursor</code> from the supplied {@link Cursor}
* object.
*
* @param cursor
* @param properties properties read from this cursor
*/
public TodorooCursor(Cursor cursor, Property<?>[] properties) {
super(cursor);
this.cursor = cursor;
this.properties = properties;
columnIndexCache = new WeakHashMap<String, Integer>();
}
/**
* Get the value for the given property on the underlying {@link Cursor}
*
* @param <PROPERTY_TYPE> type to return
* @param property to retrieve
* @return
*/
public <PROPERTY_TYPE> PROPERTY_TYPE get(Property<PROPERTY_TYPE> property) {
return (PROPERTY_TYPE)property.accept(reader, this);
}
/**
* @return underlying cursor
*/
public Cursor getCursor() {
return cursor;
}
/**
* Gets entire property list
* @return
*/
public Property<?>[] getProperties() {
return properties;
}
/**
* Use cache to get the column index for the given field name
*/
public synchronized int getColumnIndexFromCache(String field) {
Integer index = columnIndexCache.get(field);
if(index == null) {
index = getColumnIndexOrThrow(field);
columnIndexCache.put(field, index);
}
return index;
}
/**
* Visitor that reads the given property from a cursor
*
* @author Tim Su <tim@todoroo.com>
*
*/
public static class CursorReadingVisitor implements PropertyVisitor<Object, TodorooCursor<?>> {
public Object visitDouble(Property<Double> property,
TodorooCursor<?> cursor) {
int column = columnIndex(property, cursor);
if(property.checkFlag(Property.PROP_FLAG_NULLABLE) && cursor.isNull(column))
return null;
return cursor.getDouble(column);
}
public Object visitInteger(Property<Integer> property,
TodorooCursor<?> cursor) {
int column = columnIndex(property, cursor);
if(property.checkFlag(Property.PROP_FLAG_NULLABLE) && cursor.isNull(column))
return null;
return cursor.getInt(column);
}
public Object visitLong(Property<Long> property, TodorooCursor<?> cursor) {
int column = columnIndex(property, cursor);
if(property.checkFlag(Property.PROP_FLAG_NULLABLE) && cursor.isNull(column))
return null;
return cursor.getLong(column);
}
public Object visitString(Property<String> property,
TodorooCursor<?> cursor) {
int column = columnIndex(property, cursor);
if(property.checkFlag(Property.PROP_FLAG_NULLABLE) && cursor.isNull(column))
return null;
return cursor.getString(column);
}
private int columnIndex(Property<?> property, TodorooCursor<?> cursor) {
return cursor.getColumnIndexFromCache(property.getColumnName());
}
}
}