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 }