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

Add support for javax.persistence EntityListener #394

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
16 changes: 15 additions & 1 deletion library/src/main/java/com/orm/SugarContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import android.content.Context;

import com.google.common.collect.MapMaker;
import com.orm.entity.EntityListenerManager;
import com.orm.serializer.EntitySerializerManager;

import java.util.concurrent.ConcurrentMap;

Expand All @@ -12,13 +14,17 @@ public class SugarContext {
private SugarDb sugarDb;
private Context context;
private ConcurrentMap<Object, Long> entitiesMap;
private EntityListenerManager entitylistenerManager;
private EntitySerializerManager entitySerializerManager;

private SugarContext(Context context) {
this.context = context;
this.sugarDb = new SugarDb(context);
this.entitiesMap = new MapMaker().weakKeys().makeMap();
this.entitylistenerManager = new EntityListenerManager(context);
this.entitySerializerManager = new EntitySerializerManager(context);
}

public static SugarContext getSugarContext() {
if (instance == null) {
throw new NullPointerException("SugarContext has not been initialized properly. Call SugarContext.init(Context) in your Application.onCreate() method and SugarContext.terminate() in your Application.onTerminate() method.");
Expand Down Expand Up @@ -56,4 +62,12 @@ protected SugarDb getSugarDb() {
ConcurrentMap<Object, Long> getEntitiesMap() {
return entitiesMap;
}

public EntityListenerManager getEntitylistenerManager() {
return entitylistenerManager;
}

public EntitySerializerManager getEntitySerializerManager() {
return entitySerializerManager;
}
}
66 changes: 48 additions & 18 deletions library/src/main/java/com/orm/SugarRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@
import android.util.Log;

import com.orm.dsl.Table;
import com.orm.entity.annotation.PostPersist;
import com.orm.entity.annotation.PostRemove;
import com.orm.entity.annotation.PrePersist;
import com.orm.entity.annotation.PreRemove;
import com.orm.serializer.EntitySerializerManager;
import com.orm.util.NamingHelper;
import com.orm.util.ReflectionUtil;
import com.orm.util.QueryBuilder;
import com.orm.util.ReflectionUtil;

import java.lang.String;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -50,7 +54,7 @@ public static <T> void saveInTx(Collection<T> objects) {
try {
sqLiteDatabase.beginTransaction();
sqLiteDatabase.setLockingEnabled(false);
for (T object: objects) {
for (T object : objects) {
save(object);
}
sqLiteDatabase.setTransactionSuccessful();
Expand Down Expand Up @@ -93,7 +97,7 @@ public static <T> int deleteInTx(Collection<T> objects) {
public static <T> List<T> listAll(Class<T> type) {
return find(type, null, null, null, null, null);
}

public static <T> List<T> listAll(Class<T> type, String orderBy) {
return find(type, null, null, null, orderBy, null);
}
Expand All @@ -113,7 +117,7 @@ public static <T> List<T> findById(Class<T> type, String[] ids) {
return find(type, whereClause, ids);
}

public static <T> T first(Class<T>type) {
public static <T> T first(Class<T> type) {
List<T> list = findWithQuery(type,
"SELECT * FROM " + NamingHelper.toSQLName(type) + " ORDER BY ID ASC LIMIT 1");
if (list.isEmpty()) {
Expand All @@ -122,7 +126,7 @@ public static <T> T first(Class<T>type) {
return list.get(0);
}

public static <T> T last(Class<T>type) {
public static <T> T last(Class<T> type) {
List<T> list = findWithQuery(type,
"SELECT * FROM " + NamingHelper.toSQLName(type) + " ORDER BY ID DESC LIMIT 1");
if (list.isEmpty()) {
Expand Down Expand Up @@ -168,7 +172,8 @@ public static <T> List<T> findWithQuery(Class<T> type, String query, String... a
try {
while (c.moveToNext()) {
entity = type.getDeclaredConstructor().newInstance();
inflate(c, entity, getSugarContext().getEntitiesMap());
inflate(c, entity, getSugarContext().getEntitiesMap(),
getSugarContext().getEntitySerializerManager());
toRet.add(entity);
}
} catch (Exception e) {
Expand All @@ -194,7 +199,8 @@ public static <T> List<T> find(Class<T> type, String whereClause, String[] where
try {
while (c.moveToNext()) {
entity = type.getDeclaredConstructor().newInstance();
inflate(c, entity, getSugarContext().getEntitiesMap());
inflate(c, entity, getSugarContext().getEntitiesMap(),
getSugarContext().getEntitySerializerManager());
toRet.add(entity);
}
} catch (Exception e) {
Expand All @@ -210,15 +216,15 @@ public static <T> long count(Class<?> type) {
}

public static <T> long count(Class<?> type, String whereClause, String[] whereArgs) {
return count(type, whereClause, whereArgs, null, null, null);
return count(type, whereClause, whereArgs, null, null, null);
}

public static <T> long count(Class<?> type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) {
SugarDb db = getSugarContext().getSugarDb();
SQLiteDatabase sqLiteDatabase = db.getDB();

long toRet = -1;
String filter = (!TextUtils.isEmpty(whereClause)) ? " where " + whereClause : "";
String filter = (!TextUtils.isEmpty(whereClause)) ? " where " + whereClause : "";
SQLiteStatement sqliteStatement;
try {
sqliteStatement = sqLiteDatabase.compileStatement("SELECT count(*) FROM " + NamingHelper.toSQLName(type) + filter);
Expand Down Expand Up @@ -247,20 +253,30 @@ public static long save(Object object) {
}

static long save(SQLiteDatabase db, Object object) {
getSugarContext().getEntitylistenerManager().notify(object, PrePersist.class);

Map<Object, Long> entitiesMap = getSugarContext().getEntitiesMap();
List<Field> columns = ReflectionUtil.getTableFields(object.getClass());
ContentValues values = new ContentValues(columns.size());
Field idField = null;
for (Field column : columns) {
ReflectionUtil.addFieldValueToColumn(values, column, object, entitiesMap);
if (column.getType().isAssignableFrom(SugarRecord.class) || column.getType().isAnnotationPresent(Table.class)) {
try {
save(column.get(object));
} catch (IllegalAccessException e) {
Log.e("Sugar", "Unable to access objects child (field): " + column.getName(), e);
}
}
EntitySerializerManager serializerManager = getSugarContext().getEntitySerializerManager();
ReflectionUtil.addFieldValueToColumn(values, column, object, entitiesMap, serializerManager);
if (column.getName().equals("id")) {
idField = column;
}
}

boolean isSugarEntity = isSugarEntity(object.getClass());
if (isSugarEntity && entitiesMap.containsKey(object)) {
values.put("id", entitiesMap.get(object));
values.put("id", entitiesMap.get(object));
}

long id = db.insertWithOnConflict(NamingHelper.toSQLName(object.getClass()), null, values,
Expand All @@ -281,6 +297,8 @@ static long save(SQLiteDatabase db, Object object) {
((SugarRecord) object).setId(id);
}

getSugarContext().getEntitylistenerManager().notify(object, PostPersist.class);

Log.i("Sugar", object.getClass().getSimpleName() + " saved : " + id);

return id;
Expand All @@ -290,14 +308,16 @@ public static boolean isSugarEntity(Class<?> objectClass) {
return objectClass.isAnnotationPresent(Table.class) || SugarRecord.class.isAssignableFrom(objectClass);
}

private static void inflate(Cursor cursor, Object object, Map<Object, Long> entitiesMap) {
private static void inflate(Cursor cursor, Object object, Map<Object, Long> entitiesMap,
EntitySerializerManager entitySerializerManager) {

List<Field> columns = ReflectionUtil.getTableFields(object.getClass());
if (!entitiesMap.containsKey(object)) {
entitiesMap.put(object, cursor.getLong(cursor.getColumnIndex(("ID"))));
}

for (Field field : columns) {
field.setAccessible(true);
field.setAccessible(true);
Class<?> fieldType = field.getType();
if (isSugarEntity(fieldType)) {
try {
Expand All @@ -307,7 +327,7 @@ private static void inflate(Cursor cursor, Object object, Map<Object, Long> enti
e.printStackTrace();
}
} else {
ReflectionUtil.setFieldValueFromCursor(cursor, field, object);
ReflectionUtil.setFieldValueFromCursor(cursor, field, object, entitySerializerManager);
}
}
}
Expand All @@ -316,15 +336,19 @@ public boolean delete() {
Long id = getId();
Class<?> type = getClass();
if (id != null && id > 0L) {
getSugarContext().getEntitylistenerManager().notify(this, PreRemove.class);

SQLiteDatabase db = getSugarContext().getSugarDb().getDB();
Log.i("Sugar", type.getSimpleName() + " deleted : " + id);

getSugarContext().getEntitylistenerManager().notify(this, PostRemove.class);
return db.delete(NamingHelper.toSQLName(type), "Id=?", new String[]{id.toString()}) == 1;
} else {
Log.i("Sugar", "Cannot delete object: " + type.getSimpleName() + " - object has not been saved");
return false;
}
}

public static boolean delete(Object object) {
Class<?> type = object.getClass();
if (type.isAnnotationPresent(Table.class)) {
Expand All @@ -333,8 +357,12 @@ public static boolean delete(Object object) {
field.setAccessible(true);
Long id = (Long) field.get(object);
if (id != null && id > 0L) {
getSugarContext().getEntitylistenerManager().notify(object, PreRemove.class);
SQLiteDatabase db = getSugarContext().getSugarDb().getDB();

boolean deleted = db.delete(NamingHelper.toSQLName(type), "Id=?", new String[]{id.toString()}) == 1;

getSugarContext().getEntitylistenerManager().notify(object, PostRemove.class);
Log.i("Sugar", type.getSimpleName() + " deleted : " + id);
return deleted;
} else {
Expand Down Expand Up @@ -362,7 +390,8 @@ public long save() {

@SuppressWarnings("unchecked")
void inflate(Cursor cursor) {
inflate(cursor, this, getSugarContext().getEntitiesMap());
inflate(cursor, this, getSugarContext().getEntitiesMap(),
getSugarContext().getEntitySerializerManager());
}

public Long getId() {
Expand Down Expand Up @@ -400,7 +429,8 @@ public E next() {

try {
entity = type.getDeclaredConstructor().newInstance();
inflate(cursor, entity, getSugarContext().getEntitiesMap());
inflate(cursor, entity, getSugarContext().getEntitiesMap(),
getSugarContext().getEntitySerializerManager());
} catch (Exception e) {
e.printStackTrace();
} finally {
Expand Down
135 changes: 135 additions & 0 deletions library/src/main/java/com/orm/entity/EntityListenerManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package com.orm.entity;

import android.content.Context;
import android.util.Log;

import com.google.common.collect.Lists;
import com.orm.entity.annotation.EntityListeners;
import com.orm.entity.annotation.PostPersist;
import com.orm.entity.annotation.PostRemove;
import com.orm.entity.annotation.PrePersist;
import com.orm.entity.annotation.PreRemove;
import com.orm.util.ReflectionUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EntityListenerManager {

private Map<Class, List<EntityListenerMeta>> entityListenerMetaMap;

private Context context;

public EntityListenerManager(Context context) {
this.context = context;
load();
}

public void notify(Object entity, Class listenerType) {
List<EntityListenerMeta> entityListenerMetas = entityListenerMetaMap.get(entity.getClass());

if (entityListenerMetas != null && !entityListenerMetas.isEmpty()) {
for (EntityListenerMeta meta : entityListenerMetas) {
notify(entity, listenerType, meta);
}
}
}

private void notify(Object entity, Class listenerType, EntityListenerMeta meta) {
if (listenerType == PrePersist.class && meta.getPrePersist() != null) {
execute(meta.getListener(), meta.getPrePersist(), entity);
} else if (listenerType == PreRemove.class && meta.getPreRemove() != null) {
execute(meta.getListener(), meta.getPreRemove(), entity);
} else if (listenerType == PostPersist.class && meta.getPostPersist() != null) {
execute(meta.getListener(), meta.getPostPersist(), entity);
} else if (listenerType == PostRemove.class && meta.getPostRemove() != null) {
execute(meta.getListener(), meta.getPostRemove(), entity);
}
}

private void execute(Object obj, Method method, Object entity) {
try {
method.invoke(obj, entity);
} catch (IllegalAccessException e) {
Log.w("Sugar", "Cannot invoke method '" + method.getName() + "' with entity '" + entity.getClass() + "'");
} catch (InvocationTargetException e) {
Log.w("Sugar", "Cannot invoke method '" + method.getName() + "' with entity '" + entity.getClass() + "'");
}
}

private void load() {
entityListenerMetaMap = new HashMap<>();

List<Class> domainClasses = ReflectionUtil.getDomainClasses(context);

for (Class domainClass : domainClasses) {
processDomainClass(domainClass);
}
}

private void processDomainClass(Class domainClass) {
EntityListeners annotation = (EntityListeners) domainClass.getAnnotation(EntityListeners.class);

if (annotation != null) {
Class[] listenerClasses = annotation.value();

if (listenerClasses != null && listenerClasses.length > 0) {
List<EntityListenerMeta> metaList = processListenerClasses(domainClass, listenerClasses);

if (metaList != null && !metaList.isEmpty()) {
entityListenerMetaMap.put(domainClass, metaList);
}
}
}
}

private List<EntityListenerMeta> processListenerClasses(Class domainClass, Class[] listenerClasses) {
List<EntityListenerMeta> metaList = Lists.newArrayList();

for (Class listenerClass : listenerClasses) {
Log.i("Sugar", "Found EntityListener for domain class '" + domainClass + "': " + listenerClass);

EntityListenerMeta meta = processListenerClass(domainClass, listenerClass);
if (meta != null) {
metaList.add(meta);
}
}

return metaList;
}

private EntityListenerMeta processListenerClass(Class domainClass, Class listenerClass) {
try {
EntityListenerMeta meta = new EntityListenerMeta(context, domainClass, listenerClass);
meta.setPrePersist(findMethod(listenerClass, PrePersist.class));
meta.setPreRemove(findMethod(listenerClass, PreRemove.class));
meta.setPostPersist(findMethod(listenerClass, PostPersist.class));
meta.setPostRemove(findMethod(listenerClass, PostRemove.class));

return meta;
} catch (IllegalAccessException e) {
Log.e("Sugar", "Unable to instantiate EntityListener of class: " + listenerClass, e);
} catch (InstantiationException e) {
Log.e("Sugar", "Unable to instantiate EntityListener of class: " + listenerClass, e);
} catch (NoSuchMethodException e) {
Log.e("Sugar", "Unable to instantiate EntityListener of class: " + listenerClass, e);
} catch (InvocationTargetException e) {
Log.e("Sugar", "Unable to instantiate EntityListener of class: " + listenerClass, e);
}

return null;
}

private Method findMethod(Class source, Class annotation) {
for (Method method : source.getMethods()) {
if (method.getAnnotation(annotation) != null) {
return method;
}
}

return null;
}
}
Loading