/*
* Copyright (c) 2010-2015 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.prism.util;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.evolveum.midpoint.prism.DefinitionImpl;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import org.apache.commons.lang.SerializationUtils;
import com.evolveum.midpoint.prism.Definition;
import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.RawType;
import org.springframework.util.ClassUtils;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
/**
* @author semancik
*
*/
public class CloneUtil {
private static final Trace PERFORMANCE_ADVISOR = TraceManager.getPerformanceAdvisorTrace();
public static <T> T clone(T orig) {
if (orig == null) {
return null;
}
Class<? extends Object> origClass = orig.getClass();
if (ClassUtils.isPrimitiveOrWrapper(origClass)) {
return orig;
}
if (origClass.isArray()) {
return cloneArray(orig);
}
if (orig instanceof PolyString) {
// PolyString is immutable
return orig;
}
if (orig instanceof String) {
// ...and so is String
return orig;
}
if (orig instanceof QName) {
// the same here
return orig;
}
if (origClass.isEnum()) {
return orig;
}
// if (orig.getClass().equals(QName.class)) {
// QName origQN = (QName) orig;
// return (T) new QName(origQN.getNamespaceURI(), origQN.getLocalPart(), origQN.getPrefix());
// }
if (orig instanceof RawType){
return (T) ((RawType) orig).clone();
}
if (orig instanceof Item<?,?>) {
return (T) ((Item<?,?>)orig).clone();
}
if (orig instanceof PrismValue) {
return (T) ((PrismValue)orig).clone();
}
if (orig instanceof ObjectDelta<?>) {
return (T) ((ObjectDelta<?>)orig).clone();
}
if (orig instanceof ObjectDeltaType) {
return (T) ((ObjectDeltaType) orig).clone();
}
if (orig instanceof ItemDelta<?,?>) {
return (T) ((ItemDelta<?,?>)orig).clone();
}
if (orig instanceof DefinitionImpl) {
return (T) ((DefinitionImpl)orig).clone();
}
/*
* In some environments we cannot clone XMLGregorianCalendar because of this:
* Error when cloning class org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl, will try serialization instead.
* java.lang.IllegalAccessException: Class com.evolveum.midpoint.prism.util.CloneUtil can not access a member of
* class org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl with modifiers "public"
*/
if (orig instanceof XMLGregorianCalendar) {
return (T) XmlTypeConverter.createXMLGregorianCalendar((XMLGregorianCalendar) orig);
}
if (orig instanceof Cloneable) {
T clone = javaLangClone(orig);
if (clone != null) {
return clone;
}
}
if (orig instanceof Serializable) {
// Brute force
if (PERFORMANCE_ADVISOR.isDebugEnabled()) {
PERFORMANCE_ADVISOR.debug("Cloning a Serializable ({}). It could harm performance.", orig.getClass());
}
return (T)SerializationUtils.clone((Serializable)orig);
}
throw new IllegalArgumentException("Cannot clone "+orig+" ("+origClass+")");
}
public static <T> List<T> cloneCollectionMembers(Collection<T> collection) {
if (collection == null) {
return null;
}
List<T> clonedCollection = new ArrayList<>(collection.size());
for (T element : collection) {
clonedCollection.add(clone(element));
}
return clonedCollection;
}
public static <T> List<T> cloneListMembers(List<T> list) {
List<T> clonedCollection = new ArrayList<>(list.size());
for (T element : list) {
clonedCollection.add(clone(element));
}
return clonedCollection;
}
private static <T> T cloneArray(T orig) {
int length = Array.getLength(orig);
T clone = (T) Array.newInstance(orig.getClass().getComponentType(), length);
System.arraycopy(orig, 0, clone, 0, length);
return clone;
}
public static <T> T javaLangClone(T orig) {
try {
Method cloneMethod = orig.getClass().getMethod("clone");
Object clone = cloneMethod.invoke(orig);
return (T) clone;
} catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException|RuntimeException e) {
if (PERFORMANCE_ADVISOR.isDebugEnabled()) {
PERFORMANCE_ADVISOR.debug("Error when cloning {}, will try serialization instead.", orig.getClass(), e);
}
return null;
}
}
}