KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > map > AshwoodEntitySorter


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56
57 package org.objectstyle.cayenne.map;
58
59 import java.util.ArrayList JavaDoc;
60 import java.util.Collection JavaDoc;
61 import java.util.Collections JavaDoc;
62 import java.util.Comparator JavaDoc;
63 import java.util.HashMap JavaDoc;
64 import java.util.Iterator JavaDoc;
65 import java.util.List JavaDoc;
66 import java.util.Map JavaDoc;
67
68 import org.apache.commons.collections.comparators.ReverseComparator;
69 import org.objectstyle.ashwood.dbutil.DbUtils;
70 import org.objectstyle.ashwood.dbutil.ForeignKey;
71 import org.objectstyle.ashwood.dbutil.Table;
72 import org.objectstyle.ashwood.graph.CollectionFactory;
73 import org.objectstyle.ashwood.graph.Digraph;
74 import org.objectstyle.ashwood.graph.IndegreeTopologicalSort;
75 import org.objectstyle.ashwood.graph.MapDigraph;
76 import org.objectstyle.ashwood.graph.StrongConnection;
77 import org.objectstyle.cayenne.CayenneRuntimeException;
78 import org.objectstyle.cayenne.DataObject;
79 import org.objectstyle.cayenne.DataRow;
80 import org.objectstyle.cayenne.ObjectId;
81 import org.objectstyle.cayenne.PersistenceState;
82 import org.objectstyle.cayenne.access.DataContext;
83 import org.objectstyle.cayenne.access.QueryEngine;
84
85 /**
86  * Implements dependency sorting algorithms for ObjEntities,
87  * DbEntities and DataObjects. Presently it works for acyclic database
88  * schemas with possible multi-reflexive tables. The class uses topological
89  * sorting from <a HREF="http://objectstyle.org/ashwood/">ASHWOOD library</a>.
90  *
91  * @author Andriy Shapochka, Andrei Adamchik
92  * @since 1.1
93  */

