KickJava   Java API By Example, From Geeks To Geeks.

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


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.Collections JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator 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.ObjectId;
30 import org.apache.cayenne.access.DataDomainSyncBucket.PropagatedValueFactory;
31 import org.apache.cayenne.access.util.DefaultOperationObserver;
32 import org.apache.cayenne.dba.PkGenerator;
33 import org.apache.cayenne.exp.Expression;
34 import org.apache.cayenne.exp.ExpressionFactory;
35 import org.apache.cayenne.map.DbAttribute;
36 import org.apache.cayenne.map.DbEntity;
37 import org.apache.cayenne.map.DbJoin;
38 import org.apache.cayenne.map.DbRelationship;
39 import org.apache.cayenne.map.ObjRelationship;
40 import org.apache.cayenne.query.Query;
41 import org.apache.cayenne.query.SelectQuery;
42
43 /**
44  * A holder of flattened relationship modification data.
45  *
46  * @since 1.2
47  * @author Andrus Adamchik
48  */

49 final class FlattenedArcKey {
50
51     ObjectId sourceId;
52     ObjectId destinationId;
53     ObjRelationship relationship;
54     ObjRelationship reverseRelationship;
55     String JavaDoc compareToken;
56
57     FlattenedArcKey(ObjectId sourceId, ObjectId destinationId,
58             ObjRelationship relationship) {
59
60         this.sourceId = sourceId;
61         this.destinationId = destinationId;
62         this.relationship = relationship;
63         this.reverseRelationship = relationship.getReverseRelationship();
64
65         // build a string token to make comparison (or at least hashcode) indepent from
66
// direction
67
String JavaDoc relName1 = relationship.getName();
68         if (reverseRelationship != null) {
69             String JavaDoc relName2 = reverseRelationship.getName();
70
71             // Find the lexically lesser name and use it as the name of the source, then
72
// use the second.
73
// If equal (the same name), it doesn't matter which order...
74
if (relName1.compareTo(relName2) <= 0) {
75                 this.compareToken = relName1 + "." + relName2;
76             }
77             else {
78                 this.compareToken = relName2 + "." + relName1;
79             }
80         }
81         else {
82             this.compareToken = relName1;
83         }
84     }
85
86     /**
87      * Returns a join DbEntity for the single-step flattened relationship.
88      */

89     DbEntity getJoinEntity() {
90         List JavaDoc relList = relationship.getDbRelationships();
91         if (relList.size() != 2) {
92             throw new CayenneRuntimeException(
93                     "Only single-step flattened relationships are supported in this operation: "
94                             + relationship);
95         }
96
97         DbRelationship firstDbRel = (DbRelationship) relList.get(0);
98         return (DbEntity) firstDbRel.getTargetEntity();
99     }
100
101     /**
102      * Returns a snapshot for join record for the single-step flattened relationship,
103      * generating value for the primary key column if it is not propagated via the
104      * relationships.
105      */

106     Map JavaDoc buildJoinSnapshotForInsert(DataNode node) {
107         Map JavaDoc snapshot = lazyJoinSnapshot();
108
109         boolean autoPkDone = false;
110         DbEntity joinEntity = getJoinEntity();
111         List JavaDoc pkAttributes = joinEntity.getPrimaryKey();
112         Iterator JavaDoc it = pkAttributes.iterator();
113
114         while (it.hasNext()) {
115             DbAttribute dbAttr = (DbAttribute) it.next();
116             String JavaDoc dbAttrName = dbAttr.getName();
117             if (snapshot.containsKey(dbAttrName)) {
118                 continue;
119             }
120
121             if (autoPkDone) {
122                 throw new CayenneRuntimeException(
123                         "Primary Key autogeneration only works for a single attribute.");
124             }
125
126             // finally, use database generation mechanism
127
try {
128                 PkGenerator pkGenerator = node.getAdapter().getPkGenerator();
129                 Object JavaDoc pkValue = pkGenerator.generatePkForDbEntity(node, joinEntity);
130                 snapshot.put(dbAttrName, pkValue);
131                 autoPkDone = true;
132             }
133             catch (Exception JavaDoc ex) {
134                 throw new CayenneRuntimeException("Error generating PK: "
135                         + ex.getMessage(), ex);
136             }
137         }
138
139         return snapshot;
140     }
141
142     /**
143      * Returns pk snapshots for join records for the single-stp flattened relationship.
144      * Multiple joins between the same pair of objects are theoretically possible, so the
145      * return value is a list.
146      */

147     List JavaDoc buildJoinSnapshotsForDelete(DataNode node) {
148         Map JavaDoc snapshot = eagerJoinSnapshot();
149
150         DbEntity joinEntity = getJoinEntity();
151         List JavaDoc pkAttributes = joinEntity.getPrimaryKey();
152
153         boolean fetchKey = false;
154         Iterator JavaDoc it = pkAttributes.iterator();
155         while (it.hasNext()) {
156             DbAttribute dbAttr = (DbAttribute) it.next();
157             String JavaDoc dbAttrName = dbAttr.getName();
158             if (!snapshot.containsKey(dbAttrName)) {
159                 fetchKey = true;
160                 break;
161             }
162         }
163
164         if (!fetchKey) {
165             return Collections.singletonList(snapshot);
166         }
167
168         // ok, the key is not included in snapshot, must do the fetch...
169
// TODO: this should be optimized in the future, but now DeleteBatchQuery
170
// expects a PK snapshot, so we must provide it.
171

172         SelectQuery query = new SelectQuery(joinEntity, ExpressionFactory.matchAllDbExp(
173                 snapshot,
174                 Expression.EQUAL_TO));
175         query.setFetchingDataRows(true);
176
177         it = pkAttributes.iterator();
178         while (it.hasNext()) {
179             DbAttribute dbAttr = (DbAttribute) it.next();
180             query.addCustomDbAttribute(dbAttr.getName());
181         }
182
183         final List JavaDoc[] result = new List JavaDoc[1];
184
185         node.performQueries(Collections.singleton(query), new DefaultOperationObserver() {
186
187             public void nextDataRows(Query query, List JavaDoc dataRows) {
188                 result[0] = dataRows;
189             }
190         });
191
192         return result[0];
193     }
194
195     boolean isBidirectional() {
196         return reverseRelationship != null;
197     }
198
199     public int hashCode() {
200         // TODO: use hashcode builder to make a better hashcode.
201
return sourceId.hashCode() + destinationId.hashCode() + compareToken.hashCode();
202     }
203
204     /**
205      * Defines equal based on whether the relationship is bidirectional.
206      */

207     public boolean equals(Object JavaDoc object) {
208
209         if (this == object) {
210             return true;
211         }
212
213         if (!(object instanceof FlattenedArcKey)) {
214             return false;
215         }
216
217         FlattenedArcKey update = (FlattenedArcKey) object;
218
219         if (!this.compareToken.equals(update.compareToken)) {
220             return false;
221         }
222
223         boolean bidi = isBidirectional();
224         if (bidi != update.isBidirectional()) {
225             return false;
226         }
227
228         return (bidi) ? bidiEquals(update) : uniEquals(update);
229     }
230
231     private boolean bidiEquals(FlattenedArcKey update) {
232         return (sourceId.equals(update.sourceId) && destinationId
233                 .equals(update.destinationId))
234                 || (this.sourceId.equals(update.destinationId) && this.destinationId
235                         .equals(update.sourceId));
236     }
237
238     private boolean uniEquals(FlattenedArcKey update) {
239         return (this.sourceId.equals(update.sourceId) && this.destinationId
240                 .equals(update.destinationId));
241     }
242
243     private Map JavaDoc eagerJoinSnapshot() {
244
245         List JavaDoc relList = relationship.getDbRelationships();
246         if (relList.size() != 2) {
247             throw new CayenneRuntimeException(
248                     "Only single-step flattened relationships are supported in this operation: "
249                             + relationship);
250         }
251
252         DbRelationship firstDbRel = (DbRelationship) relList.get(0);
253         DbRelationship secondDbRel = (DbRelationship) relList.get(1);
254
255         Map JavaDoc sourceId = this.sourceId.getIdSnapshot();
256         Map JavaDoc destinationId = this.destinationId.getIdSnapshot();
257
258         Map JavaDoc snapshot = new HashMap JavaDoc(sourceId.size() + destinationId.size(), 1);
259         List JavaDoc joins = firstDbRel.getJoins();
260         for (int i = 0, numJoins = joins.size(); i < numJoins; i++) {
261             DbJoin join = (DbJoin) joins.get(i);
262             snapshot.put(join.getTargetName(), sourceId.get(join.getSourceName()));
263         }
264
265         joins = secondDbRel.getJoins();
266         for (int i = 0, numJoins = joins.size(); i < numJoins; i++) {
267             DbJoin join = (DbJoin) joins.get(i);
268             snapshot.put(join.getSourceName(), destinationId.get(join.getTargetName()));
269         }
270
271         return snapshot;
272     }
273
274     private Map JavaDoc lazyJoinSnapshot() {
275
276         List JavaDoc relList = relationship.getDbRelationships();
277         if (relList.size() != 2) {
278             throw new CayenneRuntimeException(
279                     "Only single-step flattened relationships are supported in this operation: "
280                             + relationship);
281         }
282
283         DbRelationship firstDbRel = (DbRelationship) relList.get(0);
284         DbRelationship secondDbRel = (DbRelationship) relList.get(1);
285
286         List JavaDoc fromSourceJoins = firstDbRel.getJoins();
287         List JavaDoc toTargetJoins = secondDbRel.getJoins();
288
289         Map JavaDoc snapshot = new HashMap JavaDoc(fromSourceJoins.size() + toTargetJoins.size(), 1);
290
291         for (int i = 0, numJoins = fromSourceJoins.size(); i < numJoins; i++) {
292             DbJoin join = (DbJoin) fromSourceJoins.get(i);
293
294             Object JavaDoc value = new PropagatedValueFactory(sourceId, join.getSourceName());
295             snapshot.put(join.getTargetName(), value);
296         }
297
298         for (int i = 0, numJoins = toTargetJoins.size(); i < numJoins; i++) {
299             DbJoin join = (DbJoin) toTargetJoins.get(i);
300             Object JavaDoc value = new PropagatedValueFactory(destinationId, join.getTargetName());
301             snapshot.put(join.getSourceName(), value);
302         }
303
304         return snapshot;
305     }
306 }
307
Popular Tags