001    package org.apache.ojb.otm.copy;
002    
003    /* Copyright 2003-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import org.apache.ojb.broker.PersistenceBroker;
019    import org.apache.ojb.broker.metadata.*;
020    import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
021    import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
022    import org.apache.ojb.broker.core.proxy.ProxyHelper;
023    import org.apache.ojb.broker.util.ConstructorHelper;
024    import org.apache.ojb.broker.util.IdentityMapFactory;
025    
026    import java.lang.reflect.Constructor;
027    import java.util.Map;
028    import java.util.Collection;
029    import java.util.Iterator;
030    
031    /**
032     * recursively copies an object based on the ClassDescriptor
033     * User: matthew.baird
034     * Date: Jul 7, 2003
035     * Time: 1:41:58 PM
036     */
037    public final class MetadataObjectCopyStrategy implements ObjectCopyStrategy
038    {
039        private static final ReflectiveObjectCopyStrategy _reflective = new ReflectiveObjectCopyStrategy();
040        private static final SerializeObjectCopyStrategy _serialize = new SerializeObjectCopyStrategy();
041    
042        /**
043         * Uses an IdentityMap to make sure we don't recurse infinitely on the same object in a cyclic object model.
044         * Proxies
045         * @param obj
046         * @return
047         */
048        public Object copy(final Object obj, final PersistenceBroker broker)
049        {
050            return clone(obj, IdentityMapFactory.getIdentityMap(), broker);
051        }
052    
053        private static Object clone(final Object toCopy, final Map objMap, final PersistenceBroker broker)
054        {
055            /**
056             * first, check to make sure we aren't recursing to some object that we've already copied.
057             * if the toCopy is in the objMap, just return it.
058             */
059            if (objMap.containsKey(toCopy)) return objMap.get(toCopy);
060            /**
061             * if null, return null, duh
062             */
063            if (toCopy == null)
064                return null;
065    
066            /**
067             * if this is a proxy, just copy the proxy, don't materialize it, and stop recursing
068             */
069            if (ProxyHelper.isVirtualOjbProxy(toCopy))
070            {
071                return _reflective.copy(toCopy, null);
072            }
073            else if (ProxyHelper.isNormalOjbProxy(toCopy))
074            {
075                return _serialize.copy(toCopy, null);
076            }
077    
078            /**
079             * if no classdescriptor exists for this object, just return this object, we
080             * can't copy it.
081             */
082            final ClassDescriptor cld = broker.getClassDescriptor(toCopy.getClass());
083            if (cld == null)
084            {
085                return _reflective.copy(toCopy, null);
086            }
087    
088            final Object retval;
089            try
090            {
091                final Constructor con = cld.getZeroArgumentConstructor();
092                retval = ConstructorHelper.instantiate(con);
093                objMap.put(toCopy,retval);
094            }
095            catch (InstantiationException e)
096            {
097                throw new ObjectCopyException("InstantiationException", e);
098            }
099    
100            /**
101             * first copy all the fields
102             * fields are not mapped objects (ie ObjectReferenceDescriptors)
103             */
104            final FieldDescriptor[] fieldDescs = cld.getFieldDescriptions();
105    //        final BrokerHelper brokerHelper = broker.serviceBrokerHelper();
106            for (int i = 0; i < fieldDescs.length; i++)
107            {
108                final FieldDescriptor fd = fieldDescs[i];
109                final PersistentField f = fd.getPersistentField();
110                Object fieldValue = f.get(toCopy);
111    /*
112    arminw:
113    TODO: ensure that the autoincrement values be assigned before the copy was done
114    If possible we should avoid to declare BrokerHelper#getAutoIncrementValue public and
115    if we copy an object user don't expect the change of fields.
116    */
117    //            // If the field is auto increment, assign its value before copying!
118    //            if (fd.isAutoIncrement())
119    //            {
120    //                fieldValue = brokerHelper.getAutoIncrementValue(fd, toCopy, fieldValue);
121    //            }
122    
123                f.set(retval, fieldValue);
124            }
125    
126            /**
127             * then copy all the 1:1 references
128             */
129            final Collection refDescsCol = cld.getObjectReferenceDescriptors();
130            final ObjectReferenceDescriptor[] rds = (ObjectReferenceDescriptor[]) refDescsCol.toArray(new ObjectReferenceDescriptor[refDescsCol.size()]);
131            for (int i = 0; i < rds.length; i++)
132            {
133                final ObjectReferenceDescriptor rd = rds[i];
134                final PersistentField f = rd.getPersistentField();
135                /**
136                 * recursively copy the referenced objects
137                 * register in the objMap first
138                 */
139                final Object object = f.get(toCopy);
140                final Object clone = clone(object, objMap, broker);
141                objMap.put(object, clone);
142                f.set(retval, clone);
143            }
144            /**
145             * then copy all the 1:M and M:N references
146             */
147            final Collection colDescsCol = cld.getCollectionDescriptors();
148            final Iterator it = colDescsCol.iterator();
149            while (it.hasNext())
150            {
151                final CollectionDescriptor cd = (CollectionDescriptor) it.next();
152                final PersistentField f = cd.getPersistentField();
153                final Object collection = f.get(toCopy);
154                /**
155                 * handle collection proxies where the entire Collection is a big proxy
156                 * (vs all the elements in the collection are proxies
157                 */
158                if (collection == null)
159                {
160                    f.set(retval, null);
161                }
162                else if (collection instanceof CollectionProxyDefaultImpl)
163                {
164                    f.set(retval, _reflective.copy(collection, null));
165                }
166                else if (collection instanceof Collection)
167                {
168                    try
169                    {
170                        final Collection newCollection = (Collection) collection.getClass().newInstance();
171                        final Iterator tempIter = ((Collection) collection).iterator();
172                        Object obj;
173                        while (tempIter.hasNext())
174                        {
175                            obj = tempIter.next();
176                            /**
177                            * if this is a proxy, just copy the proxy, don't materialize it, and stop recursing
178                            */
179                            if (ProxyHelper.isNormalOjbProxy(obj))  // tomdz: what about VirtualProxy ?
180                            {
181                                newCollection.add(obj);
182                            }
183                            else
184                            {
185                                final Object clone = clone(obj, objMap, broker);
186                                objMap.put(obj, clone);
187                                newCollection.add(clone);
188                            }
189                        }
190                        f.set(retval, newCollection);
191                    }
192                    catch (InstantiationException e)
193                    {
194                        throw new ObjectCopyException("InstantiationException", e);
195                    }
196                    catch (IllegalAccessException e)
197                    {
198                        throw new ObjectCopyException("IllegalAccessException", e);
199                    }
200                }
201                else
202                {
203                    throw new java.lang.UnsupportedOperationException("MetadataObjectCopyStrategy cannot handle Collection of type: " + collection.getClass().getName());
204                }
205            }
206            return retval;
207        }
208    }