Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Custom ID annotation, option to define one of your model's columns as secondary id #400

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 86 additions & 71 deletions src/com/activeandroid/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.activeandroid.annotation.Table;
import com.activeandroid.content.ContentProvider;
import com.activeandroid.query.Delete;
import com.activeandroid.query.Select;
Expand Down Expand Up @@ -81,85 +82,99 @@ public final Long save() {

field.setAccessible(true);

try {
Object value = field.get(this);

if (value != null) {
final TypeSerializer typeSerializer = Cache.getParserForType(fieldType);
if (typeSerializer != null) {
// serialize data
value = typeSerializer.serialize(value);
// set new object type
if (value != null) {
fieldType = value.getClass();
// check that the serializer returned what it promised
if (!fieldType.equals(typeSerializer.getSerializedType())) {
Log.w(String.format("TypeSerializer returned wrong type: expected a %s but got a %s",
typeSerializer.getSerializedType(), fieldType));
}
}
}
}

// TODO: Find a smarter way to do this? This if block is necessary because we
// can't know the type until runtime.
if (value == null) {
values.putNull(fieldName);
}
else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
values.put(fieldName, (Byte) value);
}
else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
values.put(fieldName, (Short) value);
}
else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
values.put(fieldName, (Integer) value);
}
else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
values.put(fieldName, (Long) value);
}
else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
values.put(fieldName, (Float) value);
}
else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
values.put(fieldName, (Double) value);
}
else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
values.put(fieldName, (Boolean) value);
}
else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
values.put(fieldName, value.toString());
}
else if (fieldType.equals(String.class)) {
values.put(fieldName, value.toString());
}
else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
values.put(fieldName, (byte[]) value);
}
else if (ReflectionUtils.isModel(fieldType)) {
values.put(fieldName, ((Model) value).getId());
}
else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
values.put(fieldName, ((Enum<?>) value).name());
}
}
catch (IllegalArgumentException e) {
Log.e(e.getClass().getName(), e);
}
catch (IllegalAccessException e) {
Log.e(e.getClass().getName(), e);
}
// Skip the ID column
if (!fieldName.equals(idName)) {
try {
Object value = field.get(this);

if (value != null) {
final TypeSerializer typeSerializer = Cache.getParserForType(fieldType);
if (typeSerializer != null) {
// serialize data
value = typeSerializer.serialize(value);
// set new object type
if (value != null) {
fieldType = value.getClass();
// check that the serializer returned what it promised
if (!fieldType.equals(typeSerializer.getSerializedType())) {
Log.w(String.format("TypeSerializer returned wrong type: expected a %s but got a %s",
typeSerializer.getSerializedType(), fieldType));
}
}
}
}

// TODO: Find a smarter way to do this? This if block is necessary because we
// can't know the type until runtime.
if (value == null) {
values.putNull(fieldName);
} else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
values.put(fieldName, (Byte) value);
} else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
values.put(fieldName, (Short) value);
} else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
values.put(fieldName, (Integer) value);
} else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
values.put(fieldName, (Long) value);
} else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
values.put(fieldName, (Float) value);
} else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
values.put(fieldName, (Double) value);
} else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
values.put(fieldName, (Boolean) value);
} else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
values.put(fieldName, value.toString());
} else if (fieldType.equals(String.class)) {
values.put(fieldName, value.toString());
} else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
values.put(fieldName, (byte[]) value);
} else if (ReflectionUtils.isModel(fieldType)) {
values.put(fieldName, ((Model) value).getId());
} else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
values.put(fieldName, ((Enum<?>) value).name());
}
} catch (IllegalArgumentException e) {
Log.e(e.getClass().getName(), e);
} catch (IllegalAccessException e) {
Log.e(e.getClass().getName(), e);
}
}
}

if (mId == null) {
mId = db.insert(mTableInfo.getTableName(), null, values);
// Check if the custom id name is defined
if (!mTableInfo.getCustomIdName().equals(Table.DEFAULT_CUSTOM_ID_NAME)) {

String customIdValue = values.get(mTableInfo.getCustomIdName()).toString();

Cursor cursor = db.query(mTableInfo.getTableName(), null, mTableInfo.getCustomIdName() + " = ?",
new String[]{customIdValue}, null, null, null, "1");

// Check if a row already exists
if (cursor != null && cursor.getCount() > 0) {

cursor.moveToFirst();
mId = cursor.getLong(cursor.getColumnIndex(idName));

db.update(mTableInfo.getTableName(), values, mTableInfo.getCustomIdName() + " = ?",
new String[]{customIdValue});
}
else {
mId = db.insert(mTableInfo.getTableName(), null, values);
}

cursor.close();
}
else {
mId = db.insert(mTableInfo.getTableName(), null, values);
}
}
else {
db.update(mTableInfo.getTableName(), values, idName+"=" + mId, null);
}

