package com.cim.idm.wmsextend.generic.util;

import com.cim.idm.framework.data.EventInfo;
import com.cim.idm.framework.data.TransitionInfo;
import com.cim.idm.framework.exception.ErrorSignal;
import com.cim.idm.framework.exception.IDMFrameErrorSignal;
import com.cim.idm.framework.exception.ext.*;
import com.cim.idm.framework.orm.MetaAttribute;
import com.cim.idm.framework.orm.PersistentClass;
import com.cim.idm.framework.orm.info.DataInfo;
import com.cim.idm.framework.orm.info.KeyInfo;
import com.cim.idm.framework.orm.info.UserColumnMark;
import com.cim.idm.framework.orm.info.access.ObjectValueUserColumnsAccessor;
import com.cim.idm.framework.orm.info.access.UserColumnsAccessor;
import com.cim.idm.framework.util.time.TimeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class ObjectUtil
{
	private static Log	log	= LogFactory.getLog(ObjectUtil.class);

	public static Object newInstanceByClassName(String className)
	{
		try
		{
			Class<?> clazz = Class.forName(className);
			return clazz.newInstance();
		} 
		catch (Exception e)
		{
			return null;
		}
	}
	
	public static Object copyTo(Object object) throws IDMFrameErrorSignal
	{
		try
		{
			Object copyData = object.getClass().newInstance();
			copyField(object, copyData);
			
			try
			{
				KeyInfo sourceKey = getKeyInfo(object);
				Object copyKeyData = sourceKey.getClass().newInstance();
				copyField(sourceKey, copyKeyData);
				setFieldValue(copyData, "key", copyKeyData);
			} 
			catch (Exception e)
			{}
			
			return copyData;
		} 
		catch (Throwable e)
		{
			throw ErrorSignal.getNotifyException(e);
		}
	}
	/**
	 * Copy the property values of the given <code>DataInfo</code>
	 * <p>Note: the <code>DataInfo</code> should like a bean ,The input <code>DataInfo</code> should contain get and set function for each property. 
	 * <p>This is just a convenience method. For more complex transfer needs,
	 * consider using a full BeanWrapper.
	 * @param object the input <code>DataInfo</code>
	 * @return a copy of <code>DataInfo</code>
	 */
	public static DataInfo copyToByBean(DataInfo object)
	{
		DataInfo copyData = null;
		try {
			copyData = object.getClass().newInstance();
		} catch (InstantiationException e) {
			throw new FrameworkErrorSignal("Empty constructor is not define.", e.getMessage());
		} catch (IllegalAccessException e) {
			throw new FrameworkErrorSignal("Illegal Access Privilege.",e.getMessage());
		}
		BeanUtils.copyProperties(object, copyData);
		return copyData;
	}
	
	public static void copyField4HistoryAdaptor(Object source, String sourceFieldName, Object target, String targetFieldName)
	{
		try
		{
			if (sourceFieldName.equals("userColumns") && targetFieldName.equals("userColumns"))
			{
				Map<String, String> userColumns = getUserColumsValue(source);
				setUserColumsValueToUserColumns(target, userColumns);
				return;
			}

			Field sourceField = source.getClass().getDeclaredField(sourceFieldName);
			Field targetField = target.getClass().getDeclaredField(targetFieldName);

			sourceField.setAccessible(true);
			targetField.setAccessible(true);
			copyFieldValue(targetField, target, sourceField.get(source));
		} 
		catch (Exception e)
		{} 
	}

	public static void copyField(Object source, String sourceFieldName, Object target, String targetFieldName)
	{
		try
		{
			if (sourceFieldName.equals("userColumns") && targetFieldName.equals("userColumns"))
			{
				Map<String, String> userColumns = getUserColumsValue(source);
				setUserColumsValueToUserColumns(target, userColumns);
				return;
			}

			Field sourceField = source.getClass().getDeclaredField(sourceFieldName);
			Field targetField = target.getClass().getDeclaredField(targetFieldName);

			sourceField.setAccessible(true);
			targetField.setAccessible(true);
			copyFieldValue(targetField, target, sourceField.get(source));
		} 
		catch (Throwable e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
	}

	public static void copyField(Object source, Field sourceField, Object target, String targetFieldName)
	{
		try
		{
			Field targetField = target.getClass().getDeclaredField(targetFieldName);
			sourceField.setAccessible(true);
			targetField.setAccessible(true);
			copyFieldValue(targetField, target, sourceField.get(source));
		} 
		catch (Throwable e)
		{}
	}

	public static void copyField(Object source, Object target, String fieldName)
	{
		try
		{
			Field targetField = target.getClass().getDeclaredField(fieldName);
			Field sourceField = source.getClass().getDeclaredField(fieldName);
			sourceField.setAccessible(true);
			targetField.setAccessible(true);
			copyFieldValue(targetField, target, sourceField.get(source));
		} 
		catch (Throwable e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
	}

	public static void copyField(Object source, Object target)
	{
		try
		{
			Field[] sourcefields = source.getClass().getDeclaredFields();

			for (int i = 0; i < sourcefields.length; i++)
			{
				sourcefields[i].setAccessible(true);
				try
				{
					Field targetField = target.getClass().getDeclaredField(sourcefields[i].getName());
					targetField.setAccessible(true);
					copyFieldValue(targetField, target, sourcefields[i].get(source));
				} catch (Exception e)
				{}
			}

			if (source instanceof UserColumnsAccessor)
			{
				try
				{
					Map<String, String> userColumnValue = getUserColumsValue(source);
					setUserColumsValueToUserColumns(target, userColumnValue);
				} catch (Exception e)
				{}
			}
			
			if (source instanceof ObjectValueUserColumnsAccessor){
				try{
					Map<String, Object> userColumnValue = getUserColumsObjectValue(source);
					setObjectValueUserColumnsToUserColumns(target, userColumnValue);
				}catch(Exception e){
				}
			}
		} 
		catch (Throwable e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
	}

	public static void copyFieldNotEqual(Object source, Object target)
	{
		try
		{
			Field[] sourcefields = source.getClass().getDeclaredFields();

			for (int i = 0; i < sourcefields.length; i++)
			{
				sourcefields[i].setAccessible(true);
				try
				{
					Field targetField = target.getClass().getDeclaredField(sourcefields[i].getName());
					if (targetField != null)
					{
						targetField.setAccessible(true);
						copyFieldValue(targetField, target, sourcefields[i].get(source));
					}
				} catch (Exception e)
				{}
			}
			if (source instanceof UserColumnsAccessor)
			{
				try
				{
					Map<String, String> udfValue = getUserColumsValue(source);
					setUserColumsValueToUserColumns(target, udfValue);
				} catch (Exception e)
				{}
			}
		} 
		catch (Throwable e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
	}

	public static void copyField(Object target, String targetFieldName, Object value)
	{
		try
		{
			Field targetField = target.getClass().getDeclaredField(targetFieldName);
			targetField.setAccessible(true);
			copyFieldValue(targetField, target, value);
		}
		catch (Throwable e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
	}
	
	public static Object getPrimativeValue( Class<?> type, Object value )
	{
		if ( type.equals( Double.class ) || type.equals( double.class ))
			return Double.parseDouble(String.valueOf( value ));
		else if ( type.equals( Long.class ) || type.equals( long.class ))
			return Long.parseLong(String.valueOf( value ));
		else if ( type.equals( Integer.class ) || type.equals( int.class ))
			return Integer.parseInt(String.valueOf( value ));
		else if ( type.equals( Float.class ) || type.equals( float.class ))
			return Double.parseDouble(String.valueOf( value ));
		else if ( type.equals( Short.class ) || type.equals( short.class ))
			return Short.parseShort(String.valueOf( value ));
		else if ( type.equals( Boolean.class ) || type.equals( boolean.class ))
			return Boolean.parseBoolean(String.valueOf( value ));
		else
			return value;
	}

	public static void copyFieldValue(Field field, Object target, Object value)
	{
		try
		{
			Class<?> type = field.getType();
			
			field.setAccessible(true);
			
			if ( type.equals( Double.class ) || type.equals( double.class ))
				field.set(target, Double.parseDouble(String.valueOf(value != null ? value : 0.0 )));
			else if ( type.equals( Long.class ) || type.equals( long.class ))
				field.set(target, Long.parseLong(String.valueOf(value != null ? value : 0)));
			else if ( type.equals( Integer.class ) || type.equals( int.class ))
				field.set(target, Integer.parseInt(String.valueOf(value != null ? value : 0)));
			else if ( type.equals( Float.class ) || type.equals( float.class ))
				field.set(target, Double.parseDouble(String.valueOf(value != null ? value : 0.0)));
			else if ( type.equals( Short.class ) || type.equals( short.class ))
				field.set(target, Short.parseShort(String.valueOf(value != null ? value : 0.0)));
			else if ( type.equals( Boolean.class ) || type.equals( boolean.class ))
				field.set(target, Boolean.parseBoolean(String.valueOf(value != null ? value : false)));
			else if ( (type.equals( Timestamp.class )|| type.equals(Date.class)) && value != null)
				if(value.getClass().getName().equals("java.sql.Timestamp"))
					field.set(target, value);
				else if(value.getClass().getName().equals("java.util.Date"))
					field.set(target, new Timestamp(((Date)value).getTime()));
				else
					field.set(target, Timestamp.valueOf(String.valueOf(value)));
			else
			{
				if (field.getName().equalsIgnoreCase("key"))
				{
					if (ObjectUtil.getFieldValue(target, "key") != null && !ObjectUtil.getFieldValue(target, "key").getClass().equals(value.getClass()))
						return;
				}
				
				field.set(target, value);
			}
		} 
		catch (Throwable e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}

	}

	public static String getFieldNamesByCommaDelimeter(Object object)
	{
		StringBuilder strBuilder = new StringBuilder();
		
		try
		{
			Field[] fields = object.getClass().getDeclaredFields();
			
			for (int i = 0; i < fields.length; i++)
				strBuilder.append(fields[i].getName()).append(",");
		} 
		catch (Throwable e)
		{}
		
		return strBuilder.delete(strBuilder.length() - 1, strBuilder.length()).toString();
	}

	public static List<String> getFieldNames(Object object)
	{
		List<String> arrayList = new ArrayList<String>();
		
		try
		{
			Field[] fields = object.getClass().getDeclaredFields();
			
			for (int i = 0; i < fields.length; i++)
				arrayList.add(fields[i].getName());
		} 
		catch (Exception e)
		{}
		
		return arrayList;
	}
	
	public static List<String> getFieldNamesWithKey(DataInfo object)
	{
		List<String> arrayList = new ArrayList<String>();
		
		try
		{
			Field[] fields = object.getClass().getDeclaredFields();
			
			for (int i = 0; i < fields.length; i++)
				arrayList.add(fields[i].getName());
			
			Field[] keyFields = null;
			
			
			if(object.getKey() == null)
			{
				KeyInfo historyDataKey =
						(KeyInfo) ObjectUtil.newInstanceByClassName(object.getClass().getName() + "Key");
				
				keyFields = historyDataKey.getClass().getDeclaredFields();
				
			}
			else
			{
				keyFields = object.getKey().getClass().getDeclaredFields();
			}
			for ( Field field : keyFields )
			{
				arrayList.add(field.getName());
			}
		} 
		catch (Exception e)
		{}
		
		return arrayList;
	}

	public static Object[] appendFirstObjects(Object[] obj, Object append)
	{
		List<Object> objects = new ArrayList<Object>();
		objects.add(append);
		
		if (obj != null)
		{
			for (int i = 0; i < obj.length; i++)
				objects.add(obj[i]);
		}
		
		return objects.toArray();
	}

	public static Object[] appendObjects(Object[] obj, Object append)
	{
		List<Object> objects = new ArrayList<Object>();
		
		if (obj != null)
		{
			for (int i = 0; i < obj.length; i++)
				objects.add(obj[i]);
		}
		
		objects.add(append);
		return objects.toArray();
	}

	public static Object[] appendObjects(Object[] obj, Object[] append)
	{
		List<Object> objects = new ArrayList<Object>();
		
		if (obj != null)
		{
			for (int i = 0; i < obj.length; i++)
				objects.add(obj[i]);
		}
		
		for (int i = 0; i < append.length; i++)
			objects.add(append[i]);
		
		return objects.toArray();
	}

	public static List<Object> getFieldValues(Object object)
	{
		List arrayList = new ArrayList();
		
		try
		{
			Field[] fields = object.getClass().getDeclaredFields();
			for (int i = 0; i < fields.length; i++)
			{
				fields[i].setAccessible(true);
				arrayList.add(fields[i].get(object));
			}
		} 
		catch (Exception e)
		{}
		
		return arrayList;
	}
	
	public static Field getField(Object object, String fieldName)
	{
		try {
			return  object.getClass().getDeclaredField(fieldName);
		} catch (Exception e) {
			return null;
		}
	}
	

	
	public static Map<String, Object> getFieldValueMapOnlyData( DataInfo object )
	{
		Map<String, Object> map = new HashMap<String, Object>();
		
		Field[] fields = object.getClass().getDeclaredFields();
		for ( Field field : fields )
		{
			try
			{
				field.setAccessible( true );
				map.put( field.getName(), field.get( object ));
			}
			catch ( Exception ex )
			{
				if ( log.isDebugEnabled())
					log.error( ex, ex );
				else
					log.error( ex );
			}
		}
				
		return map;
	}
	
	public static Map<String, Object> getFieldValueMapOnlyDataWithValue( DataInfo object )
	{
		Map<String, Object> map = new HashMap<String, Object>();
		
		Field[] fields = object.getClass().getDeclaredFields();
		for ( Field field : fields )
		{
			try
			{
				field.setAccessible( true );
				if(field.get(object) != null)
					map.put( field.getName(), field.get( object ));
			}
			catch ( Exception ex )
			{
				if ( log.isDebugEnabled())
					log.error( ex, ex );
				else
					log.error( ex );
			}
		}
				
		return map;
	}
	
	public static Map<String, Object> getFieldValueMapOnlyKey( Object object )
	{
		boolean bDataInfo =  false;
		Map<String, Object> map = new HashMap<String, Object>();
		Field[] keyFields = null;				
		if (object instanceof DataInfo)
		{
			bDataInfo = true;
			keyFields = ((DataInfo)object).getKey().getClass().getDeclaredFields();
		}
		else if (object instanceof KeyInfo)
		{
			keyFields = object.getClass().getDeclaredFields();
		}
		
		for ( Field field : keyFields )
		{
			try
			{
				field.setAccessible( true );
				if (bDataInfo)
					map.put( field.getName(), field.get( ((DataInfo)object).getKey()));
				else
					map.put( field.getName(), field.get( object));
					
			}
			catch ( Exception ex )
			{
				if ( log.isDebugEnabled())
					log.error( ex, ex );
				else
					log.error( ex );
			}
		}
		
		return map;
	}
	
	
	
	public static Map<String, Object> getFieldValueMap( DataInfo object )
	{
		Map<String, Object> map = new HashMap<String, Object>();
		
		Field[] fields = object.getClass().getDeclaredFields();
		for ( Field field : fields )
		{
			try
			{
				field.setAccessible( true );
				map.put( field.getName(), field.get( object ));
			}
			catch ( Exception ex )
			{
				if ( log.isDebugEnabled())
					log.error( ex, ex );
				else
					log.error( ex );
			}
		}
		
		Field[] keyFields = null;
		
		
		if(object.getKey() == null)
		{
			KeyInfo historyDataKey =
					(KeyInfo) ObjectUtil.newInstanceByClassName(object.getClass().getName() + "Key");
			
			keyFields = historyDataKey.getClass().getDeclaredFields();
			
		}
		else
		{
			keyFields = object.getKey().getClass().getDeclaredFields();
		}
		for ( Field field : keyFields )
		{
			try
			{
				field.setAccessible( true );
				map.put( field.getName(), field.get( object.getKey()));
			}
			catch ( Exception ex )
			{
				if ( log.isDebugEnabled())
					log.error( ex, ex );
				else
					log.error( ex );
			}
		}
		
		return map;
	}

	public static Object getFieldValue(Object object, String fieldName)
	{
		try
		{
			if (fieldName.equalsIgnoreCase("userColumns"))
				return getUserColumsValue(object);

			Field field = object.getClass().getDeclaredField(fieldName);
			field.setAccessible(true);
			return field.get(object);
		} 
		catch (NoSuchFieldException e)
		{
//			String newFieldName = getChangeField(fieldName);
//			if (!newFieldName.equals(fieldName))
//				return getFieldValue(object, newFieldName);
			String newFieldName = findFieldNameIgnoreCase(fieldName, object.getClass().getDeclaredFields());
			if (!StringUtils.isEmpty(newFieldName))
			{
				try
				{
					Field field = object.getClass().getDeclaredField(fieldName);
					field.setAccessible(true);
					return field.get(object);
				}catch (NoSuchFieldException e1)
				{
					if (log.isDebugEnabled())
						log.warn(e, e);
					else
						log.warn(e);
				}
				catch (Exception e1)
				{
					if (log.isDebugEnabled())
						log.warn(e, e);
					else
						log.warn(e);
				}
			}			
		} 
		catch (Exception e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
		
		return null;
	}

	public static Object getUserColumFieldValue(Object object, String fieldName)
	{
		return getUserColumsValue(object).get(fieldName);
	}

	public static Map<String, String> getUserColumsValue(Object dataInfo)
	{
		try
		{
			if(dataInfo instanceof UserColumnsAccessor)
				return (( UserColumnsAccessor ) dataInfo) .getUserColumns();
			else
				return null;
		}
		catch (Exception e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
		
		return null;
	}
	
	public static Map<String, Object> getUserColumsObjectValue(Object dataInfo)
	{
		try
		{
			if(dataInfo instanceof ObjectValueUserColumnsAccessor)
				return (( ObjectValueUserColumnsAccessor ) dataInfo) .getUserColumns();
			else
				return null;
		}
		catch (Exception e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
		
		return null;
	}

	public static String getChangeField(String fieldName)
	{
		char first = fieldName.charAt(0);
		int asciiCode = (int) first;
		
		if (asciiCode >= 65 && asciiCode <= 90)
		{
			String fisrt = fieldName.substring(0, 1);
			return fisrt.toLowerCase() + fieldName.substring(1, fieldName.length());
		}
		
		return fieldName;
	}
	
	public static String findFieldNameIgnoreCase(String fieldName, Field[] fiedls)
	{
		for(Field field : fiedls)
		{
			if(fieldName.equalsIgnoreCase(field.getName()))
				return field.getName();
		}
		return "";
	}

	public static boolean hasFiled(Object object, String fieldName)
	{
		try
		{
			Field field = object.getClass().getDeclaredField(fieldName);
			return true;
		} 
		catch (NoSuchFieldException ex)
		{
			String newFieldName = getChangeField(fieldName);
			if (!newFieldName.equals(fieldName))
				return hasFiled(object, newFieldName);
		} 
		catch (Throwable e)
		{}
		
		return false;
	}

	public static void setFieldValue(Object object, String fieldName, Object value)
	{
		try
		{
			if (fieldName.equals("userColumns"))
				setUserColumsValueToUserColumns(object, (Map<String, String>) value);
			else if(fieldName.equals("objectValueUserColumns"))
				setObjectValueUserColumnsToUserColumns(object, (Map<String, ?>)value);
			else
			{
				try
				{
					Field field = object.getClass().getDeclaredField(fieldName);
					field.setAccessible(true);
					copyFieldValue(field, object, value);
				} 
				catch (NoSuchFieldException e)
				{
					//e.printStackTrace();
					fieldName = getChangeField(fieldName);
					Field field = object.getClass().getDeclaredField(fieldName);
					field.setAccessible(true);
					copyFieldValue(field, object, value);
				}
			}
		} 
		catch (Exception e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
	}

	public static void setUserColumsValueToUserColumns(Object dataInfo, Map<String, String> value)
	{
		try
		{
			((UserColumnsAccessor)dataInfo).setUserColumns( value );
		} 
		catch (Exception e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
	}

	public static void setObjectValueUserColumnsToUserColumns(Object dataInfo, Map<String, ?> value)
	{
		try
		{
			((ObjectValueUserColumnsAccessor)dataInfo).setUserColumns( value );
		} 
		catch (Exception e)
		{
			if (log.isDebugEnabled())
				log.warn(e, e);
			else
				log.warn(e);
		}
	}
	
	/**
	 * Set up "Target UDF" by using component of "Source UDF".
	 * 
	 * <pre>
	 * If "Target UDF" is empty, fill all component of "Source UDF".
	 * If it is not empty, set up "Target value" by using "Source Value" that are in line with "Source" and "Target of Key" 
	 * </pre>
	 * 
	 * @param targetuserColumns
	 *            
	 * @param sourceuserColumns
	 *            
	 */
	public static void setUserColumns(Map<String, String> targetUserColumns, Map<String, String> sourceUserColumns)
	{
		if(targetUserColumns == null || sourceUserColumns == null)
		{
			log.warn("TargetUsercolumnns or SourceUserColumns can't be null");
			return;
		}
		if (targetUserColumns == null || targetUserColumns.size() == 0)
		{
			for (String srcKkey : sourceUserColumns.keySet())
				targetUserColumns.put(srcKkey, sourceUserColumns.get(srcKkey));
		}
		else
		{
			for (String srcKkey : sourceUserColumns.keySet())
			{
				if (targetUserColumns.containsKey(srcKkey))
					targetUserColumns.put(srcKkey, sourceUserColumns.get(srcKkey));
			}
		}
	}

	public static boolean isNullOrNullString(Object object)
	{

		try
		{
			Field[] fields = object.getClass().getDeclaredFields();

			for (int i = 0; i < fields.length; i++)
			{
				fields[i].setAccessible(true);
				if (fields[i].get(object) == null)
					return true;
				else if (fields[i].get(object) instanceof String)
				{
					if (fields[i].get(object).toString().length() == 0)
						return true;
				}
			}
		} 
		catch (Exception e)
		{}
		
		return false;
	}

	public static String getString(KeyInfo keyInfo)
	{
		if (keyInfo == null)
			return "";
		
		StringBuilder strBuilder = new StringBuilder();
		try
		{
			Class c = Class.forName(keyInfo.getClass().getName());
			Method[] methods = c.getMethods();
			for (int i = 0; i < methods.length; i++)
			{
				if (methods[i].getName().startsWith("get") && !methods[i].getName().equalsIgnoreCase("getclass"))
				{
					strBuilder.append(methods[i].getName().substring(3, methods[i].getName().length()))
							.append(":")
							.append(methods[i].invoke(keyInfo, null).toString());
					strBuilder.append(",");
				}
			}
			strBuilder.setLength(strBuilder.length() - 1);
		} 
		catch (Exception e)
		{}
		
		return strBuilder.toString();
	}
	
	public static String getString(KeyInfo[] keyInfoList)
	{
		if (keyInfoList == null || keyInfoList.length == 0)
			return "";
		
		StringBuilder strBuilder = new StringBuilder();
		try
		{
			Class c = Class.forName(keyInfoList[0].getClass().getName());
			Method[] methods = c.getMethods();
			int nKeyCount = 0;
			for(KeyInfo key : keyInfoList)
			{
				
				strBuilder.append(nKeyCount +"st={ ");
				for (int i = 0; i < methods.length; i++)
				{
					if (methods[i].getName().startsWith("get") && !methods[i].getName().equalsIgnoreCase("getclass"))
					{
						strBuilder.append(methods[i].getName().substring(3, methods[i].getName().length()))
								.append(":")
								.append(methods[i].invoke(key, null).toString());
						strBuilder.append(",");
					}
				}
				strBuilder.setLength(strBuilder.length() - 1);
				strBuilder.append(" } \t");
				nKeyCount++;
			}
		} 
		catch (Exception e)
		{}
		
		return strBuilder.toString();
	}

	public static String getString(Map<String, ?> map)
	{
		if (map == null)
			return "";
		
		StringBuilder strBuilder = new StringBuilder();
		try
		{
			Iterator keys = map.keySet().iterator();
			while (keys.hasNext())
			{
				String keyName = (String) keys.next();
				strBuilder.append(keyName).append("=").append(map.get(keyName)).append(",");
			}
			
			strBuilder.setLength(strBuilder.length() - 1);
		} 
		catch (Exception e)
		{}
		
		return strBuilder.toString();
	}

	public static String getString(DataInfo dataInfo)
	{
		if (dataInfo == null)
			return "";
		
		StringBuilder strBuilder = new StringBuilder();
		KeyInfo keyInfo = getKeyInfo(dataInfo);
		
		if (keyInfo != null)
		{
			strBuilder.append(getString(keyInfo));
			strBuilder.append(",");
		}
		try
		{
			Method[] methods = dataInfo.getClass().getMethods();
			for (Method method : methods)
			{
				if (method.getName().startsWith("get")
					&& method.getName().equalsIgnoreCase("getClass") == false
					&& method.getName().equalsIgnoreCase("getKey") == false)
				{
					strBuilder.append(method.getName().substring(3, method.getName().length()));
					strBuilder.append(":");
					strBuilder.append(method.invoke(dataInfo, null).toString());
					strBuilder.append(",");
				}
			}
			strBuilder.setLength(strBuilder.length() - 1);
		} 
		catch (Exception e)
		{}
		
		return strBuilder.toString();
	}
	


	public static String getString(String where, Object[] bindSet)
	{
		if (bindSet == null)
			return where;
		
		StringBuilder str = new StringBuilder();
		String v = "";
		int j = 0;
		
		for (int i = 0; i < where.length(); i++)
		{
			char a = where.charAt(i);
			v = String.valueOf(a);
			
			if (v.equalsIgnoreCase("?"))
			{
				str.append(bindSet[j]);
				j++;
			}
			else
				str.append(v);
		}
		
		return str.toString();
	}

	public static Object[] getObjectValue(Map<String, ?> map)
	{
		return map.values().toArray();
	}

	public static String getString(Object[] bindSet)
	{
		if (bindSet == null || bindSet.length == 0)
			return "";
		
		StringBuilder str = new StringBuilder();
		
		for (int i = 0; i < bindSet.length; i++)
			str.append(bindSet[i]).append(",");
		
		return str.delete(str.length() - 1, str.length()).toString();
	}

	public static Object convertBigDecimal(BigDecimal obj)
	{
		if (!obj.toString().contains("."))
			return Long.valueOf(obj.toString());
		else
			return Double.valueOf(obj.toString());

	}

	public static KeyInfo getKeyInfo(Object dataInfo)
	{
		if (dataInfo instanceof DataInfo)
		{
			try
			{
				return (KeyInfo) ObjectUtil.getFieldValue(dataInfo, "key");
			} 
			catch (Exception e)
			{
				if (log.isDebugEnabled())
					log.warn(e, e);
				else
					log.warn(e);
			}
			return null;
		}
		
		else if (dataInfo instanceof KeyInfo)
			return (KeyInfo) dataInfo;
		else
			return null;
	}
	
	/**
	 * 
	 * @param dataInfo
	 *            extends com.cim.idm.framework.orm.info.DataInfo
	 * @param eventInfo
	 *            extends com.cim.idm.framework.data.EventInfo
	 * @param transitionInfo
	 *            extends com.cim.idm.framework.data.TransitionInfo
	 * @return N/A
	 * @throws InvalidStateTransitionSignal
	 * @throws FrameworkErrorSignal
	 * @throws NotFoundSignal
	 * @throws DuplicateNameSignal
	 * @test
	 * @code
	 * @endcode
	 */
	public static void setEventInfo(DataInfo dataInfo, EventInfo eventInfo, TransitionInfo transitionInfo)
			throws FrameworkErrorSignal
	{
		if (StringUtils.isEmpty(eventInfo.getEventName()))
		{
			String eventName = transitionInfo.getClass().getSimpleName();
			if (eventName.endsWith("Info"))
				eventInfo.setEventName(eventName.substring(0, eventName.length() - 4));
			else
				eventInfo.setEventName(eventName);
		}

		String datalastEventtimeKey = (String) ObjectUtil.getFieldValue(dataInfo, "lastEventTimeKey");

		if (StringUtils.isNotEmpty(eventInfo.getLastEventTimekey()))
		{
			if (eventInfo.getLastEventTimekey().equals(datalastEventtimeKey) == false)
			{
				throw new FrameworkErrorSignal(ExceptionKey.InvalidTransaction_Exception,
						ObjectUtil.getString(dataInfo.getKey()), datalastEventtimeKey, "EventInfo",
						eventInfo.getLastEventTimekey());
			}
		}
		else
		{
			eventInfo.setLastEventTimekey("");
		}
		
		// if eventInfo's lastEventtimekey is Empty, so set eventInfo's lastEventTimekey. and if lastEventtime is null , set lastEventTime .
//		TimeUtils.setTime4EventInfo(datalastEventtimeKey, eventInfo);

		if (StringUtils.isNotEmpty(datalastEventtimeKey))
		{
			int timeKeyDiff = datalastEventtimeKey.compareTo(eventInfo.getEventTimeKey());
			if (timeKeyDiff >= 0)
			{
				log.warn(String.format("Check Timekey validation: %s (LastEventTimekey:%s, NewEventTimekey:%s)",
						eventInfo.isCheckTimekeyValidation(), datalastEventtimeKey, eventInfo.getEventTimeKey()));

				if (eventInfo.isCheckTimekeyValidation())
				{
					throw new FrameworkErrorSignal(ExceptionKey.InvalidEventTime_Exception,
							ObjectUtil.getString(dataInfo.getKey()), eventInfo.getEventName(),
							eventInfo.getEventTimeKey(), (String) ObjectUtil.getFieldValue(dataInfo, "lastEventName"),
							datalastEventtimeKey);
				}
				else
				{
					//if checkTimekeyValidation is false, set a new eventTimekey for this eventInfo.
					try
					{
						//if using Sequence4Timekey , set EventTimekey with DB sequence
						//if not, using lastEventTimekey+ 1(s) 
						if ( new TimeUtils().isUseSequence4TimeKey() )
						{
							eventInfo.setEventTimeKey(new TimeUtils().getCurrentEventTimeKey());
						}
						else
						{
							SimpleDateFormat sdf = new SimpleDateFormat(TimeUtils.FORMAT_SEQTIMEKEY);
							Date date;
							date = sdf.parse(datalastEventtimeKey);

							Calendar calendar = Calendar.getInstance();
							calendar.setTime(date);
							calendar.add(Calendar.MILLISECOND, 1);

							String newTimekey = sdf.format(calendar.getTime());
							eventInfo.setEventTimeKey(newTimekey);
						}
						log.info(String.format("Increment EventTimekey (LastEventTimekey:%s, NewEventTimekey:%s)", 
								datalastEventtimeKey, eventInfo.getEventTimeKey()));
					}
					catch ( ParseException e )
					{
						throw new FrameworkErrorSignal(ExceptionKey.UnExpected_Exception, e);
					}
				}
			}
		}

		ObjectUtil.setFieldValue(dataInfo, "lastEventName", eventInfo.getEventName());
		ObjectUtil.setFieldValue(dataInfo, "lastEventTimeKey", eventInfo.getEventTimeKey());
		ObjectUtil.setFieldValue(dataInfo, "lastEventTime", eventInfo.getEventTime());
		ObjectUtil.setFieldValue(dataInfo, "lastEventUser", eventInfo.getEventUser());
		ObjectUtil.setFieldValue(dataInfo, "lastEventComment", eventInfo.getEventComment());

		if (ObjectUtil.hasFiled(dataInfo, "lastEventFlag"))
			ObjectUtil.setFieldValue(dataInfo, "lastEventFlag", "N");

		if (ObjectUtil.hasFiled(dataInfo, "reasonCodeType"))
			ObjectUtil.setFieldValue(dataInfo, "reasonCodeType", eventInfo.getReasonCodeType());
		if (ObjectUtil.hasFiled(dataInfo, "reasonCode"))
			ObjectUtil.setFieldValue(dataInfo, "reasonCode", eventInfo.getReasonCode());

		if ("Lot".equalsIgnoreCase(dataInfo.getClass().getSimpleName()))
		{
			ObjectUtil.setFieldValue(dataInfo, "sourceLotName", "");
			ObjectUtil.setFieldValue(dataInfo, "destinationLotName", "");
		}
		else if ("Product".equalsIgnoreCase(dataInfo.getClass().getSimpleName()))
		{
			ObjectUtil.setFieldValue(dataInfo, "sourceProductName", "");
			ObjectUtil.setFieldValue(dataInfo, "destinationProductName", "");
		}

	}
	public static void setEventInfo(DataInfo dataInfo, EventInfo eventInfo, TransitionInfo transitionInfo, PersistentClass persistentClass)
			throws FrameworkErrorSignal
	{
		if (StringUtils.isEmpty(eventInfo.getEventName()))
		{
			String eventName = transitionInfo.getClass().getSimpleName();
			if (eventName.endsWith("Info"))
				eventInfo.setEventName(eventName.substring(0, eventName.length() - 4));
			else
				eventInfo.setEventName(eventName);
		}

		String datalastEventtimeKey = (String) ObjectUtil.getFieldValue(dataInfo, "lastEventTimeKey");

		if (StringUtils.isNotEmpty(eventInfo.getLastEventTimekey()))
		{
			if (eventInfo.getLastEventTimekey().equals(datalastEventtimeKey) == false)
			{
				throw new FrameworkErrorSignal(ExceptionKey.InvalidTransaction_Exception,
						ObjectUtil.getString(dataInfo.getKey()), datalastEventtimeKey, "EventInfo",
						eventInfo.getLastEventTimekey());
			}
		}
		else
		{
			eventInfo.setLastEventTimekey("");
		}

//		TimeUtils.setTime4EventInfo(datalastEventtimeKey, eventInfo);

		if (StringUtils.isNotEmpty(datalastEventtimeKey))
		{
			int timeKeyDiff = datalastEventtimeKey.compareTo(eventInfo.getEventTimeKey());
			if (timeKeyDiff >= 0)
			{
				log.warn(String.format("Check Timekey validation: %s (LastEventTimekey:%s, NewEventTimekey:%s)",
						eventInfo.isCheckTimekeyValidation(), datalastEventtimeKey, eventInfo.getEventTimeKey()));

				if (eventInfo.isCheckTimekeyValidation())
				{
					throw new FrameworkErrorSignal(ExceptionKey.InvalidEventTime_Exception,
							ObjectUtil.getString(dataInfo.getKey()), eventInfo.getEventName(),
							eventInfo.getEventTimeKey(), (String) ObjectUtil.getFieldValue(dataInfo, "lastEventName"),
							datalastEventtimeKey);
				}
				else
				{
					try
					{
						if ( new TimeUtils().isUseSequence4TimeKey() )
						{
							eventInfo.setEventTimeKey(new TimeUtils().getCurrentEventTimeKey());
						}
						else
						{
							SimpleDateFormat sdf = new SimpleDateFormat(TimeUtils.FORMAT_SEQTIMEKEY);
							Date date;
							date = sdf.parse(datalastEventtimeKey);

							Calendar calendar = Calendar.getInstance();
							calendar.setTime(date);
							calendar.add(Calendar.MILLISECOND, 1);

							String newTimekey = sdf.format(calendar.getTime());
							eventInfo.setEventTimeKey(newTimekey);
						}
						log.info(String.format("Increment EventTimekey (LastEventTimekey:%s, NewEventTimekey:%s)", 
								datalastEventtimeKey, eventInfo.getEventTimeKey()));
					}
					catch ( ParseException e )
					{
						throw new FrameworkErrorSignal(ExceptionKey.UnExpected_Exception, e);
					}
				}
			}
		}

		persistentClass.addUpdateMetaAttribute(new MetaAttribute("lastEventName", eventInfo.getEventName()));
		persistentClass.addUpdateMetaAttribute(new MetaAttribute("lastEventTimeKey", eventInfo.getEventTimeKey()));
		persistentClass.addUpdateMetaAttribute(new MetaAttribute("lastEventTime", eventInfo.getEventTime()));
		persistentClass.addUpdateMetaAttribute(new MetaAttribute("lastEventUser", eventInfo.getEventUser()));
		persistentClass.addUpdateMetaAttribute(new MetaAttribute("lastEventComment",eventInfo.getEventComment()));

		if (ObjectUtil.hasFiled(dataInfo, "reasonCodeType"))
			persistentClass.addUpdateMetaAttribute(new MetaAttribute("reasonCodeType", eventInfo.getReasonCodeType()));
		if (ObjectUtil.hasFiled(dataInfo, "reasonCode"))
			persistentClass.addUpdateMetaAttribute(new MetaAttribute("reasonCode", eventInfo.getReasonCode()));

	}
	/**
	 * 
	 * @param dataObject
	 *           String
	 * @param sourceUserColums
	 *            Map<String, String>
	 * @return N/A
	 * @test
	 * @code
	 * @endcode
	 */
	public static void setUserColumsToData(DataInfo dataObject, Map<String, ?> sourceUserColums)
	{
		if(sourceUserColums==null) return;
		sourceUserColums.forEach((key, value)->{
			ObjectUtil.setFieldValue(dataObject, key, value);
		});
	}
	
	public static void setUserColumsValue(DataInfo dataObject, Map<String, String> sourceUserColums, PersistentClass persistentClass)
	{		
		if(sourceUserColums==null) return;
		for(String key : sourceUserColums.keySet())
		{
			Object value = sourceUserColums.get(key);
			try {
				Field field = dataObject.getClass().getDeclaredField(key);
				if(field!=null)
					persistentClass.addUpdateMetaAttribute(new MetaAttribute(field.getName(), value));
			} catch (SecurityException e) {
				log.warn(e,e);
			} catch (NoSuchFieldException e) {
				log.warn(e,e);
			}
		}
	}
	
	public static void setObjectUserColumnsToData(DataInfo dataObject, Map<String, Object> sourceUserColums)
	{
		if(sourceUserColums==null) return;
		for(String key : sourceUserColums.keySet())
		{
			Object value = sourceUserColums.get(key);
			
			ObjectUtil.setFieldValue(dataObject, key, value);
		}
	}
	
	public static void setObjectUserColumnsValue(DataInfo dataObject, Map<String, Object> sourceUserColums, PersistentClass persistentClass)
	{
		if(sourceUserColums==null) return;
		for(String key : sourceUserColums.keySet())
		{
			Object value = sourceUserColums.get(key);
			try {
				Field field = dataObject.getClass().getDeclaredField(key);
				if(field!=null)
					persistentClass.addUpdateMetaAttribute(new MetaAttribute(field.getName(), value));
			} catch (SecurityException e) {
				log.warn(e,e);
			} catch (NoSuchFieldException e) {
				log.warn(e,e);
			}
		}
	}
	
//	public static Map<String, String> makeDataToHashMapExceptKey(Object object)
//	{
//		Map<String, String> fieldList = new HashMap<String, String>();
//		
//		try
//		{
//			Field[] fields = object.getClass().getDeclaredFields();
//			
//			for (int i = 0; i < fields.length; i++)
//			{
//				if(!fields[i].getName().equals("key"))
//				{
//					if(fields[i].get(object) != null)
//						fieldList.put(fields[i].getName(), String.valueOf(fields[i].get(object)));
//				}
//			}
//		} 
//		catch (Exception e)
//		{}
//		
//		return fieldList;
//	}
	
	public static Map<String, String> makeDataToHashMapExceptKey(Object object)
	{
		Map<String, String> fieldList = new HashMap<String, String>();
	  
		try
		{
			Field[] fields = object.getClass().getDeclaredFields();
	   
			for (int i = 0; i < fields.length; i++)
			{
				if(!fields[i].getName().equals("key"))
				{
					try
					{
						fields[i].setAccessible(true);
						
						if(fields[i].get(object) != null && fields[i].getAnnotation(UserColumnMark.class) != null)
							fieldList.put(fields[i].getName(), String.valueOf(fields[i].get(object)));
					}
					catch(Exception e)
					{}
				}
			}
		} 
		catch (Exception e)
		{}
	  
		return fieldList;
	 }
	
	public static List<String> getUserColumnsFieldNames(Object object)
	{
		List<String> fieldList = new ArrayList<String>();
	  
		try
		{
			Field[] fields = object.getClass().getDeclaredFields();
	   
			for (int i = 0; i < fields.length; i++)
			{
				if(fields[i].getAnnotation(UserColumnMark.class) != null)
					fieldList.add(fields[i].getName());
			}
		} 
		catch (Exception e)
		{}
	  
		return fieldList;
	 }

	public static void setObjectValueUserColumns(Map<String, Object> targetUserColumns,
			Map<String, ?> sourceUserColumns) {
		if(targetUserColumns == null || sourceUserColumns == null)
		{
			log.warn("TargetUsercolumnns or SourceUserColumns can't be null");
			return;
		}
		if (targetUserColumns.size() == 0)
		{
			for (String srcKey : sourceUserColumns.keySet())
				targetUserColumns.put(srcKey, sourceUserColumns.get(srcKey));
		}
		else
		{
			for (String srcKey : sourceUserColumns.keySet())
			{
				if (targetUserColumns.containsKey(srcKey))
					if(!sourceUserColumns.get(srcKey).getClass().getName().equals(targetUserColumns.get(srcKey).getClass().getName()))
						log.warn("TargetUsercolumnns and SourceUserColumns at key [" + srcKey+"] not same type , TargetUsercolumnns' will be cover by SourceUserColumns");

				targetUserColumns.put(srcKey, sourceUserColumns.get(srcKey));
			}
		}
		
	}
}
