KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56 package org.objectstyle.cayenne;
57
58 import java.io.Serializable JavaDoc;
59 import java.util.Arrays JavaDoc;
60 import java.util.Collections JavaDoc;
61 import java.util.HashMap JavaDoc;
62 import java.util.Iterator JavaDoc;
63 import java.util.Map JavaDoc;
64
65 import org.apache.commons.lang.builder.EqualsBuilder;
66 import org.apache.commons.lang.builder.HashCodeBuilder;
67
68 /**
69  * An ObjectId is a globally unique identifier of DataObjects.
70  * <p>
71  * Each non-transient DataObject has an associated ObjectId. It is a global object
72  * identifier and does not depend on the DataContext of a particular object instance.
73  * ObjectId conceptually close to a RDBMS primary key idea. Among other things ObjectId is
74  * used to ensure object uniqueness within DataContext.
75  * </p>
76  *
77  * @author Andrei Adamchik
78  */

79 public class ObjectId implements Serializable JavaDoc {
80
81     // Keys: DbAttribute names
82
// Values: database values of the corresponding attribute
83
protected Map JavaDoc objectIdKeys;
84     protected Class JavaDoc objectClass;
85
86     /**
87      * @since 1.2
88      */

89     protected Map JavaDoc replacementIdMap;
90
91     // TODO: caching hash code may cause issues on deserilaization in a different VM...
92
// need custom readObject/writeObject
93

94     // cache hashCode, since ObjectId is immutable
95
int hashCode = Integer.MIN_VALUE;
96
97     /**
98      * Convenience constructor for entities that have a single Integer as their id.
99      */

100     public ObjectId(Class JavaDoc objectClass, String JavaDoc keyName, int id) {
101         this(objectClass, keyName, new Integer JavaDoc(id));
102     }
103
104     /**
105      * Convenience constructor for entities that have a single column as their id.
106      */

107     public ObjectId(Class JavaDoc objectClass, String JavaDoc keyName, Object JavaDoc id) {
108         this.objectClass = objectClass;
109         this.setIdKeys(Collections.singletonMap(keyName, id));
110     }
111
112     /**
113      * Creates a new ObjectId.
114      */

115     public ObjectId(Class JavaDoc objectClass, Map JavaDoc idKeys) {
116         this.objectClass = objectClass;
117         if (idKeys != null) {
118             this.setIdKeys(Collections.unmodifiableMap(idKeys));
119         }
120         else {
121             this.setIdKeys(Collections.EMPTY_MAP);
122         }
123     }
124
125     protected void setIdKeys(Map JavaDoc idKeys) {
126         this.objectIdKeys = idKeys;
127     }
128
129     public boolean equals(Object JavaDoc object) {
130         if (!(object instanceof ObjectId)) {
131             return false;
132         }
133
134         if (this == object) {
135             return true;
136         }
137
138         ObjectId id = (ObjectId) object;
139
140         // use the class name because two Objectid's should be equal
141
// even if their objClass'es were loaded by different class loaders.
142
if (!objectClass.getName().equals(id.objectClass.getName())) {
143             return false;
144         }
145
146         if (id.objectIdKeys == null && objectIdKeys == null) {
147             return true;
148         }
149
150         if (id.objectIdKeys == null || objectIdKeys == null) {
151             return false;
152         }
153
154         if (id.objectIdKeys.size() != objectIdKeys.size()) {
155             return false;
156         }
157
158         EqualsBuilder builder = new EqualsBuilder();
159         Iterator JavaDoc entries = objectIdKeys.entrySet().iterator();
160         while (entries.hasNext()) {
161             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
162
163             Object JavaDoc key = entry.getKey();
164             Object JavaDoc value = entry.getValue();
165             if (value == null) {
166                 if (id.objectIdKeys.get(key) != null || !id.objectIdKeys.containsKey(key)) {
167                     return false;
168                 }
169             }
170             else {
171                 // takes care of comparing primitive arrays, such as byte[]
172
builder.append(value, id.objectIdKeys.get(key));
173                 if (!builder.isEquals()) {
174                     return false;
175                 }
176             }
177         }
178
179         return true;
180     }
181
182     /**
183      * Returns a map of id components. Keys in the map are DbAttribute names, values are
184      * database values of corresponding columns.
185      */

186     public Map JavaDoc getIdSnapshot() {
187         return objectIdKeys;
188     }
189
190     /**
191      * Returns a value of id attribute identified by the name of DbAttribute.
192      */

193     public Object JavaDoc getValueForAttribute(String JavaDoc attrName) {
194         return getIdSnapshot().get(attrName);
195     }
196
197     /**
198      * Always returns <code>false</code>.
199      */

200     public boolean isTemporary() {
201         return false;
202     }
203
204     public String JavaDoc toString() {
205         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(objectClass.getName());
206         if (isTemporary())
207             buf.append(" (temp)");
208         buf.append(": ");
209         if (objectIdKeys != null) {
210             Iterator JavaDoc it = objectIdKeys.keySet().iterator();
211             while (it.hasNext()) {
212                 String JavaDoc nextKey = (String JavaDoc) it.next();
213                 Object JavaDoc value = objectIdKeys.get(nextKey);
214                 buf.append(" <").append(nextKey).append(": ").append(value).append('>');
215             }
216         }
217         return buf.toString();
218     }
219
220     /**
221      * @see java.lang.Object#hashCode()
222      */

223     public int hashCode() {
224         if (this.hashCode == Integer.MIN_VALUE) {
225             // build and cache hashCode
226

227             HashCodeBuilder builder = new HashCodeBuilder(3, 5);
228
229             // use the class name because two Objectid's should be equal
230
// even if their objClass'es were loaded by different class loaders.
231
builder.append(objectClass.getName().hashCode());
232
233             if (objectIdKeys != null) {
234                 int len = objectIdKeys.size();
235
236                 // handle cheap and most common case - single key
237
if (len == 1) {
238                     Iterator JavaDoc entries = objectIdKeys.entrySet().iterator();
239                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
240                     builder.append(entry.getKey()).append(entry.getValue());
241                 }
242                 // handle multiple keys - must sort the keys to use with HashCodeBuilder
243
else {
244                     Object JavaDoc[] keys = objectIdKeys.keySet().toArray();
245                     Arrays.sort(keys);
246
247                     for (int i = 0; i < len; i++) {
248                         // HashCodeBuilder will take care of processing object if it
249
// happens to be a primitive array such as byte[]
250

251                         // also we don't have to append the key hashcode, its index will
252
// work
253
builder.append(i).append(objectIdKeys.get(keys[i]));
254                     }
255                 }
256             }
257
258             this.hashCode = builder.toHashCode();
259         }
260
261         return this.hashCode;
262     }
263
264     /**
265      * Returns a Java class of persistent objects identified by this id.
266      *
267      * @since 1.2 Renamed from getObjClass().
268      */

269     public Class JavaDoc getObjectClass() {
270         return objectClass;
271     }
272
273     /**
274      * @deprecated since 1.2 use getObjectClass().
275      */

276     public Class JavaDoc getObjClass() {
277         return getObjectClass();
278     }
279
280     /**
281      * Returns a replacement ObjectId associated with this id. Replacement ObjectId is
282      * either a permananent ObjectId for an uncommitted object or a new id for object
283      * whose id depends on its relationships.
284      *
285      * @deprecated Since 1.2 replacement id is built by appending to replacementIdMap.
286      */

287     public ObjectId getReplacementId() {
288         return (isReplacementIdAttached()) ? createReplacementId() : null;
289     }
290
291     /**
292      * Initializes a replacement ObjectId.
293      *
294      * @deprecated Since 1.2 replacement id is built by appending to replacementIdMap.
295      */

296     public void setReplacementId(ObjectId replacementId) {
297         if(replacementId == null) {
298             replacementIdMap = null;
299         }
300         else {
301             Map JavaDoc map = getReplacementIdMap();
302             map.clear();
303             map.putAll(replacementId.getIdSnapshot());
304         }
305     }
306
307     /**
308      * Returns non-null mutable map that can be used to append replacement id values. This
309      * allows to incrementally build a replacement ObjectId.
310      *
311      * @since 1.2
312      */

313     public Map JavaDoc getReplacementIdMap() {
314         if (replacementIdMap == null) {
315             replacementIdMap = new HashMap JavaDoc();
316         }
317
318         return replacementIdMap;
319     }
320
321     /**
322      * Creates and returns a replacement ObjectId. No validation of ID is done.
323      *
324      * @since 1.2
325      */

326     public ObjectId createReplacementId() {
327         return new ObjectId(getObjectClass(), replacementIdMap);
328     }
329
330     /**
331      * Returns true if there is full or partial replacement id attached to this id. This
332      * method is preferrable to "!getReplacementIdMap().isEmpty()" as it avoids unneeded
333      * replacement id map creation.
334      *
335      * @since 1.2
336      */

337     public boolean isReplacementIdAttached() {
338         return replacementIdMap != null && !replacementIdMap.isEmpty();
339     }
340 }
Popular Tags