001 /*--------------------------------------------------------------------------+ 002 $Id: CloneUtils.java 26268 2010-02-18 10:44:30Z juergens $ 003 | | 004 | Copyright 2005-2010 Technische Universitaet Muenchen | 005 | | 006 | Licensed under the Apache License, Version 2.0 (the "License"); | 007 | you may not use this file except in compliance with the License. | 008 | You may obtain a copy of the License at | 009 | | 010 | http://www.apache.org/licenses/LICENSE-2.0 | 011 | | 012 | Unless required by applicable law or agreed to in writing, software | 013 | distributed under the License is distributed on an "AS IS" BASIS, | 014 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 015 | See the License for the specific language governing permissions and | 016 | limitations under the License. | 017 +--------------------------------------------------------------------------*/ 018 package edu.tum.cs.commons.clone; 019 020 import java.lang.reflect.Method; 021 import java.lang.reflect.Modifier; 022 import java.util.ArrayList; 023 import java.util.Collection; 024 import java.util.HashMap; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Map.Entry; 028 029 /** 030 * Collection of utility methods to simplify cloning. 031 * 032 * @author Benjamin Hummel 033 * @author $Author: juergens $ 034 * @version $Rev: 26268 $ 035 * @levd.rating GREEN Hash: 67F5FD6BA376F3700299C536C919917E 036 * 037 */ 038 public class CloneUtils { 039 040 /** 041 * Duplicate all entries of the source map to the target map and create 042 * clones of all contained objects. 043 * 044 * @param source 045 * the source for the key value pairs. 046 * @param target 047 * the map to insert the created clones into. 048 */ 049 public static void cloneMapEntries(Map<String, Object> source, 050 Map<String, Object> target) throws DeepCloneException { 051 052 for (String key : source.keySet()) { 053 target.put(key, cloneAsDeepAsPossible(source.get(key))); 054 } 055 } 056 057 /** 058 * Clone the provided object if supported. If the depth of the structure is 059 * deeper than 3 an exception is thrown. For some of the special cases 060 * handled see {@link #cloneAsDeepAsPossible(Object, int)}. 061 * 062 * @param o 063 * the object to be cloned. 064 * @return the cloned (or original) object. 065 * @throws DeepCloneException 066 * if the depth of the cloned struture is too high or the 067 * underlying clone methods failed. 068 */ 069 public static Object cloneAsDeepAsPossible(Object o) 070 throws DeepCloneException { 071 return cloneAsDeepAsPossible(o, 3); 072 } 073 074 /** 075 * Clone the provided object if supported. The following cases are 076 * explicitly handled: 077 * <ul> 078 * <li>For {@link IDeepCloneable}s the {@link IDeepCloneable#deepClone()} 079 * method is used, ignoring the maximal depth.</li> 080 * <li>For {@link Map}s the contents (keys and values) are cloned and put 081 * into a {@link HashMap}. The order is not preserved.</li> 082 * <li>Arrays are cloned as expected.</li> 083 * <li>All {@link Collection}s are cloned as an {@link ArrayList}</li> 084 * <li>Is nothing matched so far, and the object implements 085 * {@link Cloneable}, and has a public clone method, the 086 * {@link Object#clone()} method is used.</li> 087 * <li>In any other case the object itself is returned (uncloned). </li> 088 * </ul> 089 * 090 * @param o 091 * the object to be cloned. 092 * @param maxDepth 093 * the maximal depth of the structure before an exception is 094 * thrown. A depth of 0 indicates that no recursive calls are 095 * allowed, depth 1 allows 1 recursive call, and so on. 096 * @return the cloned (or original) object. 097 * @throws DeepCloneException 098 * if the depth of the cloned struture is too high or the 099 * underlying clone methods failed. 100 */ 101 public static Object cloneAsDeepAsPossible(Object o, int maxDepth) 102 throws DeepCloneException { 103 if (maxDepth < 0) { 104 throw new DeepCloneException( 105 "Reached maximal allowed cloning depth."); 106 } 107 // decrement for recursive calls 108 maxDepth -= 1; 109 110 try { 111 if (o == null) { 112 return null; 113 } 114 115 if (o instanceof IDeepCloneable) { 116 return ((IDeepCloneable) o).deepClone(); 117 } 118 119 if (o instanceof Map<?, ?>) { 120 Map<Object, Object> result = new HashMap<Object, Object>(); 121 for (Object child : ((Map<?,?>) o).entrySet()) { 122 @SuppressWarnings("unchecked") 123 Entry<Object, Object> entry = (Entry) child; 124 result.put(cloneAsDeepAsPossible(entry.getKey(), maxDepth), 125 cloneAsDeepAsPossible(entry.getValue(), maxDepth)); 126 } 127 return result; 128 } 129 130 if (o instanceof Object[]) { 131 Object[] result = ((Object[]) o).clone(); 132 for (int i = 0; i < result.length; ++i) { 133 result[i] = cloneAsDeepAsPossible(result[i], maxDepth); 134 } 135 return result; 136 } 137 138 if (o instanceof Collection<?>) { 139 List<Object> result = new ArrayList<Object>(); 140 for (Object child : (Collection<?>) o) { 141 result.add(cloneAsDeepAsPossible(child, maxDepth)); 142 } 143 return result; 144 } 145 146 if (o instanceof Cloneable) { 147 Method clone = o.getClass().getMethod("clone"); 148 if (Modifier.isPublic(clone.getModifiers())) { 149 return clone.invoke(o); 150 } 151 } 152 153 // nothing else worked, so return uncloned 154 return o; 155 156 } catch (DeepCloneException ex) { 157 throw ex; 158 } catch (Exception ex) { 159 throw new DeepCloneException(ex); 160 } 161 } 162 }