package org.openmeetings.utils.mappings;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.slf4j.Logger;
import org.openmeetings.app.remote.red5.ScopeApplicationAdapter;
import org.red5.logging.Red5LoggerFactory;
/**
* Class to cast any LinkedHashMap to its JavaBean repraesentant
* the idiom is that the attribute name in the LinkedHashMap is the same as in the JavaBean/Pojo
*
* if the attribute's of the Bean are private (meaning it IS a Bean) then it will use the getters and setters
* if the attribute's are public it will assign directly
* if the attribute is final it will show an error in log
*
* if the HashMap contains an null for a primitive attribute it will not assign that value
*
* if the HashMap contains subelments nested as LinkedHashMap's it will add these Sub-Elements to the Main-Object
* for an exmaple see:
* http://openmeetings.googlecode.com/svn/branches/dev/xmlcrm/java/src/test/org/xmlcrm/utils/TestReflectionApi.java
*
* TODO:
* If the Sub Item is not an Object but a Set (meaning a List of Object) this List must be
* cast to Objects of the Bean too
*
* @author swagner
*
*
*/
public class CastMapToObject {
private static final Logger log = Red5LoggerFactory.getLogger(CastMapToObject.class, ScopeApplicationAdapter.webAppRootKey);
private CastMapToObject() {}
private static CastMapToObject instance = null;
public static synchronized CastMapToObject getInstance() {
if (instance == null) {
instance = new CastMapToObject();
}
return instance;
}
public Object castByGivenObject(Map values, Class targetClass){
try {
// if (valuesObj.getClass().getClass().getName().equals(ObjectMap.class.getName())){
// ObjectMap values = (ObjectMap) valuesObj;
// } else if (valuesObj.getClass().getClass().getName().equals(LinkedHashMap.class.getName())){
// LinkedHashMap values = (LinkedHashMap) valuesObj;
// }
Object returnObject = targetClass.newInstance();
// log.error("returnObject");
// log.error(returnObject);
// log.error( "class " + targetClass.getName() );
// log.error (" number of declared fields: " + targetClass.getDeclaredFields().length );
LinkedHashMap<String,LinkedHashMap<String,Object>> structuredMethodMap = StructureMethodList.getInstance().parseClassToMethodList(targetClass);
for ( Field anyField : targetClass.getDeclaredFields() ) {
String fieldName = anyField.getName();
Class fieldType = anyField.getType();
String fieldTypeName = anyField.getType().getName();
if (this.compareTypeNameToBasicTypes(fieldTypeName)) {
//log.info("Found Type: " + fieldName);
//Get value from set
Object t = values.get(fieldName);
//log.info("fieldName Value: "+t);
//log.info("fieldName Value: "+anyField.getModifiers());
int mod = anyField.getModifiers();
if (Modifier.isPrivate(mod) && !Modifier.isFinal(mod)){
//log.info("is private so get setter method "+fieldName);
LinkedHashMap<String,Object> methodSummery = structuredMethodMap.get(fieldName);
if (methodSummery!=null) {
if (methodSummery.get("setter")!=null) {
String methodSetterName = methodSummery.get("setter").toString();
Class[] paramTypes = (Class[]) methodSummery.get("setterParamTypes");
Method m = targetClass.getMethod(methodSetterName, paramTypes);
Class paramType = paramTypes[0];
//try to cast the Given Object to the necessary Object
if (t!=null && !paramType.getName().equals(t.getClass().getName())){
for (Constructor crt : paramType.getConstructors()) {
if (crt.getParameterTypes()[0].getName().equals("java.lang.String")){
t = crt.newInstance(t.toString());
}
}
}
if (paramType.isPrimitive() && t==null){
//cannot cast null to primitve
} else {
Object[] arguments = new Object[]{ t };
m.invoke(returnObject,arguments);
}
} else {
log.error("could not find a setter-method from Structured table. Is there a setter-method for " + fieldName + " in Class " + targetClass.getName());
}
} else {
log.error("could not find a method from Structured table. Is there a method for " + fieldName + " in Class " + targetClass.getName());
}
} else if (Modifier.isPublic(mod) && !Modifier.isFinal(mod)){
if (t!=null && !anyField.getType().getName().equals(t.getClass().getName())){
for (Constructor crt : anyField.getType().getConstructors()) {
if (crt.getParameterTypes()[0].getName().equals("java.lang.String")){
t = crt.newInstance(t.toString());
}
}
//Is public attribute so set it directly
anyField.set(returnObject, t);
}
} else if (Modifier.isFinal(mod)) {
log.error("Final attributes cannot be changed ");
} else {
log.error("Unhandled Modifier Type: " + mod);
}
} else {
//This will cast nested Object to the current Object
//it does not matter how deep it is nested
// log.error("fieldType "+fieldType.getName());
//Check if the Attribute in the bean is a List
if (fieldType.getName().equals("java.util.Set")) {
//Todo: Cast Set to Object
// log.error("compareBeanTypeToAllowedListTypes true " + fieldType.getName());
// log.error("compareBeanTypeToAllowedListTypes true " + fieldName);
Object valueOfHashMap = values.get(fieldName);
if (valueOfHashMap!=null){
// log.error("compareBeanTypeToAllowedListTypes true " + valueOfHashMap.getClass().getName());
String valueTypeOfHashMap = valueOfHashMap.getClass().getName();
HashSet s = new HashSet();
if (this.compareTypeNameToAllowedListTypes(valueTypeOfHashMap)) {
Map m = (Map) valueOfHashMap;
for (Iterator it = m.keySet().iterator();it.hasNext();) {
String key = it.next().toString();
// log.error("key: "+key);
Object listObject = m.get(key);
// log.error("listObject: "+listObject);
// log.error("listObject: "+listObject.getClass().getName());
}
}
}
//otherwise do it as Object
} else {
// log.error("otherwise do it as Object "+fieldType.getName());
Object valueOfHashMap = values.get(fieldName);
if (valueOfHashMap!=null){
String valueTypeOfHashMap = valueOfHashMap.getClass().getName();
if (this.compareTypeNameToAllowedListTypes(valueTypeOfHashMap)) {
log.error(valueTypeOfHashMap);
log.error(fieldType.getName());
//Get value from set
Object t = this.castByGivenObject((Map)valueOfHashMap, fieldType);
int mod = anyField.getModifiers();
if (Modifier.isPrivate(mod) && !Modifier.isFinal(mod)){
//log.info("is private so get setter method "+fieldName);
LinkedHashMap<String,Object> methodSummery = structuredMethodMap.get(fieldName);
if (methodSummery!=null) {
if (methodSummery.get("setter")!=null) {
String methodSetterName = methodSummery.get("setter").toString();
Class[] paramTypes = (Class[]) methodSummery.get("setterParamTypes");
Method m = targetClass.getMethod(methodSetterName, paramTypes);
Class paramType = paramTypes[0];
//log.error("paramType: "+paramType.getName());
if (paramType.isPrimitive() && t==null){
//cannot cast null to primitve
} else {
Object[] arguments = new Object[]{ t };
m.invoke(returnObject,arguments);
}
} else {
log.error("could not find a setter-method from Structured table. Is there a setter-method for " + fieldName + " in Class " + targetClass.getName());
}
} else {
log.error("could not find a method from Structured table. Is there a method for " + fieldName + " in Class " + targetClass.getName());
}
} else if (Modifier.isPublic(mod) && !Modifier.isFinal(mod)){
//Is public attribute so set it directly
anyField.set(returnObject, t);
} else if (Modifier.isFinal(mod)) {
log.error("Final attributes cannot be changed ");
} else {
log.error("Unhandled Modifier Type: " + mod);
}
}
} else {
//There is no nested Object for that given
log.error("There is no nested Object for that given: Attribute: " + fieldName + " Class " + targetClass.getName());
}
}
}
}
return returnObject;
} catch (Exception ex) {
log.error("[castByGivenObject]: " ,ex);
}
return null;
}
private boolean compareTypeNameToBasicTypes(String fieldTypeName) {
try {
for (Iterator it = CastBasicTypes.getCompareTypesSimple().iterator();it.hasNext();) {
if (fieldTypeName.equals(it.next())) return true;
}
return false;
} catch (Exception ex) {
log.error("[compareTypeNameToBasicTypes]",ex);
return false;
}
}
private boolean compareTypeNameToAllowedListTypes(String fieldTypeName) {
try {
//log.error("compareTypeNameToAllowedListTypes"+ fieldTypeName);
for (Iterator it = CastBasicTypes.getAllowedListTypes().iterator();it.hasNext();) {
if (fieldTypeName.equals(it.next())) return true;
}
return false;
} catch (Exception ex) {
log.error("[compareTypeNameToBasicTypes]",ex);
return false;
}
}
}