001    package org.apache.ojb.broker.accesslayer;
002    
003    /* Copyright 2002-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 java.lang.reflect.Array;
019    import java.util.ArrayList;
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.HashSet;
023    import java.util.Iterator;
024    import java.util.List;
025    
026    import org.apache.ojb.broker.Identity;
027    import org.apache.ojb.broker.ManageableCollection;
028    import org.apache.ojb.broker.OJBRuntimeException;
029    import org.apache.ojb.broker.PersistenceBroker;
030    import org.apache.ojb.broker.IdentityFactory;
031    import org.apache.ojb.broker.core.PersistenceBrokerImpl;
032    import org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl;
033    import org.apache.ojb.broker.core.proxy.ProxyHelper;
034    import org.apache.ojb.broker.metadata.ClassDescriptor;
035    import org.apache.ojb.broker.metadata.CollectionDescriptor;
036    import org.apache.ojb.broker.metadata.FieldHelper;
037    import org.apache.ojb.broker.metadata.MetadataException;
038    import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
039    import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
040    import org.apache.ojb.broker.query.Query;
041    import org.apache.ojb.broker.query.QueryByCriteria;
042    import org.apache.ojb.broker.util.BrokerHelper;
043    import org.apache.ojb.broker.util.collections.RemovalAwareCollection;
044    import org.apache.ojb.broker.util.collections.RemovalAwareList;
045    import org.apache.ojb.broker.util.collections.RemovalAwareSet;
046    
047    /**
048     * Relationship Prefetcher for Collections.
049     *
050     * @author <a href="mailto:jbraeuchi@gmx.ch">Jakob Braeuchi</a>
051     * @version $Id: CollectionPrefetcher.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
052     */
053    public class CollectionPrefetcher extends RelationshipPrefetcherImpl
054    {
055    
056        /**
057         * Constructor for CollectionPrefetcher.
058         *
059         * @param aBroker
060         * @param anOrd
061         */
062        public CollectionPrefetcher(PersistenceBrokerImpl aBroker, ObjectReferenceDescriptor anOrd)
063        {
064            super(aBroker, anOrd);
065        }
066    
067        /**
068         * Build the multiple queries for one relationship because of limitation of IN(...)
069         *
070         * @param owners Collection containing all objects of the ONE side
071         */
072        protected Query[] buildPrefetchQueries(Collection owners, Collection children)
073        {
074            ClassDescriptor cld = getOwnerClassDescriptor();
075            Class topLevelClass = getBroker().getTopLevelClass(cld.getClassOfObject());
076            BrokerHelper helper = getBroker().serviceBrokerHelper();
077            Collection queries = new ArrayList(owners.size());
078            Collection idsSubset = new HashSet(owners.size());
079            Object[] fkValues;
080            Object owner;
081            Identity id;
082    
083            Iterator iter = owners.iterator();
084            while (iter.hasNext())
085            {
086                owner = iter.next();
087                fkValues = helper.extractValueArray(helper.getKeyValues(cld, owner));
088                id = getBroker().serviceIdentity().buildIdentity(null, topLevelClass, fkValues);
089                idsSubset.add(id);
090                if (idsSubset.size() == pkLimit)
091                {
092                    queries.add(buildPrefetchQuery(idsSubset));
093                    idsSubset.clear();
094                }
095            }
096    
097            if (idsSubset.size() > 0)
098            {
099                queries.add(buildPrefetchQuery(idsSubset));
100            }
101    
102            return (Query[]) queries.toArray(new Query[queries.size()]);
103        }
104    
105        /**
106         * Build the query to perform a batched read get orderBy settings from CollectionDescriptor
107         *
108         * @param ids Collection containing all identities of objects of the ONE side
109         */
110        protected Query buildPrefetchQuery(Collection ids)
111        {
112            CollectionDescriptor cds = getCollectionDescriptor();
113            QueryByCriteria query = buildPrefetchQuery(ids, cds.getForeignKeyFieldDescriptors(getItemClassDescriptor()));
114    
115            // check if collection must be ordered
116            if (!cds.getOrderBy().isEmpty())
117            {
118                Iterator iter = cds.getOrderBy().iterator();
119                while (iter.hasNext())
120                {
121                    query.addOrderBy((FieldHelper) iter.next());
122                }
123            }
124    
125            return query;
126        }
127    
128        /**
129         * associate the batched Children with their owner object loop over children
130         */
131        protected void associateBatched(Collection owners, Collection children)
132        {
133            CollectionDescriptor cds = getCollectionDescriptor();
134            PersistentField field = cds.getPersistentField();
135            PersistenceBroker pb = getBroker();
136            Class ownerTopLevelClass = pb.getTopLevelClass(getOwnerClassDescriptor().getClassOfObject());
137            Class collectionClass = cds.getCollectionClass(); // this collection type will be used:
138            HashMap ownerIdsToLists = new HashMap(owners.size());
139    
140            IdentityFactory identityFactory = pb.serviceIdentity();
141            // initialize the owner list map
142            for (Iterator it = owners.iterator(); it.hasNext();)
143            {
144                Object owner = it.next();
145                ownerIdsToLists.put(identityFactory.buildIdentity(getOwnerClassDescriptor(), owner), new ArrayList());
146            }
147    
148            // build the children lists for the owners
149            for (Iterator it = children.iterator(); it.hasNext();)
150            {
151                Object child = it.next();
152                // BRJ: use cld for real class, relatedObject could be Proxy
153                ClassDescriptor cld = getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(child));
154    
155                Object[] fkValues = cds.getForeignKeyValues(child, cld);
156                Identity ownerId = identityFactory.buildIdentity(null, ownerTopLevelClass, fkValues);
157                List list = (List) ownerIdsToLists.get(ownerId);
158                if (list != null)
159                {
160                    list.add(child);
161                }
162            }
163    
164            // connect children list to owners
165            for (Iterator it = owners.iterator(); it.hasNext();)
166            {
167                Object result;
168                Object owner = it.next();
169                Identity ownerId = identityFactory.buildIdentity(owner);
170                List list = (List) ownerIdsToLists.get(ownerId);
171    
172                if ((collectionClass == null) && field.getType().isArray())
173                {
174                    int length = list.size();
175                    Class itemtype = field.getType().getComponentType();
176                    result = Array.newInstance(itemtype, length);
177                    for (int j = 0; j < length; j++)
178                    {
179                        Array.set(result, j, list.get(j));
180                    }
181                }
182                else
183                {
184                    ManageableCollection col = createCollection(cds, collectionClass);
185                    for (Iterator it2 = list.iterator(); it2.hasNext();)
186                    {
187                        col.ojbAdd(it2.next());
188                    }
189                    result = col;
190                }
191    
192                Object value = field.get(owner);
193                if ((value instanceof CollectionProxyDefaultImpl) && (result instanceof Collection))
194                {
195                    ((CollectionProxyDefaultImpl) value).setData((Collection) result);
196                }
197                else
198                {
199                    field.set(owner, result);
200                }
201            }
202        }
203    
204        /**
205         * Create a collection object of the given collection type. If none has been given,
206         * OJB uses RemovalAwareList, RemovalAwareSet, or RemovalAwareCollection depending
207         * on the field type. 
208         * 
209         * @param desc            The collection descriptor
210         * @param collectionClass The collection class specified in the collection-descriptor
211         * @return The collection object
212         */
213        protected ManageableCollection createCollection(CollectionDescriptor desc, Class collectionClass)
214        {
215            Class                fieldType = desc.getPersistentField().getType();
216            ManageableCollection col;
217    
218            if (collectionClass == null)
219            {
220                if (ManageableCollection.class.isAssignableFrom(fieldType))
221                {
222                    try
223                    {
224                        col = (ManageableCollection)fieldType.newInstance();
225                    }
226                    catch (Exception e)
227                    {
228                        throw new OJBRuntimeException("Cannot instantiate the default collection type "+fieldType.getName()+" of collection "+desc.getAttributeName()+" in type "+desc.getClassDescriptor().getClassNameOfObject());
229                    }
230                }
231                else if (fieldType.isAssignableFrom(RemovalAwareCollection.class))
232                {
233                    col = new RemovalAwareCollection();
234                }
235                else if (fieldType.isAssignableFrom(RemovalAwareList.class))
236                {
237                    col = new RemovalAwareList();
238                }
239                else if (fieldType.isAssignableFrom(RemovalAwareSet.class))
240                {
241                    col = new RemovalAwareSet();
242                }
243                else
244                {
245                    throw new MetadataException("Cannot determine a default collection type for collection "+desc.getAttributeName()+" in type "+desc.getClassDescriptor().getClassNameOfObject());
246                }
247            }
248            else
249            {
250                try
251                {
252                    col = (ManageableCollection)collectionClass.newInstance();
253                }
254                catch (Exception e)
255                {
256                    throw new OJBRuntimeException("Cannot instantiate the collection class "+collectionClass.getName()+" of collection "+desc.getAttributeName()+" in type "+desc.getClassDescriptor().getClassNameOfObject());
257                }
258            }
259            return col;
260        }
261    
262        protected CollectionDescriptor getCollectionDescriptor()
263        {
264            return (CollectionDescriptor) getObjectReferenceDescriptor();
265        }
266    }