package com.delcyon.capo.util;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import org.w3c.dom.Element;
import com.delcyon.capo.util.CloneControl.Clone;
public abstract class EqualityProcessor {
private static volatile ConcurrentHashMap<Integer, Object> systemFieldCloneControlHashMap = new ConcurrentHashMap<Integer, Object>();
/**
* Compares to objects to see if they are equal. It also does null comparisons
* @param object1
* @param object2
* @return
*/
public static boolean areSame(Object object1, Object object2) {
if (object1 == null ^ object2 == null){
return false;
}
else if (object1 == null & object2 == null){
return true;
}
else return object1.equals(object2);
}
public static <T> T clone(T cloneable) throws Exception
{
try
{
return clone(null, cloneable);
} catch (StackOverflowError stackOverflowError)
{
throw new StackOverflowError("Couldn't clone "+cloneable);
}
}
/**
* This is a utility method that can be used to clone anything, regardless of whether or not that object implements the clone interface.
* @see CloneControl CloneControl for more information, on controlling what gets cloned.
* @param cloneable, any object, does NOT have to implement clonable
* @return cloned object, unless it couldn't be cloned in which case it will return null;
* @throws Exception
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T> T clone(Object clonedParent,T cloneable) throws Exception
{
try
{
if(cloneable == null)
{
return null;
}
if(ReflectionUtility.isPrimitive(cloneable.getClass()))
{
return (T) ReflectionUtility.getPrimitiveInstance(cloneable.getClass(), ReflectionUtility.getSerializedString(cloneable));
}
T clone = null;
if (ReflectionUtility.hasDefaultContructor(cloneable.getClass()))
{
Constructor<T> constructor = ReflectionUtility.getDefaultConstructor(cloneable.getClass());
clone = (T) constructor.newInstance();
}
else
{
clone = (T) ReflectionUtility.getComplexInstance(cloneable.getClass(),cloneable);
}
if (cloneable instanceof ControlledClone)
{
((ControlledClone) cloneable).preClone(clonedParent, clone);
}
Vector<Field> fieldVector = ReflectionUtility.getFieldVector(cloneable);
CloneControl classCloneControl = cloneable.getClass().getAnnotation(CloneControl.class);
if(classCloneControl != null)
{
if(classCloneControl.filter() == Clone.exclude && classCloneControl.modifiers() == 0)
{
fieldVector.clear();
}
}
for (Field field : fieldVector)
{
if (Modifier.isStatic(field.getModifiers()) == false)
{
field.setAccessible(true);
CloneControl fieldCloneControl = null;
Integer fieldID = field.hashCode();
if(systemFieldCloneControlHashMap.containsKey(fieldID))
{
if(systemFieldCloneControlHashMap.get(fieldID) instanceof CloneControl)
{
fieldCloneControl = (CloneControl) systemFieldCloneControlHashMap.get(fieldID);
}
}
else
{
fieldCloneControl = field.getAnnotation(CloneControl.class);
if(fieldCloneControl != null)
{
systemFieldCloneControlHashMap.put(fieldID, fieldCloneControl);
}
else
{
systemFieldCloneControlHashMap.put(fieldID, fieldID);
}
}
boolean forceInclude = false;
if(fieldCloneControl != null)
{
if(fieldCloneControl.filter() == Clone.exclude)
{
continue;
}
else
{
forceInclude = true;
}
}
if(classCloneControl != null && forceInclude == false)
{
if(classCloneControl.filter() == Clone.exclude)
{
if((field.getModifiers() & classCloneControl.modifiers()) != 0) //exclude if the modifiers match
{
continue;
}
}
else
{
if((field.getModifiers() & classCloneControl.modifiers()) == 0) //exclude if the modifiers don't match
{
continue;
}
}
}
Object cloneableFieldInstance = field.get(cloneable);
if(cloneableFieldInstance != null)
{
if(field.getType().isPrimitive()) //check for pure primitives, which can be copied straight across
{
field.set(clone, cloneableFieldInstance);
}
else if(ReflectionUtility.isPrimitive(field.getType()))
{
field.set(clone, ReflectionUtility.getPrimitiveInstance(field.getType(), ReflectionUtility.getSerializedString(cloneableFieldInstance)));
}
else
{
Method cloneMethod = null;
//check to see if this complex type implements clone
Vector<Method> childMethodVector = ReflectionUtility.getMethodVector(cloneableFieldInstance);
for (Method childMethod : childMethodVector)
{
if(childMethod.getName().equals("clone") && childMethod.getParameterTypes().length == 0 && Modifier.isPublic(childMethod.getModifiers()) == true)
{
cloneMethod = childMethod;
}
}
//
//do a standard element clone, and don't trust any of these types cloning methods, as the don't call clone on their contained objects
if (Element.class.isAssignableFrom(field.getType()))
{
field.set(clone, ((Element)cloneableFieldInstance).cloneNode(true));
}
//implement a collection clone
else if (Collection.class.isAssignableFrom(field.getType()))
{
Collection clonableCollection = (Collection) cloneableFieldInstance;
Collection clonedCollection = (Collection) cloneableFieldInstance.getClass().newInstance();
field.set(clone, clonedCollection);
for (Object object : clonableCollection)
{
clonedCollection.add(clone(clone,object));
}
}
//do an array clone
else if (field.getType().isArray() == true)
{
int length = Array.getLength(cloneableFieldInstance);
Object arrayObject = Array.newInstance(field.getType().getComponentType(), length);
field.set(clone, arrayObject);
for(int index = 0; index < length; index++)
{
Array.set(arrayObject, index, clone(clone,Array.get(cloneableFieldInstance, index)));
}
}
else if (Map.class.isAssignableFrom(field.getType()))
{
Map clonableMap = (Map) cloneableFieldInstance;
Map clonedMap = (Map) cloneableFieldInstance.getClass().newInstance();
field.set(clone, clonedMap);
Set<Entry> entrySet = clonableMap.entrySet();
for (Entry entry : entrySet)
{
clonedMap.put(clone(clone,entry.getKey()), clone(clone,entry.getValue()));
}
}
else if (cloneMethod != null)
{
field.set(clone,cloneMethod.invoke(cloneableFieldInstance));
}
else
{
field.set(clone,clone(clone,cloneableFieldInstance));
}
}
}
}
}
if (cloneable instanceof ControlledClone)
{
((ControlledClone) cloneable).postClone(clonedParent, clone);
}
return clone;
} catch (StackOverflowError stackOverflowError)
{
throw new StackOverflowError("Couldn't clone "+cloneable.getClass());
}
}
}