001package org.unix4j.util; 002 003import java.util.Arrays; 004import java.util.List; 005import java.util.Map; 006import java.util.Set; 007 008/** 009 * Utility class with static methods for cloning, for instance to deep-clone 010 * nested lists, sets and maps. 011 */ 012public class CloneUtil { 013 014 /** 015 * Creates a deep clone of the specified value and returns it. Deep clone 016 * means that lists, sets, maps, arrays and other cloneable objects are 017 * recursively cloned. 018 * 019 * @param <T> the type of the value to clone 020 * @param value the value to clone 021 * @return a deep clone of the specified value 022 */ 023 @SuppressWarnings("unchecked") 024 public static <T> T cloneDeep(T value) { 025 if (value instanceof List) { 026 return (T) cloneList((List<?>) value); 027 } 028 if (value instanceof Set) { 029 return (T) cloneSet((Set<?>) value); 030 } 031 if (value instanceof Map) { 032 return (T) cloneMap((Map<?, ?>) value); 033 } 034 if (value instanceof Cloneable || value.getClass().isArray()) { 035 return (T) cloneReflective(value); 036 } 037 return value; 038 } 039 040 /** 041 * Clones the given value and returns the non-deep clone. Cloning is 042 * performed by calling the clone() method through reflection. If no such 043 * method exists or if calling the clone method fails, an exception is 044 * thrown. 045 * 046 * @param <T> 047 * the type of the value to clone 048 * @param value 049 * the value to clone 050 * @return the non-deep clone of the value 051 * @throws IllegalArgumentException 052 * if no clone method exists or if invoking the clone method 053 * caused an exception 054 */ 055 @SuppressWarnings("unchecked") 056 public static <T> T cloneReflective(T value) { 057 try { 058 return (T) value.getClass().getMethod("clone").invoke(value); 059 } catch (Exception e) { 060 throw new IllegalArgumentException("clone failed for value type " + value.getClass().getName() + ", e=" + e, e); 061 } 062 } 063 064 @SuppressWarnings({ "rawtypes", "unchecked" }) 065 private static Map cloneMap(Map value) { 066 final Map<Object, Object> clone = cloneReflective(value); 067 for (final Map.Entry e : clone.entrySet()) { 068 e.setValue(cloneDeep(e.getValue())); 069 } 070 return clone; 071 } 072 073 @SuppressWarnings({ "rawtypes", "unchecked" }) 074 private static List cloneList(List value) { 075 final List<Object> clone; 076 if (value.getClass().getName().equals("java.util.Arrays$ArrayList")) { 077 clone = Arrays.asList(value.toArray()); 078 } else { 079 clone = cloneReflective(value); 080 } 081 for (int i = 0; i < clone.size(); i++) { 082 clone.set(i, cloneDeep(clone.get(i))); 083 } 084 return clone; 085 } 086 087 @SuppressWarnings({ "unchecked", "rawtypes" }) 088 private static Set cloneSet(Set value) { 089 final Set<Object> clone = cloneReflective(value); 090 clone.clear(); 091 for (final Object element : value) { 092 clone.add(cloneDeep(element)); 093 } 094 return clone; 095 } 096 097 // no instances 098 private CloneUtil() { 099 super(); 100 } 101}