package org.kroz.activerecord; import java.lang.reflect.Field; import java.util.Hashtable; import org.kroz.activerecord.utils.ARConst; import org.kroz.activerecord.utils.Logg; import android.util.Log; /** * Provides convenience methods to handle entites and corresponding classes * * @author Vladimir Kroz */ public class EntitiesHelper { private static final String AR_BASE_CLASS_NAME = ActiveRecordBase.class .getSimpleName(); private static final String AR_PK_FIELD_JAVA_NAME = "id"; /** * Copies all fields from src to dst which have the same name and type * * @param dst * destination object * @param src * source object * @return populated destination object on success, null otherwise */ static public <T1, T2> T1 copyFields(T1 dst, T2 src) { if (null == src) { Logg.w(ARConst.TAG, "(%t) %s error: null source object", EntitiesHelper.class.getName()); return null; } if (null == dst) { Logg.w(ARConst.TAG, "(%t) %s error: null destination object", EntitiesHelper.class.getName()); return null; } // Build list of fields in target object Hashtable<String, Field> dstFields = new Hashtable<String, Field>(); for (Field field : dst.getClass().getFields()) { dstFields.put(field.getName(), field); } // Build list of fields in source object Hashtable<String, Field> srcFields = new Hashtable<String, Field>(); for (Field field : src.getClass().getFields()) { srcFields.put(field.getName(), field); } try { copyPkFields(dst, src, dstFields, srcFields); copyDataFields(dst, src, dstFields, srcFields); } catch (SecurityException e) { Logg.w(ARConst.TAG, e, "(%t) %s error: %s", EntitiesHelper.class.getName(), e.getLocalizedMessage()); return null; } catch (IllegalArgumentException e) { Logg.w(ARConst.TAG, e, "(%t) %s error: %s", EntitiesHelper.class.getName(), e.getLocalizedMessage()); return null; } catch (NoSuchFieldException e) { Logg.w(ARConst.TAG, e, "(%t) %s error: %s", EntitiesHelper.class.getName(), e.getLocalizedMessage()); return null; } catch (IllegalAccessException e) { Logg.w(ARConst.TAG, e, "(%t) %s error: %s", EntitiesHelper.class.getName(), e.getLocalizedMessage()); return null; } return dst; } /** * Copies all fields from src to dst which have the same name and type, * except id or _id field * * @param dst * destination object * @param src * source object * @return populated destination object on success, null otherwise */ static public <T1, T2> T1 copyFieldsWithoutID(T1 dst, T2 src) { if (null == src) { Logg.w(ARConst.TAG, "(%t) %s error: null source object", EntitiesHelper.class.getName()); return null; } if (null == dst) { Logg.w(ARConst.TAG, "(%t) %s error: null source object", EntitiesHelper.class.getName()); return null; } // Build list of fields in target object Hashtable<String, Field> dstFields = new Hashtable<String, Field>(); for (Field field : dst.getClass().getFields()) { dstFields.put(field.getName(), field); } // Build list of fields in source object Hashtable<String, Field> srcFields = new Hashtable<String, Field>(); for (Field field : src.getClass().getFields()) { srcFields.put(field.getName(), field); } try { copyDataFields(dst, src, dstFields, srcFields); } catch (IllegalArgumentException e) { e.printStackTrace(); dst = null; } catch (IllegalAccessException e) { e.printStackTrace(); dst = null; } return dst; } /** * Copies ID field. If dst or src are subclasses of ActiveRecordBase, then * copies id fields of parent class * * @param dst * @param src * @param dstFields * @throws NoSuchFieldException * @throws SecurityException * @throws IllegalAccessException * @throws IllegalArgumentException */ private static <T2, T1> void copyPkFields(T1 dst, T2 src, Hashtable<String, Field> dstFields, Hashtable<String, Field> srcFields) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { boolean srcIsAR = (src.getClass().getSuperclass().getSimpleName() .equals(AR_BASE_CLASS_NAME)) ? true : false; boolean dstIsAR = (dst.getClass().getSuperclass().getSimpleName() .equals(AR_BASE_CLASS_NAME)) ? true : false; boolean dstHasId = (dstFields.containsKey(AR_PK_FIELD_JAVA_NAME)) ? true : false; boolean srcHasId = (srcFields.containsKey(AR_PK_FIELD_JAVA_NAME)) ? true : false; if (srcIsAR && dstIsAR) { ((ActiveRecordBase) dst)._id = ((ActiveRecordBase) src)._id; } else if (srcIsAR && dstHasId) { Field dstId = dst.getClass().getField("id"); if (dstId.getType().getSimpleName().equals("long")) { dstId.setLong(dst, ((ActiveRecordBase) src)._id); } else { Logg.w(ARConst.TAG, "(%t) %s.copyPkFields(): field '%s.%s' must have type 'long'. Copy operation skipped", EntitiesHelper.class.getName(), dst.getClass() .getSimpleName(), dstId.getName()); throw new IllegalArgumentException( String.format( "Field '%s.%s' must have type 'long'. Copy operation skipped", dst.getClass().getSimpleName(), dstId.getName())); } } else if (srcHasId && dstIsAR) { Field srcId = src.getClass().getField("id"); if (srcId.getType().getSimpleName().equals("long")) { ((ActiveRecordBase) dst)._id = srcId.getLong(src); } else if (srcId.getType().getSimpleName().equals("int")) { ((ActiveRecordBase) dst)._id = srcId.getInt(src); } else { Logg.w(ARConst.TAG, "%s.copyPkFields(): field '%s.%s' must have type 'long' or 'int'. Copy operation skipped", EntitiesHelper.class.getName(), src.getClass() .getSimpleName(), srcId.getName()); throw new IllegalArgumentException( String.format( "Field '%s.%s' must have type 'long' or 'int'. Copy operation skipped", src.getClass().getSimpleName(), srcId.getName())); } } else if (srcHasId && dstHasId) { Field srcId = src.getClass().getField("id"); Field dstId = dst.getClass().getField("id"); dstId.setLong(dst, srcId.getLong(src)); } } private static <T2, T1> void copyDataFields(T1 dst, T2 src, Hashtable<String, Field> dstFields, Hashtable<String, Field> srcFields) throws IllegalArgumentException, IllegalAccessException { // Iterate through fields of source object for (Field srcField : srcFields.values()) { String srcFieldName = srcField.getName(); // Skip ID field - it's copied by special method if (srcFieldName.equalsIgnoreCase("id")) { if (dstFields.containsKey("issueID")) { Field dstField = dstFields.get("issueID"); Object srcValue = srcField.get(src); Log.i("MagazinReader", "copying id to issueID: " + srcValue); dstField.set(dst, srcValue); } continue; } // If destination object has field corresponding with the field // in source object // Then copy value from source field to destination if (dstFields.containsKey(srcFieldName)) { // Get destination field from list Field dstField = dstFields.get(srcFieldName); // Additional check - fields should have similar type String srcFldTypeName = srcField.getType().getSimpleName(); String dstFldTypeName = dstField.getType().getSimpleName(); if (!srcFldTypeName.equals(dstFldTypeName)) continue; // Assign values // Need this long constructto handle various types via // reflection mechanism if (dstFldTypeName.equals("long")) { long srcValue; srcValue = srcField.getLong(src); dstField.setLong(dst, srcValue); } if (dstFldTypeName.equals("int")) { int srcValue = srcField.getInt(src); dstField.setInt(dst, srcValue); } else if (dstFldTypeName.equals("short")) { short srcValue = srcField.getShort(src); dstField.setShort(dst, srcValue); } else if (dstFldTypeName.equals("float")) { float srcValue = srcField.getFloat(src); dstField.setFloat(dst, srcValue); } else if (dstFldTypeName.equals("double")) { double srcValue = srcField.getDouble(src); dstField.setDouble(dst, srcValue); } else if (dstFldTypeName.equals("boolean")) { boolean srcValue = srcField.getBoolean(src); dstField.setBoolean(dst, srcValue); } else { Object srcValue = srcField.get(src); dstField.set(dst, srcValue); } } } } }