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