KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.map;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.Comparator JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30
31 import org.apache.cayenne.CayenneRuntimeException;
32 import org.apache.cayenne.DataRow;
33 import org.apache.cayenne.ObjectContext;
34 import org.apache.cayenne.ObjectId;
35 import org.apache.cayenne.Persistent;
36 import org.apache.cayenne.QueryResponse;
37 import org.apache.cayenne.query.ObjectIdQuery;
38 import org.apache.cayenne.reflect.ClassDescriptor;
39 import org.apache.commons.collections.comparators.ReverseComparator;
40 import org.objectstyle.ashwood.dbutil.DbUtils;
41 import org.objectstyle.ashwood.dbutil.ForeignKey;
42 import org.objectstyle.ashwood.dbutil.Table;
43 import org.objectstyle.ashwood.graph.CollectionFactory;
44 import org.objectstyle.ashwood.graph.Digraph;
45 import org.objectstyle.ashwood.graph.IndegreeTopologicalSort;
46 import org.objectstyle.ashwood.graph.MapDigraph;
47 import org.objectstyle.ashwood.graph.StrongConnection;
48
49 /**
50  * Implements dependency sorting algorithms for ObjEntities, DbEntities and DataObjects.
51  * Presently it works for acyclic database schemas with possible multi-reflexive tables.
52  * The class uses topological sorting from the <a
53  * HREF="http://objectstyle.org/ashwood/">Ashwood library</a>.
54  *
55  * @author Andriy Shapochka, Andrus Adamchik
56  * @since 1.1
57  */

