1 package org.apache.torque.om;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import org.apache.commons.lang.ObjectUtils;
24
25 /***
26 * This class can be used as an ObjectKey to uniquely identify an
27 * object within an application where the key consists of multiple
28 * entities (such a String[] representing a multi-column primary key).
29 *
30 * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
31 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
32 * @author <a href="mailto:drfish@cox.net">J. Russell Smyth</a>
33 * @version $Id: ComboKey.java 473821 2006-11-11 22:37:25Z tv $
34 */
35 public class ComboKey extends ObjectKey
36 {
37 /***
38 * Serial version
39 */
40 private static final long serialVersionUID = -264927663211141894L;
41
42
43
44 /*** The single character used to separate key values in a string. */
45 public static final char SEPARATOR = ':';
46
47 /*** The single character used to separate key values in a string. */
48 public static final String SEPARATOR_STRING = ":";
49
50 /*** The array of the keys */
51 private SimpleKey[] key;
52
53 /***
54 * Creates an ComboKey whose internal representation will be
55 * set later, through a set method
56 */
57 public ComboKey()
58 {
59 }
60
61 /***
62 * Creates a ComboKey whose internal representation is an
63 * array of SimpleKeys.
64 *
65 * @param keys the key values
66 */
67 public ComboKey(SimpleKey[] keys)
68 {
69 setValue(keys);
70 }
71
72 /***
73 * Sets the internal representation to a String array.
74 *
75 * @param keys the key values
76 * @see #toString()
77 */
78 public ComboKey(String keys)
79 {
80 setValue(keys);
81 }
82
83 /***
84 * Sets the internal representation using a SimpleKey array.
85 *
86 * @param keys the key values
87 */
88 public void setValue(SimpleKey[] keys)
89 {
90 this.key = keys;
91 }
92
93 /***
94 * Sets the internal representation using a String of the
95 * form produced by the toString method.
96 *
97 * @param keys the key values
98 */
99 public void setValue(String keys)
100 {
101 int startPtr = 0;
102 int indexOfSep = keys.indexOf(SEPARATOR);
103 ArrayList tmpKeys = new ArrayList();
104 while (indexOfSep != -1)
105 {
106 if (indexOfSep == startPtr)
107 {
108 tmpKeys.add(null);
109 }
110 else
111 {
112 char keyType = keys.charAt(startPtr);
113 String keyString = keys.substring(startPtr + 1, indexOfSep);
114
115 SimpleKey newKey = null;
116 switch(keyType)
117 {
118 case 'N':
119 newKey = new NumberKey(keyString);
120 break;
121 case 'S':
122 newKey = new StringKey(keyString);
123 break;
124 case 'D':
125 try
126 {
127 newKey = new DateKey(keyString);
128 }
129 catch (NumberFormatException nfe)
130 {
131 newKey = new DateKey();
132 }
133 break;
134 default:
135
136 }
137 tmpKeys.add(newKey);
138 }
139 startPtr = indexOfSep + 1;
140 indexOfSep = keys.indexOf(SEPARATOR, startPtr);
141 }
142
143 this.key = new SimpleKey[tmpKeys.size()];
144 for (int i = 0; i < this.key.length; i++)
145 {
146 this.key[i] = (SimpleKey) tmpKeys.get(i);
147 }
148 }
149
150 /***
151 * Sets the internal representation using a ComboKey.
152 *
153 * @param keys the key values
154 */
155 public void setValue(ComboKey keys)
156 {
157 setValue((SimpleKey[]) keys.getValue());
158 }
159
160 /***
161 * Get the underlying object.
162 *
163 * @return the underlying object
164 */
165 public Object getValue()
166 {
167 return key;
168 }
169
170 /***
171 * This method will return true if the conditions for a looseEquals
172 * are met and in addition no parts of the keys are null.
173 *
174 * @param keyObj the comparison value
175 * @return whether the two objects are equal
176 */
177 public boolean equals(Object keyObj)
178 {
179 boolean isEqual = false;
180
181 if (key != null)
182 {
183
184 isEqual = true;
185 SimpleKey[] keys = key;
186 for (int i = 0; i < keys.length && isEqual; i++)
187 {
188 isEqual &= keys[i] != null && keys[i].getValue() != null;
189 }
190
191 isEqual &= looseEquals(keyObj);
192 }
193
194 return isEqual;
195 }
196
197 /***
198 * keyObj is equal to this ComboKey if keyObj is a ComboKey, String,
199 * ObjectKey[], or String[] that contains the same information this key
200 * contains.
201 * For example A String[] might be equal to this key, if this key was
202 * instantiated with a String[] and the arrays contain equal Strings.
203 * Another example, would be if keyObj is an ComboKey that was
204 * instantiated with a ObjectKey[] and this ComboKey was instantiated with
205 * a String[], but the ObjectKeys in the ObjectKey[] were instantiated
206 * with Strings that equal the Strings in this KeyObject's String[]
207 * This method is not as strict as the equals method which does not
208 * allow any null keys parts, while the internal key may not be null
209 * portions may be, and the two object will be considered equal if
210 * their null portions match.
211 *
212 * @param keyObj the comparison value
213 * @return whether the two objects are equal
214 */
215 public boolean looseEquals(Object keyObj)
216 {
217 boolean isEqual = false;
218
219 if (key != null)
220 {
221
222
223
224
225 if (keyObj instanceof String)
226 {
227 isEqual = toString().equals(keyObj);
228 }
229
230
231 else if (keyObj instanceof ComboKey)
232 {
233 SimpleKey[] obj = (SimpleKey[])
234 ((ComboKey) keyObj).getValue();
235
236 SimpleKey[] keys1 = key;
237 SimpleKey[] keys2 = obj;
238 isEqual = keys1.length == keys2.length;
239 for (int i = 0; i < keys1.length && isEqual; i++)
240 {
241 isEqual &= ObjectUtils.equals(keys1[i], keys2[i]);
242 }
243 }
244 else if (keyObj instanceof SimpleKey[])
245 {
246 SimpleKey[] keys1 = key;
247 SimpleKey[] keys2 = (SimpleKey[]) keyObj;
248 isEqual = keys1.length == keys2.length;
249 for (int i = 0; i < keys1.length && isEqual; i++)
250 {
251 isEqual &= ObjectUtils.equals(keys1[i], keys2[i]);
252 }
253 }
254 }
255 return isEqual;
256 }
257
258 /***
259 *
260 * @param sb the StringBuffer to append
261 * @see #toString()
262 */
263 public void appendTo(StringBuffer sb)
264 {
265 if (key != null)
266 {
267 SimpleKey[] keys = key;
268 for (int i = 0; i < keys.length; i++)
269 {
270 if (keys[i] != null)
271 {
272 if (keys[i] instanceof StringKey)
273 {
274 sb.append("S");
275 }
276 else if (keys[i] instanceof NumberKey)
277 {
278 sb.append("N");
279 }
280 else if (keys[i] instanceof DateKey)
281 {
282 sb.append("D");
283 }
284 else
285 {
286
287 sb.append("U");
288 }
289 keys[i].appendTo(sb);
290 }
291
292 sb.append(SEPARATOR);
293 }
294 }
295 }
296
297 /***
298 * if the underlying key array is not null and the first element is
299 * not null this method returns the hashcode of the first element
300 * in the key. Otherwise calls ObjectKey.hashCode()
301 *
302 * @return an <code>int</code> value
303 */
304 public int hashCode()
305 {
306 if (key == null)
307 {
308 return super.hashCode();
309 }
310
311 SimpleKey sk = key[0];
312 if (sk == null)
313 {
314 return super.hashCode();
315 }
316
317 return sk.hashCode();
318 }
319
320 /***
321 * A String that may consist of one section or multiple sections
322 * separated by a colon. <br/>
323 * Each Key is represented by <code>[type N|S|D][value][:]</code>. <p/>
324 * Example: <br/>
325 * the ComboKey(StringKey("key1"), NumberKey(2)) is represented as
326 * <code><b>Skey1:N2:</b></code>
327 *
328 * @return a String representation
329 */
330 public String toString()
331 {
332 StringBuffer sbuf = new StringBuffer();
333 appendTo(sbuf);
334 return sbuf.toString();
335 }
336 }