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