Cache.getContext().getContentResolver()
.notifyChange(ContentProvider.createUri(mTableInfo.getType(), mId), null);
.notifyChange(ContentProvider.createUri(mTableInfo.getType(), mId), null);
return mId;
}

Expand Down Expand Up @@ -303,7 +318,7 @@ public boolean equals(Object obj) {
if (obj instanceof Model && this.mId != null) {
final Model other = (Model) obj;

return this.mId.equals(other.mId)
return this.mId.equals(other.mId)
&& (this.mTableInfo.getTableName().equals(other.mTableInfo.getTableName()));
} else {
return this == obj;
Expand Down
48 changes: 38 additions & 10 deletions src/com/activeandroid/TableInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
* limitations under the License.
*/

import android.text.TextUtils;
import android.util.Log;

import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;
import com.activeandroid.util.ReflectionUtils;
import com.activeandroid.util.SQLiteUtils;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -24,13 +32,6 @@
import java.util.List;
import java.util.Map;

import android.text.TextUtils;
import android.util.Log;

import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;
import com.activeandroid.util.ReflectionUtils;

public final class TableInfo {
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
Expand All @@ -39,8 +40,10 @@ public final class TableInfo {
private Class<? extends Model> mType;
private String mTableName;
private String mIdName = Table.DEFAULT_ID_NAME;
private String mCustomIdName = Table.DEFAULT_CUSTOM_ID_NAME;


private Map<Field, String> mColumnNames = new LinkedHashMap<Field, String>();
private Map<Field, String> mColumnNames = new LinkedHashMap<Field, String>();

//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
Expand All @@ -54,6 +57,7 @@ public TableInfo(Class<? extends Model> type) {
if (tableAnnotation != null) {
mTableName = tableAnnotation.name();
mIdName = tableAnnotation.id();
mCustomIdName = tableAnnotation.customIdName();
}
else {
mTableName = type.getSimpleName();
Expand All @@ -66,6 +70,9 @@ public TableInfo(Class<? extends Model> type) {
List<Field> fields = new LinkedList<Field>(ReflectionUtils.getDeclaredColumnFields(type));
Collections.reverse(fields);

boolean isCustomIdSupplied = !mCustomIdName.equals(Table.DEFAULT_CUSTOM_ID_NAME);
Field customIdField = null;

for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
final Column columnAnnotation = field.getAnnotation(Column.class);
Expand All @@ -74,12 +81,30 @@ public TableInfo(Class<? extends Model> type) {
columnName = field.getName();
}

if (isCustomIdSupplied && columnName.equals(mCustomIdName)) {
customIdField = field;
}

mColumnNames.put(field, columnName);
}
}

if (isCustomIdSupplied) {
if (customIdField == null) {
Log.e(com.activeandroid.util.Log.sTag,
"Given custom Id doesn't exists in table columns",
new Throwable("Custom Id defined as " + mCustomIdName + ", but doesn't exists in table columns."));
}
else if (!SQLiteUtils.TYPE_MAP.containsKey(customIdField.getType())) {
Log.e(com.activeandroid.util.Log.sTag,
"Given custom Id is of an illegal type",
new Throwable("Custom Id type " + customIdField.getType() + " isn't a legal id type"));
}
}
}



//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -96,15 +121,18 @@ public String getIdName() {
return mIdName;
}

public Collection<Field> getFields() {
public String getCustomIdName() {
return mCustomIdName;
}

public Collection<Field> getFields() {
return mColumnNames.keySet();
}

public String getColumnName(Field field) {
return mColumnNames.get(field);
}


private Field getIdField(Class<?> type) {
if (type.equals(Model.class)) {
try {
Expand Down
3 changes: 3 additions & 0 deletions src/com/activeandroid/annotation/Table.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@
public static final String DEFAULT_ID_NAME = "Id";
public String name();
public String id() default DEFAULT_ID_NAME;

public static final String DEFAULT_CUSTOM_ID_NAME = "";
public String customIdName() default DEFAULT_CUSTOM_ID_NAME;
}
2 changes: 1 addition & 1 deletion src/com/activeandroid/util/Log.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public final class Log {
// PUBLIC MEMBERS
//////////////////////////////////////////////////////////////////////////////////////

private static String sTag = "ActiveAndroid";
public static final String sTag = "ActiveAndroid";
private static boolean sEnabled = false;

//////////////////////////////////////////////////////////////////////////////////////
Expand Down
2 changes: 1 addition & 1 deletion src/com/activeandroid/util/SQLiteUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public enum SQLiteType {
//////////////////////////////////////////////////////////////////////////////////////

@SuppressWarnings("serial")
private static final HashMap<Class<?>, SQLiteType> TYPE_MAP = new HashMap<Class<?>, SQLiteType>() {
public static final HashMap<Class<?>, SQLiteType> TYPE_MAP = new HashMap<Class<?>, SQLiteType>() {
{
put(byte.class, SQLiteType.INTEGER);
put(short.class, SQLiteType.INTEGER);
Expand Down