View Javadoc

1   package org.apache.torque.om;
2   
3   /* ====================================================================
4    * The Apache Software License, Version 1.1
5    *
6    * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
7    * reserved.
8    *
9    * Redistribution and use in source and binary forms, with or without
10   * modification, are permitted provided that the following conditions
11   * are met:
12   *
13   * 1. Redistributions of source code must retain the above copyright
14   *    notice, this list of conditions and the following disclaimer.
15   *
16   * 2. Redistributions in binary form must reproduce the above copyright
17   *    notice, this list of conditions and the following disclaimer in
18   *    the documentation and/or other materials provided with the
19   *    distribution.
20   *
21   * 3. The end-user documentation included with the redistribution,
22   *    if any, must include the following acknowledgment:
23   *       "This product includes software developed by the
24   *        Apache Software Foundation (http://www.apache.org/)."
25   *    Alternately, this acknowledgment may appear in the software itself,
26   *    if and wherever such third-party acknowledgments normally appear.
27   *
28   * 4. The names "Apache" and "Apache Software Foundation" and
29   *    "Apache Turbine" must not be used to endorse or promote products
30   *    derived from this software without prior written permission. For
31   *    written permission, please contact apache@apache.org.
32   *
33   * 5. Products derived from this software may not be called "Apache",
34   *    "Apache Turbine", nor may "Apache" appear in their name, without
35   *    prior written permission of the Apache Software Foundation.
36   *
37   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48   * SUCH DAMAGE.
49   * ====================================================================
50   *
51   * This software consists of voluntary contributions made by many
52   * individuals on behalf of the Apache Software Foundation.  For more
53   * information on the Apache Software Foundation, please see
54   * <http://www.apache.org/>.
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      // might want to shift these to TR.props
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                         // unextepcted key type
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             // check that all keys are not null
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             // Checks  a compound key (ObjectKey[] or String[]
252             // based) with the delimited String created by the
253             // toString() method.  Slightly expensive, but should be less
254             // than parsing the String into its constituents.
255             if (keyObj instanceof String)
256             {
257                 isEqual = toString().equals(keyObj);
258             }
259             // check against a ObjectKey. Two keys are equal, if their
260             // internal keys equivalent.
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                         // unknown type
317                         sb.append("U");
318                     }
319                     keys[i].appendTo(sb);
320                 }
321                 // MUST BE ADDED AFTER EACH KEY, IN CASE OF NULL KEY!
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 }