KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > ObjectId


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;
21
22 import java.io.Serializable JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.Collections 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.util.IDUtil;
32 import org.apache.cayenne.util.Util;
33 import org.apache.commons.lang.builder.EqualsBuilder;
34 import org.apache.commons.lang.builder.HashCodeBuilder;
35
36 /**
37  * A portable global identifier for persistent objects. ObjectId can be temporary (used
38  * for transient or new uncommitted objects) or permanent (used for objects that have been
39  * already stored in DB). A temporary ObjectId stores object entity name and a
40  * pseudo-unique binary key; permanent id stores a map of values from an external
41  * persistent store (aka "primary key").
42  *
43  * @author Andrus Adamchik
44  */

45 public class ObjectId implements Serializable JavaDoc {
46
47     protected String JavaDoc entityName;
48     protected Map JavaDoc objectIdKeys;
49
50     private String JavaDoc singleKey;
51     private Object JavaDoc singleValue;
52
53     protected byte[] key;
54
55     protected Map JavaDoc replacementIdMap;
56
57     // hash code is transient to make sure id is portable across VM
58
transient int hashCode;
59
60     // exists for deserialization with Hessian and similar
61
private ObjectId() {
62     }
63
64     /**
65      * Creates a TEMPORARY ObjectId. Assignes a generated unique key.
66      *
67      * @since 1.2
68      */

69     public ObjectId(String JavaDoc entityName) {
70         this.entityName = entityName;
71         this.key = IDUtil.pseudoUniqueByteSequence8();
72     }
73
74     /**
75      * Creates a TEMPORARY id with a specified entity name and a binary key. It is a
76      * caller responsibility to provide a globally unique binary key.
77      *
78      * @since 1.2
79      */

80     public ObjectId(String JavaDoc entityName, byte[] key) {
81         this.entityName = entityName;
82         this.key = key;
83     }
84
85     /**
86      * Creates a portable permanent ObjectId.
87      *
88      * @since 1.2
89      */

90     public ObjectId(String JavaDoc entityName, String JavaDoc key, int value) {
91         this(entityName, key, new Integer JavaDoc(value));
92     }
93
94     /**
95      * Creates a portable permanent ObjectId.
96      *
97      * @since 1.2
98      */

99     public ObjectId(String JavaDoc entityName, String JavaDoc key, Object JavaDoc value) {
100         this.entityName = entityName;
101
102         this.singleKey = key;
103         this.singleValue = value;
104     }
105
106     /**
107      * Creates a portable permanent ObjectId.
108      *
109      * @since 1.2
110      */

111     public ObjectId(String JavaDoc entityName, Map JavaDoc idMap) {
112         this.entityName = entityName;
113
114         if (idMap == null || idMap.size() == 0) {
115
116         }
117         else if (idMap.size() == 1) {
118             Map.Entry JavaDoc e = (Map.Entry JavaDoc) idMap.entrySet().iterator().next();
119             this.singleKey = String.valueOf(e.getKey());
120             this.singleValue = e.getValue();
121         }
122         else {
123
124             // we have to create a copy of the map, otherwise we may run into
125
// serialization
126
// problems with hessian
127
this.objectIdKeys = new HashMap JavaDoc(idMap);
128         }
129     }
130
131     public boolean isTemporary() {
132         return key != null;
133     }
134
135     /**
136      * @since 1.2
137      */

138     public String JavaDoc getEntityName() {
139         return entityName;
140     }
141
142     public byte[] getKey() {
143         return key;
144     }
145
146     /**
147      * Returns an unmodifiable Map of persistent id values, essentailly a primary key map.
148      * For temporary id returns replacement id, if it was already created. Otherwise
149      * returns an empty map.
150      */

151     public Map JavaDoc getIdSnapshot() {
152         if (isTemporary()) {
153             return (replacementIdMap == null) ? Collections.EMPTY_MAP : Collections
154                     .unmodifiableMap(replacementIdMap);
155         }
156
157         if (singleKey != null) {
158             return Collections.singletonMap(singleKey, singleValue);
159         }
160
161         return objectIdKeys != null
162                 ? Collections.unmodifiableMap(objectIdKeys)
163                 : Collections.EMPTY_MAP;
164     }
165
166     public boolean equals(Object JavaDoc object) {
167         if (this == object) {
168             return true;
169         }
170
171         if (!(object instanceof ObjectId)) {
172             return false;
173         }
174
175         ObjectId id = (ObjectId) object;
176
177         if (!Util.nullSafeEquals(entityName, id.entityName)) {
178             return false;
179         }
180
181         if (isTemporary()) {
182             return new EqualsBuilder().append(key, id.key).isEquals();
183         }
184
185         if (singleKey != null) {
186             return Util.nullSafeEquals(singleKey, id.singleKey)
187                     && valueEquals(singleValue, id.singleValue);
188         }
189
190         if (id.objectIdKeys == null) {
191             return objectIdKeys == null;
192         }
193
194         if (id.objectIdKeys.size() != objectIdKeys.size()) {
195             return false;
196         }
197
198         Iterator JavaDoc entries = objectIdKeys.entrySet().iterator();
199         while (entries.hasNext()) {
200             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
201             Object JavaDoc entryKey = entry.getKey();
202             Object JavaDoc entryValue = entry.getValue();
203
204             if (entryValue == null) {
205                 if (id.objectIdKeys.get(entryKey) != null
206                         || !id.objectIdKeys.containsKey(entryKey)) {
207                     return false;
208                 }
209             }
210             else {
211                 if (!valueEquals(entryValue, id.objectIdKeys.get(entryKey))) {
212                     return false;
213                 }
214             }
215         }
216
217         return true;
218     }
219
220     private final boolean valueEquals(Object JavaDoc o1, Object JavaDoc o2) {
221         if (o1 == o2) {
222             return true;
223         }
224
225         if (o1 == null) {
226             return o2 == null;
227         }
228
229         if (o1 instanceof Number JavaDoc) {
230             return o2 instanceof Number JavaDoc
231                     && ((Number JavaDoc) o1).longValue() == ((Number JavaDoc) o2).longValue();
232         }
233
234         if (o1.getClass().isArray()) {
235             return new EqualsBuilder().append(o1, o2).isEquals();
236         }
237
238         return Util.nullSafeEquals(o1, o2);
239     }
240
241     public int hashCode() {
242
243         if (this.hashCode == 0) {
244
245             HashCodeBuilder builder = new HashCodeBuilder(3, 5);
246             builder.append(entityName.hashCode());
247
248             if (key != null) {
249                 builder.append(key);
250             }
251             else if (singleKey != null) {
252                 builder.append(singleKey.hashCode());
253
254                 // must reconcile all possible numeric types
255
if (singleValue instanceof Number JavaDoc) {
256                     builder.append(((Number JavaDoc) singleValue).longValue());
257                 }
258                 else {
259                     builder.append(singleValue);
260                 }
261             }
262             else if (objectIdKeys != null) {
263                 int len = objectIdKeys.size();
264
265                 // handle multiple keys - must sort the keys to use with HashCodeBuilder
266

267                 Object JavaDoc[] keys = objectIdKeys.keySet().toArray();
268                 Arrays.sort(keys);
269
270                 for (int i = 0; i < len; i++) {
271                     // HashCodeBuilder will take care of processing object if it
272
// happens to be a primitive array such as byte[]
273

274                     // also we don't have to append the key hashcode, its index will
275
// work
276
builder.append(i);
277
278                     Object JavaDoc value = objectIdKeys.get(keys[i]);
279                     // must reconcile all possible numeric types
280
if (value instanceof Number JavaDoc) {
281                         builder.append(((Number JavaDoc) value).longValue());
282                     }
283                     else {
284                         builder.append(value);
285                     }
286                 }
287             }
288
289             this.hashCode = builder.toHashCode();
290             assert hashCode != 0 : "Generated zero hashCode";
291         }
292
293         return hashCode;
294     }
295
296     /**
297      * Returns a non-null mutable map that can be used to append replacement id values.
298      * This allows to incrementally build a replacement GlobalID.
299      *
300      * @since 1.2
301      */

302     public Map JavaDoc getReplacementIdMap() {
303         if (replacementIdMap == null) {
304             replacementIdMap = new HashMap JavaDoc();
305         }
306
307         return replacementIdMap;
308     }
309
310     /**
311      * Creates and returns a replacement ObjectId. No validation of ID is done.
312      *
313      * @since 1.2
314      */

315     public ObjectId createReplacementId() {
316         // merge existing and replaced ids to handle a replaced subset of
317
// a compound primary key
318
Map JavaDoc newIdMap = new HashMap JavaDoc(getIdSnapshot());
319         if (replacementIdMap != null) {
320             newIdMap.putAll(replacementIdMap);
321         }
322         return new ObjectId(getEntityName(), newIdMap);
323     }
324
325     /**
326      * Returns true if there is full or partial replacement id attached to this id. This
327      * method is preferrable to "!getReplacementIdMap().isEmpty()" as it avoids unneeded
328      * replacement id map creation.
329      */

330     public boolean isReplacementIdAttached() {
331         return replacementIdMap != null && !replacementIdMap.isEmpty();
332     }
333
334     /**
335      * A standard toString method used for debugging. It is guaranteed to produce the same
336      * string if two ObjectIds are equal.
337      */

338     public String JavaDoc toString() {
339
340         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
341
342         buffer.append("<ObjectId:").append(entityName);
343
344         if (isTemporary()) {
345             buffer.append(", TEMP:");
346             for (int i = 0; i < key.length; i++) {
347                 IDUtil.appendFormattedByte(buffer, key[i]);
348             }
349         }
350         else if (singleKey != null) {
351             buffer.append(", ").append(String.valueOf(singleKey)).append("=").append(
352                     singleValue);
353         }
354         else if (objectIdKeys != null) {
355
356             
357             // ensure consistent order of the keys, so that toString could be used as a
358
// unique key, just like id itself
359

360             List JavaDoc keys = new ArrayList JavaDoc(objectIdKeys.keySet());
361             Collections.sort(keys);
362             Iterator JavaDoc it = keys.iterator();
363             while (it.hasNext()) {
364                 Object JavaDoc key = it.next();
365                 buffer.append(", ");
366                 buffer.append(String.valueOf(key)).append("=").append(
367                         objectIdKeys.get(key));
368             }
369         }
370
371         buffer.append(">");
372         return buffer.toString();
373     }
374 }
375
Popular Tags