94 public class AshwoodEntitySorter implements EntitySorter {
95     protected Collection JavaDoc dataMaps;
96     protected Map JavaDoc dbEntityToTableMap;
97     protected Digraph referentialDigraph;
98     protected Digraph contractedReferentialDigraph;
99     protected Map JavaDoc components;
100     protected Map JavaDoc reflexiveDbEntities;
101
102     protected TableComparator tableComparator;
103     protected DbEntityComparator dbEntityComparator;
104     protected ObjEntityComparator objEntityComparator;
105
106     // used for lazy initialization
107
protected boolean dirty;
108
109     public AshwoodEntitySorter(Collection JavaDoc dataMaps) {
110         setDataMaps(dataMaps);
111     }
112     
113     /**
114      * Reindexes internal sorter.
115      */

116     protected synchronized void _indexSorter() {
117         if (!dirty) {
118             return;
119         }
120
121         Collection JavaDoc tables = new ArrayList JavaDoc(64);
122         dbEntityToTableMap = new HashMap JavaDoc(64);
123         reflexiveDbEntities = new HashMap JavaDoc(32);
124         for (Iterator JavaDoc i = dataMaps.iterator(); i.hasNext();) {
125             DataMap map = (DataMap) i.next();
126             Iterator JavaDoc entitiesToConvert = map.getDbEntities().iterator();
127             while (entitiesToConvert.hasNext()) {
128                 DbEntity entity = (DbEntity) entitiesToConvert.next();
129                 Table table =
130                     new Table(entity.getCatalog(), entity.getSchema(), entity.getName());
131                 fillInMetadata(table, entity);
132                 dbEntityToTableMap.put(entity, table);
133                 tables.add(table);
134             }
135         }
136         referentialDigraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
137         DbUtils.buildReferentialDigraph(referentialDigraph, tables);
138         StrongConnection contractor =
139             new StrongConnection(referentialDigraph, CollectionFactory.ARRAYLIST_FACTORY);
140         contractedReferentialDigraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
141         contractor.contract(
142             contractedReferentialDigraph,
143             CollectionFactory.ARRAYLIST_FACTORY);
144         IndegreeTopologicalSort sorter =
145             new IndegreeTopologicalSort(contractedReferentialDigraph);
146         components = new HashMap JavaDoc(contractedReferentialDigraph.order());
147         int componentIndex = 0;
148         while (sorter.hasNext()) {
149             Collection JavaDoc component = (Collection JavaDoc) sorter.next();
150             ComponentRecord rec = new ComponentRecord(componentIndex++, component);
151             for (Iterator JavaDoc i = component.iterator(); i.hasNext();) {
152                 components.put(i.next(), rec);
153             }
154         }
155
156         tableComparator = new TableComparator();
157         dbEntityComparator = new DbEntityComparator();
158         objEntityComparator = new ObjEntityComparator();
159
160         // clear dirty flag
161
this.dirty = false;
162     }
163
164     /**
165      * @since 1.1
166      */

167     public synchronized void setDataMaps(Collection JavaDoc dataMaps) {
168         this.dirty = true;
169         this.dataMaps = dataMaps != null ? dataMaps : Collections.EMPTY_LIST;
170     }
171
172     /**
173       * Marks this instance as "dirty", so that it will be indexed lazily on
174       * the next invocation.
175       */

176     public void indexSorter(QueryEngine queryEngine) {
177         setDataMaps(queryEngine.getDataMaps());
178     }
179
180     public void sortDbEntities(List JavaDoc dbEntities, boolean deleteOrder) {
181         _indexSorter();
182         Collections.sort(dbEntities, getDbEntityComparator(deleteOrder));
183     }
184
185     public void sortObjEntities(List JavaDoc objEntities, boolean deleteOrder) {
186         _indexSorter();
187         Collections.sort(objEntities, getObjEntityComparator(deleteOrder));
188     }
189
190     public void sortObjectsForEntity(
191         ObjEntity objEntity,
192         List JavaDoc objects,
193         boolean deleteOrder) {
194
195         // don't forget to index the sorter
196
_indexSorter();
197
198         DbEntity dbEntity = objEntity.getDbEntity();
199
200         // if no sorting is required
201
if (!isReflexive(dbEntity)) {
202             return;
203         }
204
205         int size = objects.size();
206         List JavaDoc reflexiveRels = (List JavaDoc) reflexiveDbEntities.get(dbEntity);
207         String JavaDoc[] reflexiveRelNames = new String JavaDoc[reflexiveRels.size()];
208         for (int i = 0; i < reflexiveRelNames.length; i++) {
209             DbRelationship dbRel = (DbRelationship) reflexiveRels.get(i);
210             ObjRelationship objRel =
211                 (dbRel != null
212                     ? objEntity.getRelationshipForDbRelationship(dbRel)
213                     : null);
214             reflexiveRelNames[i] = (objRel != null ? objRel.getName() : null);
215         }
216
217         List JavaDoc sorted = new ArrayList JavaDoc(size);
218
219         Digraph objectDependencyGraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
220         Object JavaDoc[] masters = new Object JavaDoc[reflexiveRelNames.length];
221         for (int i = 0; i < size; i++) {
222             DataObject current = (DataObject) objects.get(i);
223             objectDependencyGraph.addVertex(current);
224             int actualMasterCount = 0;
225             for (int k = 0; k < reflexiveRelNames.length; k++) {
226                 String JavaDoc reflexiveRelName = reflexiveRelNames[k];
227
228                 if (reflexiveRelName == null) {
229                     continue;
230                 }
231
232                 masters[k] =
233                     (reflexiveRelName != null)
234                         ? current.readProperty(reflexiveRelName)
235                         : null;
236
237                 if (masters[k] == null) {
238                     masters[k] =
239                         findReflexiveMaster(
240                             current,
241                             (ObjRelationship) objEntity.getRelationship(reflexiveRelName),
242                             current.getObjectId().getObjectClass());
243                 }
244
245                 if (masters[k] != null) {
246                     actualMasterCount++;
247                 }
248             }
249
250             int mastersFound = 0;
251             for (int j = 0; j < size && mastersFound < actualMasterCount; j++) {
252
253                 if (i == j) {
254                     continue;
255                 }
256
257                 DataObject masterCandidate = (DataObject) objects.get(j);
258                 for (int k = 0; k < masters.length; k++) {
259                     if (masterCandidate.equals(masters[k])) {
260                         objectDependencyGraph.putArc(
261                             masterCandidate,
262                             current,
263                             Boolean.TRUE);
264                         mastersFound++;
265                     }
266                 }
267             }
268         }
269
270         IndegreeTopologicalSort sorter =
271             new IndegreeTopologicalSort(objectDependencyGraph);
272
273         while (sorter.hasNext()) {
274             DataObject o = (DataObject) sorter.next();
275             if (o == null)
276                 throw new CayenneRuntimeException(
277                     "Sorting objects for "
278                         + objEntity.getClassName()
279                         + " failed. Cycles found.");
280             sorted.add(o);
281         }
282
283         // since API requires sorting within the same array,
284
// simply replace all objects with objects in the right order...
285
// may come up with something cleaner later
286
objects.clear();
287         objects.addAll(sorted);
288
289         if (deleteOrder) {
290             Collections.reverse(objects);
291         }
292     }
293     
294     protected void fillInMetadata(Table table, DbEntity entity) {
295         //in this case quite a dummy
296
short keySequence = 1;
297         Iterator JavaDoc i = entity.getRelationshipMap().values().iterator();
298
299         while (i.hasNext()) {
300             DbRelationship candidate = (DbRelationship) i.next();
301             if ((!candidate.isToMany() && !candidate.isToDependentPK())
302                 || candidate.isToMasterPK()) {
303                 DbEntity target = (DbEntity) candidate.getTargetEntity();
304                 boolean newReflexive = entity.equals(target);
305                 Iterator JavaDoc j = candidate.getJoins().iterator();
306                 while (j.hasNext()) {
307                     DbJoin join = (DbJoin) j.next();
308                     DbAttribute targetAttribute = join.getTarget();
309                     if (targetAttribute.isPrimaryKey()) {
310                         ForeignKey fk = new ForeignKey();
311                         fk.setPkTableCatalog(target.getCatalog());
312                         fk.setPkTableSchema(target.getSchema());
313                         fk.setPkTableName(target.getName());
314                         fk.setPkColumnName(targetAttribute.getName());
315                         fk.setColumnName(join.getSourceName());
316                         fk.setKeySequence(keySequence++);
317                         table.addForeignKey(fk);
318
319                         if (newReflexive) {
320                             List JavaDoc reflexiveRels = (List JavaDoc) reflexiveDbEntities.get(entity);
321                             if (reflexiveRels == null) {
322                                 reflexiveRels = new ArrayList JavaDoc(1);
323                                 reflexiveDbEntities.put(entity, reflexiveRels);
324                             }
325                             reflexiveRels.add(candidate);
326                             newReflexive = false;
327                         }
328                     }
329                 }
330             }
331         }
332     }
333
334     protected DataObject findReflexiveMaster(
335         DataObject object,
336         ObjRelationship toOneRel,
337         Class JavaDoc targetClass) {
338         DbRelationship finalRel = (DbRelationship) toOneRel.getDbRelationships().get(0);
339
340         DataRow snapshot = null;
341
342         // IMPORTANT: don't try to get snapshots for new objects, this will result in exception
343
if (object.getPersistenceState() != PersistenceState.NEW) {
344             DataContext context = object.getDataContext();
345             snapshot =
346                 context.getObjectStore().getSnapshot(object.getObjectId(), context);
347         }
348
349         if (snapshot == null) {
350             snapshot = object.getDataContext().currentSnapshot(object);
351         }
352
353         ObjectId id = snapshot.createTargetObjectId(targetClass, finalRel);
354         return (id != null) ? object.getDataContext().registeredObject(id) : null;
355     }
356
357     protected Comparator JavaDoc getDbEntityComparator(boolean dependantFirst) {
358         Comparator JavaDoc c = dbEntityComparator;
359         if (dependantFirst) {
360             c = new ReverseComparator(c);
361         }
362         return c;
363     }
364
365     protected Comparator JavaDoc getObjEntityComparator(boolean dependantFirst) {
366         Comparator JavaDoc c = objEntityComparator;
367         if (dependantFirst) {
368             c = new ReverseComparator(c);
369         }
370         return c;
371     }
372
373     protected Table getTable(DbEntity dbEntity) {
374         return (dbEntity != null) ? (Table) dbEntityToTableMap.get(dbEntity) : null;
375     }
376
377     protected Table getTable(ObjEntity objEntity) {
378         return getTable(objEntity.getDbEntity());
379     }
380
381     protected boolean isReflexive(DbEntity metadata) {
382         return reflexiveDbEntities.containsKey(metadata);
383     }
384
385     private final class DbEntityComparator implements Comparator JavaDoc {
386         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
387             if (o1 == o2)
388                 return 0;
389             Table t1 = getTable((DbEntity) o1);
390             Table t2 = getTable((DbEntity) o2);
391             return tableComparator.compare(t1, t2);
392         }
393     }
394
395     private final class ObjEntityComparator implements Comparator JavaDoc {
396         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
397             if (o1 == o2)
398                 return 0;
399             Table t1 = getTable((ObjEntity) o1);
400             Table t2 = getTable((ObjEntity) o2);
401             return tableComparator.compare(t1, t2);
402         }
403     }
404
405     private final class TableComparator implements Comparator JavaDoc {
406         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
407             int result = 0;
408             Table t1 = (Table) o1;
409             Table t2 = (Table) o2;
410             if (t1 == t2)
411                 return 0;
412             if (t1 == null)
413                 result = -1;
414             else if (t2 == null)
415                 result = 1;
416             else {
417                 ComponentRecord rec1 = (ComponentRecord) components.get(t1);
418                 ComponentRecord rec2 = (ComponentRecord) components.get(t2);
419                 int index1 = rec1.index;
420                 int index2 = rec2.index;
421                 result = (index1 > index2 ? 1 : (index1 < index2 ? -1 : 0));
422                 if (result != 0 && rec1.component == rec2.component)
423                     result = 0;
424             }
425             return result;
426         }
427     }
428
429     private final static class ComponentRecord {
430         ComponentRecord(int index, Collection JavaDoc component) {
431             this.index = index;
432             this.component = component;
433         }
434         int index;
435         Collection JavaDoc component;
436     }
437
438 }
Popular Tags