View Javadoc

1   package org.apache.torque.om;
2   
3   /*
4    * Copyright 2001-2005 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
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      // might want to shift these to TR.props
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                         // unextepcted key type
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             // check that all keys are not null
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             // Checks  a compound key (ObjectKey[] or String[]
214             // based) with the delimited String created by the
215             // toString() method.  Slightly expensive, but should be less
216             // than parsing the String into its constituents.
217             if (keyObj instanceof String)
218             {
219                 isEqual = toString().equals(keyObj);
220             }
221             // check against a ObjectKey. Two keys are equal, if their
222             // internal keys equivalent.
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                         // unknown type
279                         sb.append("U");
280                     }
281                     keys[i].appendTo(sb);
282                 }
283                 // MUST BE ADDED AFTER EACH KEY, IN CASE OF NULL KEY!
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 }