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    }