001    /*
002      Copyright (C) 2003 Laurent Martelli <laurent@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful, but
010      WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
012      Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public
015      License along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017      USA */
018    
019    package org.objectweb.jac.core;
020    
021    import java.lang.reflect.Modifier;
022    import java.util.Collection;
023    import java.util.Iterator;
024    import org.apache.log4j.Logger;
025    import org.objectweb.jac.core.rtti.ClassItem;
026    import org.objectweb.jac.core.rtti.ClassRepository;
027    import org.objectweb.jac.core.rtti.CollectionItem;
028    import org.objectweb.jac.core.rtti.FieldItem;
029    import org.objectweb.jac.core.rtti.MethodItem;
030    import org.objectweb.jac.core.rtti.NoSuchMethodException;
031    import org.objectweb.jac.core.rtti.RttiAC;
032    import org.objectweb.jac.util.ExtArrays;
033    
034    public class Utils {
035        static Logger logger = Logger.getLogger("clone");
036    
037        /**
038         * "Semantic" clone. Same as <code>clone(o,null)</code>
039         *
040         * @param o the object to clone
041         * @return the cloned object
042         *
043         * @see #clone(Object,FieldItem)
044         */
045        public static Object clone(Object o) 
046            throws InstantiationException, IllegalAccessException, Exception 
047        {
048            return clone(o,(FieldItem)null);
049        }
050    
051        /**
052         * "Semantic" clone. Collections marked as aggregation are
053         * recursively cloned (objects in the collection are cloned),
054         * otherwise the collection of the cloned objet will contains the
055         * same objects as the source object.
056         *
057         * @param o the object to clone
058         * @param ignoredRelation do not clone this relation and leave it
059         * empty. If null, all relations are cloned.
060         * @return the cloned object 
061         *
062         * @see #clone(Object)     
063         */
064        public static Object clone(Object o, FieldItem ignoredRelation) 
065            throws InstantiationException, IllegalAccessException, Exception 
066        {
067            logger.debug("Cloning "+o);
068            ClassRepository cr = ClassRepository.get();
069            ClassItem cli = cr.getClass(o);
070            Object clone = cli.newInstance();
071            Iterator i = cli.getAllFields().iterator();
072            while (i.hasNext()) {
073                FieldItem field = (FieldItem)i.next();
074                if (field.isCalculated() || ignoredRelation==field)
075                    continue;
076                if (field.isPrimitive()) {
077                    logger.debug("  copying value of fied "+field.getName());
078                    try {
079                        Object fieldValue = field.getThroughAccessor(o);
080                        
081                        // Cloneable is useless in this generic context
082                        try {
083                            MethodItem mClone = cr.getClass(fieldValue).getMethod("clone()");
084                            if (mClone!=null && Modifier.isPublic(mClone.getModifiers()))
085                                fieldValue = mClone.invoke(fieldValue, ExtArrays.emptyObjectArray);
086                        } catch(NoSuchMethodException e) {
087                        }
088                        field.setThroughWriter(clone,fieldValue);
089                    } catch (Exception e) {
090                        logger.error("clone("+o+"): failed to clone field "+field,e);
091                    }
092                } else if (field instanceof CollectionItem) {
093                    CollectionItem collection = (CollectionItem)field;
094                    logger.debug("  copying collection "+field.getName());
095                    if (collection.isMap()) {
096                    } else {
097                        Iterator j = ((Collection)collection.getThroughAccessor(o)).iterator();
098                        while(j.hasNext()) {
099                            Object item = j.next();
100                            if (collection.isAggregation()) {
101                                item = clone(item,(FieldItem)field.getAttribute(RttiAC.OPPOSITE_ROLE));
102                            }
103                            collection.addThroughAdder(clone,item);
104                        }
105                    }
106                } else {
107                    field.setThroughWriter(clone,field.getThroughAccessor(o));
108                }
109            }
110            logger.debug(o+" cloned");
111            return clone;
112        }
113    
114        public static Object clone(Object o, String ignoredRelation) 
115            throws InstantiationException, IllegalAccessException, Exception 
116        {
117            return clone(o,ClassRepository.get().getClass(o).getField(ignoredRelation));
118        }
119    }