/* Copyright (c) 1995-2000, The Hypersonic SQL Group.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Hypersonic SQL Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Hypersonic SQL Group.
*
*
* For work added by the HSQL Development Group:
*
* Copyright (c) 2001-2009, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import java.util.Locale;
import org.hsqldb.persist.HsqlDatabaseProperties;
// fredt@users 20020305 - patch 1.7.0 - change to 2D string arrays
// sqlbob@users 20020420- patch 1.7.0 - added HEXTORAW and RAWTOHEX.
// boucherb@user 20020918 - doc 1.7.2 - added JavaDoc and code comments
// fredt@user 20021021 - doc 1.7.2 - modified JavaDoc
// boucherb@users 20030201 - patch 1.7.2 - direct calls for org.hsqldb.Library
// fredt@users - patch 1.8.0 - new functions added
// boucherb@users 20060428 - patch 1.8.0 - Bug fix for [ 1455637 ] allow double args for modulo
// fredt@users - patch 1.9.0 - most methods removed - see legacy support details
/**
* fredt - since the introduction of SQL built-in functions and rewrite of
* OpenGroup CLI functions as SQL functions, most previous methods have been
* removed. The remaining methods are called when SQL functions are executed.
*/
/**
* Provides the HSQLDB implementation of standard Open Group SQL CLI
* <em>Extended Scalar Functions</em> and other public HSQLDB SQL functions.<p>
*
* All methods here that have a Connection parameter are dummies and should
* not be called from user supplied Java procedure or trigger code. Use real
* SQL functions should be called instead in these instances.
*
* For 1.9.0, several methods have been deleted. These methods are now supported
* by other classes. All the deleted method signatures are still supported.
*
* Extensively rewritten and extended in successive versions of HSQLDB.
*
* @author Thomas Mueller (Hypersonic SQL Group)
* @version 1.8.0
* @since Hypersonic SQL
*/
public class Library {
private Library() {}
/**
* Retrieves a <em>magically</em> rounded </code>double</code> value produced
* from the given <code>double</code> value. This method provides special
* handling for numbers close to zero and performs rounding only for
* numbers within a specific range, returning precisely the given value
* if it does not lie in this range. <p>
*
* Special handling includes: <p>
*
* <UL>
* <LI> input in the interval -0.0000000000001..0.0000000000001 returns 0.0
* <LI> input outside the interval -1000000000000..1000000000000 returns
* input unchanged
* <LI> input is converted to String form
* <LI> input with a <code>String</code> form length greater than 16 returns
* input unchaged
* <LI> <code>String</code> form with last four characters of '...000x' where
* x != '.' is converted to '...0000'
* <LI> <code>String</code> form with last four characters of '...9999' is
* converted to '...999999'
* <LI> the <code>java.lang.Double.doubleValue</code> of the <code>String</code>
* form is returned
* </UL>
* @param d the double value for which to retrieve the <em>magically</em>
* rounded value
* @return the <em>magically</em> rounded value produced
*/
public static double roundMagic(double d) {
// this function rounds numbers in a good way but slow:
// - special handling for numbers around 0
// - only numbers <= +/-1000000000000
// - convert to a string
// - check the last 4 characters:
// '000x' becomes '0000'
// '999x' becomes '999999' (this is rounded automatically)
if ((d < 0.0000000000001) && (d > -0.0000000000001)) {
return 0.0;
}
if ((d > 1000000000000.) || (d < -1000000000000.)) {
return d;
}
StringBuffer sb = new StringBuffer();
sb.append(d);
int len = sb.length();
if (len < 16) {
return d;
}
char cx = sb.charAt(len - 1);
char c1 = sb.charAt(len - 2);
char c2 = sb.charAt(len - 3);
char c3 = sb.charAt(len - 4);
if ((c1 == '0') && (c2 == '0') && (c3 == '0') && (cx != '.')) {
sb.setCharAt(len - 1, '0');
} else if ((c1 == '9') && (c2 == '9') && (c3 == '9') && (cx != '.')) {
sb.setCharAt(len - 1, '9');
sb.append('9');
sb.append('9');
}
return Double.valueOf(sb.toString()).doubleValue();
}
/**
* Returns the given <code>double</code> value, rounded to the given
* <code>int</code> places right of the decimal point. If
* the supplied rounding place value is negative, rounding is performed
* to the left of the decimal point, using its magnitude (absolute value).
* @param d the value to be rounded
* @param p the rounding place value
* @return <code>d</code> rounded
*/
public static double round(double d, int p) {
double f = Math.pow(10., p);
return Math.round(d * f) / f;
}
// STRING FUNCTIONS
/**
* Returns the character string corresponding to the given ASCII
* (or Unicode) value.
*
* <b>Note:</b> <p>
*
* In some SQL CLI
* implementations, a <code>null</code> is returned if the range is outside 0..255.
* In HSQLDB, the corresponding Unicode character is returned
* unchecked.
* @param code the character code for which to return a String
* representation
* @return the String representation of the character
*/
public static String character(int code) {
return String.valueOf((char) code);
}
/**
* Returns a count of the characters that do not match when comparing
* the 4 digit numeric SOUNDEX character sequences for the
* given <code>String</code> objects. If either <code>String</code> object is
* <code>null</code>, zero is returned.
* @param s1 the first <code>String</code>
* @param s2 the second <code>String</code>
* @return the number of differences between the <code>SOUNDEX</code> of
* <code>s1</code> and the <code>SOUNDEX</code> of <code>s2</code>
*/
// fredt@users 20020305 - patch 460907 by fredt - soundex
public static int difference(String s1, String s2) {
/** @todo: check if this is the standard algorithm */
if ((s1 == null) || (s2 == null)) {
return 0;
}
s1 = soundex(s1);
s2 = soundex(s2);
int e = 0;
for (int i = 0; i < 4; i++) {
if (s1.charAt(i) != s2.charAt(i)) {
e++;
}
}
return e;
}
/**
* Returns the starting position of the first occurrence of
* the given <code>search</code> <code>String</code> object within
* the given <code>String</code> object, <code>s</code>.
*
* The search for the first occurrence of <code>search</code> begins with
* the first character position in <code>s</code>, unless the optional
* argument, <code>start</code>, is specified (non-null). If
* <code>start</code> is specified, the search begins with the character
* position indicated by the value of <code>start</code>, where the
* first character position in <code>s</code> is indicated by the value 1.
* If <code>search</code> is not found within <code>s</code>, the
* value 0 is returned.
* @param search the <code>String</code> occurence to find in <code>s</code>
* @param s the <code>String</code> within which to find the first
* occurence of <code>search</code>
* @param start the optional character position from which to start
* looking in <code>s</code>
* @return the one-based starting position of the first occurrence of
* <code>search</code> within <code>s</code>, or 0 if not found
*/
public static int locate(String search, String s, Integer start) {
if (s == null || search == null) {
return 0;
}
int i = (start == null) ? 0
: start.intValue() - 1;
return s.indexOf(search, (i < 0) ? 0
: i) + 1;
}
/**
* Returns a <code>String</code> composed of the given <code>String</code>,
* repeated <code>count</code> times.
*
* @param s the <code>String</code> to repeat
* @param count the number of repetitions
* @return the given <code>String</code>, repeated <code>count</code> times
*/
public static String repeat(String s, Integer count) {
if (s == null || count == null || count.intValue() < 0) {
return null;
}
int i = count.intValue();
StringBuffer sb = new StringBuffer(s.length() * i);
while (i-- > 0) {
sb.append(s);
}
return sb.toString();
}
// fredt@users - 20020903 - patch 1.7.1 - bug fix to allow multiple replaces
/**
* Replaces all occurrences of <code>replace</code> in <code>s</code>
* with the <code>String</code> object: <code>with</code>
* @param s the target for replacement
* @param replace the substring(s), if any, in <code>s</code> to replace
* @param with the value to substitute for <code>replace</code>
* @return <code>s</code>, with all occurences of <code>replace</code>
* replaced by <code>with</code>
*/
public static String replace(String s, String replace, String with) {
if (s == null || replace == null) {
return s;
}
if (with == null) {
with = "";
}
StringBuffer b = new StringBuffer();
int start = 0;
int lenreplace = replace.length();
while (true) {
int i = s.indexOf(replace, start);
if (i == -1) {
b.append(s.substring(start));
break;
}
b.append(s.substring(start, i));
b.append(with);
start = i + lenreplace;
}
return b.toString();
}
// fredt@users 20011010 - patch 460907 by fredt - soundex
/**
* Returns a four character code representing the sound of the given
* <code>String</code>. Non-ASCCI characters in the
* input <code>String</code> are ignored. <p>
*
* This method was
* rewritten for HSQLDB by fredt@users to comply with the description at
* <a href="http://www.archives.gov/genealogy/census/soundex.html">
* http://www.archives.gov/genealogy/census/soundex.html </a>.<p>
* @param s the <code>String</code> for which to calculate the 4 character
* <code>SOUNDEX</code> value
* @return the 4 character <code>SOUNDEX</code> value for the given
* <code>String</code>
*/
public static String soundex(String s) {
if (s == null) {
return s;
}
s = s.toUpperCase(Locale.ENGLISH);
int len = s.length();
char[] b = new char[] {
'0', '0', '0', '0'
};
char lastdigit = '0';
for (int i = 0, j = 0; i < len && j < 4; i++) {
char c = s.charAt(i);
char newdigit;
if ("AEIOUY".indexOf(c) != -1) {
newdigit = '7';
} else if (c == 'H' || c == 'W') {
newdigit = '8';
} else if ("BFPV".indexOf(c) != -1) {
newdigit = '1';
} else if ("CGJKQSXZ".indexOf(c) != -1) {
newdigit = '2';
} else if (c == 'D' || c == 'T') {
newdigit = '3';
} else if (c == 'L') {
newdigit = '4';
} else if (c == 'M' || c == 'N') {
newdigit = '5';
} else if (c == 'R') {
newdigit = '6';
} else {
continue;
}
if (j == 0) {
b[j++] = c;
lastdigit = newdigit;
} else if (newdigit <= '6') {
if (newdigit != lastdigit) {
b[j++] = newdigit;
lastdigit = newdigit;
}
} else if (newdigit == '7') {
lastdigit = newdigit;
}
}
return new String(b, 0, 4);
}
/**
* Returns the characters from the given <code>String</code>, starting at
* the indicated one-based <code>start</code> position and extending the
* (optional) indicated <code>length</code>. If <code>length</code> is not
* specified (is <code>null</code>), the remainder of <code>s</code> is
* implied.
*
* The rules for boundary conditions on s, start and length are,
* in order of precedence: <p>
*
* 1.) if s is null, return null
*
* 2.) If length is less than 1, return null.
*
* 3.) If start is 0, it is treated as 1.
*
* 4.) If start is positive, count from the beginning of s to find
* the first character postion.
*
* 5.) If start is negative, count backwards from the end of s
* to find the first character.
*
* 6.) If, after applying 2.) or 3.), the start position lies outside s,
* then return null
*
* 7.) if length is ommited or is greated than the number of characters
* from the start position to the end of s, return the remaineder of s,
* starting with the start position.
*
* @param s the <code>String</code> from which to produce the indicated
* substring
* @param start the starting position of the desired substring
* @param length the length of the desired substring
* @return the indicted substring of <code>s</code>.
*/
/**
* Retrieves the full version number of this database product. <p>
*
* @return database version number as a <code>String</code> object
* @since 1.8.0.4
*/
public static String getDatabaseFullProductVersion() {
return HsqlDatabaseProperties.THIS_FULL_VERSION;
}
/**
* Retrieves the name of this database product. <p>
*
* @return database product name as a <code>String</code> object
* @since 1.7.2
*/
public static String getDatabaseProductName() {
return HsqlDatabaseProperties.PRODUCT_NAME;
}
/**
* Retrieves the version number of this database product. <p>
*
* @return database version number as a <code>String</code> object
* @since 1.7.2
*/
public static String getDatabaseProductVersion() {
return HsqlDatabaseProperties.THIS_VERSION;
}
/**
* Retrieves the major version number of this database. <p>
*
* @return the database's major version as an <code>int</code> value
* @since 1.7.2
*/
public static int getDatabaseMajorVersion() {
return HsqlDatabaseProperties.MAJOR;
}
/**
* Retrieves the major version number of this database. <p>
*
* @return the database's major version as an <code>int</code> value
* @since 1.7.2
*/
public static int getDatabaseMinorVersion() {
return HsqlDatabaseProperties.MINOR;
}
}