KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > access > ObjectTreeResolver


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.access;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.LinkedList JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27
28 import org.apache.cayenne.CayenneRuntimeException;
29 import org.apache.cayenne.DataRow;
30 import org.apache.cayenne.ObjectId;
31 import org.apache.cayenne.PersistenceState;
32 import org.apache.cayenne.Persistent;
33 import org.apache.cayenne.map.ObjEntity;
34 import org.apache.cayenne.query.PrefetchProcessor;
35 import org.apache.cayenne.query.PrefetchTreeNode;
36 import org.apache.cayenne.query.QueryMetadata;
37 import org.apache.cayenne.reflect.ArcProperty;
38 import org.apache.cayenne.reflect.ClassDescriptor;
39
40 /**
41  * Processes a number of DataRow sets corresponding to a given prefetch tree, resolving
42  * DataRows to an object tree. Can process any combination of joint and disjoint sets, per
43  * prefetch tree.
44  *
45  * @since 1.2
46  * @author Andrus Adamchik
47  */

48 class ObjectTreeResolver {
49
50     DataContext context;
51     QueryMetadata queryMetadata;
52     DataRowStore cache;
53
54     ObjectTreeResolver(DataContext context, QueryMetadata queryMetadata) {
55         this.queryMetadata = queryMetadata;
56         this.context = context;
57         this.cache = context.getObjectStore().getDataRowCache();
58     }
59
60     /**
61      * Properly synchronized version of 'resolveObjectTree'.
62      */

63     List JavaDoc synchronizedObjectsFromDataRows(
64             PrefetchTreeNode tree,
65             List JavaDoc mainResultRows,
66             Map JavaDoc extraResultsByPath) {
67
68         synchronized (context.getObjectStore()) {
69             synchronized (cache) {
70                 return resolveObjectTree(tree, mainResultRows, extraResultsByPath);
71             }
72         }
73     }
74
75     List JavaDoc resolveObjectTree(
76             PrefetchTreeNode tree,
77             List JavaDoc mainResultRows,
78             Map JavaDoc extraResultsByPath) {
79
80         // create a copy of the tree using DecoratedPrefetchNodes and then traverse it
81
// resolving objects...
82
PrefetchProcessorNode decoratedTree = new TreeBuilder(
83                 mainResultRows,
84                 extraResultsByPath).buildTree(tree);
85
86         // do a single path for disjoint prefetches, joint subtrees will be processed at
87
// each disjoint node that is a parent of joint prefetches.
88
decoratedTree.traverse(new DisjointProcessor());
89
90         // connect related objects
91
decoratedTree.traverse(new PostProcessor());
92
93         return decoratedTree.getObjects() != null
94                 ? decoratedTree.getObjects()
95                 : new ArrayList JavaDoc(1);
96     }
97
98     // A PrefetchProcessor that creates a replica of a PrefetchTree with node
99
// subclasses that can carry extra info needed during traversal.
100
final class TreeBuilder implements PrefetchProcessor {
101
102         PrefetchProcessorNode root;
103         LinkedList JavaDoc nodeStack;
104
105         List JavaDoc mainResultRows;
106         Map JavaDoc extraResultsByPath;
107
108         TreeBuilder(List JavaDoc mainResultRows, Map JavaDoc extraResultsByPath) {
109             this.mainResultRows = mainResultRows;
110             this.extraResultsByPath = extraResultsByPath;
111         }
112
113         PrefetchProcessorNode buildTree(PrefetchTreeNode tree) {
114             // reset state
115
this.nodeStack = new LinkedList JavaDoc();
116             this.root = null;
117
118             tree.traverse(this);
119
120             if (root == null) {
121                 throw new CayenneRuntimeException(
122                         "Failed to create prefetch processing tree.");
123             }
124
125             return root;
126         }
127
128         public boolean startPhantomPrefetch(PrefetchTreeNode node) {
129
130             // root should be treated as disjoint
131
if (getParent() == null) {
132                 return startDisjointPrefetch(node);
133             }
134             else {
135                 PrefetchProcessorNode decorated = new PrefetchProcessorNode(
136                         getParent(),
137                         node.getName());
138
139                 decorated.setPhantom(true);
140                 return addNode(decorated);
141             }
142         }
143
144         public boolean startDisjointPrefetch(PrefetchTreeNode node) {
145
146             // look ahead for joint children as joint children will require a different
147
// node type.
148

149             // TODO, Andrus, 11/16/2005 - minor inefficiency: 'adjacentJointNodes' would
150
// grab ALL nodes, we just need to find first and stop...
151
PrefetchProcessorNode decorated = !node.adjacentJointNodes().isEmpty()
152                     ? new PrefetchProcessorJointNode(getParent(), node.getName())
153                     : new PrefetchProcessorNode(getParent(), node.getName());
154             decorated.setPhantom(false);
155
156             // semantics has to be "DISJOINT" even if the node is joint, as semantics
157
// defines relationship with parent..
158
decorated.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
159             return addNode(decorated);
160         }
161
162         public boolean startJointPrefetch(PrefetchTreeNode node) {
163             PrefetchProcessorJointNode decorated = new PrefetchProcessorJointNode(
164                     getParent(),
165                     node.getName());
166             decorated.setPhantom(false);
167             decorated.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
168             boolean result = addNode(decorated);
169
170             // set "jointChildren" flag on all nodes in the same "join group"
171
PrefetchProcessorNode groupNode = decorated;
172             while (groupNode.getParent() != null && !groupNode.isDisjointPrefetch()) {
173                 groupNode = (PrefetchProcessorNode) groupNode.getParent();
174                 groupNode.setJointChildren(true);
175             }
176
177             return result;
178         }
179
180         public boolean startUnknownPrefetch(PrefetchTreeNode node) {
181             // handle unknown as disjoint...
182
return startDisjointPrefetch(node);
183         }
184
185         public void finishPrefetch(PrefetchTreeNode node) {
186             // pop stack...
187
nodeStack.removeLast();
188         }
189
190         boolean addNode(PrefetchProcessorNode node) {
191
192             List JavaDoc rows;
193             ArcProperty arc;
194             ClassDescriptor descriptor;
195
196             PrefetchProcessorNode currentNode = getParent();
197
198             if (currentNode != null) {
199                 rows = (List JavaDoc) extraResultsByPath.get(node.getPath());
200                 arc = (ArcProperty) currentNode
201                         .getResolver()
202                         .getDescriptor()
203                         .getProperty(node.getName());
204
205                 if (arc == null) {
206                     throw new CayenneRuntimeException("No relationship with name '"
207                             + node.getName()
208                             + "' found in entity "
209                             + currentNode.getResolver().getEntity().getName());
210                 }
211
212                 descriptor = arc.getTargetDescriptor();
213             }
214             else {
215                 arc = null;
216                 descriptor = queryMetadata.getClassDescriptor();
217                 rows = mainResultRows;
218             }
219
220             node.setDataRows(rows);
221             node.setResolver(new ObjectResolver(context, descriptor, queryMetadata
222                     .isRefreshingObjects(), queryMetadata.isResolvingInherited()));
223             node.setIncoming(arc);
224
225             if (currentNode != null) {
226                 currentNode.addChild(node);
227             }
228
229             node.afterInit();
230
231             // push node on stack
232
if (nodeStack.isEmpty()) {
233                 root = node;
234             }
235             nodeStack.addLast(node);
236
237             return true;
238         }
239
240         PrefetchProcessorNode getParent() {
241             return (nodeStack.isEmpty()) ? null : (PrefetchProcessorNode) nodeStack
242                     .getLast();
243         }
244     }
245
246     final class DisjointProcessor implements PrefetchProcessor {
247
248         public boolean startDisjointPrefetch(PrefetchTreeNode node) {
249
250             PrefetchProcessorNode processorNode = (PrefetchProcessorNode) node;
251
252             // this means something bad happened during fetch
253
if (processorNode.getDataRows() == null) {
254                 return false;
255             }
256
257             // ... continue with processing even if the objects list is empty to handle
258
// multi-step prefetches.
259
if (processorNode.getDataRows().isEmpty()) {
260                 return true;
261             }
262
263             List JavaDoc objects;
264
265             // disjoint node that is an instance of DecoratedJointNode is a top
266
// of a local joint prefetch "group"...
267
if (processorNode instanceof PrefetchProcessorJointNode) {
268                 JointProcessor subprocessor = new JointProcessor(
269                         (PrefetchProcessorJointNode) processorNode);
270                 Iterator JavaDoc it = processorNode.getDataRows().iterator();
271                 while (it.hasNext()) {
272                     subprocessor.setCurrentFlatRow((DataRow) it.next());
273                     processorNode.traverse(subprocessor);
274                 }
275
276                 objects = processorNode.getObjects();
277
278                 cache.snapshotsUpdatedForObjects(
279                         objects,
280                         ((PrefetchProcessorJointNode) processorNode).getResolvedRows(),
281                         queryMetadata.isRefreshingObjects());
282             }
283             // disjoint prefetch on flattened relationships still requires manual matching
284
else if (processorNode.getIncoming() != null
285                     && processorNode.getIncoming().getRelationship().isFlattened()) {
286
287                 objects = processorNode.getResolver().relatedObjectsFromDataRows(
288                         processorNode.getDataRows(),
289                         processorNode);
290                 processorNode.setObjects(objects);
291             }
292             else {
293                 objects = processorNode.getResolver().objectsFromDataRows(
294                         processorNode.getDataRows());
295                 processorNode.setObjects(objects);
296             }
297
298             // ... continue with processing even if the objects list is empty to handle
299
// multi-step prefetches.
300
if (objects.isEmpty()) {
301                 return true;
302             }
303
304             // create temporary relationship mapping if needed..; flattened relationships
305
// are matched with parents during resolving phase, so skip them here.
306
if (processorNode.isPartitionedByParent()
307                     && !processorNode.getIncoming().getRelationship().isFlattened()) {
308
309                 ObjEntity sourceObjEntity = null;
310                 String JavaDoc relatedIdPrefix = null;
311
312                 // determine resolution strategy
313
ArcProperty reverseArc = processorNode
314                         .getIncoming()
315                         .getComplimentaryReverseArc();
316
317                 // if null, prepare for manual matching
318
if (reverseArc == null) {
319                     relatedIdPrefix = processorNode
320                             .getIncoming()
321                             .getRelationship()
322                             .getReverseDbRelationshipPath()
323                             + ".";
324
325                     sourceObjEntity = (ObjEntity) processorNode
326                             .getIncoming()
327                             .getRelationship()
328                             .getSourceEntity();
329                 }
330
331                 Iterator JavaDoc it = objects.iterator();
332                 while (it.hasNext()) {
333                     Persistent destinationObject = (Persistent) it.next();
334                     Persistent sourceObject = null;
335
336                     if (reverseArc != null) {
337                         sourceObject = (Persistent) reverseArc
338                                 .readProperty(destinationObject);
339                     }
340                     else {
341                         ObjectStore objectStore = context.getObjectStore();
342
343                         // prefetched snapshots contain parent ids prefixed with
344
// relationship name.
345

346                         DataRow snapshot = objectStore.getSnapshot(destinationObject
347                                 .getObjectId());
348
349                         ObjectId id = processorNode.getResolver().createObjectId(
350                                 snapshot,
351                                 sourceObjEntity,
352                                 relatedIdPrefix);
353
354                         sourceObject = (Persistent) objectStore.getNode(id);
355                     }
356
357                     // don't attach to hollow objects
358
if (sourceObject != null
359                             && sourceObject.getPersistenceState() != PersistenceState.HOLLOW) {
360                         processorNode.linkToParent(destinationObject, sourceObject);
361                     }
362                 }
363             }
364
365             return true;
366         }
367
368         public boolean startJointPrefetch(PrefetchTreeNode node) {
369             // allow joint prefetch nodes to process their children, but skip their own
370
// processing.
371
return true;
372         }
373
374         public boolean startPhantomPrefetch(PrefetchTreeNode node) {
375             return true;
376         }
377
378         public boolean startUnknownPrefetch(PrefetchTreeNode node) {
379             throw new CayenneRuntimeException("Unknown prefetch node: " + node);
380         }
381
382         public void finishPrefetch(PrefetchTreeNode node) {
383             // noop
384
}
385     }
386
387     // a processor of a single joint result set that walks a subtree of prefetch nodes
388
// that use this result set.
389
final class JointProcessor implements PrefetchProcessor {
390
391         DataRow currentFlatRow;
392         PrefetchProcessorNode rootNode;
393
394         JointProcessor(PrefetchProcessorJointNode rootNode) {
395             this.rootNode = rootNode;
396         }
397
398         void setCurrentFlatRow(DataRow currentFlatRow) {
399             this.currentFlatRow = currentFlatRow;
400         }
401
402         public boolean startDisjointPrefetch(PrefetchTreeNode node) {
403             // disjoint prefetch that is not the root terminates the walk...
404
return node == rootNode ? startJointPrefetch(node) : false;
405         }
406
407         public boolean startJointPrefetch(PrefetchTreeNode node) {
408             PrefetchProcessorJointNode processorNode = (PrefetchProcessorJointNode) node;
409
410             Persistent object = null;
411
412             // find existing object, if found skip further processing
413
Map JavaDoc id = processorNode.idFromFlatRow(currentFlatRow);
414             object = processorNode.getResolved(id);
415
416             if (object == null) {
417
418                 DataRow row = processorNode.rowFromFlatRow(currentFlatRow);
419                 object = processorNode.getResolver().objectFromDataRow(row);
420
421                 processorNode.putResolved(id, object);
422                 processorNode.addObject(object, row);
423             }
424
425             // categorization by parent needed even if an object is already there
426
// (many-to-many case)
427
if (processorNode.isPartitionedByParent()) {
428
429                 PrefetchProcessorNode parent = (PrefetchProcessorNode) processorNode
430                         .getParent();
431                 processorNode.linkToParent(object, parent.getLastResolved());
432             }
433
434             processorNode.setLastResolved(object);
435             return processorNode.isJointChildren();
436         }
437
438         public boolean startPhantomPrefetch(PrefetchTreeNode node) {
439             return ((PrefetchProcessorNode) node).isJointChildren();
440         }
441
442         public boolean startUnknownPrefetch(PrefetchTreeNode node) {
443             throw new CayenneRuntimeException("Unknown prefetch node: " + node);
444         }
445
446         public void finishPrefetch(PrefetchTreeNode node) {
447             // noop
448
}
449     }
450
451     // processor that converts temporary associations between DataObjects to Cayenne
452
// relationships and also fires snapshot update events
453
final class PostProcessor implements PrefetchProcessor {
454
455         public void finishPrefetch(PrefetchTreeNode node) {
456         }
457
458         public boolean startDisjointPrefetch(PrefetchTreeNode node) {
459             ((PrefetchProcessorNode) node).connectToParents();
460             return true;
461         }
462
463         public boolean startJointPrefetch(PrefetchTreeNode node) {
464             PrefetchProcessorJointNode processorNode = (PrefetchProcessorJointNode) node;
465
466             if (!processorNode.getObjects().isEmpty()) {
467                 cache.snapshotsUpdatedForObjects(
468                         processorNode.getObjects(),
469                         processorNode.getResolvedRows(),
470                         queryMetadata.isRefreshingObjects());
471                 processorNode.connectToParents();
472             }
473
474             return true;
475         }
476
477         public boolean startPhantomPrefetch(PrefetchTreeNode node) {
478             return true;
479         }
480
481         public boolean startUnknownPrefetch(PrefetchTreeNode node) {
482             throw new CayenneRuntimeException("Unknown prefetch node: " + node);
483         }
484     }
485 }
486
Popular Tags