package mongodb.tools;
import mongodb.tools.annotation.Id;
import mongodb.tools.annotation.Ignore;
import mongodb.tools.annotation.Version;
import mongodb.tools.exception.VersionMismatchException;
import org.bson.Document;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
import java.beans.Introspector;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
/**
* Created by emeric_martineau on 11/03/2015.
*/
public class MongoDbMapping {
/**
* Field name of version.
*/
public static final String VERSION_FIELD = "<version>";
/**
* Map to map primitive class.
*/
private static final Map<Class<?>, Class<?>> primitiveMap = new HashMap<Class<?>, Class<?>>();
static {
primitiveMap.put(Boolean.TYPE, Boolean.class);
primitiveMap.put(Byte.TYPE, Byte.class);
primitiveMap.put(Character.TYPE, Character.class);
primitiveMap.put(Short.TYPE, Short.class);
primitiveMap.put(Integer.TYPE, Integer.class);
primitiveMap.put(Long.TYPE, Long.class);
primitiveMap.put(Double.TYPE, Double.class);
primitiveMap.put(Float.TYPE, Float.class);
primitiveMap.put(Void.TYPE, Void.TYPE);
}
/**
* Convert object to MondogDB object. Support direct object (int, String...) and Iterable. Not support array, Map...
*
* @see mongodb.tools.annotation.Ignore
* @see mongodb.tools.annotation.Id
*
* @param obj object to convert
*
* @return mongodb object
*
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public static Document toMongo(final Object obj)
throws InvocationTargetException, IllegalAccessException {
Document result;
if (obj == null) {
result = null;
} else {
result = new Document();
Class tClass = obj.getClass();
Version versionAnnotation = (Version) tClass.getAnnotation(Version.class);
if (versionAnnotation != null) {
result.append(VERSION_FIELD, versionAnnotation.version());
}
Method[] methods = tClass.getMethods();
String methodName;
// Fielname
String fieldName;
// Data of field
Object data;
// Use if data is iterable
List temporaryList;
// Current object of list
Object objList;
for (int i = 0; i < methods.length; i++) {
methodName = methods[i].getName();
if (methodName.startsWith("get") && !"getClass".equals(methodName)
&& !methods[i].isAnnotationPresent(Ignore.class)
&& !methods[i].isAnnotationPresent(Id.class)) {
// Found getter.
fieldName = Introspector.decapitalize(methods[i].getName().substring(3));
if (methods[i].getParameterCount() == 0) {
data = methods[i].invoke(obj);
if (data instanceof Iterator) {
Iterator it = ((List) data).iterator();
temporaryList = new LinkedList();
while (it.hasNext()) {
objList = it.next();
temporaryList.add(toMongo(objList));
}
}
result.append(fieldName, data);
}
}
}
}
return result;
}
/**
* Convert MongoDb object to POJO
*
* @param mongoObj mongodb object
* @param clazz class to instanciate
*
* @return object
*
* @throws IllegalAccessException
* @throws InstantiationException
* @throws VersionMismatchException if object is strict and version not match
*/
public static Object fromMongoDb(final Document mongoObj, final Class clazz)
throws IllegalAccessException, InstantiationException, VersionMismatchException, InvocationTargetException, ClassNotFoundException {
Object result;
if (mongoObj == null) {
result = null;
} else {
result = clazz.newInstance();
Class tClass = result.getClass();
Method[] methods = tClass.getMethods();
/* Class annotation */
Version versionAnnotation = (Version) tClass.getAnnotation(Version.class);
String property;
Object value;
/* Setter method */
Method setter;
/* Parameter of setter */
Class<?> parameter;
Type[] type;
for (Map.Entry<String, Object> setKey : mongoObj.entrySet()) {
property = setKey.getKey();
value = setKey.getValue();
if (VERSION_FIELD.equals(property)) {
// Check version
if (versionAnnotation != null && versionAnnotation.strict()) {
int version = Integer.valueOf((String) value);
if (version != versionAnnotation.version()) {
throw new VersionMismatchException(String.format("Version %d found, version %d expected.",
version, versionAnnotation.version()));
}
}
} else {
setter = findSetter(methods, property);
if (setter != null) {
parameter = setter.getParameterTypes()[0];
type = setter.getGenericParameterTypes();
if (parameter.isPrimitive()) {
invokePrimitive(result, setter, parameter, value);
} else if (parameter.isEnum()) {
invokeEnum(result, setter, parameter, value);
} else if (parameter.isArray()) {
// TODO
} else if (parameter.getName().equals("java.lang.String")) {
setter.invoke(result, value);
} else if (parameter == List.class) {
invokeList(result, setter, type, value);
}
System.out.println(parameter);
// si c'est un talbeau
// si c'est une map
}
}
}
}
return result;
}
/**
* Find method setter with only one parameter.
*
* @param methods methods list
* @param fieldName fieldName
*
* @return method
*/
private static Method findSetter(final Method[] methods, final String fieldName) {
String methodName = "set".concat(Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1));
System.out.println(fieldName);
// Search method
Method m = null;
for (Method currentMethod : methods) {
if (methodName.equals(currentMethod.getName())) {
// Now check only one parameter
if (currentMethod.getParameterCount() == 1) {
m = currentMethod;
break;
}
}
}
return m;
}
/**
* Invoke primitive setter.
*
* @param obj object to call setter
* @param m method to call
* @param parameter parametter of setter
* @param value value to setup
*
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private static void invokePrimitive(Object obj, Method m, Class<?> parameter, Object value)
throws InvocationTargetException, IllegalAccessException {
if (parameter == Boolean.TYPE) {
m.invoke(obj, Boolean.valueOf((String) value));
} else if (parameter == Byte.TYPE) {
m.invoke(obj, Byte.valueOf((String) value));
} else if (parameter == Character.TYPE) {
m.invoke(obj, Character.valueOf((char) value));
} else if (parameter == Short.TYPE) {
m.invoke(obj, Short.valueOf((String) value));
} else if (parameter == Integer.TYPE) {
m.invoke(obj, Integer.valueOf((String) value));
} else if (parameter == Long.TYPE) {
m.invoke(obj, Long.valueOf((String) value));
} else if (parameter == Double.TYPE) {
m.invoke(obj, Double.valueOf((String) value));
} else if (parameter == Float.TYPE) {
m.invoke(obj, Float.valueOf((String) value));
}
}
/**
* Invoke enum setter.
*
* @param obj object to call setter
* @param m method to call
* @param parameter parametter of setter
* @param value value to setup
*
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private static void invokeEnum(Object obj, Method m, Class<?> parameter, Object value)
throws InvocationTargetException, IllegalAccessException {
Enum enumValue = Enum.valueOf((Class<Enum>) parameter, (String) value);
m.invoke(obj, enumValue);
}
/**
* Invoke collection setter.
*
* @param obj object to call setter
* @param m method to call
* @param parameter parametter of setter
* @param value value to setup
*
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws InstantiationException
*/
private static void invokeList(Object obj, Method m, Type[] parameter, Object value)
throws InvocationTargetException, IllegalAccessException, InstantiationException,
VersionMismatchException, ClassNotFoundException {
List srcList = (List) value;
List destList = null;
Class classOfList = getGenericClass(parameter);
if (value != null) {
destList = new ArrayList(srcList.size());
for (Object item : srcList) {
if (item == null) {
destList.add(null);
}else if (item instanceof Document) {
fromMongoDb((Document) item, classOfList);
} else {
destList.add(item);
}
}
}
m.invoke(obj, destList);
}
/**
* Found type of generic
*
* @param parameter parameter of method
*
* @return class to be instanciate
*
* @throws ClassNotFoundException
*/
private static Class getGenericClass(Type[] parameter) throws ClassNotFoundException {
Type type = ((ParameterizedTypeImpl) parameter[0]).getActualTypeArguments()[0];
return Class.forName(type.getTypeName());
}
}
jeudi 11 juin 2015
Java et la réflexion [4/4]
Maintenant que vous maîtriser les annotations et la réflexion en java, voici le code source complet du convertissant Java -> Mongo/Mongo -> Java :
Inscription à :
Publier les commentaires (Atom)
Aucun commentaire:
Enregistrer un commentaire