KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > Library


1 /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the Hypersonic SQL Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * This software consists of voluntary contributions made by many individuals
31  * on behalf of the Hypersonic SQL Group.
32  *
33  *
34  * For work added by the HSQL Development Group:
35  *
36  * Copyright (c) 2001-2005, The HSQL Development Group
37  * All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions are met:
41  *
42  * Redistributions of source code must retain the above copyright notice, this
43  * list of conditions and the following disclaimer.
44  *
45  * Redistributions in binary form must reproduce the above copyright notice,
46  * this list of conditions and the following disclaimer in the documentation
47  * and/or other materials provided with the distribution.
48  *
49  * Neither the name of the HSQL Development Group nor the names of its
50  * contributors may be used to endorse or promote products derived from this
51  * software without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
54  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
57  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
58  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
59  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
60  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
61  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64  */

65
66
67 package org.hsqldb;
68
69 import java.sql.Connection JavaDoc;
70 import java.sql.Date JavaDoc;
71 import java.sql.Time JavaDoc;
72 import java.sql.Timestamp JavaDoc;
73 import java.text.FieldPosition JavaDoc;
74 import java.text.SimpleDateFormat JavaDoc;
75 import java.util.Calendar JavaDoc;
76 import java.util.Locale JavaDoc;
77 import java.util.Random JavaDoc;
78
79 import org.hsqldb.lib.HashMap;
80 import org.hsqldb.lib.IntValueHashMap;
81 import org.hsqldb.persist.HsqlDatabaseProperties;
82 import org.hsqldb.store.ValuePool;
83
84 // fredt@users 20020210 - patch 513005 by sqlbob@users (RMP) - ABS function
85
// fredt@users 20020305 - patch 1.7.0 - change to 2D string arrays
86
// sqlbob@users 20020420- patch 1.7.0 - added HEXTORAW and RAWTOHEX.
87
// boucherb@user 20020918 - doc 1.7.2 - added JavaDoc and code comments
88
// fredt@user 20021021 - doc 1.7.2 - modified JavaDoc
89
// boucherb@users 20030201 - patch 1.7.2 - direct calls for org.hsqldb.Library
90
// fredt@users - patch 1.8.0 - new functions added
91

92 /**
93  * fredt - todo - since the introduction of SQL built-in functions and
94  * evaluation of several session-dependent methods outside this class,
95  * several methods here are dummies. These methods are still reported in
96  * system tables incorrectly as corresponding to the SQL function names.
97  */

98
99 /**
100  * Provides the HSQLDB implementation of standard Open Group SQL CLI
101  * <em>Extended Scalar Functions</em> and other public HSQLDB SQL functions.<p>
102  *
103  * All methods here that have a Connection parameter are dummies and should
104  * not be called from user supplied Java procedure or trigger code. Use real
105  * SQL functions should be called instead in these instances.
106  *
107  * Extensively rewritten and extended in successive versions of HSQLDB.
108  *
109  * @author Thomas Mueller (Hypersonic SQL Group)
110  * @version 1.8.0
111  * @since Hypersonic SQL
112  */

