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 }