/*
* Copyright (c) www.bugull.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.tooan.ynpay.third.mongodb;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.tooan.ynpay.third.jfinal.log.Logger;
import net.tooan.ynpay.third.mongodb.annotations.Default;
import net.tooan.ynpay.third.mongodb.annotations.Ref;
import net.tooan.ynpay.third.mongodb.annotations.RefList;
import net.tooan.ynpay.third.mongodb.cache.DaoCache;
import net.tooan.ynpay.third.mongodb.cache.FieldsCache;
import net.tooan.ynpay.third.mongodb.exception.FieldException;
import net.tooan.ynpay.third.mongodb.mapper.*;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.Map.Entry;
/**
* The utility class for ODM(Object Document Mapping), mainly fetch lazy and cascade data.
*
* @author Frank Wen(xbwen@hotmail.com)
*/
@SuppressWarnings("unchecked")
public class BuguMapper {
private final static Logger logger = Logger.getLogger(BuguMapper.class);
/**
* Fetch out the lazy @Property, @Embed, @EmbedList field of a list
*
* @param list the list needs to operate on
*/
public static void fetchLazy(List list) {
for (Object o : list) {
if (o != null) {
BuguEntity obj = (BuguEntity) o;
BuguEntity newObj = (BuguEntity) DaoCache.getInstance().get(obj.getClass()).findOne(obj.getId());
FieldUtil.copy(newObj, obj);
}
}
}
/**
* Fetch out the lazy @Property, @Embed, @EmbedList field of an entity.
* <p>The entity must be an element of a list.</p>
*
* @param obj the entity needs to operate on
*/
public static void fetchLazy(BuguEntity obj) {
BuguEntity newObj = (BuguEntity) DaoCache.getInstance().get(obj.getClass()).findOne(obj.getId());
FieldUtil.copy(newObj, obj);
}
/**
* Fetch out the cascade @Ref or @RefList entity.
*
* @param obj the entity needs to operate on
* @param names the fields' names
*/
public static void fetchCascade(BuguEntity obj, String... names) {
if (obj != null) {
for (String name : names) {
String remainder = null;
int index = name.indexOf(".");
if (index > 0) {
remainder = name.substring(index + 1);
name = name.substring(0, index);
}
fetchOneLevel(obj, name);
if (remainder != null) {
fetchRemainder(obj, name, remainder);
}
}
}
}
/**
* Fetch out the cascade @Ref or @RefList entity.
*
* @param list the list needs to operate on
* @param names the fields' names
*/
public static void fetchCascade(List list, String... names) {
for (Object o : list) {
if (o != null) {
BuguEntity obj = (BuguEntity) o;
fetchCascade(obj, names);
}
}
}
private static void fetchOneLevel(BuguEntity obj, String fieldName) {
Field field = null;
try {
field = FieldsCache.getInstance().getField(obj.getClass(), fieldName);
} catch (FieldException ex) {
logger.error(ex.getMessage(), ex);
}
if (field.getAnnotation(Ref.class) != null) {
fetchRef(obj, field);
} else if (field.getAnnotation(RefList.class) != null) {
fetchRefList(obj, field);
}
}
private static void fetchRemainder(BuguEntity obj, String fieldName, String remainder) {
Field field = null;
try {
field = FieldsCache.getInstance().getField(obj.getClass(), fieldName);
} catch (FieldException ex) {
logger.error(ex.getMessage(), ex);
}
Object value = FieldUtil.get(obj, field);
if (value == null) {
return;
}
if (field.getAnnotation(Ref.class) != null) {
BuguEntity entity = (BuguEntity) value;
fetchCascade(entity, remainder);
} else if (field.getAnnotation(RefList.class) != null) {
Class type = field.getType();
if (DataType.isList(type)) {
List<BuguEntity> list = (List<BuguEntity>) value;
for (BuguEntity entity : list) {
fetchCascade(entity, remainder);
}
} else if (DataType.isSet(type)) {
Set<BuguEntity> set = (Set<BuguEntity>) value;
for (BuguEntity entity : set) {
fetchCascade(entity, remainder);
}
} else if (DataType.isMap(type)) {
Map<Object, BuguEntity> map = (Map<Object, BuguEntity>) value;
for (Entry<Object, BuguEntity> entry : map.entrySet()) {
fetchCascade(entry.getValue(), remainder);
}
}
}
}
private static void fetchRef(BuguEntity obj, Field field) {
Object o = FieldUtil.get(obj, field);
if (o == null) {
return;
}
BuguEntity refObj = (BuguEntity) o;
String id = refObj.getId();
Class cls = FieldUtil.getRealType(field);
InternalDao dao = DaoCache.getInstance().get(cls);
Object value = dao.findOne(id);
FieldUtil.set(obj, field, value);
}
private static void fetchRefList(BuguEntity obj, Field field) {
Class<?> type = field.getType();
if (type.isArray()) {
fetchArray(obj, field, type.getComponentType());
} else {
ParameterizedType paramType = (ParameterizedType) field.getGenericType();
Type[] types = paramType.getActualTypeArguments();
int len = types.length;
if (len == 1) {
//for List and Set
fetchListAndSet(obj, field, (Class) types[0]);
} else if (len == 2) {
//for Map
fetchMap(obj, field, (Class) types[1]);
}
}
}
private static void fetchArray(BuguEntity obj, Field field, Class clazz) {
Object o = FieldUtil.get(obj, field);
if (o == null) {
return;
}
int len = Array.getLength(o);
clazz = FieldUtil.getRealType(clazz, field);
Object arr = Array.newInstance(clazz, len);
List<Object> idList = new ArrayList<Object>();
for (int i = 0; i < len; i++) {
Object item = Array.get(o, i);
if (item != null) {
BuguEntity entity = (BuguEntity) item;
Object dbId = IdUtil.toDbId(entity.getClass(), entity.getId());
idList.add(dbId);
}
}
DBObject in = new BasicDBObject(Operator.IN, idList);
DBObject query = new BasicDBObject(Operator.ID, in);
InternalDao dao = DaoCache.getInstance().get(clazz);
RefList refList = field.getAnnotation(RefList.class);
String sort = refList.sort();
List<BuguEntity> entityList = null;
if (sort.equals(Default.SORT)) {
entityList = dao.find(query);
} else {
entityList = dao.find(query, MapperUtil.getSort(sort));
}
if (entityList.size() != len) {
len = entityList.size();
arr = Array.newInstance(clazz, len);
}
for (int i = 0; i < len; i++) {
Array.set(arr, i, entityList.get(i));
}
FieldUtil.set(obj, field, arr);
}
private static void fetchListAndSet(BuguEntity obj, Field field, Class clazz) {
Object o = FieldUtil.get(obj, field);
if (o == null) {
return;
}
Class type = field.getType();
RefList refList = field.getAnnotation(RefList.class);
clazz = FieldUtil.getRealType(clazz, field);
InternalDao dao = DaoCache.getInstance().get(clazz);
if (DataType.isList(type)) {
List<BuguEntity> list = (List<BuguEntity>) o;
List<Object> idList = new ArrayList<Object>();
for (BuguEntity ent : list) {
if (ent != null) {
Object dbId = IdUtil.toDbId(ent.getClass(), ent.getId());
idList.add(dbId);
}
}
DBObject in = new BasicDBObject(Operator.IN, idList);
DBObject query = new BasicDBObject(Operator.ID, in);
String sort = refList.sort();
List result = null;
if (sort.equals(Default.SORT)) {
result = dao.find(query);
} else {
result = dao.find(query, MapperUtil.getSort(sort));
}
FieldUtil.set(obj, field, result);
} else if (DataType.isSet(type)) {
Set<BuguEntity> set = (Set<BuguEntity>) o;
List<Object> idList = new ArrayList<Object>();
for (BuguEntity ent : set) {
if (ent != null) {
Object dbId = IdUtil.toDbId(ent.getClass(), ent.getId());
idList.add(dbId);
}
}
DBObject in = new BasicDBObject(Operator.IN, idList);
DBObject query = new BasicDBObject(Operator.ID, in);
String sort = refList.sort();
List result = null;
if (sort.equals(Default.SORT)) {
result = dao.find(query);
} else {
result = dao.find(query, MapperUtil.getSort(sort));
}
FieldUtil.set(obj, field, new HashSet(result));
}
}
private static void fetchMap(BuguEntity obj, Field field, Class clazz) {
Object o = FieldUtil.get(obj, field);
if (o == null) {
return;
}
Map<Object, BuguEntity> map = (Map<Object, BuguEntity>) o;
Map result = new HashMap();
clazz = FieldUtil.getRealType(clazz, field);
InternalDao dao = DaoCache.getInstance().get(clazz);
for (Entry<Object, BuguEntity> entry : map.entrySet()) {
BuguEntity refObj = entry.getValue();
if (refObj != null) {
String id = refObj.getId();
Object value = dao.findOne(id);
result.put(entry.getKey(), value);
} else {
result.put(entry.getKey(), null);
}
}
FieldUtil.set(obj, field, result);
}
}