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