View Javadoc

1   package org.apache.torque.om;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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      // might want to shift these to TR.props
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                         // unextepcted key type
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             // check that all keys are not null
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             // Checks  a compound key (ObjectKey[] or String[]
222             // based) with the delimited String created by the
223             // toString() method.  Slightly expensive, but should be less
224             // than parsing the String into its constituents.
225             if (keyObj instanceof String)
226             {
227                 isEqual = toString().equals(keyObj);
228             }
229             // check against a ObjectKey. Two keys are equal, if their
230             // internal keys equivalent.
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                         // unknown type
287                         sb.append("U");
288                     }
289                     keys[i].appendTo(sb);
290                 }
291                 // MUST BE ADDED AFTER EACH KEY, IN CASE OF NULL KEY!
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 }