Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/objectstyle/cayenne/access/EntityResolver.java


1   /* ====================================================================
2    *
3    * The ObjectStyle Group Software License, Version 1.0
4    *
5    * Copyright (c) 2002-2003 The ObjectStyle Group
6    * and individual authors of the software.  All rights reserved.
7    *
8    * Redistribution and use in source and binary forms, with or without
9    * modification, are permitted provided that the following conditions
10   * are met:
11   *
12   * 1. Redistributions of source code must retain the above copyright
13   *    notice, this list of conditions and the following disclaimer.
14   *
15   * 2. Redistributions in binary form must reproduce the above copyright
16   *    notice, this list of conditions and the following disclaimer in
17   *    the documentation and/or other materials provided with the
18   *    distribution.
19   *
20   * 3. The end-user documentation included with the redistribution, if
21   *    any, must include the following acknowlegement:
22   *       "This product includes software developed by the
23   *        ObjectStyle Group (http://objectstyle.org/)."
24   *    Alternately, this acknowlegement may appear in the software itself,
25   *    if and wherever such third-party acknowlegements normally appear.
26   *
27   * 4. The names "ObjectStyle Group" and "Cayenne"
28   *    must not be used to endorse or promote products derived
29   *    from this software without prior written permission. For written
30   *    permission, please contact andrus@objectstyle.org.
31   *
32   * 5. Products derived from this software may not be called "ObjectStyle"
33   *    nor may "ObjectStyle" appear in their names without prior written
34   *    permission of the ObjectStyle Group.
35   *
36   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39   * DISCLAIMED.  IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
40   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47   * SUCH DAMAGE.
48   * ====================================================================
49   *
50   * This software consists of voluntary contributions made by many
51   * individuals on behalf of the ObjectStyle Group.  For more
52   * information on the ObjectStyle Group, please see
53   * <http://objectstyle.org/>.
54   *
55   */
56  package org.objectstyle.cayenne.access;
57  
58  import java.util.ArrayList;
59  import java.util.Collection;
60  import java.util.Collections;
61  import java.util.HashMap;
62  import java.util.Iterator;
63  import java.util.List;
64  import java.util.Map;
65  
66  import org.objectstyle.cayenne.CayenneRuntimeException;
67  import org.objectstyle.cayenne.DataObject;
68  import org.objectstyle.cayenne.conf.Configuration;
69  import org.objectstyle.cayenne.map.DataMap;
70  import org.objectstyle.cayenne.map.DbEntity;
71  import org.objectstyle.cayenne.map.Entity;
72  import org.objectstyle.cayenne.map.ObjEntity;
73  import org.objectstyle.cayenne.map.Procedure;
74  import org.objectstyle.cayenne.query.Query;
75  import org.objectstyle.cayenne.query.SelectQuery;
76  
77  /**
78   * EntityResolver encapsulates resolving between ObjEntities, DbEntities,
79   * DataObject Classes, and Entity names. An instance is typically obtained
80   * from a QueryEngine by getEntityResolver. EntityResolver is thread-safe.
81   *
82   * @author Craig Miskell
83   * @author Andrei Adamchik
84   */
85  public class EntityResolver {
86      protected Map dbEntityCache;
87      protected Map objEntityCache;
88      protected Map procedureCache;
89      protected List maps;
90      protected List mapsRef;
91  
92      public EntityResolver() {
93          this.maps = new ArrayList();
94          this.mapsRef = Collections.unmodifiableList(maps);
95          this.dbEntityCache = new HashMap();
96          this.objEntityCache = new HashMap();
97          this.procedureCache = new HashMap();
98      }
99  
100     /**
101      * Constructor for EntityResolver.
102      */
103     public EntityResolver(Collection dataMaps) {
104         this();
105         this.maps.addAll(dataMaps); //Take a copy
106         this.constructCache();
107     }
108 
109     /**
110      * Adds a DataMap to the list handled by resolver.
111      */
112     public synchronized void addDataMap(DataMap map) {
113         if (!maps.contains(map)) {
114             maps.add(map);
115             clearCache();
116         }
117     }
118 
119     public synchronized void removeDataMap(DataMap map) {
120         if (maps.remove(map)) {
121             clearCache();
122         }
123     }
124 
125     /**
126      * Returns a DataMap matching the name.
127      */
128     public synchronized DataMap getDataMap(String mapName) {
129         if (mapName == null) {
130             return null;
131         }
132 
133         Iterator it = maps.iterator();
134         while (it.hasNext()) {
135             DataMap map = (DataMap) it.next();
136             if (mapName.equals(map.getName())) {
137                 return map;
138             }
139         }
140 
141         return null;
142     }
143 
144     public synchronized void setDataMaps(Collection maps) {
145         this.maps.clear();
146         this.maps.addAll(maps);
147         clearCache();
148     }
149 
150     /**
151      * Returns an unmodifiable collection of DataMaps.
152      */
153     public Collection getDataMaps() {
154         return mapsRef;
155     }
156 
157     /**
158      * Removes all entity mappings from the cache.
159      * Cache can be rebuilt either explicitly by calling
160      * <code>constructCache</code>, or on demand by calling any of the
161      * <code>lookup...</code> methods.
162      */
163     protected synchronized void clearCache() {
164         dbEntityCache.clear();
165         objEntityCache.clear();
166         procedureCache.clear();
167     }
168 
169     /**
170      * Creates caches of DbEntities by ObjEntity,
171      * DataObject class, and ObjEntity name using internal
172      * list of maps.
173      */
174     protected synchronized void constructCache() {
175         clearCache();
176         Iterator mapIterator = maps.iterator();
177         while (mapIterator.hasNext()) {
178             DataMap thisMap = (DataMap) mapIterator.next();
179 
180             // index entities
181             Iterator objEntities = thisMap.getObjEntities().iterator();
182             while (objEntities.hasNext()) {
183                 ObjEntity oe = (ObjEntity) objEntities.next();
184                 DbEntity de = oe.getDbEntity();
185                 dbEntityCache.put(oe, de);
186                 Class entityClass;
187                 try {
188                     entityClass =
189                         Configuration.getResourceLoader().loadClass(
190                             oe.getClassName());
191                 } catch (ClassNotFoundException e) {
192                     throw new CayenneRuntimeException(
193                         "Cannot find class " + oe.getClassName());
194                 }
195 
196                 if (objEntityCache.get(entityClass) != null) {
197                     throw new CayenneRuntimeException(
198                         getClass().getName()
199                             + ": More than one ObjEntity ("
200                             + oe.getName()
201                             + " and "
202                             + ((ObjEntity) objEntityCache.get(entityClass))
203                                 .getName()
204                             + ") uses the class "
205                             + entityClass.getName());
206                 }
207 
208                 dbEntityCache.put(entityClass, de);
209                 objEntityCache.put(entityClass, oe);
210                 dbEntityCache.put(oe.getName(), de);
211                 objEntityCache.put(oe.getName(), oe);
212             }
213 
214             // index stored procedures
215             Iterator procedures = thisMap.getProcedures().iterator();
216             while (procedures.hasNext()) {
217                 Procedure proc = (Procedure) procedures.next();
218                 procedureCache.put(proc.getName(), proc);
219             }
220         }
221     }
222 
223     /**
224      * Looks in the DataMap's that this object was created with for the DbEntity
225      * that services the specified class
226      * @return the required DbEntity, or null if none matches the specifier
227      */
228     public synchronized DbEntity lookupDbEntity(Class aClass) {
229         return this._lookupDbEntity(aClass);
230     }
231 
232     /**
233      * Looks in the DataMap's that this object was created with for the DbEntity
234      * that services the specified objentity
235      * @return the required DbEntity, or null if none matches the specifier
236      */
237     public synchronized DbEntity lookupDbEntity(ObjEntity entity) {
238         return this._lookupDbEntity(entity);
239     }
240 
241     /**
242      * Looks in the DataMap's that this object was created with for the DbEntity
243      * that services the class with the given name
244      * @return the required DbEntity, or null if none matches the specifier
245      */
246     public synchronized DbEntity lookupDbEntity(String entityName) {
247         return this._lookupDbEntity(entityName);
248     }
249 
250     /**
251      * Looks in the DataMap's that this object was created with for the DbEntity
252      * that services the specified data Object
253      * @return the required DbEntity, or null if none matches the specifier
254      */
255     public synchronized DbEntity lookupDbEntity(DataObject dataObject) {
256         return this._lookupDbEntity(dataObject.getClass());
257     }
258 
259     /**
260      * Internal usage only - provides the type-unsafe implementation which services
261      * the four typesafe public lookupDbEntity methods
262      * Looks in the DataMap's that this object was created with for the ObjEntity that maps to the
263      * specified object.  Object may be a Entity name, ObjEntity, DataObject class
264      * (Class object for a class which implements the DataObject interface), or a DataObject
265      * instance itself
266      *
267      * @return the required DbEntity, or null if none matches the specifier
268      */
269     private synchronized DbEntity _lookupDbEntity(Object object) {
270         if (object instanceof DbEntity) {
271             return (DbEntity) object;
272         }
273 
274         DbEntity result = (DbEntity) dbEntityCache.get(object);
275         if (result == null) {
276             // reconstruct cache just in case some of the datamaps
277             // have changed and now contain the required information
278             constructCache();
279             result = (DbEntity) dbEntityCache.get(object);
280         }
281         return result;
282     }
283 
284     /**
285      * Looks up the DbEntity for the given query by using the query's getRoot method and passing to lookupDbEntity
286      * @return the root DbEntity of the query
287      */
288     public synchronized DbEntity lookupDbEntity(Query q) {
289         Object root = q.getRoot();
290         if (root instanceof DbEntity) {
291             return (DbEntity) root;
292         } else if (root instanceof Class) {
293             return this.lookupDbEntity((Class) root);
294         } else if (root instanceof ObjEntity) {
295             return this.lookupDbEntity((ObjEntity) root);
296         } else if (root instanceof String) {
297             return this.lookupDbEntity((String) root);
298         } else if (root instanceof DataObject) {
299             return this.lookupDbEntity((DataObject) root);
300         }
301         return null;
302     }
303 
304     /**
305      * Looks in the DataMap's that this object was created with for the ObjEntity that maps to the
306      * services the specified class
307      * @return the required ObjEntity or null if there is none that matches the specifier
308      */
309     public synchronized ObjEntity lookupObjEntity(Class aClass) {
310         return this._lookupObjEntity(aClass);
311     }
312 
313     /**
314      * Looks in the DataMap's that this object was created with for the ObjEntity that maps to the
315      * services the class with the given name
316      * @return the required ObjEntity or null if there is none that matches the specifier
317      */
318     public synchronized ObjEntity lookupObjEntity(String entityName) {
319         return this._lookupObjEntity(entityName);
320     }
321 
322     /**
323      * Looks in the DataMap's that this object was created with for the ObjEntity
324      * that services the specified data Object
325      * @return the required ObjEntity, or null if none matches the specifier
326      */
327     public synchronized ObjEntity lookupObjEntity(DataObject dataObject) {
328         return this._lookupObjEntity(dataObject.getClass());
329     }
330 
331     /**
332      * Internal usage only - provides the type-unsafe implementation which services
333      * the three typesafe public lookupObjEntity methods
334      * Looks in the DataMap's that this object was created with for the ObjEntity that maps to the
335      * specified object. Object may be a Entity name, DataObject instance or DataObject class
336      * (Class object for a class which implements the DataObject interface)
337      *
338      * @return the required ObjEntity or null if there is none that matches the specifier
339      */
340     private synchronized ObjEntity _lookupObjEntity(Object object) {
341         if (object instanceof ObjEntity) {
342             return (ObjEntity) object;
343         }
344 
345         if (object instanceof DataObject) {
346             object = object.getClass();
347         }
348 
349         ObjEntity result = (ObjEntity) objEntityCache.get(object);
350         if (result == null) {
351             // reconstruct cache just in case some of the datamaps
352             // have changed and now contain the required information
353             constructCache();
354             result = (ObjEntity) objEntityCache.get(object);
355         }
356         return result;
357     }
358 
359     /**
360      * Looks up the ObjEntity for the given query by using the query's getRoot method and passing to lookupObjEntity
361      * @return the root ObjEntity of the query
362      * @throws CayenneRuntimeException if the root of the query is a DbEntity (it is not reliably possible to map
363      * from a DbEntity to an ObjEntity as a DbEntity may be the source for multiple ObjEntities.  It is not safe
364      * to rely on such behaviour).
365      */
366     public synchronized ObjEntity lookupObjEntity(Query q) {
367 
368         Object root = q.getRoot();
369         if (root instanceof DbEntity) {
370             throw new CayenneRuntimeException(
371                 "Cannot safely resolve the ObjEntity for the query "
372                     + q
373                     + " because the root of the query is a DbEntity");
374         } else if (root instanceof ObjEntity) {
375             return (ObjEntity) root;
376         } else if (root instanceof Class) {
377             return this.lookupObjEntity((Class) root);
378         } else if (root instanceof String) {
379             return this.lookupObjEntity((String) root);
380         } else if (root instanceof DataObject) {
381             return this.lookupObjEntity((DataObject) root);
382         }
383         return null;
384     }
385 
386     /**
387      * Searches for the named query associated with the ObjEntity corresponding
388      * to the Java class specified. Returns such query if found, null otherwise.
389      */
390     public SelectQuery lookupQuery(Class queryRoot, String queryName) {
391         Entity ent = lookupObjEntity(queryRoot);
392         return (ent != null) ? ent.getQuery(queryName) : null;
393     }
394 
395     public Procedure lookupProcedure(Query q) {
396         Object root = q.getRoot();
397         if (root instanceof Procedure) {
398             return (Procedure) root;
399         } else if (root instanceof String) {
400             return this.lookupProcedure((String) root);
401         }
402         return null;
403     }
404 
405     public Procedure lookupProcedure(String procedureName) {
406         
407         Procedure result = (Procedure) procedureCache.get(procedureName);
408         if (result == null) {
409             // reconstruct cache just in case some of the datamaps
410             // have changed and now contain the required information
411             constructCache();
412             result = (Procedure) procedureCache.get(procedureName);
413         }
414         
415         return result;
416     }
417 }