001: /*
002: * Helma License Notice
003: *
004: * The contents of this file are subject to the Helma License
005: * Version 2.0 (the "License"). You may not use this file except in
006: * compliance with the License. A copy of the License is available at
007: * http://adele.helma.org/download/helma/license.txt
008: *
009: * Copyright 1998-2003 Helma Software. All Rights Reserved.
010: *
011: * $RCSfile$
012: * $Author: root $
013: * $Revision: 8604 $
014: * $Date: 2007-09-28 15:16:38 +0200 (Fre, 28 Sep 2007) $
015: */
016:
017: package helma.objectmodel.db;
018:
019: import java.io.IOException;
020: import java.io.ObjectInputStream;
021: import java.io.ObjectOutputStream;
022: import java.io.Serializable;
023: import java.util.Map;
024:
025: /**
026: * This is the internal representation of a database key with multiple
027: * columns. It is constructed from the logical table (type) name and the
028: * column name/column value pairs that identify the key's object
029: *
030: * NOTE: This class doesn't fully support the Key interface - getID always
031: * returns null since there is no unique key (at least we don't know about it).
032: */
033: public final class MultiKey implements Key, Serializable {
034: // the name of the prototype which defines the storage of this object.
035: // this is the name of the object's prototype, or one of its ancestors.
036: // If null, the object is stored in the embedded db.
037: private String storageName;
038:
039: // the id that defines this key's object within the above storage space
040: private Map parts;
041:
042: // lazily initialized hashcode
043: private transient int hashcode = 0;
044:
045: static final long serialVersionUID = -9173409137561990089L;
046:
047: /**
048: * Make a key for a persistent Object, describing its datasource and key parts.
049: */
050: public MultiKey(DbMapping dbmap, Map parts) {
051: this .parts = parts;
052: this .storageName = getStorageNameFromParts(dbmap, parts);
053: }
054:
055: /**
056: * Get the actual dbmapping prototype name out of the parts map if possible.
057: * This is necessary to implement references to unspecified prototype targets.
058: * @param dbmap the nominal/static dbmapping
059: * @param parts the parts map
060: * @return the actual dbmapping name
061: */
062: private String getStorageNameFromParts(DbMapping dbmap, Map parts) {
063: if (dbmap == null)
064: return null;
065: String protoName = (String) parts.get("$prototype");
066: if (protoName != null) {
067: DbMapping dynamap = dbmap.app.getDbMapping(protoName);
068: if (dynamap != null) {
069: return (dynamap.getStorageTypeName());
070: }
071: }
072: return dbmap.getStorageTypeName();
073: }
074:
075: /**
076: *
077: *
078: * @param what the other key to be compared with this one
079: *
080: * @return true if both keys are identical
081: */
082: public boolean equals(Object what) {
083: if (what == this ) {
084: return true;
085: }
086:
087: if (!(what instanceof MultiKey)) {
088: return false;
089: }
090:
091: MultiKey k = (MultiKey) what;
092:
093: // storageName is an interned string (by DbMapping, from where we got it)
094: // so we can compare by using == instead of the equals method.
095: return (storageName == k.storageName)
096: && ((parts == k.parts) || parts.equals(k.parts));
097: }
098:
099: /**
100: *
101: *
102: * @return this key's hash code
103: */
104: public int hashCode() {
105: if (hashcode == 0) {
106: hashcode = (storageName == null) ? (17 + (37 * parts
107: .hashCode()))
108: : (17 + (37 * storageName.hashCode()) + (+37 * parts
109: .hashCode()));
110: }
111:
112: return hashcode;
113: }
114:
115: /**
116: *
117: *
118: * @return the key of this key's object's parent object
119: */
120: public Key getParentKey() {
121: return null;
122: }
123:
124: /**
125: *
126: *
127: * @return the unique storage name for this key's object
128: */
129: public String getStorageName() {
130: return storageName;
131: }
132:
133: /**
134: *
135: *
136: * @return this key's object's id
137: */
138: public String getID() {
139: return null;
140: }
141:
142: /**
143: *
144: *
145: * @return a string representation for this key
146: */
147: public String toString() {
148: return (storageName == null) ? ("[" + parts + "]")
149: : (storageName + "[" + parts + "]");
150: }
151:
152: // We implement write/readObject to set storageName
153: // to the interned version of the string.
154:
155: private void writeObject(ObjectOutputStream stream)
156: throws IOException {
157: stream.writeObject(storageName);
158: stream.writeObject(parts);
159: }
160:
161: private void readObject(ObjectInputStream stream)
162: throws IOException, ClassNotFoundException {
163: storageName = (String) stream.readObject();
164: parts = (Map) stream.readObject();
165: // if storageName is not null, set it to the interned version
166: if (storageName != null) {
167: storageName = storageName.intern();
168: }
169: }
170:
171: }
|