58 public class AshwoodEntitySorter implements EntitySorter {
59
60     protected Collection JavaDoc dataMaps;
61     protected Map JavaDoc dbEntityToTableMap;
62     protected Digraph referentialDigraph;
63     protected Digraph contractedReferentialDigraph;
64     protected Map JavaDoc components;
65     protected Map JavaDoc reflexiveDbEntities;
66
67     protected TableComparator tableComparator;
68     protected DbEntityComparator dbEntityComparator;
69     protected ObjEntityComparator objEntityComparator;
70
71     // used for lazy initialization
72
protected boolean dirty;
73
74     public AshwoodEntitySorter(Collection JavaDoc dataMaps) {
75         tableComparator = new TableComparator();
76         dbEntityComparator = new DbEntityComparator();
77         objEntityComparator = new ObjEntityComparator();
78
79         setDataMaps(dataMaps);
80     }
81
82     /**
83      * Reindexes internal sorter.
84      */

85     protected synchronized void _indexSorter() {
86         if (!dirty) {
87             return;
88         }
89
90         Collection JavaDoc tables = new ArrayList JavaDoc(64);
91         dbEntityToTableMap = new HashMap JavaDoc(64);
92         reflexiveDbEntities = new HashMap JavaDoc(32);
93         for (Iterator JavaDoc i = dataMaps.iterator(); i.hasNext();) {
94             DataMap map = (DataMap) i.next();
95             Iterator JavaDoc entitiesToConvert = map.getDbEntities().iterator();
96             while (entitiesToConvert.hasNext()) {
97                 DbEntity entity = (DbEntity) entitiesToConvert.next();
98                 Table table = new Table(entity.getCatalog(), entity.getSchema(), entity
99                         .getName());
100                 fillInMetadata(table, entity);
101                 dbEntityToTableMap.put(entity, table);
102                 tables.add(table);
103             }
104         }
105         referentialDigraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
106         DbUtils.buildReferentialDigraph(referentialDigraph, tables);
107         StrongConnection contractor = new StrongConnection(
108                 referentialDigraph,
109                 CollectionFactory.ARRAYLIST_FACTORY);
110         contractedReferentialDigraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
111         contractor.contract(
112                 contractedReferentialDigraph,
113                 CollectionFactory.ARRAYLIST_FACTORY);
114         IndegreeTopologicalSort sorter = new IndegreeTopologicalSort(
115                 contractedReferentialDigraph);
116         components = new HashMap JavaDoc(contractedReferentialDigraph.order());
117         int componentIndex = 0;
118         while (sorter.hasNext()) {
119             Collection JavaDoc component = (Collection JavaDoc) sorter.next();
120             ComponentRecord rec = new ComponentRecord(componentIndex++, component);
121             for (Iterator JavaDoc i = component.iterator(); i.hasNext();) {
122                 components.put(i.next(), rec);
123             }
124         }
125
126         // clear dirty flag
127
this.dirty = false;
128     }
129
130     /**
131      * @since 1.1
132      */

133     public synchronized void setDataMaps(Collection JavaDoc dataMaps) {
134         this.dirty = true;
135         this.dataMaps = dataMaps != null ? dataMaps : Collections.EMPTY_LIST;
136     }
137
138     public void sortDbEntities(List JavaDoc dbEntities, boolean deleteOrder) {
139         _indexSorter();
140         Collections.sort(dbEntities, getDbEntityComparator(deleteOrder));
141     }
142
143     public void sortObjEntities(List JavaDoc objEntities, boolean deleteOrder) {
144         _indexSorter();
145         Collections.sort(objEntities, getObjEntityComparator(deleteOrder));
146     }
147
148     public void sortObjectsForEntity(
149             ObjEntity objEntity,
150             List JavaDoc objects,
151             boolean deleteOrder) {
152
153         // don't forget to index the sorter
154
_indexSorter();
155
156         DbEntity dbEntity = objEntity.getDbEntity();
157
158         // if no sorting is required
159
if (!isReflexive(dbEntity)) {
160             return;
161         }
162
163         int size = objects.size();
164         if (size == 0) {
165             return;
166         }
167
168         EntityResolver resolver = ((Persistent) objects.get(0))
169                 .getObjectContext()
170                 .getEntityResolver();
171         ClassDescriptor descriptor = resolver.getClassDescriptor(objEntity.getName());
172
173         List JavaDoc reflexiveRels = (List JavaDoc) reflexiveDbEntities.get(dbEntity);
174         String JavaDoc[] reflexiveRelNames = new String JavaDoc[reflexiveRels.size()];
175         for (int i = 0; i < reflexiveRelNames.length; i++) {
176             DbRelationship dbRel = (DbRelationship) reflexiveRels.get(i);
177             ObjRelationship objRel = (dbRel != null ? objEntity
178                     .getRelationshipForDbRelationship(dbRel) : null);
179             reflexiveRelNames[i] = (objRel != null ? objRel.getName() : null);
180         }
181
182         List JavaDoc sorted = new ArrayList JavaDoc(size);
183
184         Digraph objectDependencyGraph = new MapDigraph(MapDigraph.HASHMAP_FACTORY);
185         Object JavaDoc[] masters = new Object JavaDoc[reflexiveRelNames.length];
186         for (int i = 0; i < size; i++) {
187             Persistent current = (Persistent) objects.get(i);
188             objectDependencyGraph.addVertex(current);
189             int actualMasterCount = 0;
190             for (int k = 0; k < reflexiveRelNames.length; k++) {
191                 String JavaDoc reflexiveRelName = reflexiveRelNames[k];
192
193                 if (reflexiveRelName == null) {
194                     continue;
195                 }
196
197                 masters[k] = (reflexiveRelName != null) ? descriptor.getProperty(
198                         reflexiveRelName).readProperty(current) : null;
199
200                 if (masters[k] == null) {
201                     masters[k] = findReflexiveMaster(current, (ObjRelationship) objEntity
202                             .getRelationship(reflexiveRelName), current
203                             .getObjectId()
204                             .getEntityName());
205                 }
206
207                 if (masters[k] != null) {
208                     actualMasterCount++;
209                 }
210             }
211
212             int mastersFound = 0;
213             for (int j = 0; j < size && mastersFound < actualMasterCount; j++) {
214
215                 if (i == j) {
216                     continue;
217                 }
218
219                 Object JavaDoc masterCandidate = objects.get(j);
220                 for (int k = 0; k < masters.length; k++) {
221                     if (masterCandidate.equals(masters[k])) {
222                         objectDependencyGraph.putArc(
223                                 masterCandidate,
224                                 current,
225                                 Boolean.TRUE);
226                         mastersFound++;
227                     }
228                 }
229             }
230         }
231
232         IndegreeTopologicalSort sorter = new IndegreeTopologicalSort(
233                 objectDependencyGraph);
234
235         while (sorter.hasNext()) {
236             Object JavaDoc o = sorter.next();
237             if (o == null)
238                 throw new CayenneRuntimeException("Sorting objects for "
239                         + objEntity.getClassName()
240                         + " failed. Cycles found.");
241             sorted.add(o);
242         }
243
244         // since API requires sorting within the same array,
245
// simply replace all objects with objects in the right order...
246
// may come up with something cleaner later
247
objects.clear();
248         objects.addAll(sorted);
249
250         if (deleteOrder) {
251             Collections.reverse(objects);
252         }
253     }
254
255     protected void fillInMetadata(Table table, DbEntity entity) {
256         // in this case quite a dummy
257
short keySequence = 1;
258         Iterator JavaDoc i = entity.getRelationshipMap().values().iterator();
259
260         while (i.hasNext()) {
261             DbRelationship candidate = (DbRelationship) i.next();
262             if ((!candidate.isToMany() && !candidate.isToDependentPK())
263                     || candidate.isToMasterPK()) {
264                 DbEntity target = (DbEntity) candidate.getTargetEntity();
265                 boolean newReflexive = entity.equals(target);
266                 Iterator JavaDoc j = candidate.getJoins().iterator();
267                 while (j.hasNext()) {
268                     DbJoin join = (DbJoin) j.next();
269                     DbAttribute targetAttribute = join.getTarget();
270                     if (targetAttribute.isPrimaryKey()) {
271                         ForeignKey fk = new ForeignKey();
272                         fk.setPkTableCatalog(target.getCatalog());
273                         fk.setPkTableSchema(target.getSchema());
274                         fk.setPkTableName(target.getName());
275                         fk.setPkColumnName(targetAttribute.getName());
276                         fk.setColumnName(join.getSourceName());
277                         fk.setKeySequence(keySequence++);
278                         table.addForeignKey(fk);
279
280                         if (newReflexive) {
281                             List JavaDoc reflexiveRels = (List JavaDoc) reflexiveDbEntities.get(entity);
282                             if (reflexiveRels == null) {
283                                 reflexiveRels = new ArrayList JavaDoc(1);
284                                 reflexiveDbEntities.put(entity, reflexiveRels);
285                             }
286                             reflexiveRels.add(candidate);
287                             newReflexive = false;
288                         }
289                     }
290                 }
291             }
292         }
293     }
294
295     protected Object JavaDoc findReflexiveMaster(
296             Persistent object,
297             ObjRelationship toOneRel,
298             String JavaDoc targetEntityName) {
299
300         DbRelationship finalRel = (DbRelationship) toOneRel.getDbRelationships().get(0);
301         ObjectContext context = object.getObjectContext();
302
303         // find committed snapshot - so we can't fetch from the context as it will return
304
// dirty snapshot; must go down the stack instead
305
ObjectIdQuery query = new ObjectIdQuery(
306                 object.getObjectId(),
307                 true,
308                 ObjectIdQuery.CACHE);
309         QueryResponse response = context.getChannel().onQuery(null, query);
310         List JavaDoc result = response.firstList();
311         if(result == null || result.size() == 0) {
312             return null;
313         }
314         
315         DataRow snapshot = (DataRow) result.get(0);
316
317         ObjectId id = snapshot.createTargetObjectId(targetEntityName, finalRel);
318         return (id != null) ? context.localObject(id, null) : null;
319     }
320
321     protected Comparator JavaDoc getDbEntityComparator(boolean dependantFirst) {
322         Comparator JavaDoc c = dbEntityComparator;
323         if (dependantFirst) {
324             c = new ReverseComparator(c);
325         }
326         return c;
327     }
328
329     protected Comparator JavaDoc getObjEntityComparator(boolean dependantFirst) {
330         Comparator JavaDoc c = objEntityComparator;
331         if (dependantFirst) {
332             c = new ReverseComparator(c);
333         }
334         return c;
335     }
336
337     protected Table getTable(DbEntity dbEntity) {
338         return (dbEntity != null) ? (Table) dbEntityToTableMap.get(dbEntity) : null;
339     }
340
341     protected Table getTable(ObjEntity objEntity) {
342         return getTable(objEntity.getDbEntity());
343     }
344
345     protected boolean isReflexive(DbEntity metadata) {
346         return reflexiveDbEntities.containsKey(metadata);
347     }
348
349     private final class DbEntityComparator implements Comparator JavaDoc {
350
351         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
352             if (o1 == o2)
353                 return 0;
354             Table t1 = getTable((DbEntity) o1);
355             Table t2 = getTable((DbEntity) o2);
356             return tableComparator.compare(t1, t2);
357         }
358     }
359
360     private final class ObjEntityComparator implements Comparator JavaDoc {
361
362         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
363             if (o1 == o2)
364                 return 0;
365             Table t1 = getTable((ObjEntity) o1);
366             Table t2 = getTable((ObjEntity) o2);
367             return tableComparator.compare(t1, t2);
368         }
369     }
370
371     private final class TableComparator implements Comparator JavaDoc {
372
373         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
374             int result = 0;
375             Table t1 = (Table) o1;
376             Table t2 = (Table) o2;
377             if (t1 == t2)
378                 return 0;
379             if (t1 == null)
380                 result = -1;
381             else if (t2 == null)
382                 result = 1;
383             else {
384                 ComponentRecord rec1 = (ComponentRecord) components.get(t1);
385                 ComponentRecord rec2 = (ComponentRecord) components.get(t2);
386                 int index1 = rec1.index;
387                 int index2 = rec2.index;
388                 result = (index1 > index2 ? 1 : (index1 < index2 ? -1 : 0));
389                 if (result != 0 && rec1.component == rec2.component)
390                     result = 0;
391             }
392             return result;
393         }
394     }
395
396     private final static class ComponentRecord {
397
398         ComponentRecord(int index, Collection JavaDoc component) {
399             this.index = index;
400             this.component = component;
401         }
402
403         int index;
404         Collection JavaDoc component;
405     }
406
407 }
408
Popular Tags