Coverage Report - org.kuali.rice.krad.uif.util.CloneUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
CloneUtils
0%
0/116
0%
0/74
4.538
 
 1  
 /*
 2  
  * Copyright 2011 The Kuali Foundation Licensed under the Educational Community
 3  
  * License, Version 1.0 (the "License"); you may not use this file except in
 4  
  * compliance with the License. You may obtain a copy of the License at
 5  
  * http://www.opensource.org/licenses/ecl1.php Unless required by applicable law
 6  
  * or agreed to in writing, software distributed under the License is
 7  
  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 8  
  * KIND, either express or implied. See the License for the specific language
 9  
  * governing permissions and limitations under the License.
 10  
  */
 11  
 package org.kuali.rice.krad.uif.util;
 12  
 
 13  
 import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
 14  
 import org.hibernate.dialect.Oracle10gDialect;
 15  
 import org.kuali.rice.krad.uif.core.ReferenceCopy;
 16  
 
 17  
 import java.lang.reflect.Field;
 18  
 import java.lang.reflect.Modifier;
 19  
 import java.util.ArrayList;
 20  
 import java.util.Arrays;
 21  
 import java.util.Collections;
 22  
 import java.util.HashMap;
 23  
 import java.util.Iterator;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 import java.util.Map.Entry;
 27  
 import java.util.concurrent.atomic.AtomicInteger;
 28  
 import java.util.concurrent.atomic.AtomicLong;
 29  
 
 30  
 /**
 31  
  * Utility class for copying objects using reflection. Modified from the jCommon
 32  
  * library: http://www.matthicks.com/2008/05/fastest-deep-cloning.html
 33  
  * 
 34  
  * @author Matt Hicks
 35  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 36  
  */
 37  0
 public class CloneUtils {
 38  0
     private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CloneUtils.class);
 39  
 
 40  0
     private static final Map<String, Field[]> fieldCache = new HashMap<String, Field[]>();
 41  0
     private static final Map<String, Field> internalFields = new HashMap<String, Field>();
 42  
 
 43  
     public static final <O> O deepClone(O original) throws Exception {
 44  0
         return deepCloneReflection(original);
 45  
     }
 46  
 
 47  
     @SuppressWarnings("unchecked")
 48  
     public static final <O> O deepCloneReflection(O original) throws Exception {
 49  0
         return (O) deepCloneReflectionInternal(original, new HashMap<Object, Object>(), false);
 50  
     }
 51  
 
 52  
     protected static final Object deepCloneReflectionInternal(Object original, Map<Object, Object> cache,
 53  
             boolean referenceCollectionCopy) throws Exception {
 54  0
         if (original == null) { // No need to clone nulls
 55  0
             return original;
 56  
         }
 57  0
         else if (cache.containsKey(original)) {
 58  0
             return cache.get(original);
 59  
         }
 60  
 
 61  
         // Deep clone
 62  0
         Object clone = null;
 63  0
         if (List.class.isAssignableFrom(original.getClass())) {
 64  0
             clone = deepCloneList(original, cache, referenceCollectionCopy);
 65  
         }
 66  0
         else if (Map.class.isAssignableFrom(original.getClass())) {
 67  0
             clone = deepCloneMap(original, cache, referenceCollectionCopy);
 68  
         }
 69  
         else {
 70  0
             clone = deepCloneObject(original, cache);
 71  
         }
 72  
 
 73  0
         return clone;
 74  
     }
 75  
 
 76  
     protected static Object deepCloneObject(Object original, Map<Object, Object> cache) throws Exception {
 77  0
         if (original instanceof Number) { // Numbers are immutable
 78  0
             if (original instanceof AtomicInteger) {
 79  
                 // AtomicIntegers are mutable
 80  
             }
 81  0
             else if (original instanceof AtomicLong) {
 82  
                 // AtomLongs are mutable
 83  
             }
 84  
             else {
 85  0
                 return original;
 86  
             }
 87  
         }
 88  0
         else if (original instanceof String) { // Strings are immutable
 89  0
             return original;
 90  
         }
 91  0
         else if (original instanceof Character) { // Characters are immutable
 92  0
             return original;
 93  
         }
 94  0
         else if (original instanceof Class) { // Classes are immutable
 95  0
             return original;
 96  
         }
 97  0
         else if (original instanceof Boolean) {
 98  0
             return new Boolean(((Boolean) original).booleanValue());
 99  
         }
 100  
 
 101  
         // To our understanding, this is a mutable object, so clone it
 102  0
         Class<?> c = original.getClass();
 103  0
         Field[] fields = getFields(c, false);
 104  
         try {
 105  0
             Object copy = instantiate(original);
 106  
 
 107  
             // Put into cache
 108  0
             cache.put(original, copy);
 109  
 
 110  
             // iterate through and copy fields
 111  0
             for (Field f : fields) {
 112  0
                 Object object = f.get(original);
 113  
 
 114  0
                 boolean referenceCopy = false;
 115  0
                 boolean referenceCollectionCopy = false;
 116  0
                 ReferenceCopy copyAnnotation = f.getAnnotation(ReferenceCopy.class);
 117  0
                 if (copyAnnotation != null) {
 118  0
                     referenceCopy = true;
 119  0
                     referenceCollectionCopy = copyAnnotation.newCollectionInstance();
 120  
                 }
 121  
 
 122  0
                 if (!referenceCopy || referenceCollectionCopy) {
 123  0
                     object = CloneUtils.deepCloneReflectionInternal(object, cache, referenceCollectionCopy);
 124  
                 }
 125  0
                 f.set(copy, object);
 126  
             }
 127  
 
 128  0
             return copy;
 129  
         }
 130  0
         catch (Throwable t) {
 131  0
             LOG.warn("Exception during clone (returning original): " + t.getMessage());
 132  0
             return original;
 133  
         }
 134  
     }
 135  
 
 136  
     @SuppressWarnings("unchecked")
 137  
     protected static Object deepCloneMap(Object original, Map<Object, Object> cache, boolean referenceCollectionCopy)
 138  
             throws Exception {
 139  
         // Instantiate a new instance
 140  0
         Map<Object, Object> clone = (Map<Object, Object>) instantiate(original);
 141  
 
 142  
         // Populate data
 143  0
         for (Entry<Object, Object> entry : ((Map<Object, Object>) original).entrySet()) {
 144  0
             if (referenceCollectionCopy) {
 145  0
                 clone.put(entry.getKey(), entry.getValue());
 146  
             }
 147  
             else {
 148  0
                 clone.put(deepCloneReflectionInternal(entry.getKey(), cache, false),
 149  
                         deepCloneReflectionInternal(entry.getValue(), cache, false));
 150  
             }
 151  
         }
 152  
 
 153  0
         return clone;
 154  
     }
 155  
 
 156  
     @SuppressWarnings("unchecked")
 157  
     protected static Object deepCloneList(Object original, Map<Object, Object> cache, boolean referenceCollectionCopy)
 158  
             throws Exception {
 159  
         // Instantiate a new instance
 160  0
         List<Object> clone = (List<Object>) instantiate(original);
 161  
 
 162  
         // Populate data
 163  0
         for (Iterator<Object> iterator = ((List<Object>) original).iterator(); iterator.hasNext();) {
 164  0
             Object object = iterator.next();
 165  0
             if (referenceCollectionCopy) {
 166  0
                 clone.add(object);
 167  
             }
 168  
             else {
 169  0
                 clone.add(deepCloneReflectionInternal(object, cache, false));
 170  
             }
 171  0
         }
 172  
 
 173  0
         return clone;
 174  
     }
 175  
 
 176  
     protected static final Object instantiate(Object original) throws InstantiationException, IllegalAccessException {
 177  0
         return original.getClass().newInstance();
 178  
     }
 179  
 
 180  
     public static Field[] getFields(Object object, boolean includeStatic) {
 181  0
         return getFields(object, includeStatic, true);
 182  
     }
 183  
 
 184  
     public static Field[] getFields(Object object, boolean includeStatic, boolean includeTransient) {
 185  0
         Class<?> c = object.getClass();
 186  0
         return getFields(c, includeStatic, includeTransient);
 187  
     }
 188  
 
 189  
     public static Field[] getFields(Class<?> c, boolean includeStatic) {
 190  0
         return getFields(c, includeStatic, true);
 191  
     }
 192  
 
 193  
     public static Field[] getFields(Class<?> c, boolean includeStatic, boolean includeTransient) {
 194  0
         String cacheKey = c.getCanonicalName() + ":" + includeStatic;
 195  0
         Field[] array = fieldCache.get(cacheKey);
 196  
 
 197  0
         if (array == null) {
 198  0
             ArrayList<Field> fields = new ArrayList<Field>();
 199  
 
 200  0
             List<Class<?>> classes = getClassHierarchy(c, false);
 201  
 
 202  
             // Reverse order so we make sure we maintain consistent order
 203  0
             Collections.reverse(classes);
 204  
 
 205  0
             for (Class<?> clazz : classes) {
 206  0
                 Field[] allFields = clazz.getDeclaredFields();
 207  0
                 for (Field f : allFields) {
 208  0
                     if ((!includeTransient) && ((f.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT)) {
 209  0
                         continue;
 210  
                     }
 211  0
                     else if (f.isSynthetic()) {
 212  
                         // Synthetic fields are bad!!!
 213  0
                         continue;
 214  
                     }
 215  0
                     boolean isStatic = (f.getModifiers() & Modifier.STATIC) == Modifier.STATIC;
 216  0
                     if ((isStatic) && (!includeStatic)) {
 217  0
                         continue;
 218  
                     }
 219  0
                     if (f.getName().equalsIgnoreCase("serialVersionUID")) {
 220  0
                         continue;
 221  
                     }
 222  0
                     f.setAccessible(true);
 223  0
                     fields.add(f);
 224  
                 }
 225  0
             }
 226  
 
 227  0
             array = fields.toArray(new Field[fields.size()]);
 228  0
             fieldCache.put(cacheKey, array);
 229  
         }
 230  0
         return array;
 231  
     }
 232  
 
 233  
     protected static final Field internalField(Object object, String fieldName) {
 234  0
         if (object == null) {
 235  0
             System.out.println("Internal Field: " + object + ", " + fieldName);
 236  0
             return null;
 237  
         }
 238  
 
 239  0
         String key = object.getClass().getCanonicalName() + "." + fieldName;
 240  0
         Field field = internalFields.get(key);
 241  0
         if (field == null) {
 242  0
             Field[] fields = getFields(object.getClass(), false);
 243  
 
 244  0
             for (Field f : fields) {
 245  0
                 String name = f.getName();
 246  0
                 if (name.equals(fieldName)) {
 247  0
                     field = f;
 248  0
                     internalFields.put(key, field);
 249  0
                     break;
 250  
                 }
 251  
             }
 252  
         }
 253  
 
 254  0
         return field;
 255  
     }
 256  
 
 257  
     protected static List<Class<?>> getClassHierarchy(Class<?> c, boolean includeInterfaces) {
 258  0
         List<Class<?>> classes = new ArrayList<Class<?>>();
 259  0
         while (c != Object.class) {
 260  0
             classes.add(c);
 261  0
             if (includeInterfaces) {
 262  0
                 Class<?>[] interfaces = c.getInterfaces();
 263  0
                 for (Class<?> i : interfaces) {
 264  0
                     classes.add(i);
 265  
                 }
 266  
             }
 267  0
             c = c.getSuperclass();
 268  0
             if (c == null) {
 269  0
                 break;
 270  
             }
 271  
         }
 272  
 
 273  0
         return classes;
 274  
     }
 275  
 
 276  
 }