113 public class Library {
114
115     static final SimpleDateFormat JavaDoc tocharFormat = new SimpleDateFormat JavaDoc();
116     static final SimpleDateFormat JavaDoc daynameFormat = new SimpleDateFormat JavaDoc("EEEE",
117         Locale.ENGLISH);
118     static final SimpleDateFormat JavaDoc monthnameFormat =
119         new SimpleDateFormat JavaDoc("MMMM", Locale.ENGLISH);
120     static final StringBuffer JavaDoc daynameBuffer = new StringBuffer JavaDoc();
121     static final StringBuffer JavaDoc monthnameBuffer = new StringBuffer JavaDoc();
122     static final FieldPosition JavaDoc monthPosition =
123         new FieldPosition JavaDoc(SimpleDateFormat.MONTH_FIELD);
124     static final FieldPosition JavaDoc dayPosition =
125         new FieldPosition JavaDoc(SimpleDateFormat.DAY_OF_WEEK_FIELD);
126     public static final String JavaDoc[][] sNumeric = {
127         {
128             "ABS", "org.hsqldb.Library.abs"
129         }, {
130             "ACOS", "java.lang.Math.acos"
131         }, {
132             "ASIN", "java.lang.Math.asin"
133         }, {
134             "ATAN", "java.lang.Math.atan"
135         }, {
136             "ATAN2", "java.lang.Math.atan2"
137         }, {
138             "CEILING", "java.lang.Math.ceil"
139         }, {
140             "COS", "java.lang.Math.cos"
141         }, {
142             "COT", "org.hsqldb.Library.cot"
143         }, {
144             "DEGREES", "java.lang.Math.toDegrees"
145         }, {
146             "EXP", "java.lang.Math.exp"
147         }, {
148             "FLOOR", "java.lang.Math.floor"
149         }, {
150             "LOG", "java.lang.Math.log"
151         }, {
152             "LOG10", "org.hsqldb.Library.log10"
153         }, {
154             "MOD", "org.hsqldb.Library.mod"
155         }, {
156             "PI", "org.hsqldb.Library.pi"
157         }, {
158             "POWER", "java.lang.Math.pow"
159         }, {
160             "RADIANS", "java.lang.Math.toRadians"
161         }, {
162             "RAND", "java.lang.Math.random"
163         }, {
164             "ROUND", "org.hsqldb.Library.round"
165         }, {
166             "SIGN", "org.hsqldb.Library.sign"
167         }, {
168             "SIN", "java.lang.Math.sin"
169         }, {
170             "SQRT", "java.lang.Math.sqrt"
171         }, {
172             "TAN", "java.lang.Math.tan"
173         }, {
174             "TRUNCATE", "org.hsqldb.Library.truncate"
175         }, {
176             "BITAND", "org.hsqldb.Library.bitand"
177         }, {
178             "BITOR", "org.hsqldb.Library.bitor"
179         }, {
180             "BITXOR", "org.hsqldb.Library.bitxor"
181         }, {
182             "ROUNDMAGIC", "org.hsqldb.Library.roundMagic"
183         }
184     };
185
186 // fredt@users 20010701 - patch 418023 by deforest@users
187
// the definition for SUBSTR was added
188
public static final String JavaDoc[][] sString = {
189         {
190             "ASCII", "org.hsqldb.Library.ascii"
191         }, {
192             "BIT_LENGTH", "org.hsqldb.Library.bitLength"
193         }, {
194             "CHAR", "org.hsqldb.Library.character"
195         }, {
196             "CHAR_LENGTH", "org.hsqldb.Library.length"
197         }, {
198             "CHARACTER_LENGTH", "org.hsqldb.Library.length"
199         }, {
200             "CONCAT", "org.hsqldb.Library.concat"
201         }, {
202             "DIFFERENCE", "org.hsqldb.Library.difference"
203         }, {
204             "HEXTORAW", "org.hsqldb.Library.hexToRaw"
205         }, {
206             "INSERT", "org.hsqldb.Library.insert"
207         }, {
208             "LCASE", "org.hsqldb.Library.lcase"
209         }, {
210             "LEFT", "org.hsqldb.Library.left"
211         }, {
212             "LENGTH", "org.hsqldb.Library.length"
213         }, {
214             "LOCATE", "org.hsqldb.Library.locate"
215         }, {
216             "LTRIM", "org.hsqldb.Library.ltrim"
217         }, {
218             "OCTET_LENGTH", "org.hsqldb.Library.octetLength"
219         }, {
220             "RAWTOHEX", "org.hsqldb.Library.rawToHex"
221         }, {
222             "REPEAT", "org.hsqldb.Library.repeat"
223         }, {
224             "REPLACE", "org.hsqldb.Library.replace"
225         }, {
226             "RIGHT", "org.hsqldb.Library.right"
227         }, {
228             "RTRIM", "org.hsqldb.Library.rtrim"
229         }, {
230             "SOUNDEX", "org.hsqldb.Library.soundex"
231         }, {
232             "SPACE", "org.hsqldb.Library.space"
233         }, {
234             "SUBSTR", "org.hsqldb.Library.substring"
235         }, {
236             "SUBSTRING", "org.hsqldb.Library.substring"
237         }, {
238             "UCASE", "org.hsqldb.Library.ucase"
239         }, {
240             "LOWER", "org.hsqldb.Library.lcase"
241         }, {
242             "UPPER", "org.hsqldb.Library.ucase"
243         }
244     };
245     public static final String JavaDoc[][] sTimeDate = {
246         {
247             "CURDATE", "org.hsqldb.Library.curdate"
248         }, {
249             "CURTIME", "org.hsqldb.Library.curtime"
250         }, {
251             "DATEDIFF", "org.hsqldb.Library.datediff"
252         }, {
253             "DAYNAME", "org.hsqldb.Library.dayname"
254         }, {
255             "DAY", "org.hsqldb.Library.dayofmonth"
256         }, {
257             "DAYOFMONTH", "org.hsqldb.Library.dayofmonth"
258         }, {
259             "DAYOFWEEK", "org.hsqldb.Library.dayofweek"
260         }, {
261             "DAYOFYEAR", "org.hsqldb.Library.dayofyear"
262         }, {
263             "HOUR", "org.hsqldb.Library.hour"
264         }, {
265             "MINUTE", "org.hsqldb.Library.minute"
266         }, {
267             "MONTH", "org.hsqldb.Library.month"
268         }, {
269             "MONTHNAME", "org.hsqldb.Library.monthname"
270         }, {
271             "NOW", "org.hsqldb.Library.now"
272         }, {
273             "QUARTER", "org.hsqldb.Library.quarter"
274         }, {
275             "SECOND", "org.hsqldb.Library.second"
276         }, {
277             "WEEK", "org.hsqldb.Library.week"
278         }, {
279             "YEAR", "org.hsqldb.Library.year"
280         }, {
281             "TO_CHAR", "org.hsqldb.Library.to_char"
282         }
283     };
284     public static final String JavaDoc[][] sSystem = {
285         {
286             "DATABASE", "org.hsqldb.Library.database"
287         }, {
288             "USER", "org.hsqldb.Library.user"
289         }, {
290             "IDENTITY", "org.hsqldb.Library.identity"
291         }
292     };
293
294     private Library() {}
295
296     static HashMap getAliasMap() {
297
298         HashMap h = new HashMap(83, 1);
299
300         register(h, sNumeric);
301         register(h, sString);
302         register(h, sTimeDate);
303         register(h, sSystem);
304
305         return h;
306     }
307
308     private static void register(HashMap h, String JavaDoc[][] s) {
309
310         for (int i = 0; i < s.length; i++) {
311             h.put(s[i][0], s[i][1]);
312         }
313     }
314
315     private static final Random JavaDoc rRandom = new Random JavaDoc();
316
317     // NUMERIC FUNCTIONS
318
// fredt@users 20020220 - patch 489184 by xclayl@users - thread safety
319

320     /**
321      * Returns the next pseudorandom, uniformly distributed <code>double</code> value
322      * between 0.0 and 1.0 from a single, system-wide random number generator's
323      * sequence, optionally re-seeding (and thus resetting) the generator sequence.
324      *
325      * If the seed value is <code>null</code>, then the underlying random number
326      * generator retrieves the next value in its current sequence, else the seed
327      * alters the state of the generator object so as to be in exactly the same state
328      * as if it had just been created with the seed value.
329      * @param seed an optional parameter with which to reseed the underlying
330      * pseudorandom number generator
331      * @return the next pseudorandom, uniformly distributed <code>double</code> value between
332      * 0.0 and 1.0
333      */

334     public static double rand(Integer JavaDoc seed) {
335
336         // boucherb@users 20020918
337
// CHECKME: perhaps rRandom should be a member of Session,
338
// since otherwise connections are *not* guranteed to get the
339
// same pseudorandom sequence, given the same set of calls to this
340
// SQL function. This makes comparitive analysis difficult.
341
// In fact, rRandom will be shared across multiple in-process
342
// database instances, so it is not even guaranteed that the
343
// sole connection to one instance will get the same sequence given
344
// the same set of calls to this SQL function.
345
synchronized (rRandom) {
346             if (seed != null) {
347                 rRandom.setSeed(seed.intValue());
348             }
349
350             return rRandom.nextDouble();
351         }
352     }
353
354     /**
355      * Returns the absolute value of the given <code>double</code> value.
356      * @param d the number for which to determine the absolute value
357      * @return the absolute value of <code>d</code>, as a <code>double</code>
358      */

359     public static double abs(double d) {
360         return Math.abs(d);
361     }
362
363     // this magic number works for 100000000000000; but not for 0.1 and 0.01
364
private static final double LOG10_FACTOR = 0.43429448190325183;
365
366     /**
367      * Returns the base 10 logarithm of the given <code>double</code> value.
368      * @param x the value for which to calculate the base 10 logarithm
369      * @return the base 10 logarithm of <code>x</code>, as a <code>double</code>
370      */

371     public static double log10(double x) {
372         return roundMagic(Math.log(x) * LOG10_FACTOR);
373     }
374
375     /**
376      * Retrieves a <em>magically</em> rounded </code>double</code> value produced
377      * from the given <code>double</code> value. This method provides special
378      * handling for numbers close to zero and performs rounding only for
379      * numbers within a specific range, returning precisely the given value
380      * if it does not lie in this range. <p>
381      *
382      * Special handling includes: <p>
383      *
384      * <UL>
385      * <LI> input in the interval -0.0000000000001..0.0000000000001 returns 0.0
386      * <LI> input outside the interval -1000000000000..1000000000000 returns
387      * input unchanged
388      * <LI> input is converted to String form
389      * <LI> input with a <code>String</code> form length greater than 16 returns
390      * input unchaged
391      * <LI> <code>String</code> form with last four characters of '...000x' where
392      * x != '.' is converted to '...0000'
393      * <LI> <code>String</code> form with last four characters of '...9999' is
394      * converted to '...999999'
395      * <LI> the <code>java.lang.Double.doubleValue</code> of the <code>String</code>
396      * form is returned
397      * </UL>
398      * @param d the double value for which to retrieve the <em>magically</em>
399      * rounded value
400      * @return the <em>magically</em> rounded value produced
401      */

402     public static double roundMagic(double d) {
403
404         // this function rounds numbers in a good way but slow:
405
// - special handling for numbers around 0
406
// - only numbers <= +/-1000000000000
407
// - convert to a string
408
// - check the last 4 characters:
409
// '000x' becomes '0000'
410
// '999x' becomes '999999' (this is rounded automatically)
411
if ((d < 0.0000000000001) && (d > -0.0000000000001)) {
412             return 0.0;
413         }
414
415         if ((d > 1000000000000.) || (d < -1000000000000.)) {
416             return d;
417         }
418
419         StringBuffer JavaDoc s = new StringBuffer JavaDoc();
420
421         s.append(d);
422
423         int len = s.length();
424
425         if (len < 16) {
426             return d;
427         }
428
429         char cx = s.charAt(len - 1);
430         char c1 = s.charAt(len - 2);
431         char c2 = s.charAt(len - 3);
432         char c3 = s.charAt(len - 4);
433
434         if ((c1 == '0') && (c2 == '0') && (c3 == '0') && (cx != '.')) {
435             s.setCharAt(len - 1, '0');
436         } else if ((c1 == '9') && (c2 == '9') && (c3 == '9') && (cx != '.')) {
437             s.setCharAt(len - 1, '9');
438             s.append('9');
439             s.append('9');
440         }
441
442         return Double.valueOf(s.toString()).doubleValue();
443     }
444
445     /**
446      * Returns the cotangent of the given <code>double</code> value
447      * expressed in radians.
448      * @param d the angle, expressed in radians
449      * @return the cotangent
450      */

451     public static double cot(double d) {
452         return 1. / Math.tan(d);
453     }
454
455     /**
456      * Returns the remainder (modulus) of the first given integer divided
457      * by the second. <p>
458      *
459      * @param i1 the numerator
460      * @param i2 the divisor
461      * @return <code>i1</code> % <code>i2</code>, as an <code>int</code>
462      */

463     public static int mod(int i1, int i2) {
464         return i1 % i2;
465     }
466
467     /**
468      * Returns the constant value, pi.
469      * @return pi as a <code>double</code> value
470      */

471     public static double pi() {
472         return Math.PI;
473     }
474
475     /**
476      * Returns the given <code>double</code> value, rounded to the given
477      * <code>int</code> places right of the decimal point. If
478      * the supplied rounding place value is negative, rounding is performed
479      * to the left of the decimal point, using its magnitude (absolute value).
480      * @param d the value to be rounded
481      * @param p the rounding place value
482      * @return <code>d</code> rounded
483      */

484     public static double round(double d, int p) {
485
486         double f = Math.pow(10., p);
487
488         return Math.round(d * f) / f;
489     }
490
491     /**
492      * Returns an indicator of the sign of the given <code>double</code>
493      * value. If the value is less than zero, -1 is returned. If the value
494      * equals zero, 0 is returned. If the value is greater than zero, 1 is
495      * returned.
496      * @param d the value
497      * @return the sign of <code>d</code>
498      */

499     public static int sign(double d) {
500
501         return (d < 0) ? -1
502                        : ((d > 0) ? 1
503                                   : 0);
504     }
505
506     /**
507      * Returns the given <code>double</code> value, truncated to
508      * the given <code>int</code> places right of the decimal point.
509      * If the given place value is negative, the given <code>double</code>
510      * value is truncated to the left of the decimal point, using the
511      * magnitude (aboslute value) of the place value.
512      * @param d the value to truncate
513      * @param p the places left or right of the decimal point at which to
514      * truncate
515      * @return <code>d</code>, truncated
516      */

517     public static double truncate(double d, int p) {
518
519         double f = Math.pow(10., p);
520         double g = d * f;
521
522         return ((d < 0) ? Math.ceil(g)
523                         : Math.floor(g)) / f;
524     }
525
526     /**
527      * Returns the bit-wise logical <em>and</em> of the given
528      * integer values.
529      * @param i the first value
530      * @param j the second value
531      * @return the bit-wise logical <em>and</em> of
532      * <code>i</code> and <code>j</code>
533      */

534     public static int bitand(int i, int j) {
535         return i & j;
536     }
537
538     /**
539      * Returns the bit-wise logical <em>or</em> of the given
540      * integer values.
541      *
542      * @param i the first value
543      * @param j the second value
544      * @return the bit-wise logical <em>or</em> of
545      * <code>i</code> and <code>j</code>
546      */

547     public static int bitor(int i, int j) {
548         return i | j;
549     }
550
551     /**
552      * Returns the bit-wise logical <em>xor</em> of the given
553      * integer values.
554      *
555      * @param i the first value
556      * @param j the second value
557      * @return the bit-wise logical <em>xor</em> of
558      * <code>i</code> and <code>j</code>
559      *
560      * @since 1.8.0
561      */

562     public static int bitxor(int i, int j) {
563         return i ^ j;
564     }
565
566     // STRING FUNCTIONS
567

568     /**
569      * Returns the Unicode code value of the leftmost character of
570      * <code>s</code> as an <code>int</code>. This is the same as the
571      * ASCII value if the string contains only ASCII characters.
572      * @param s the <code>String</code> to evaluate
573      * @return the integer Unicode value of the
574      * leftmost character
575      */

576     public static Integer JavaDoc ascii(String JavaDoc s) {
577
578         if ((s == null) || (s.length() == 0)) {
579             return null;
580         }
581
582         return ValuePool.getInt(s.charAt(0));
583     }
584
585     /**
586      * Returns the character string corresponding to the given ASCII
587      * (or Unicode) value.
588      *
589      * <b>Note:</b> <p>
590      *
591      * In some SQL CLI
592      * implementations, a <code>null</code> is returned if the range is outside 0..255.
593      * In HSQLDB, the corresponding Unicode character is returned
594      * unchecked.
595      * @param code the character code for which to return a String
596      * representation
597      * @return the String representation of the character
598      */

599     public static String JavaDoc character(int code) {
600         return String.valueOf((char) code);
601     }
602
603     /**
604      * Returns a <code>String</code> object that is the result of an
605      * concatenation of the given <code>String</code> objects. <p>
606      *
607      * <b>When only one string is NULL, the result is different from that
608      * returned by an (string1 || string2) expression:
609      *
610      * <UL>
611      * <LI> if both <code>String</code> objects are <code>null</code>, return
612      * <code>null</code>
613      * <LI> if only one string is <code>null</code>, return the other
614      * <LI> if both <code>String</code> objects are non-null, return as a
615      * <code>String</code> object the character sequence obtained by listing,
616      * in left to right order, the characters of the first string followed by
617      * the characters of the second
618      * </UL>
619      * @param s1 the first <code>String</code>
620      * @param s2 the second <code>String</code>
621      * @return <code>s1</code> concatentated with <code>s2</code>
622      */

623     public static String JavaDoc concat(String JavaDoc s1, String JavaDoc s2) {
624
625         if (s1 == null) {
626             if (s2 == null) {
627                 return null;
628             }
629
630             return s2;
631         }
632
633         if (s2 == null) {
634             return s1;
635         }
636
637         return s1.concat(s2);
638     }
639
640     /**
641      * Returns a count of the characters that do not match when comparing
642      * the 4 digit numeric SOUNDEX character sequences for the
643      * given <code>String</code> objects. If either <code>String</code> object is
644      * <code>null</code>, zero is returned.
645      * @param s1 the first <code>String</code>
646      * @param s2 the second <code>String</code>
647      * @return the number of differences between the <code>SOUNDEX</code> of
648      * <code>s1</code> and the <code>SOUNDEX</code> of <code>s2</code>
649      */

650
651 // fredt@users 20020305 - patch 460907 by fredt - soundex
652
public static int difference(String JavaDoc s1, String JavaDoc s2) {
653
654         // todo: check if this is the standard algorithm
655
if ((s1 == null) || (s2 == null)) {
656             return 0;
657         }
658
659         s1 = soundex(s1);
660         s2 = soundex(s2);
661
662         int e = 0;
663
664         for (int i = 0; i < 4; i++) {
665             if (s1.charAt(i) != s2.charAt(i)) {
666                 e++;
667             }
668         }
669
670         return e;
671     }
672
673     /**
674      * Converts a <code>String</code> of hexidecimal digit characters to a raw
675      * binary value, represented as a <code>String</code>.<p>
676      *
677      * The given <code>String</code> object must consist of a sequence of
678      * 4 digit hexidecimal character substrings.<p> If its length is not
679      * evenly divisible by 4, <code>null</code> is returned. If any of
680      * its 4 character subsequences cannot be parsed as a
681      * 4 digit, base 16 value, then a NumberFormatException is thrown.
682      *
683      * This conversion has the effect of reducing the character count 4:1.
684      *
685      * @param s a <code>String</code> of hexidecimal digit characters
686      * @return an equivalent raw binary value, represented as a
687      * <code>String</code>
688      */

689     public static String JavaDoc hexToRaw(String JavaDoc s) {
690
691         if (s == null) {
692             return null;
693         }
694
695         char raw;
696         StringBuffer JavaDoc to = new StringBuffer JavaDoc();
697         int len = s.length();
698
699         if (len % 4 != 0) {
700             return null;
701         }
702
703         for (int i = 0; i < len; i += 4) {
704             raw = (char) Integer.parseInt(s.substring(i, i + 4), 16);
705
706             to.append(raw);
707         }
708
709         return (to.toString());
710     }
711
712     /**
713      * Returns a character sequence which is the result of writing the
714      * first <code>length</code> number of characters from the second
715      * given <code>String</code> over the first string. The start position
716      * in the first string where the characters are overwritten is given by
717      * <code>start</code>.<p>
718      *
719      * <b>Note:</b> In order of precedence, boundry conditions are handled as
720      * follows:<p>
721      *
722      * <UL>
723      * <LI>if either supplied <code>String</code> is null, then the other is
724      * returned; the check starts with the first given <code>String</code>.
725      * <LI>if <code>start</code> is less than one, <code>s1</code> is returned
726      * <LI>if <code>length</code> is less than or equal to zero,
727      * <code>s1</code> is returned
728      * <LI>if the length of <code>s2</code> is zero, <code>s1</code> is returned
729      * <LI>if <code>start</code> is greater than the length of <code>s1</code>,
730      * <code>s1</code> is returned
731      * <LI>if <code>length</code> is such that, taken together with
732      * <code>start</code>, the indicated interval extends
733      * beyond the end of <code>s1</code>, then the insertion is performed
734      * precisely as if upon a copy of <code>s1</code> extended in length
735      * to just include the indicated interval
736      * </UL>
737      * @param s1 the <code>String</code> into which to insert <code>s2</code>
738      * @param start the position, with origin one, at which to start the insertion
739      * @param length the number of characters in <code>s1</code> to replace
740      * @param s2 the <code>String</code> to insert into <code>s1</code>
741      * @return <code>s2</code> inserted into <code>s1</code>, as indicated
742      * by <code>start</code> and <code>length</code> and adjusted for
743      * boundry conditions
744      */

745     public static String JavaDoc insert(String JavaDoc s1, int start, int length, String JavaDoc s2) {
746
747         if (s1 == null) {
748             return s2;
749         }
750
751         if (s2 == null) {
752             return s1;
753         }
754
755         int len1 = s1.length();
756         int len2 = s2.length();
757
758         start--;
759
760         if (start < 0 || length <= 0 || len2 == 0 || start > len1) {
761             return s1;
762         }
763
764         if (start + length > len1) {
765             length = len1 - start;
766         }
767
768         return s1.substring(0, start) + s2 + s1.substring(start + length);
769     }
770
771     /**
772      * Returns a copy of the given <code>String</code>, with all upper case
773      * characters converted to lower case. This uses the default Java String
774      * conversion.
775      * @param s the <code>String</code> from which to produce a lower case
776      * version
777      * @return a lower case version of <code>s</code>
778      */

779     public static String JavaDoc lcase(String JavaDoc s) {
780         return (s == null) ? null
781                            : s.toLowerCase();
782     }
783
784     /**
785      * Returns the leftmost <code>count</code> characters from the given
786      * <code>String</code>. <p>
787      *
788      * <b>Note:</b> boundry conditions are handled in the following order of
789      * precedence:
790      *
791      * <UL>
792      * <LI> if <code>s</code> is <code>null</code>, then <code>null</code>
793      * is returned
794      * <LI> if <code>count</code> is less than 1, then a zero-length
795      * <code>String</code> is returned
796      * <LI> if <code>count</code> is greater than the length of <code>s</code>,
797      * then a copy of <code>s</code> is returned
798      * </UL>
799      * @param s the <code>String</code> from which to retrieve the leftmost
800      * characters
801      * @param count the count of leftmost characters to retrieve
802      * @return the leftmost <code>count</code> characters of <code>s</code>
803      */

804     public static String JavaDoc left(String JavaDoc s, int count) {
805
806         if (s == null) {
807             return null;
808         }
809
810         return s.substring(0, ((count < 0) ? 0
811                                            : (count < s.length()) ? count
812                                                                   : s.length()));
813     }
814
815 // fredt@users - 20020819 - patch 595854 by thomasm@users
816

817     /**
818      * Returns the number of characters in the given <code>String</code>.
819      * This includes trailing blanks.
820      *
821      * @param s the <code>String</code> for which to determine length
822      * @return the length of <code>s</code>, including trailing blanks
823      */

824     public static Integer JavaDoc length(String JavaDoc s) {
825         return s == null ? null
826                          : ValuePool.getInt(s.length());
827     }
828
829     /**
830      * Returns the number of bytes in the given <code>String</code>.
831      * This includes trailing blanks.
832      *
833      * @param s the <code>String</code> for which to determine the octet length
834      * @return the octet length of <code>s</code>, including trailing blanks
835      * @since 1.7.2
836      */

837     public static Integer JavaDoc octetLength(String JavaDoc s) {
838         return s == null ? null
839                          : ValuePool.getInt(s.length() * 2);
840     }
841
842     /**
843      * Returns the number of bits in the given <code>String</code>.
844      * This includes trailing blanks.
845      *
846      * @param s the <code>String</code> for which to determine the bit length
847      * @return the bit length of <code>s</code>, including trailing blanks
848      * @since 1.7.2
849      */

850     public static Integer JavaDoc bitLength(String JavaDoc s) {
851         return s == null ? null
852                          : ValuePool.getInt(s.length() * 16);
853     }
854
855     /**
856      * Returns the starting position of the first occurrence of
857      * the given <code>search</code> <code>String</code> object within
858      * the given <code>String</code> object, <code>s</code>.
859      *
860      * The search for the first occurrence of <code>search</code> begins with
861      * the first character position in <code>s</code>, unless the optional
862      * argument, <code>start</code>, is specified (non-null). If
863      * <code>start</code> is specified, the search begins with the character
864      * position indicated by the value of <code>start</code>, where the
865      * first character position in <code>s</code> is indicated by the value 1.
866      * If <code>search</code> is not found within <code>s</code>, the
867      * value 0 is returned.
868      * @param search the <code>String</code> occurence to find in <code>s</code>
869      * @param s the <code>String</code> within which to find the first
870      * occurence of <code>search</code>
871      * @param start the optional character position from which to start
872      * looking in <code>s</code>
873      * @return the one-based starting position of the first occurrence of
874      * <code>search</code> within <code>s</code>, or 0 if not found
875      */

876     public static int locate(String JavaDoc search, String JavaDoc s, Integer JavaDoc start) {
877
878         if (s == null || search == null) {
879             return 0;
880         }
881
882         int i = (start == null) ? 0
883                                 : start.intValue() - 1;
884
885         return s.indexOf(search, (i < 0) ? 0
886                                          : i) + 1;
887     }
888
889     /**
890      * As locate but from start position l. <p>
891      *
892      * @param search the <code>String</code> occurence to find in <code>s</code>
893      * @param s the <code>String</code> within which to find the first
894      * occurence of <code>search</code>
895      * @return the one-based starting position of the first occurrence of
896      * <code>search</code> within <code>s</code>, or 0 if not found
897      */

898     public static int position(String JavaDoc search, String JavaDoc s) {
899         return locate(search, s, null);
900     }
901
902     /**
903      * Returns the characters of the given <code>String</code>, with the
904      * leading spaces removed. Characters such as TAB are not removed.
905      *
906      * @param s the <code>String</code> from which to remove the leading blanks
907      * @return the characters of the given <code>String</code>, with the leading
908      * spaces removed
909      */

910     public static String JavaDoc ltrim(String JavaDoc