KickJava   Java API By Example, From Geeks To Geeks.

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


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.Arrays JavaDoc;
24 import java.util.HashMap JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.TreeMap JavaDoc;
29
30 import org.apache.cayenne.CayenneRuntimeException;
31 import org.apache.cayenne.DataRow;
32 import org.apache.cayenne.Persistent;
33 import org.apache.cayenne.access.jdbc.ColumnDescriptor;
34 import org.apache.cayenne.exp.Expression;
35 import org.apache.cayenne.exp.parser.ASTPath;
36 import org.apache.cayenne.map.DbAttribute;
37 import org.apache.cayenne.map.DbJoin;
38 import org.apache.cayenne.map.DbRelationship;
39 import org.apache.cayenne.map.ObjAttribute;
40 import org.apache.cayenne.map.ObjRelationship;
41 import org.apache.cayenne.query.PrefetchTreeNode;
42
43 /**
44  * A specialized PrefetchTreeNode used for joint prefetch resolving.
45  *
46  * @since 1.2
47  * @author Andrus Adamchik
48  */

49 class PrefetchProcessorJointNode extends PrefetchProcessorNode {
50
51     ColumnDescriptor[] columns;
52     int[] idIndices;
53     int rowCapacity;
54     Map JavaDoc resolved;
55     List JavaDoc resolvedRows;
56
57     PrefetchProcessorJointNode(PrefetchTreeNode parent, String JavaDoc segmentPath) {
58         super(parent, segmentPath);
59     }
60
61     void afterInit() {
62         super.afterInit();
63
64         // as node will be resolved one row at a time, init objects array here
65

66         // list may shrink as a result of duplicates in flattened rows.. so don't
67
// allocate too much space
68
int capacity = dataRows != null ? dataRows.size() : 10;
69         if (capacity > 100) {
70             capacity = capacity / 2;
71         }
72
73         objects = new ArrayList JavaDoc(capacity);
74         resolved = new HashMap JavaDoc(capacity);
75         resolvedRows = new ArrayList JavaDoc(capacity);
76         buildRowMapping();
77         buildPKIndex();
78     }
79
80     List JavaDoc getResolvedRows() {
81         return resolvedRows;
82     }
83
84     void addObject(Persistent object, DataRow row) {
85         objects.add(object);
86         resolvedRows.add(row);
87     }
88
89     /**
90      * Returns an ObjectId map from the flat row.
91      */

92     Map JavaDoc idFromFlatRow(DataRow flatRow) {
93
94         // TODO: should we also check for nulls in ID (and skip such rows) - this will
95
// likely be an indicator of an outer join ... and considering SQLTemplate,
96
// this is reasonable to expect...
97

98         Map JavaDoc id = new TreeMap JavaDoc();
99         for (int i = 0; i < idIndices.length; i++) {
100             Object JavaDoc value = flatRow.get(columns[idIndices[i]].getLabel());
101             id.put(columns[idIndices[i]].getName(), value);
102         }
103
104         return id;
105     }
106
107     /**
108      * Looks up a previously resolved object using an ObjectId map as a key. Returns null
109      * if no matching object exists.
110      */

111     Persistent getResolved(Map JavaDoc id) {
112         return (Persistent) resolved.get(id);
113     }
114
115     /**
116      * Registers an object in a map of resolved objects, connects this object to parent if
117      * parent exists.
118      */

119     void putResolved(Map JavaDoc id, Persistent object) {
120         resolved.put(id, object);
121     }
122
123     /**
124      * Returns a DataRow from the flat row.
125      */

126     DataRow rowFromFlatRow(DataRow flatRow) {
127         DataRow row = new DataRow(rowCapacity);
128
129         // extract subset of flat row columns, recasting to the target keys
130
for (int i = 0; i < columns.length; i++) {
131             row.put(columns[i].getName(), flatRow.get(columns[i].getLabel()));
132         }
133
134         return row;
135     }
136
137     // ***** private methods *****
138
// ========================================================
139

140     /**
141      * Configures row columns mapping for this node entity.
142      */

143     private void buildRowMapping() {
144         Map JavaDoc targetSource = new TreeMap JavaDoc();
145
146         // build a DB path .. find parent node that terminates the joint group...
147
PrefetchTreeNode jointRoot = this;
148         while (jointRoot.getParent() != null && !jointRoot.isDisjointPrefetch()) {
149             jointRoot = jointRoot.getParent();
150         }
151
152         String JavaDoc prefix;
153         if (jointRoot != this) {
154             Expression objectPath = Expression.fromString(getPath(jointRoot));
155             ASTPath translated = (ASTPath) ((PrefetchProcessorNode) jointRoot)
156                     .getResolver()
157                     .getEntity()
158                     .translateToDbPath(objectPath);
159
160             // make sure we do not include "db:" prefix
161
prefix = translated.getOperand(0) + ".";
162         }
163         else {
164             prefix = "";
165         }
166
167         // find propagated keys, assuming that only one-step joins
168
// share their column(s) with parent
169

170         if (getParent() != null
171                 && !getParent().isPhantom()
172                 && getIncoming() != null
173                 && !getIncoming().getRelationship().isFlattened()) {
174
175             DbRelationship r = (DbRelationship) getIncoming()
176                     .getRelationship()
177                     .getDbRelationships()
178                     .get(0);
179             Iterator JavaDoc it = r.getJoins().iterator();
180             while (it.hasNext()) {
181                 DbJoin join = (DbJoin) it.next();
182
183                 PrefetchProcessorNode parent = (PrefetchProcessorNode) getParent();
184                 String JavaDoc source;
185                 if (parent instanceof PrefetchProcessorJointNode) {
186                     source = ((PrefetchProcessorJointNode) parent).sourceForTarget(join
187                             .getSourceName());
188                 }
189                 else {
190                     source = join.getSourceName();
191                 }
192
193                 if (source == null) {
194                     throw new CayenneRuntimeException(
195                             "Propagated column value is not configured for parent node. Join: "
196                                     + join);
197                 }
198
199                 appendColumn(targetSource, join.getTargetName(), source);
200             }
201         }
202
203         // add class attributes
204
Iterator JavaDoc attributes = getResolver().getEntity().getAttributes().iterator();
205         while (attributes.hasNext()) {
206             ObjAttribute attribute = (ObjAttribute) attributes.next();
207             String JavaDoc target = attribute.getDbAttributePath();
208
209             appendColumn(targetSource, target, prefix + target);
210         }
211
212         // add relationships
213
Iterator JavaDoc relationships = getResolver().getEntity().getRelationships().iterator();
214         while (relationships.hasNext()) {
215             ObjRelationship rel = (ObjRelationship) relationships.next();
216             DbRelationship dbRel = (DbRelationship) rel.getDbRelationships().get(0);
217             Iterator JavaDoc dbAttributes = dbRel.getSourceAttributes().iterator();
218
219             while (dbAttributes.hasNext()) {
220                 DbAttribute attribute = (DbAttribute) dbAttributes.next();
221                 String JavaDoc target = attribute.getName();
222
223                 appendColumn(targetSource, target, prefix + target);
224             }
225         }
226
227         // add unmapped PK
228
Iterator JavaDoc pks = getResolver().getEntity().getDbEntity().getPrimaryKey().iterator();
229         while (pks.hasNext()) {
230             DbAttribute pk = (DbAttribute) pks.next();
231             appendColumn(targetSource, pk.getName(), prefix + pk.getName());
232         }
233
234         int size = targetSource.size();
235         this.rowCapacity = (int) Math.ceil(size / 0.75);
236         this.columns = new ColumnDescriptor[size];
237         targetSource.values().toArray(columns);
238     }
239
240     private ColumnDescriptor appendColumn(Map JavaDoc map, String JavaDoc name, String JavaDoc label) {
241         ColumnDescriptor column = (ColumnDescriptor) map.get(name);
242
243         if (column == null) {
244             column = new ColumnDescriptor();
245             column.setName(name);
246             column.setLabel(label);
247             map.put(name, column);
248         }
249
250         return column;
251     }
252
253     /**
254      * Creates an internal index of PK columns in the result.
255      */

256     private void buildPKIndex() {
257         // index PK
258
List JavaDoc pks = getResolver().getEntity().getDbEntity().getPrimaryKey();
259         this.idIndices = new int[pks.size()];
260
261         // this is needed for checking that a valid index is made
262
Arrays.fill(idIndices, -1);
263
264         for (int i = 0; i < idIndices.length; i++) {
265             DbAttribute pk = (DbAttribute) pks.get(i);
266
267             for (int j = 0; j < columns.length; j++) {
268                 if (pk.getName().equals(columns[j].getName())) {
269                     idIndices[i] = j;
270                     break;
271                 }
272             }
273
274             // sanity check
275
if (idIndices[i] == -1) {
276                 throw new CayenneRuntimeException("PK column is not part of result row: "
277                         + pk.getName());
278             }
279         }
280     }
281
282     /**
283      * Returns a source label for a given target label.
284      */

285     private String JavaDoc sourceForTarget(String JavaDoc targetColumn) {
286         if (targetColumn != null && columns != null) {
287             for (int i = 0; i < columns.length; i++) {
288                 if (targetColumn.equals(columns[i].getName())) {
289                     return columns[i].getLabel();
290                 }
291             }
292         }
293
294         return null;
295     }
296
297 }
298
Popular Tags