/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 1998, 1999 Wabasoft <www.wabasoft.com> * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.sys; import totalcross.*; import totalcross.ui.font.*; import totalcross.util.*; import totalcross.util.Comparable; import java.io.*; /** * Convert is used to convert between objects and basic types. It also contains many * other utility methods. */ public final class Convert { public static final String CRLF = "\r\n"; public static final byte[] CRLF_BYTES = {'\r','\n'}; static void newLauncherInstance() { /* guich@200b4_150: totalcross.sys.Convert is always the first native method * class created. It may have been created by a compiler or by Retroguard * during the obfuscating process. When it is created this way, the * Applet was not initialized, and such here we test and * create it if necessary. The values in the class aren't important; but * they must not be null. */ if (Launcher.instance == null) { new Launcher(); } if (Launcher.instance == null) { new Launcher(); System.out.println("******************** NULL"); } Launcher.instance.fillSettings(); // guich@tc100 } static void newLauncherInstance4D() {} static { newLauncherInstance(); } public static char []b2h = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; private static byte []h2b = new byte['f' + 1]; private static byte h2bInvalid = (byte)0xFF; static { fill(h2b, 0, h2b.length, h2bInvalid); // fill with invalid values for (int i = '0', j = 0; i <= '9'; i ++, j++) h2b[i] = (byte)j; for (int i = 'A', j = 10; i <= 'F'; i++, j++) h2b[i] = (byte)j; for (int i = 'a', j = 10; i <= 'f'; i++, j++) h2b[i] = (byte)j; } private static byte[] buffer = new byte[32]; /** The bytes are converted to char and vice-versa using the CharacterConverter associated in this charConverter member. * @see totalcross.sys.Convert#setDefaultConverter(String) * @see totalcross.sys.CharacterConverter * @see totalcross.sys.UTF8CharacterConverter */ public static totalcross.sys.CharacterConverter charConverter = new totalcross.sys.CharacterConverter(); private static Hashtable htConvs = new Hashtable(1); /** Changes the default Character Converter to the given one. * Use like * <pre>Convert.setDefaultConverter("UTF8");</pre> * to change all bytes_to_char and char_to_bytes operations to use UTF8 instead. * <pre>Convert.setDefaultConverter("");</pre> * sets back the default encoder. * @return true if the given encoder was found, false otherwise. If not found, the encoder is reset to the default one (ISO 8859-1). * @since SuperWaba 4.1 * @see totalcross.sys.Convert#charConverter * @see totalcross.sys.CharacterConverter * @see totalcross.sys.UTF8CharacterConverter */ public static boolean setDefaultConverter(String name) { if (name == null) throw new NullPointerException("Argument 'name' cannot have a null value"); boolean ok = true; CharacterConverter cc = (CharacterConverter) htConvs.get(name); if (cc == null) { try { cc = (CharacterConverter)Class.forName("totalcross.sys."+name+"CharacterConverter").newInstance(); } catch (Exception e) { cc = new CharacterConverter(); ok = false; } htConvs.put(name,cc); } charConverter = cc; return ok; } /** The minimum char value: '\u0000' */ public static final char MIN_CHAR_VALUE = '\u0000'; /** The maximum char value: */ public static final char MAX_CHAR_VALUE = '\uFFFF'; /** The maximum short value: 32767 */ public static final short MAX_SHORT_VALUE = 32767; /** The minimum short value: -32768 */ public static final short MIN_SHORT_VALUE = -32768; /** The minimum long value: -9223372036854775808 */ public static final long MIN_LONG_VALUE = 0x8000000000000000L; /** The maximum long value: 9223372036854775807 */ public static final long MAX_LONG_VALUE = 0x7fffffffffffffffL; /** The minimum int value: -2147483648 */ public static final int MIN_INT_VALUE = 0x80000000; /** The maximum int value: 2147483647 */ public static final int MAX_INT_VALUE = 0x7fffffff; /** The maximum double value: 1.7976931348623157E308d */ public static final double MAX_DOUBLE_VALUE = 1.7976931348623157E308d; // 2^53 /** The minimum double value: 4.9E-324d */ public static final double MIN_DOUBLE_VALUE = 4.9E-324d; /** The maximum number of digits in a double value, used when formatting to string. */ public static final int MAX_DOUBLE_DIGITS = 15; private static final double DOUBLE_MAX_NON_EXP = 9.007199254740992E15; // 2^53 private static final double DOUBLE_MIN_NON_EXP = 1.1102230246251565E-16; // 2^-53 static final char[] TITLE = "\u01c4\u01c5\u01c5\u01c5\u01c6\u01c5\u01c7\u01c8\u01c8\u01c8\u01c9\u01c8\u01ca\u01cb\u01cb\u01cb\u01cc\u01cb\u01f1\u01f2\u01f2\u01f2\u01f3\u01f2".toCharArray(); static final boolean useNative = !Settings.onJavaSE; private Convert() { } /** * Converts a Unicode character into its titlecase equivalent mapping. * If a mapping does not exist, then the character passed is returned. * Note that isTitleCase(toTitleCase(ch)) does not always return true. * * @param ch character to convert to titlecase * @return titlecase mapping of ch, or ch if titlecase mapping does * not exist * @see #toLowerCase(char) * @see #toUpperCase(char) */ public static char toTitleCase(char ch) { char[] title = TITLE; // As title is short, it doesn't hurt to exhaustively iterate over it. for (int i = title.length - 2; i >= 0; i -= 2) if (title[i] == ch) return title[i + 1]; return toUpperCase(ch); } /** Creates a copy of the given array. */ public static String [] cloneStringArray(String []strs) // guich@220_2 { String []ret = null; if (strs != null) { int n = strs.length; ret = new String[n]; Vm.arrayCopy(strs,0,ret,0,n); } return ret; } /** Converts the given object array to a String array by calling toString in each object. */ public static String [] toStringArray(Object []objs) // guich@200b4_26 { int i; String as[] = null; if (objs != null) { as = new String[objs.length]; for (i = objs.length; --i >= 0;) { Object o = objs[i]; as[i] = o instanceof String ? (String)o : objs[i].toString(); } } return as; } /** Convert a string to the short type. Note that this method is slower than <code>(short)Convert.toInt()</code> * @since TotalCross 1.22 */ public static short toShort(String s) throws InvalidNumberException { if (s == null) throw new NullPointerException("Argument 's' cannot have a null value"); else if (s.length() == 0) throw new InvalidNumberException("Argument 's' is empty and cannot be converted into a number."); try { int v = toInt(s); if (v < MIN_SHORT_VALUE || v > MAX_SHORT_VALUE) throw new InvalidNumberException("Value " + s + " is out of short range."); return (short)v; } catch (NumberFormatException e) { throw new InvalidNumberException("Error: "+s+" is not a valid integer value."); } } /** * Converts the given String to an int. The number may be prefixed with 0's. * @throws InvalidNumberException If the string passed is not a valid number */ public static int toInt(String s) throws InvalidNumberException { if (s == null) throw new NullPointerException("Argument 's' cannot have a null value"); else if (s.length() == 0) throw new InvalidNumberException("Argument 's' is empty and cannot be converted into a number."); try { return java.lang.Integer.parseInt(s); } catch (NumberFormatException e) { throw new InvalidNumberException("Error: "+s+" is not a valid integer value."); } } /** * Converts the given String to an int. The number may be prefixed with 0's. * If the string is not a valid number, returns defaultValue. * @since TotalCross 2.0 */ public static int toInt(String s, int defaultValue) { try { return toInt(s); } catch (InvalidNumberException ine) { return defaultValue; } } /** * Converts the given String to a double. * If the string is not a valid number, returns defaultValue. * @since TotalCross 2.0 */ public static double toDouble(String s, double defaultValue) { try { return toDouble(s); } catch (InvalidNumberException ine) { return defaultValue; } } /** Converts the given boolean to a String. */ public static String toString(boolean b) { return b ? "true" : "false"; } /** Converts the given char to a String. */ public static String toString(char c) { return String.valueOf(c); } /** Converts the given double to its bit representation in IEEE 754 format, using 4 bytes instead of 8 (a convertion to float is applied). */ public static int doubleToIntBits(double f) { return Float.floatToIntBits((float) f); } /** Converts the given IEEE 754 bit representation of a float to a double. */ public static double intBitsToDouble(int i) { return (double) Float.intBitsToFloat(i); } /** Converts the given int to a String. */ public static String toString(int i) { return java.lang.Integer.toString(i); } /** Converts the given double to a String, formatting it with the given number of decimal places. @since SuperWaba 2.0 */ public static String toString(double val, int decimalCount) { if (decimalCount < -1) throw new IllegalArgumentException("Invalid value '"+decimalCount+"' for argument '"+decimalCount+"'"); // guich@tc123_9 StringBuffer dest = new StringBuffer(25); //System.out.print(val+", "+decimalCount); long bits = Double.doubleToLongBits(val); if (val == 0) { if (decimalCount == -1) dest.append("0.0"); else { dest.append('0'); if (decimalCount > 0) dest.append('.').append(Constants.zeros,0,decimalCount); } } else if (bits == DOUBLE_NAN_BITS) dest.append("NaN"); else if (bits == DOUBLE_POSITIVE_INFINITY_BITS || bits == DOUBLE_NEGATIVE_INFINITY_BITS) dest.append(val < 0?'-':'+').append("Inf"); else { // example: -1000.5432 long integral, fract=0; int exponent; boolean floating = decimalCount < 0; if (floating) decimalCount = MAX_DOUBLE_DIGITS; if (val < 0) { val = -val; // 1000.5432 dest.append('-'); } exponent = (int) (Math.log(val) / Constants.LN10); // 3 : 1000.5432 = 1.0005432*10^3 if (DOUBLE_MIN_NON_EXP <= val && val <= DOUBLE_MAX_NON_EXP) // does it fit without sci notation? { if (decimalCount == 0) val += 0.5; integral = (long) val; // 1000 exponent = 0; } else { boolean adjusted = false; // guich@tc111_5 while (true) // guich@580_23: copied from c, which was correct. { // pow10: fast pow10 double pow10 = (exponent>=0) ? Constants.p1[exponent & 31]*Constants.p32[exponent>>5] : Constants.np32[-exponent >> 5]/Constants.p1[-exponent & 31]; // guich@570_51: when exp >= 320, p32 index is 10, and 1e320 is impossible double mant = val / pow10; if (decimalCount < 18) mant += (double) Constants.rounds5[decimalCount]; integral = (long) mant; if (integral == 0 && !adjusted) { adjusted = true; exponent--; // 0.12345 ? } else if (integral >= 10 && !adjusted) { adjusted = true; exponent++; // 10.12345 ? } else { val = mant; break; } } } if (decimalCount == 0) dest.append(integral); else { int i,firstNonZero=-1; // number of zeros between . and first non-zero double pow10 = (decimalCount>=0) ? Constants.p1[decimalCount & 31]*Constants.p32[decimalCount>>5] : Constants.np32[-decimalCount >> 5]/Constants.p1[-decimalCount & 31]; // guich@570_51: when exp >= 320, p32 index is 10, and 1e320 is impossible long ipow10 = (long)pow10; double f = val - integral; // 1000.5432-1000 = 0.5432 if (f > 1.0e-16) { fract = (long)(f * pow10 + (exponent == 0 ? 0.5 : 0)); if (fract == ipow10) // case of Convert.toString(49.999,2) { fract = 0; integral++; } } dest.append(integral); do { ipow10 /= 10; firstNonZero++; } while (ipow10 > fract); String s = Long.toString(fract); i = decimalCount - s.length(); dest.append('.'); if (0 < firstNonZero && firstNonZero < decimalCount) { i -= firstNonZero; dest.append(Constants.zeros, 0, firstNonZero); } dest.append(s); if (floating) while (dest.charAt(dest.length()-2) != '.' && dest.charAt(dest.length()-1) == '0') dest.setLength(dest.length()-1); else if (i > 0) // fill with zeros if needed dest.append(Constants.zeros,0,i<20?i:20); // this should not respect the maximum allowed width, because its just for user formatting } if (exponent != 0) dest.append('E').append(exponent); } String ret = dest.toString(); int l = ret.length(); if (l > 0 && ret.charAt(0) == '-') // guich@tc200b5: check if its -0.00... and change to 0.00... { boolean only0 = true; for (int i = 1; i < l && only0; i++) { char c = ret.charAt(i); only0 &= c == '.' || c == '0'; } if (only0) ret = ret.substring(1); // remove the - } //System.out.println(" -> "+dest); return ret; } /** Converts the String to a double. * @throws InvalidNumberException If the string passed is not a valid number @since SuperWaba 2.0 */ public static double toDouble(String s) throws InvalidNumberException { if (s == null) throw new java.lang.NullPointerException("Argument 's' cannot have a null value"); try { return Double.valueOf(s).doubleValue(); } catch (NumberFormatException e) { throw new InvalidNumberException("Error: "+s+" is not a valid double value."); } } /** formats a String as a double, <b>rounding</b> with n decimal places. * @throws InvalidNumberException If the string passed is not a valid number */ public static String toString(String doubleValue, int n) throws InvalidNumberException { if (doubleValue == null) throw new java.lang.NullPointerException("Argument 'doubleValue' cannot have a null value"); if (n < -1) throw new IllegalArgumentException("Invalid value '"+n+"' for argument '"+n+"'"); // guich@tc123_9 try { return toString(toDouble(doubleValue), n); } catch (InvalidNumberException e) { throw new InvalidNumberException("Error: "+doubleValue+" is not a valid double value."); } } /** * Returns a representation of the specified floating-point value * according to the IEEE 754 floating-point "double * format" bit layout. * <p> * Bit 63 represents the sign of the floating-point number. Bits * 62-52 represent the exponent. Bits 51-0 represent * the significand (sometimes called the mantissa) of the * floating-point number. * <p> * If the argument is positive infinity, the result is * <code>0x7ff0000000000000L</code>. * <p> * If the argument is negative infinity, the result is * <code>0xfff0000000000000L</code>. * <p> * If the argument is NaN, the result is * <code>0x7ff8000000000000L</code>. * * @param value a double precision floating-point number. * @return the bits that represent the floating-point number. * @since SuperWaba 2.0 */ public static long doubleToLongBits(double value) { return Double.doubleToLongBits(value); } /** * Returns the double-float corresponding to a given bit represention. * The argument is considered to be a representation of a * floating-point value according to the IEEE 754 floating-point * "double precision" bit layout. That floating-point * value is returned as the result. * <p> * If the argument is <code>0x7f80000000000000L</code>, the result * is positive infinity. * <p> * If the argument is <code>0xff80000000000000L</code>, the result * is negative infinity. * <p> * If the argument is any value in the range * <code>0x7ff0000000000001L</code> through * <code>0x7fffffffffffffffL</code> or in the range * <code>0xfff0000000000001L</code> through * <code>0xffffffffffffffffL</code>, the result is NaN. All IEEE 754 * NaN values are, in effect, lumped together by the Java language * into a single value. * * @param bits any <code>long</code> integer. * @return the <code>double</code> floating-point value with the same * bit pattern. * @since SuperWaba 2.0 */ public static double longBitsToDouble(long bits) { return Double.longBitsToDouble(bits); } /** returns the value of the digit in the specified radix. this method is simplified to only handle the standard ascii table. @since SuperWaba 2.0 */ public static int digitOf(char ch, int radix) { if (radix < 2 || radix > 16) throw new java.lang.IllegalArgumentException("Invalid value for argument 'radix'"); int value = -1; if (2 <= radix && radix <= 16) { if ('0' <= ch && ch <= '9') value = ch - '0'; else if ('A' <= ch && ch <= 'F') value = ch - 'A' + 10; else value = ch - 'a' + 10; } return (0 <= value && value < radix) ? value : -1; } /** returns the digit in the corresponding radix. @since SuperWaba 2.0 */ public static char forDigit(int digit, int radix) { if (radix < 2 || radix > 16) throw new java.lang.IllegalArgumentException("Invalid value for argument 'radix'"); if (digit >= radix || digit < 0 || radix < 2 || radix > 16) return '?'; if (digit < 10) return (char)('0' + digit); return (char)('a' - 10 + digit); } /** Converts the long to a String at base 10. @since SuperWaba 2.0 */ public static String toString(long l) { return toString(l,10); } /** Converts the int to a String in the given radix. Radix can be 2, 8, 10 or 16. */ public static String toString(int i, int radix) { if (radix < 2 || radix > 16) throw new java.lang.IllegalArgumentException("Invalid value for argument 'radix'"); if (i == 0) return "0"; if (radix == 10) // use a faster version return toString(i); char[] buf = new char[radix >= 8 ? 12 : 33]; int pos = buf.length; boolean negative = (i < 0); if (!negative) i = -i; while (i <= -radix) { buf[--pos] = forDigit(-(i % radix), radix); i /= radix; } buf[--pos] = forDigit(-i, radix); if (negative && radix == 10) buf[--pos] = '-'; return new String(buf, pos, buf.length-pos); } /** Converts the long to a String in the given radix. Radix can be 2, 8, 10 or 16. */ public static String toString(long i, int radix) { if (radix < 2 || radix > 16) throw new java.lang.IllegalArgumentException("Invalid value for argument 'radix'"); if (i == 0) return "0"; if (radix == 10 && useNative) // toString(long) calls toString(long,radix) on JavaSE and BlackBerry but is native in TCVM return toString(i); char[] buf = new char[radix >= 8 ? 23 : 65]; int pos = buf.length; boolean negative = (i < 0); if (!negative) i = -i; while (i <= -radix) { buf[--pos] = forDigit((int)(-(i % radix)), radix); i /= radix; } buf[--pos] = forDigit((int)(-i), radix); if (negative && radix == 10) buf[--pos] = '-'; // reverse and return the string return new String(buf, pos, buf.length-pos); } public static String toString4D(long i, int radix) { if (radix < 2 || radix > 16) throw new java.lang.IllegalArgumentException("Invalid value for argument 'radix'"); if (i == 0) return "0"; if (radix == 10) // toString(long) calls toString(long,radix) on JavaSE but is native in TCVM return toString(i); char[] buf = new char[radix >= 8 ? 23 : 65]; int pos = buf.length; boolean negative = (i < 0); if (!negative) i = -i; while (i <= -radix) { buf[--pos] = forDigit((int)(-(i % radix)), radix); i /= radix; } buf[--pos] = forDigit((int)(-i), radix); if (negative && radix == 10) buf[--pos] = '-'; // reverse and return the string return new String(buf, pos, buf.length-pos); } /** Converts the String to a long. * @throws InvalidNumberException If the string passed is not a valid number @since SuperWaba 2.0 */ public static long toLong(String s) throws InvalidNumberException // guich@500_15: made this routine identical to its C equivalent. { if (s == null) throw new java.lang.NullPointerException("Argument 's' cannot have a null value"); int i; long r = 0; long m = 1; char []ac = s.toCharArray(); int len = ac.length-1; boolean isNeg = false; // juliana@116_41: added overflow and underflow check for longs. if (len >= 0 && (ac[len] == 'L' || ac[len] == 'l')) len--; for (i=len; i >= 0; i--) { char c = ac[i]; if (c == '+') break; if (c == '-') { isNeg = true; r = -r; break; } int digit = c-'0'; if (digit < 0 || c > '9') throw new InvalidNumberException("Error: "+s+" is not a valid long value."); r += m * (c-'0'); if (m == 1) m = 10; else m *= 10; } if (isNeg != (r < 0)) // check if the value was overflown or underflown throw new InvalidNumberException("Error: "+s+" is not a valid long value."); return r; } /** Converts the String to a long in the given radix. Radix can range from 2 to 16. At any error, 0 is returned. * @throws InvalidNumberException If the string passed is not a valid number @since SuperWaba 2.0 */ public static long toLong(String s, int radix) throws InvalidNumberException // guich@500_15: made this routine identical to its C equivalent. { if (s == null) throw new java.lang.NullPointerException("Argument 's' cannot have a null value"); if (radix < 2 || radix > 16) throw new java.lang.IllegalArgumentException("Invalid value for argument 'radix'"); int i; long r = 0; long m = 1; char []ac = s.toCharArray(); int len = ac.length-1; if (ac[len] == 'L' || ac[len] == 'l') len--; for (i=len; i >= 0; i--) { char c = ac[i]; if (c == '+') break; if (c == '-') {r = -r; break;} int digit = digitOf(c,radix); if (digit < 0) throw new InvalidNumberException("Error: "+s+" is not a valid long value."); r += m * digit; m *= radix; } return r; } /** Converts the given double to a String, formatting it using scientific notation. @since SuperWaba 2.0 */ public static String toString(double d) { return toString(d,-1); // guich@200b4_201: fixed exp notation } /** Converts the char to lower case letter */ public static char toLowerCase(char c) { if (c <= 64) return c; if (('A' <= c && c <= 'Z') || ('\u00c0' <= c && c <= '\u00dd' && c != 215)) // added 215 to here too return (char)(c + 32); int idx = Constants.UPPER.indexOf("\u0000"+c); // eisvogel@450_20 return (idx >= 0)?Constants.LOWER.charAt(idx+1):(c); } /** Converts the char to upper case letter */ public static char toUpperCase(char c) { if (c <= 64) return c; if (('a' <= c && c <= 'z') || ('\u00e0' <= c && c <= '\u00fd' && c != 247)) return (char)(c - 32); int idx = Constants.LOWER.indexOf("\u0000"+c); // eisvogel@450_20 return (idx >= 0)?Constants.UPPER.charAt(idx+1):c; } /** Pads the given string with the char 160 (that have the same width of a number), putting it before or after the string. * @since TotalCross 1.53 */ public static String numberPad(String s, int size) // guich@tc115_61 { if (s == null) throw new java.lang.NullPointerException("Argument 's' cannot have a null value"); int n = size-s.length(); if (n > 0) { if (n >= sp160.length()) // more zeros than we already have? sp160 = dup('\u00A0',n+1); return sp160.substring(0,n).concat(s); } return s; } /** Converts the int to String and pads it with the char 160 (that have the same width of a number) at left. * @since TotalCross 1.53 * @see #numberPad(String, int) */ public static String numberPad(int s, int size) // guich@tc115_22 { return numberPad(toString(s), size); } /** Pads the string with zeroes at left. */ public static String zeroPad(String s, int size) { if (s == null) throw new java.lang.NullPointerException("Argument 's' cannot have a null value"); int n = size-s.length(); if (n > 0) { if (n >= Constants.zerosp.length()) // more zeros than we already have? Constants.zerosp = dup('0',n+1); return Constants.zerosp.substring(0,n).concat(s); } return s; } /** Converts the int to String and pads it with zeroes at left. * @since TotalCross 1.15 * @see #zeroPad(String, int) */ public static String zeroPad(int s, int size) // guich@tc115_22 { return zeroPad(toString(s), size); } /** * Tokenize the given input string. If there's no delim chars in input, returns the input string. Two consecutive * delimeters results in an empty String, not in NULL. */ public static String[] tokenizeString(String input, char delim) // guich@450_38: now using native indexOf { if (input == null) throw new java.lang.NullPointerException("Argument 'input' cannot have a null value"); int position = 0, newPosition; int count = 1; while ((newPosition=input.indexOf(delim,position)) >= 0) { count++; position = newPosition+1; } if (count == 1) return new String[]{input}; // if no delims, return the input itself String []as = new String[count]; // reuse the last index found as[count-1] = input.substring(position,input.length()); // get the other ones position=0; int i = 0; while (--count > 0) { newPosition = input.indexOf(delim, position); as[i++] = input.substring(position, newPosition); position = newPosition+1; } return as; } private static Vector vtok = new Vector(); // used below private static IntHashtable ihttok = new IntHashtable(10); /** * Tokenize the given input string. If there's no delim chars in input, returns the input string. The delim parameter * is not a set of possible single characters: it is a whole string that is searched inside the given input.<br> * <br> * Note that this method is much slower than the one with same name, which receives a char instead of a String as the * delimiter. * * @since SuperWaba 4.02 * @author Kathrin Braunwarth */ public static String[] tokenizeString(String input, String delim) // kb@402_40 { if (input == null) throw new java.lang.NullPointerException("Argument 'input' cannot have a null value"); if (delim == null) throw new java.lang.NullPointerException("Argument 'delim' cannot have a null value"); vtok.removeAllElements(); // here we use a vector bc the indexOf(string,string) is slower int inc = delim.length(); if (inc == 0 || input.length() == 0) return new String[]{input}; // guich@566_21 int position = 0; boolean loop = true; while (loop) { int newPosition = input.indexOf(delim, position); if (newPosition == -1) { newPosition = input.length(); loop = false; } if (position != newPosition) vtok.addElement(input.substring(position, newPosition)); position = newPosition + inc; } String[] ret = (String[])vtok.toObjectArray(); vtok.removeAllElements(); // guich@tc100: clear the strings to allow gc if needed. return ret; } /** * Splits the specified string around matches of the given delimiters. The characters in the delim argument are the * delimiters for separating tokens. Delimiter characters themselves will not be treated as tokens. * * @param input * a string to be parsed. * @param delims * the delimiters. * @return the array of strings computed by splitting this string around matches of the given delimiters * @since TotalCross 1.15 */ public static String[] tokenizeString(String input, char[] delims) { if (input == null || delims == null) throw new NullPointerException(); if (delims.length == 1) // use a faster algorithm if there's a single letter return tokenizeString(input, delims[0]); int inputLen = input.length(); int start = 0; char[] inputChars = input.toCharArray(); ihttok.clear(); for (int i = delims.length; --i >= 0;) ihttok.put(delims[i], 1); vtok.removeAllElements(); for (int i = 0; i < inputLen; i++) if (ihttok.exists(inputChars[i])) { if (i - start >= 0) vtok.addElement(new String(inputChars, start, i - start)); start = i + 1; } if (start > 0 && start <= inputLen) vtok.addElement(new String(inputChars, start, inputLen - start)); String[] ret = (String[]) vtok.toObjectArray(); vtok.removeAllElements(); return ret == null ? new String[] { input } : ret; } /** This method is useful to insert line breaks into the text used in the MessageBox constructor. It receives * a String and returns the parsed String. Here is an example of use: * <code>Convert.insertLineBreak(Settings.screenWidth-10,getFontMetrics(getFont()),"This is a very long text and i dont want to waste my time parsing it to be fit in the MessageBox!");</code> * <br><br> * If you want to use another separator besides \n, just call replace('\n',separator) in the returned string. * @param maxWidth the maximum width that is permitted for each line. For a text used in the MessageBox class, use <code>Settings.screenWidth-6</code> or a lower number. * @param fm the FontMetrics of the font you will use to display the text * @param text the text to be parsed * @return the parsed text */ public static String insertLineBreak(int maxWidth, totalcross.ui.font.FontMetrics fm, String text) // guich@200b4_30 - guich@tc100: changed to use the new StringBuffer functions { StringBuffer chars = new StringBuffer(text); // guich@tc114_76: change | to \n before applying our algorithm. int last = chars.length()-1; for (int pos = 0; pos <= last; pos++) { pos = getBreakPos(fm, chars, pos, maxWidth, true); if (pos < 0 || pos > last) // not enough space to break the string or reached the end? break; if (chars.charAt(pos) != '\n') { insertAt(chars, pos, '\n'); last++; } } return chars.toString(); } private static int getLineCount(int maxWidth, totalcross.ui.font.FontMetrics fm, String text) // guich@200b4_30 - guich@tc100: changed to use the new StringBuffer functions { StringBuffer chars = new StringBuffer(text); // guich@tc114_76: change | to \n before applying our algorithm. int last = chars.length()-1; int lines = 1; for (int pos = 0; pos <= last; pos++) { pos = getBreakPos(fm, chars, pos, maxWidth, true); if (pos < 0 || pos > last) // not enough space to break the string or reached the end? break; if (chars.charAt(pos) != '\n') { insertAt(chars, pos, '\n'); last++; lines++; } } return lines; } /** This method is useful to insert line breaks into the text used in the Toast.show; it behaves like * insertLineBreak but tries to split the string in a balanced number of chars. * @see #insertLineBreak(int, FontMetrics, String) */ public static String insertLineBreakBalanced(int maxWidth, totalcross.ui.font.FontMetrics fm, String text) // guich@200b4_30 - guich@tc100: changed to use the new StringBuffer functions { // check if the string already fits if (fm.stringWidth(text) <= maxWidth) return text; int parts = 2; StringBuffer sb = new StringBuffer(text); int lt = text.length(); for (int i = 0; i < lt; i++, parts++) { int l = lt / parts; // finds the number of parts int ww = fm.sbWidth(sb,0,l); if (ww < maxWidth) // does the first string already fits in the desired max width? { while (l < lt) // checks if the number of lines equals to the number of parts; if not, skip to next word and try again { int lines = getLineCount(ww+fm.height/4, fm, text); if (lines == parts) return insertLineBreak(ww+fm.height/4, fm, text); l++; while (l < lt && sb.charAt(l) != ' ') l++; ww = fm.sbWidth(sb,0,l); } } } return insertLineBreak(maxWidth, fm, text); } /** Finds the best position to break the line, with word-wrap and respecting \n. * @since TotalCross 1.0 */ public static int getBreakPos(FontMetrics fm, StringBuffer sb, int start, int width, boolean doWordWrap) { int oldStart = start; if (fm == null) throw new java.lang.NullPointerException("Argument 'fm' cannot have a null value"); if (sb == null) throw new java.lang.NullPointerException("Argument 'sb' cannot have a null value"); if (start < 0) throw new java.lang.IllegalArgumentException("Invalid value for argument 'start': "+start); if (width < 0) throw new java.lang.IllegalArgumentException("Invalid value for argument 'width': "+width); int n = sb.length()-start; if (doWordWrap) { int lastSpace = -1; for (; n-- > 0 && width > 0; start++) { char c = sb.charAt(start); if (c == '\n') return start; if (c == ' ') lastSpace = start; width -= fm.charWidth(c); } start--; // stop at the previous letter if (((n > 0 || width < 0) || (width == 0 && sb.charAt(start) != ' ')) && lastSpace >= 0) // if the previous space is near, break at it - guich@tc114_79: also if broke at a non-letter at the end of the string start = lastSpace <= start ? lastSpace : (lastSpace+1); // the space is "included" in the previous word else if (width < 0) // beyond limit? start = Math.max(oldStart, start-1); return start+1; } else { for (; n-- > 0 && width > 0; start++) width -= fm.charWidth(sb.charAt(start)); if (width < 0) // beyond limit? start--; return start; } } /** To be used in the qsort method; the type of sort will be automatically detected. * @see #detectSortType(Object) */ public static final int SORT_AUTODETECT = -1; /** To be used in the qsort method; the Object array will be compared converting to string. */ public static final int SORT_OBJECT = 0; /** To be used in the qsort method; the Object array contain String objects (case sensitive). */ public static final int SORT_STRING = 1; /** To be used in the qsort method; the Object array contain String objects that represents an integer. */ public static final int SORT_INT = 2; /** To be used in the qsort method; the Object array contain String objects that represents a double. */ public static final int SORT_DOUBLE = 3; /** To be used in the qsort method; the Object array contain String objects that represents a Date with day, month and year. */ public static final int SORT_DATE = 4; /** To be used in the qsort method; the Object array contain Comparable objects. */ public static final int SORT_COMPARABLE = 5; /** To be used in the qsort method; the Object array contain String objects, using a case insensitive (and slower) algorithm. * This mode is never set unless you specify it. */ public static final int SORT_STRING_NOCASE = 6; /** Applies the Quick Sort algorithm to the elements of the given array. * The type of sort is automatically detected, * You may call this way: Convert.qsort(items,0,items.length-1). * You can sort subportions of the array as well. * @see #SORT_AUTODETECT */ public static void qsort(Object []items, int first, int last) // guich@220_34 { qsort(items, first, last, SORT_AUTODETECT); } /** Returns the sort type for the given item sample (which is usually the first item of the array being sorted) based on: * <ul> * <li> If item is not of String type, SORT_OBJECT is returned. The toString method will be called for each Object to convert from Object to something comparable. * <li> If item starts with "0.0" or convert to double works, returns SORT_DOUBLE. * <li> If item equals "0" or converted to int works, returns SORT_INT. * <li> If item converted to totalcross.util.Date equals the column's data itself, returns SORT_DATE. * <li> else, the sort type is SORT_STRING. * </ul> * Important: if your data are floating point ones, be sure that the first element is NOT something * that can be converted to an integer. For example, be sure it is "2.0" and not "2" (converting "2.0" * to int results in 0, because the dot is not part of an integer number. * @see #SORT_AUTODETECT * @see #SORT_OBJECT * @see #SORT_STRING * @see #SORT_INT * @see #SORT_DOUBLE * @see #SORT_DATE * @see #SORT_COMPARABLE * @see #SORT_STRING_NOCASE */ public static int detectSortType(Object item) // guich@566_1 { int sortType; // autodetect the sort type if (item instanceof Comparable) sortType = SORT_COMPARABLE; else if (!(item instanceof String)) sortType = SORT_OBJECT; else { String s = (String)item; sortType = SORT_STRING; if (s.equals("0")) sortType = SORT_INT; else try { toInt(s); sortType = SORT_INT; } catch (InvalidNumberException e) { if (s.startsWith("0.")) // guich@566_24 sortType = SORT_DOUBLE; else try { toDouble(s); sortType = SORT_DOUBLE; } catch (InvalidNumberException ee) { try { new totalcross.util.Date(s); sortType = SORT_DATE; // no exception thrown... } catch (InvalidDateException ide) {} // is this a date in the platform-defined format? } } } return sortType; } /** Applies the Quick Sort algorithm to the elements of the given array, sorting in ascending order. * If they are Strings, * the sort will be much faster because a direct cast to String is done; * otherwise, if they are not strings, the toString() method is used to return * the string that will be used for comparision. * You may call this way: * <pre> * Convert.qsort(items,0,items.length-1, SORT_AUTODETECT). * </pre> * You can sort subportions of the array as well. * @see #SORT_AUTODETECT * @see #SORT_OBJECT * @see #SORT_STRING * @see #SORT_INT * @see #SORT_DOUBLE * @see #SORT_DATE * @see #SORT_COMPARABLE * @see #SORT_STRING_NOCASE * @see #detectSortType(Object) */ public static void qsort(Object []items, int first, int last, int sortType) // guich@566_1 { qsort(items, first, last, sortType, true); } private static void qsortComparable(Object []items, int first, int last, boolean ascending) { if (first >= last) return; int low = first; int high = last; Comparable mid = (Comparable)items[(first+last) >> 1]; while (true) { if (ascending) { while (high >= low && mid.compareTo(items[low]) > 0) // guich@566_25: added "high > low" here and below - guich@568_5: changed to >= low++; while (high >= low && mid.compareTo(items[high]) < 0) high--; } else { while (high >= low && mid.compareTo(items[low]) < 0) // guich@566_25: added "high > low" here and below - guich@568_5: changed to >= low++; while (high >= low && mid.compareTo(items[high]) > 0) high--; } if (low <= high) { Object temp = items[low]; items[low++] = items[high]; items[high--] = temp; } else break; } if (first < high) qsortComparable(items,first,high,ascending); if (low < last) qsortComparable(items,low,last,ascending); } private static void qsortObject(Object []items, int first, int last, boolean ascending) { if (first >= last) return; int low = first; int high = last; String mid = items[(first+last) >> 1].toString(); while (true) { if (ascending) { while (high >= low && mid.compareTo(items[low].toString()) > 0) // guich@566_25: added "high > low" here and below - guich@568_5: changed to >= low++; while (high >= low && mid.compareTo(items[high].toString()) < 0) high--; } else { while (high >= low && mid.compareTo(items[low].toString()) < 0) // guich@566_25: added "high > low" here and below - guich@568_5: changed to >= low++; while (high >= low && mid.compareTo(items[high].toString()) > 0) high--; } if (low <= high) { Object temp = items[low]; items[low++] = items[high]; items[high--] = temp; } else break; } if (first < high) qsortObject(items,first,high,ascending); if (low < last) qsortObject(items,low,last,ascending); } private static void qsortString(Object []items, int first, int last, boolean ascending) { if (first >= last) return; int low = first; int high = last; String mid = (String)items[(first+last) >> 1]; while (true) { if (ascending) { while (high >= low && mid.compareTo((String)items[low]) > 0) low++; while (high >= low && mid.compareTo((String)items[high]) < 0) high--; } else { while (high >= low && mid.compareTo((String)items[low]) < 0) low++; while (high >= low && mid.compareTo((String)items[high]) > 0) high--; } if (low <= high) { Object temp = items[low]; items[low++] = items[high]; items[high--] = temp; } else break; } if (first < high) qsortString(items,first,high,ascending); if (low < last) qsortString(items,low,last,ascending); } private static void qsortStringNocase(Object []items, int first, int last, boolean ascending) { if (first >= last) return; int low = first; int high = last; String mid = ((String)items[(first+last) >> 1]).toLowerCase(); while (true) { if (ascending) { while (high >= low && mid.compareTo(((String)items[low]).toLowerCase()) > 0) low++; while (high >= low && mid.compareTo(((String)items[high]).toLowerCase()) < 0) high--; } else { while (high >= low && mid.compareTo(((String)items[low]).toLowerCase()) < 0) low++; while (high >= low && mid.compareTo(((String)items[high]).toLowerCase()) > 0) high--; } if (low <= high) { Object temp = items[low]; items[low++] = items[high]; items[high--] = temp; } else break; } if (first < high) qsortStringNocase(items,first,high,ascending); if (low < last) qsortStringNocase(items,low,last,ascending); } private static void qsortInt(Object []items, int first, int last, boolean ascending) throws InvalidNumberException { if (first >= last) return; int low = first; int high = last; int mid = toInt((String)items[(first+last) >> 1], Convert.MAX_INT_VALUE); while (true) { if (ascending) { while (high >= low && mid > toInt((String)items[low], Convert.MAX_INT_VALUE)) low++; while (high >= low && mid < toInt((String)items[high], Convert.MAX_INT_VALUE)) high--; } else { while (high >= low && mid < toInt((String)items[low], Convert.MAX_INT_VALUE)) low++; while (high >= low && mid > toInt((String)items[high], Convert.MAX_INT_VALUE)) high--; } if (low <= high) { Object temp = items[low]; items[low++] = items[high]; items[high--] = temp; } else break; } if (first < high) qsortInt(items,first,high,ascending); if (low < last) qsortInt(items,low,last,ascending); } private static void qsortDouble(Object []items, int first, int last, boolean ascending) throws InvalidNumberException { if (first >= last) return; int low = first; int high = last; double mid = toDouble((String)items[(first+last) >> 1], Convert.MAX_DOUBLE_VALUE); while (true) { if (ascending) { while (high >= low && mid > toDouble((String)items[low], Convert.MAX_DOUBLE_VALUE)) low++; while (high >= low && mid < toDouble((String)items[high], Convert.MAX_DOUBLE_VALUE)) high--; } else { while (high >= low && mid < toDouble((String)items[low], Convert.MAX_DOUBLE_VALUE)) low++; while (high >= low && mid > toDouble((String)items[high], Convert.MAX_DOUBLE_VALUE)) high--; } if (low <= high) { Object temp = items[low]; items[low++] = items[high]; items[high--] = temp; } else break; } if (first < high) qsortDouble(items,first,high,ascending); if (low < last) qsortDouble(items,low,last,ascending); } private static void qsortDate(Object []items, int first, int last, boolean ascending) throws InvalidDateException { if (first >= last) return; int low = first; int high = last; Date d = new Date(); byte df = Settings.dateFormat; int mid = d.set((String)items[(first+last) >> 1], df); while (true) { if (ascending) { while (high >= low && mid > d.set((String)items[low], df)) low++; while (high >= low && mid < d.set((String)items[high], df)) high--; } else { while (high >= low && mid < d.set((String)items[low], df)) low++; while (high >= low && mid > d.set((String)items[high], df)) high--; } if (low <= high) { Object temp = items[low]; items[low++] = items[high]; items[high--] = temp; } else break; } if (first < high) qsortDate(items,first,high,ascending); if (low < last) qsortDate(items,low,last,ascending); } /** Applies the Quick Sort algorithm to the elements of the given array, in ascending or descending order. * If they are Strings, * the sort will be much faster because a direct cast to String is done; * otherwise, if they are not strings, the toString() method is used to return * the string that will be used for comparision. * You may call this way: * <pre> * Convert.qsort(items,0,items.length-1, Convert.SORT_DATE, true). * </pre> * You can sort subportions of the array as well.<br> * Use the SORT_xxx to define the sort type, or SORT_AUTODETECT to * automatically detect it. * @see #SORT_AUTODETECT * @see #SORT_OBJECT * @see #SORT_STRING * @see #SORT_INT * @see #SORT_DOUBLE * @see #SORT_DATE * @see #SORT_COMPARABLE * @see #SORT_STRING_NOCASE * @see #detectSortType(Object) */ public static void qsort(Object []items, int first, int last, int sortType, boolean ascending) // guich@566_1 { if (sortType == SORT_AUTODETECT) sortType = detectSortType(items[first]); try { switch (sortType) { case SORT_OBJECT: qsortObject (items, first, last, ascending); break; case SORT_STRING: qsortString (items, first, last, ascending); break; case SORT_STRING_NOCASE: qsortStringNocase(items, first, last, ascending); break; case SORT_INT: try { qsortInt (items, first, last, ascending); } catch (InvalidNumberException ine) { qsortDouble (items, first, last, ascending); } break; case SORT_DOUBLE: qsortDouble (items, first, last, ascending); break; case SORT_DATE: qsortDate (items, first, last, ascending); break; case SORT_COMPARABLE: qsortComparable (items, first, last, ascending); break; } } catch (Exception e) { if (Settings.onJavaSE) e.printStackTrace(); } } /** * Returns a String with the conversion of the unsigned integer to hexadecimal, using the given number of places (up * to 8). This routine is 90 times faster than toString(long, int). The hex digits are in upper case. If places is * smaller than the total number of digits, then the number will be truncated, and the lower part of the number will * be returned. * * @since SuperWaba 3.0 */ public static String unsigned2hex(int b, int places) // guich@300_8 { if (places < 0 || places > 8) throw new IllegalArgumentException("Invalid value '"+places+"' for argument '"+places+"'"); // guich@tc123_9 byte []c = new byte[places]; for (int i = places-1; i >= 0; i--) { c[i] = (byte)b2h[b & 0xF]; b >>= 4; } return new String(c); } /** * Returns an int with the conversion of the unsigned hexadecimal to integer. The hex digits can be in upper case or * lower case. * * @since TotalCross 1.0 */ public static int hex2unsigned(String s) { int places; if ((places = s.length()) > 8) throw new java.lang.IllegalArgumentException("Invalid value for argument 's'"); char[] c = s.toCharArray(); int b = 0, i =0; while (--places >= 0) { b <<= 4; b |= h2b[c[i++]]; } return b; } /** * Returns an int with the conversion of the signed hexadecimal to integer. The hex digits can be in upper case or * lower case. * * @param s * the string representation of the signed hexadecimal * @return a signed integer with the converted value * @since TotalCross 1.15 */ public static int hex2signed(String s) { if (s.charAt(0) == '-') return -hex2unsigned(s.substring(1)); return hex2unsigned(s); } /** Converts the given sequence of bytes to a String. * The hexadecimal String is in upper case. * @since TotalCross 1.0 beta 4 */ public static String bytesToHexString(byte[] b) { return bytesToHexString(b, 0, b.length); } /** Converts the given sequence of bytes to a String. * The hexadecimal String is in upper case. * @since TotalCross 1.01 */ public static String bytesToHexString(byte[] b, int off, int len) { StringBuffer sb = new StringBuffer(25); for (int count = off + len, i = 0; --count >= off; i++) { sb.append(b2h[((b[i] >> 4) & 0xF)]); sb.append(b2h[(b[i] & 0xF)]); } return sb.toString(); } /** Converts the given String into an array of bytes. * Each two consecutive characters are converted into a byte. * @param s The hex string to convert to bytes * @since TotalCross 1.0 beta 4 */ public static byte[] hexStringToBytes(String s) { return hexStringToBytes(s, false); } /** Converts the given String into an array of bytes. * Each two consecutive characters are converted into a byte. * @param s The hex string to convert to bytes * @param ignoreNonHexChars Flag indicating if non hex chars should be ignored or not * @since TotalCross 1.0 beta 5 */ public static byte[] hexStringToBytes(String s, boolean ignoreNonHexChars) { char[] chars = s.toCharArray(); int count = chars.length; if (buffer.length < count) buffer = new byte[count + 32]; byte[] buf = buffer; boolean firstChar = true; int pos = 0; byte b, invalid = h2bInvalid; for (int i = 0; --count >= 0; i++) { b = h2b[chars[i]]; if (b == invalid) // non hex char { if (!ignoreNonHexChars) throw new IllegalArgumentException("The string has one or more non hex characters."); } else { if (firstChar) buf[pos] = b; else { buf[pos] = (byte)((buf[pos] << 4) | b); pos++; } firstChar = !firstChar; } } if (!firstChar) pos++; byte[] bytes = new byte[pos]; Vm.arrayCopy(buf, 0, bytes, 0, pos); return bytes; } /** Do a rol of n bits in the given i(nt). n must be < <code>bits</code>. This differs from the * shift left operator (<<) in that the bits are not lost, * they are reinserted in order at the right. * @param i The positive number to be rolled * @param n The number of bits to be rolled, where 0 <= n <= bits * @param bits The number of bits that will be considered, where 0 < bits <= 64. Bits outside the bit range are masked out. If 0 is given, 64 will be used. * In order to make it faster, the routine does not check for out-of-bounds parameters. */ public static long rol(long i, int n, int bits) // guich@330_8: moved to here to make Math compatible with JDK versions. { long mask = (1L << bits) - 1L; // peterd@450_26: fixed this routine if (mask > 0) // guich@566_22: bits=64 -> mask=0 i &= mask; i = ( (i>>>(bits-n)) | (i<<n) ); if (mask > 0) // guich@566_22 i &= mask; return i; } /** Do a ror of n bits in the given i(nt). n must be < <code>bits</code>. This differs from the * shift right operator (>>) in that the bits are not lost, * they are reinserted in order at the left. * @param i The positive number to be rolled * @param n The number of bits to be rolled, where 0 <= n <= bits * @param bits The number of bits that will be considered, where 0 < bits <= 64 Bits outside the bit range are masked out. If 0 is given, 64 will be used. * In order to make it faster, the routine does not check for out-of-bounds parameters. */ public static long ror(long i, int n, int bits) { if (bits <= 0) return 0; // guich@566_22 long mask = (1L << bits) - 1L; // peterd@450_26: fixed this routine if (mask > 0) // guich@566_22 i &= mask; i = (i>>>n) | (i<<(bits-n)); if (mask > 0) // guich@566_22 i &= mask; return i; } /** Convert a creator id or a type to an int. * @param fourChars Four characters representing a creator id. */ public static int chars2int(String fourChars) // guich@330_17 { return (fourChars.charAt(0) << 24) | (fourChars.charAt(1) << 16) | (fourChars.charAt(2) << 8) | fourChars.charAt(3); } /** Convert an int to a creator id or a type. */ public static String int2chars(int i) // brunosoares@tc100 { byte []chars = new byte[4]; chars[0] = (byte) (i >> 24); chars[1] = (byte) (i >> 16); chars[2] = (byte) (i >> 8); chars[3] = (byte) i; return new String(chars); } /** Duplicates the given char <code>count</code> times. * @since SuperWaba 5.72 * @throws IllegalArgumentException If count is below 0 */ public static String dup(char c, int count) // guich@572_17 { if (count < 0) throw new IllegalArgumentException("count can't be below 0: "+count); char buf[] = new char[count]; fill(buf, 0, count, c); return new String(buf); } /** Gets the hashcode of the string being stored by this StringBuffer. * This saves memory since there's no need to convert the StringBuffer to a String. * @since TotalCross 1.0 */ public static int hashCode(StringBuffer sb) { if (sb == null) throw new java.lang.NullPointerException("Argument 'sb' cannot have a null value"); int hash = 0, length = sb.length(), i = 0; while (--length >= 0) hash = (hash << 5) - hash + (int)sb.charAt(i++); return hash; } /** Removes the given set of chars from the String. * Example: * <pre> * String s = Convert.remove("abcdef","df"); // returns "abce"; * </pre> * @since TotalCross 3.05 */ public static String remove(String source, String chars) { if (chars.length() == 1) // use a faster method return replace(source, chars, ""); int len = source.length(); StringBuffer sb = new StringBuffer(len); char ch; for (int i = 0; i < len; i++) if (chars.indexOf(ch=source.charAt(i)) == -1) sb.append(ch); return sb.length() == len ? source : sb.toString(); } /** Replace all occurences of the <code>from</code> String by the <code>to</code> String in the given <code>source</code> String. * @since TotalCross 1.0 */ public static String replace(String source, String from, String to) { // guich@tc123_10: pre-compute the number of changes and create a single string to store the result. // The pattern is searched inside the String and replaced inside the target string int f = from.length(); int t = to.length(); if (f == 1 && t == 1) // if the user is replacing a single char, use a faster routine return source.replace(from.charAt(0), to.charAt(0)); int last = 0,tlast=0; // count how many times the string appears int count = 0; while (last >= 0) { last = source.indexOf(from,last); if (last == -1) break; count++; last += f; } if (count == 0) // if the string doesn't appear, return itself return source; int s = source.length(); char[] target = new char[s + (t-f) * count]; // create a string with the new length last = 0; while (last >= 0) { int now = source.indexOf(from, last); if (now == -1) { // copy to the end of the string if (s != last) source.getChars(last, s, target, tlast); break; } source.getChars(last, now, target, tlast); // copy from old position into the new one tlast += now - last; last = now; to.getChars(0, t, target, tlast); last += f; tlast += t; } return new String(target); } /** Returns the number of chars in a String. * @since TotalCross 1.0 */ public static int numberOf(String s, char c) { int n = 0; for (int i = s.length()-1; i >= 0; i--) if (s.charAt(i) == c) n++; return n; } /** Inserts the given char at the position in the StringBuffer. This is to keep compatibility with JDK 1.1.x in browsers, because * StringBuffer.insert was introduced in JDK 1.2 only. * @since TotalCross 1.0 */ public static void insertAt(StringBuffer sb, int pos, char c) { int n = sb.length(); sb.append(c); if (pos < n) { for (int i = n; i > pos; i--) sb.setCharAt(i, sb.charAt(i-1)); sb.setCharAt(pos, c); } } /** This method appends a number of characters into a StringBuffer. It greatly reduces the amount of * memory needed. For example, if you're building a fixed-width line with spaces, you can do: * <pre> * int dif = colWidth - value.length(); * sb.apppend(value); * Convert.append(sb, ' ', dif); // pad column with spaces * </pre> * ... where sb is the StringBuffer you're using to concatenate the strings, value is the value you're appending, * and colWidth is the current column width. * @throws ArrayIndexOutOfBoundsException If count is below 0. * @since TotalCross 1.2 */ public static void append(StringBuffer sb, char c, int count) { if (c < 0) throw new ArrayIndexOutOfBoundsException("Count must be >= 0: "+count); sb.append(dup(c, count)); } /** Returns the bytes of the given StringBuffer using the current CharacterConverter instance. * This can help save memory since it does not create the intermediate String, in the usual code: * <pre> * byte[] b = sb.toString().getBytes(); * </pre> * There's no performance gain in Blackberry nor in Java SE. * @since TotalCross 1.23 */ public static byte[] getBytes(StringBuffer sb) { String s = sb.toString(); if (charConverter instanceof UTF8CharacterConverter) try {return s.getBytes("UTF-8");} catch (UnsupportedEncodingException uee) {} return s.getBytes(); } /** Strips the left zeros of the String. Note that Convert.toInt DOES support zeros before the number, * so there's no need to call this method before converting "0011" into 11. * @since TotalCross 1.0 */ public static String zeroUnpad(String s) { int i = 0; for (int n = s.length(); i < n && s.charAt(i) == '0';) // guich@tc114_43 i++; return i == 0 ? s : s.substring(i); } /** Returns the maximum value among the given ints * @since TotalCross 1.15 */ public static int max(int[] ai) // guich@tc115_61 { int m = ai[0]; for (int i = ai.length; --i >= 1;) if (ai[i] > m) m = ai[i]; return m; } /** Returns the sum of the given ints, in the given range (startIndex <= x < endIndex). * @since TotalCross 2.22 */ public static int sum(int[] ai, int startIndex, int endIndex) // guich@tc115_61 { int m = 0; for (int i = startIndex; i < endIndex; i++) m += ai[i]; return m; } /** Returns the sum of the given ints * @since TotalCross 1.15 */ public static int sum(int[] ai) // guich@tc115_61 { int m = 0; for (int i = ai.length; --i >= 0;) m += ai[i]; return m; } /** Returns the minimum value among the given ints * @since TotalCross 1.15 */ public static int min(int[] ai) // guich@tc115_61 { int m = ai[0]; for (int i = ai.length; --i >= 1;) if (ai[i] < m) m = ai[i]; return m; } /** Pads the given string with spaces, putting it before or after the string. * If count is less than the string's length, the string is returned without being changed. * @since TotalCross 1.15 */ public static String spacePad(String what, int count, boolean before) // guich@tc115_61 { count -= what.length(); if (count <= 0) return what; if (Constants.spaces.length() < count) Constants.spaces = dup(' ',count); String sp = Constants.spaces.substring(0,count); return before ? sp.concat(what) : what.concat(sp); } /** Concatenates two strings ensuring there's a single slash between them. Strings are normalized using / as path separator * and removing any duplicate separators. You must ensure that both strings are not null neither empty. * @since TotalCross 1.0 */ public static String appendPath(String path1, String path2) { return normalizePath(path1 + "/" + path2); } /** * Normalizes a string using / as the path separator and removing any duplicate separators. * @param path The string to normalize. * @return The normalized string. * @since TotalCross 1.20 */ public static String normalizePath(String path) //bruno@tc125_9: fixed to correctly remove unnecessary consecutive slashes (directory separators). { String onlySlashes = path.replace('\\', '/'); while (onlySlashes.indexOf("//") >= 0) onlySlashes = replace(onlySlashes, "//", "/"); return onlySlashes; } /** Converts the given int array into a byte array. The int array must be in big endian format (least significant bits at last). * This helps decrease file size if you're storing in the code an array of bytes. * <b>If the byte array has 13 or more elements, using this technique will decrease the file's size.</b> * <br><br>For example, the array * <code>{(byte)0x12,(byte)0x34,(byte)0x56,(byte)0x78,(byte)0xAB,(byte)0xCD,(byte)0xEF}</code> must be stored as * <code>{0x78563412,0x00EFCDAB}</code> and you must call this method passing 7 for count. * <br><br>You can use the following method to dump to the console the convertion of your byte arrays: * <pre> * static void bytes2intsDump(byte[] in) * { * System.out.print("Convert.ints2bytes(new int[]{"); * for (int i = 0; i < in.length;) * { * int b1 = i < in.length ? in[i++] : 0; * int b2 = i < in.length ? in[i++] : 0; * int b3 = i < in.length ? in[i++] : 0; * int b4 = i < in.length ? in[i++] : 0; * int out = b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); * System.out.print("0x"+Convert.unsigned2hex(out,8)+","); * } * System.out.println("}, "+in.length+");"); * } * </pre> * For example, calling it with: * <pre> * byte[] in = {1,2,3,4,5,6,7,8,1,2,3,4,5,6,7}; * bytes2intsDump(in); * </pre> * Will output this: * <pre> * Convert.ints2bytes(new int[]{0x04030201,0x08070605,0x04030201,0x00070605}, 15); * </pre> * Note that you must not include the bytes2intsDump into the program that is going to the device. It is a desktop-only utility method. * @param from The source int array * @param count The length of the target byte array. It may differ from <code>from.length*4</code> because the last value of the array may be padded. * @since TotalCross 1.01 */ public static byte[] ints2bytes(int[] from, int count) { byte[] to = new byte[count]; for (int j=0,t=0; j < count;t >>>= 8) { if ((j & 3) == 0) t = from[j>>2]; to[j++] = (byte)(t & 0xFF); } return to; } /** Fills the given array, within the range, with the value specified. The array is filled as from <= n < to. */ public static void fill(char[] a, int from, int to, char value) { try { java.util.Arrays.fill(a, from, to, value); } catch (Throwable t) // jdk 1.x { for (; from < to; from++) a[from] = value; } } /** Fills the given array, within the range, with the value specified. The array is filled as from <= n < to. */ public static void fill(boolean[] a, int from, int to, boolean value) { try { java.util.Arrays.fill(a, from, to, value); } catch (Throwable t) // jdk 1.x { for (; from < to; from++) a[from] = value; } } /** Fills the given array, within the range, with the value specified. The array is filled as from <= n < to. */ public static void fill(int[] a, int from, int to, int value) { try { java.util.Arrays.fill(a, from, to, value); } catch (Throwable t) // jdk 1.x { for (; from < to; from++) a[from] = value; } } /** Fills the given array, within the range, with the value specified. The array is filled as from <= n < to. */ public static void fill(double[] a, int from, int to, double value) { try { java.util.Arrays.fill(a, from, to, value); } catch (Throwable t) // jdk 1.x { for (; from < to; from++) a[from] = value; } } /** Fills the given array, within the range, with the value specified. The array is filled as from <= n < to. * The int value is casted to short. */ public static void fill(short[] a, int from, int to, int value) { try { java.util.Arrays.fill(a, from, to, (short)value); } catch (Throwable t) // jdk 1.x { for (; from < to; from++) a[from] = (short)value; } } /** Fills the given array, within the range, with the value specified. The array is filled as from <= n < to. * The int value is casted to byte. */ public static void fill(byte[] a, int from, int to, int value) { try { java.util.Arrays.fill(a, from, to, (byte)value); } catch (Throwable t) // jdk 1.x { for (; from < to; from++) a[from] = (byte)value; } } /** Fills the given array, within the range, with the value specified. The array is filled as from <= n < to. */ public static void fill(long[] a, int from, int to, long value) { try { java.util.Arrays.fill(a, from, to, value); } catch (Throwable t) // jdk 1.x { for (; from < to; from++) a[from] = value; } } /** Fills the given array, within the range, with the value specified. The array is filled as from <= n < to. */ public static void fill(Object[] a, int from, int to, Object value) { try { java.util.Arrays.fill(a, from, to, value); } catch (Throwable t) // jdk 1.x { for (; from < to; from++) a[from] = value; } } /** Returns the given double formatted using the Settings decimal and thousands separators. * @param d the double to be converted to String. * @param decimalPlaces the number of decimal places. Must be >= -1. * @since TotalCross 1.01 * @see #toCurrencyString(String,int) */ public static String toCurrencyString(double d, int decimalPlaces) // guich@tc110_76 { return toCurrencyString(toString(d, decimalPlaces), decimalPlaces); } /** Returns the given double formatted using the Settings decimal and thousands separators. * @param s the double String to be converted to String. It must already have the given number of decimal places. * @param decimalPlaces the number of decimal places. Must be >= -1. * @since TotalCross 1.14 * @see #toCurrencyString(double,int) */ public static String toCurrencyString(String s, int decimalPlaces) // guich@tc110_76 { if (Settings.decimalSeparator != '.' && decimalPlaces != 0) s = s.replace('.', Settings.decimalSeparator); // insert the thousands separators boolean isNegative = s.charAt(0) == '-'; if (isNegative) //flsobral: remove the minus sign... s = s.substring(1); for (int i = s.length()-decimalPlaces-(decimalPlaces != 0 ? 4 : 3); i > 0; i -= 3) s = s.substring(0,i)+Settings.thousandsSeparator+s.substring(i); if (isNegative) //flsobral: ... and place it back afterwards. s = '-' + s; return s; } /** Tokenizes a command line arguments, breaking it into an array of strings. Useful when getting parameters from MainWindow.getCommandLine as an * unique string and changing it to a String array as in Java's main method. Supports parsing strings with quotes, but the quote itself is not returned. * Consecutive spaces outside "" are ignored. * @since TotalCross 1.2 */ public static String[] tokenizeArguments(String arg) // guich@tc120_9 { if (arg == null) return null; Vector v = new Vector(10); char[] chars = arg.toCharArray(); StringBuffer sb = new StringBuffer(50); for (int i =0; i < chars.length; i++) switch (chars[i]) { case ' ': if (sb.length() > 0) v.addElement(sb.toString()); sb.setLength(0); break; case '"': while (++i < chars.length && chars[i] != '"') sb.append(chars[i]); v.addElement(sb.toString()); sb.setLength(0); break; default: sb.append(chars[i]); } if (sb.length() > 0) v.addElement(sb.toString()); return (String[])v.toObjectArray(); } /** Returns true if the given char is in uppercase. Supports unicode. * @since TotalCross 1.13 */ public static boolean isUpperCase(char c) // guich@tc113_4 { return ('A' <= c && c <= 'Z') || c == toUpperCase(c); } /** Returns true if the given char is in lowercase. Supports unicode. * @since TotalCross 1.13 */ public static boolean isLowerCase(char c) // guich@tc113_4 { return ('a' <= c && c <= 'z') || c == toLowerCase(c); } /** Computes the distance between a point (x,y) * and a rectangle (corners x1,y1 and x2,y2). * Note that, if the point is inside the rect, 0 is returned. * @since TotalCross 1.2 */ public static double getDistancePoint2Rect(int x, int y, int x1, int y1, int x2, int y2) { int xMx1 = x - x1; int xMx2 = x - x2; int yMy1 = y - y1; int yMy2 = y - y2; if (y < y1) { if (x < x1) return Math.sqrt(xMx1 * xMx1 + yMy1 * yMy1); // distance from point (x,y) to point (x1,y1) else if (x < x2) return y1 - y; // distance from point (x,y) to line (x1,y1)->(x2,y1) else return Math.sqrt(xMx2 * xMx2 + yMy1 * yMy1); // distance from point (x,y) to point (x2,y1) } else if (y < y2) { if (x < x1) return x1 - x; // distance from point (x,y) to line (x1,y1)->(x1,y2) else if (x < x2) return 0; else return x - x2; // distance from point (x,y) to line (x2,y1)->(x2,y2) } else { if (x < x1) return Math.sqrt(xMx1 * xMx1 + yMy2 * yMy2); // distance from point (x,y) to point (x1,y2) else if (x < x2) return y - y2; // distance from point (x,y) to line (x1,y2)->(x2,y2) else return Math.sqrt(xMx2 * xMx2 + yMy2 * yMy2); // distance from point (x,y) to point (x2,y2) } } /** Appends the given timestamp to a StringBuffer, using the current Date format and separators. * @since TotalCross 1.24 */ public static StringBuffer appendTimeStamp(StringBuffer sb, Time t, boolean appendDate, boolean appendTime) // guich@tc123_62 { if (appendDate) { int i1,i2,i3; if (Settings.dateFormat == Settings.DATE_DMY) { i1 = t.day; i2 = t.month; i3 = t.year; } else if (Settings.dateFormat == Settings.DATE_MDY) { i1 = t.month; i2 = t.day; i3 = t.year; } else { i1 = t.year; i2 = t.month; i3 = t.day; } if (i1 < 10) sb.append("0"); sb.append(i1).append(Settings.dateSeparator); if (i2 < 10) sb.append("0"); sb.append(i2).append(Settings.dateSeparator); if (i3 < 10) sb.append("0"); sb.append(i3); } if (appendDate && appendTime) sb.append(" "); t.dump(sb, Settings.timeSeparator == ':' ? ":" : String.valueOf(Settings.timeSeparator), true); return sb; } /** Given a full path name, returns the normalized path of the file, without the slash. If the file contains * only a name, without slashes, returns null. * @since TotalCross 1.27 */ public static String getFilePath(String fullPath) // guich@tc126_6 { fullPath = normalizePath(fullPath); int idx = fullPath.lastIndexOf('/'); return idx == -1 ? null : fullPath.substring(0,idx); } /** Given a full path name, returns the name of the file. If the file contains * only a name, without slashes, returns the fullPath. * @since TotalCross 1.27 */ public static String getFileName(String fullPath) // guich@tc126_6 { fullPath = normalizePath(fullPath); int idx = fullPath.lastIndexOf('/'); return idx == -1 ? fullPath : fullPath.substring(idx+1); } /** Given a full path name or a single file name, returns the name of the file without the extension. * @since TotalCross 1.27 */ public static String getFileNameOnly(String fullPath) // guich@tc126_6 { String name = getFileName(fullPath); int idx = name.lastIndexOf('.'); return idx == -1 ? name : name.substring(0,idx); } /////// double routines - same of vm ////////////////////////////////// /* Tester - currently 204 diffs int dif=0; for (int i =-324; i <= 308; i++) { double d = pow10(i); double e = Math.pow(10,i); if (d != e) { dif++; System.out.println(i+" : "+d+ " != "+e+" - "+Long.toHexString(Convert.doubleToLongBits(d))+" != "+Long.toHexString(Convert.doubleToLongBits(e))); } } System.out.println(dif); */ private static String sp160 = "\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0"; // guich@tc100: optimized the zeroPad method public static final long DOUBLE_POSITIVE_INFINITY_BITS = 0x7ff0000000000000L; public static final long DOUBLE_NEGATIVE_INFINITY_BITS = 0xfff0000000000000L; public static final long DOUBLE_NAN_BITS = 0x7ff8000000000000L; public static final double DOUBLE_POSITIVE_INFINITY_VALUE = longBitsToDouble(DOUBLE_POSITIVE_INFINITY_BITS); public static final double DOUBLE_NEGATIVE_INFINITY_VALUE = longBitsToDouble(DOUBLE_NEGATIVE_INFINITY_BITS); public static final double DOUBLE_NAN_VALUE = longBitsToDouble(DOUBLE_NAN_BITS); static class Constants // get rid of symbols not used in device. { private static String spaces = ""; private static String zerosp = "0000000000"; // guich@tc100: optimized the zeroPad method private final static String LOWER = "\u0000\u0131\u0000\u03BC\u0000\u0101\u0000\u0103\u0000\u0105\u0000\u0107\u0000\u0109\u0000\u010B\u0000\u010D\u0000\u010F\u0000\u0111\u0000\u0113\u0000\u0115\u0000\u0117\u0000\u0119\u0000\u011B\u0000\u011D\u0000\u011F\u0000\u0121\u0000\u0123\u0000\u0125\u0000\u0127\u0000\u0129\u0000\u012B\u0000\u012D\u0000\u012F\u0000\u0069\u0000\u0133\u0000\u0135\u0000\u0137\u0000\u013A\u0000\u013C\u0000\u013E\u0000\u0140\u0000\u0142\u0000\u0144\u0000\u0146\u0000\u0148\u0000\u014B\u0000\u014D\u0000\u014F\u0000\u0151\u0000\u0153\u0000\u0155\u0000\u0157\u0000\u0159\u0000\u015B\u0000\u015D\u0000\u015F\u0000\u0161\u0000\u0163\u0000\u0165\u0000\u0167\u0000\u0169\u0000\u016B\u0000\u016D\u0000\u016F\u0000\u0171\u0000\u0173\u0000\u0175\u0000\u0177\u0000\u00FF\u0000\u017A\u0000\u017C\u0000\u017E\u0000\u0073\u0000\u0253\u0000\u0183\u0000\u0185\u0000\u0254\u0000\u0188\u0000\u0256\u0000\u0257\u0000\u018C\u0000\u01DD\u0000\u0259\u0000\u025B\u0000\u0192\u0000\u0260\u0000\u0263\u0000\u0269\u0000\u0268\u0000\u0199\u0000\u026F\u0000\u0272\u0000\u0275\u0000\u01A1\u0000\u01A3\u0000\u01A5\u0000\u0280\u0000\u01A8\u0000\u0283\u0000\u01AD\u0000\u0288\u0000\u01B0\u0000\u028A\u0000\u028B\u0000\u01B4\u0000\u01B6\u0000\u0292\u0000\u01B9\u0000\u01BD\u0000\u01C6\u0000\u01C6\u0000\u01C9\u0000\u01C9\u0000\u01CC\u0000\u01CC\u0000\u01CE\u0000\u01D0\u0000\u01D2\u0000\u01D4\u0000\u01D6\u0000\u01D8\u0000\u01DA\u0000\u01DC\u0000\u01DF\u0000\u01E1\u0000\u01E3\u0000\u01E5\u0000\u01E7\u0000\u01E9\u0000\u01EB\u0000\u01ED\u0000\u01EF\u0000\u01F3\u0000\u01F3\u0000\u01F5\u0000\u0195\u0000\u01BF\u0000\u01F9\u0000\u01FB\u0000\u01FD\u0000\u01FF\u0000\u0201\u0000\u0203\u0000\u0205\u0000\u0207\u0000\u0209\u0000\u020B\u0000\u020D\u0000\u020F\u0000\u0211\u0000\u0213\u0000\u0215\u0000\u0217\u0000\u0219\u0000\u021B\u0000\u021D\u0000\u021F\u0000\u019E\u0000\u0223\u0000\u0225\u0000\u0227\u0000\u0229\u0000\u022B\u0000\u022D\u0000\u022F\u0000\u0231\u0000\u0233\u0000\u03B9\u0000\u03AC\u0000\u03AD\u0000\u03AE\u0000\u03AF\u0000\u03CC\u0000\u03CD\u0000\u03CE\u0000\u03B1\u0000\u03B2\u0000\u03B3\u0000\u03B4\u0000\u03B5\u0000\u03B6\u0000\u03B7\u0000\u03B8\u0000\u03B9\u0000\u03BA\u0000\u03BB\u0000\u03BC\u0000\u03BD\u0000\u03BE\u0000\u03BF\u0000\u03C0\u0000\u03C1\u0000\u03C3\u0000\u03C4\u0000\u03C5\u0000\u03C6\u0000\u03C7\u0000\u03C8\u0000\u03C9\u0000\u03CA\u0000\u03CB\u0000\u03C3\u0000\u03B2\u0000\u03B8\u0000\u03C6\u0000\u03C0\u0000\u03D9\u0000\u03DB\u0000\u03DD\u0000\u03DF\u0000\u03E1\u0000\u03E3\u0000\u03E5\u0000\u03E7\u0000\u03E9\u0000\u03EB\u0000\u03ED\u0000\u03EF\u0000\u03BA\u0000\u03C1\u0000\u03B8\u0000\u03B5\u0000\u03F8\u0000\u03F2\u0000\u03FB\u0000\u0450\u0000\u0451\u0000\u0452\u0000\u0453\u0000\u0454\u0000\u0455\u0000\u0456\u0000\u0457\u0000\u0458\u0000\u0459\u0000\u045A\u0000\u045B\u0000\u045C\u0000\u045D\u0000\u045E\u0000\u045F\u0000\u0430\u0000\u0431\u0000\u0432\u0000\u0433\u0000\u0434\u0000\u0435\u0000\u0436\u0000\u0437\u0000\u0438\u0000\u0439\u0000\u043A\u0000\u043B\u0000\u043C\u0000\u043D\u0000\u043E\u0000\u043F\u0000\u0440\u0000\u0441\u0000\u0442\u0000\u0443\u0000\u0444\u0000\u0445\u0000\u0446\u0000\u0447\u0000\u0448\u0000\u0449\u0000\u044A\u0000\u044B\u0000\u044C\u0000\u044D\u0000\u044E\u0000\u044F\u0000\u0461\u0000\u0463\u0000\u0465\u0000\u0467\u0000\u0469\u0000\u046B\u0000\u046D\u0000\u046F\u0000\u0471\u0000\u0473\u0000\u0475\u0000\u0477\u0000\u0479\u0000\u047B\u0000\u047D\u0000\u047F\u0000\u0481\u0000\u048B\u0000\u048D\u0000\u048F\u0000\u0491\u0000\u0493\u0000\u0495\u0000\u0497\u0000\u0499\u0000\u049B\u0000\u049D\u0000\u049F\u0000\u04A1\u0000\u04A3\u0000\u04A5\u0000\u04A7\u0000\u04A9\u0000\u04AB\u0000\u04AD\u0000\u04AF\u0000\u04B1\u0000\u04B3\u0000\u04B5\u0000\u04B7\u0000\u04B9\u0000\u04BB\u0000\u04BD\u0000\u04BF\u0000\u04C2\u0000\u04C4\u0000\u04C6\u0000\u04C8\u0000\u04CA\u0000\u04CC\u0000\u04CE\u0000\u04D1\u0000\u04D3\u0000\u04D5\u0000\u04D7\u0000\u04D9\u0000\u04DB\u0000\u04DD\u0000\u04DF\u0000\u04E1\u0000\u04E3\u0000\u04E5\u0000\u04E7\u0000\u04E9\u0000\u04EB\u0000\u04ED\u0000\u04EF\u0000\u04F1\u0000\u04F3\u0000\u04F5\u0000\u04F9\u0000\u0501\u0000\u0503\u0000\u0505\u0000\u0507\u0000\u0509\u0000\u050B\u0000\u050D\u0000\u050F\u0000\u0561\u0000\u0562\u0000\u0563\u0000\u0564\u0000\u0565\u0000\u0566\u0000\u0567\u0000\u0568\u0000\u0569\u0000\u056A\u0000\u056B\u0000\u056C\u0000\u056D\u0000\u056E\u0000\u056F\u0000\u0570\u0000\u0571\u0000\u0572\u0000\u0573\u0000\u0574\u0000\u0575\u0000\u0576\u0000\u0577\u0000\u0578\u0000\u0579\u0000\u057A\u0000\u057B\u0000\u057C\u0000\u057D\u0000\u057E\u0000\u057F\u0000\u0580\u0000\u0581\u0000\u0582\u0000\u0583\u0000\u0584\u0000\u0585\u0000\u0586\u0000\u1E01\u0000\u1E03\u0000\u1E05\u0000\u1E07\u0000\u1E09\u0000\u1E0B\u0000\u1E0D\u0000\u1E0F\u0000\u1E11\u0000\u1E13\u0000\u1E15\u0000\u1E17\u0000\u1E19\u0000\u1E1B\u0000\u1E1D\u0000\u1E1F\u0000\u1E21\u0000\u1E23\u0000\u1E25\u0000\u1E27\u0000\u1E29\u0000\u1E2B\u0000\u1E2D\u0000\u1E2F\u0000\u1E31\u0000\u1E33\u0000\u1E35\u0000\u1E37\u0000\u1E39\u0000\u1E3B\u0000\u1E3D\u0000\u1E3F\u0000\u1E41\u0000\u1E43\u0000\u1E45\u0000\u1E47\u0000\u1E49\u0000\u1E4B\u0000\u1E4D\u0000\u1E4F\u0000\u1E51\u0000\u1E53\u0000\u1E55\u0000\u1E57\u0000\u1E59\u0000\u1E5B\u0000\u1E5D\u0000\u1E5F\u0000\u1E61\u0000\u1E63\u0000\u1E65\u0000\u1E67\u0000\u1E69\u0000\u1E6B\u0000\u1E6D\u0000\u1E6F\u0000\u1E71\u0000\u1E73\u0000\u1E75\u0000\u1E77\u0000\u1E79\u0000\u1E7B\u0000\u1E7D\u0000\u1E7F\u0000\u1E81\u0000\u1E83\u0000\u1E85\u0000\u1E87\u0000\u1E89\u0000\u1E8B\u0000\u1E8D\u0000\u1E8F\u0000\u1E91\u0000\u1E93\u0000\u1E95\u0000\u1E61\u0000\u1EA1\u0000\u1EA3\u0000\u1EA5\u0000\u1EA7\u0000\u1EA9\u0000\u1EAB\u0000\u1EAD\u0000\u1EAF\u0000\u1EB1\u0000\u1EB3\u0000\u1EB5\u0000\u1EB7\u0000\u1EB9\u0000\u1EBB\u0000\u1EBD\u0000\u1EBF\u0000\u1EC1\u0000\u1EC3\u0000\u1EC5\u0000\u1EC7\u0000\u1EC9\u0000\u1ECB\u0000\u1ECD\u0000\u1ECF\u0000\u1ED1\u0000\u1ED3\u0000\u1ED5\u0000\u1ED7\u0000\u1ED9\u0000\u1EDB\u0000\u1EDD\u0000\u1EDF\u0000\u1EE1\u0000\u1EE3\u0000\u1EE5\u0000\u1EE7\u0000\u1EE9\u0000\u1EEB\u0000\u1EED\u0000\u1EEF\u0000\u1EF1\u0000\u1EF3\u0000\u1EF5\u0000\u1EF7\u0000\u1EF9\u0000\u1F00\u0000\u1F01\u0000\u1F02\u0000\u1F03\u0000\u1F04\u0000\u1F05\u0000\u1F06\u0000\u1F07\u0000\u1F10\u0000\u1F11\u0000\u1F12\u0000\u1F13\u0000\u1F14\u0000\u1F15\u0000\u1F20\u0000\u1F21\u0000\u1F22\u0000\u1F23\u0000\u1F24\u0000\u1F25\u0000\u1F26\u0000\u1F27\u0000\u1F30\u0000\u1F31\u0000\u1F32\u0000\u1F33\u0000\u1F34\u0000\u1F35\u0000\u1F36\u0000\u1F37\u0000\u1F40\u0000\u1F41\u0000\u1F42\u0000\u1F43\u0000\u1F44\u0000\u1F45\u0000\u1F51\u0000\u1F53\u0000\u1F55\u0000\u1F57\u0000\u1F60\u0000\u1F61\u0000\u1F62\u0000\u1F63\u0000\u1F64\u0000\u1F65\u0000\u1F66\u0000\u1F67\u0000\u1F80\u0000\u1F81\u0000\u1F82\u0000\u1F83\u0000\u1F84\u0000\u1F85\u0000\u1F86\u0000\u1F87\u0000\u1F90\u0000\u1F91\u0000\u1F92\u0000\u1F93\u0000\u1F94\u0000\u1F95\u0000\u1F96\u0000\u1F97\u0000\u1FA0\u0000\u1FA1\u0000\u1FA2\u0000\u1FA3\u0000\u1FA4\u0000\u1FA5\u0000\u1FA6\u0000\u1FA7\u0000\u1FB0\u0000\u1FB1\u0000\u1F70\u0000\u1F71\u0000\u1FB3\u0000\u03B9\u0000\u1F72\u0000\u1F73\u0000\u1F74\u0000\u1F75\u0000\u1FC3\u0000\u1FD0\u0000\u1FD1\u0000\u1F76\u0000\u1F77\u0000\u1FE0\u0000\u1FE1\u0000\u1F7A\u0000\u1F7B\u0000\u1FE5\u0000\u1F78\u0000\u1F79\u0000\u1F7C\u0000\u1F7D\u0000\u1FF3\u0000\u03C9\u0000\u006B\u0000\u00E5\u0000\u2170\u0000\u2171\u0000\u2172\u0000\u2173\u0000\u2174\u0000\u2175\u0000\u2176\u0000\u2177\u0000\u2178\u0000\u2179\u0000\u217A\u0000\u217B\u0000\u217C\u0000\u217D\u0000\u217E\u0000\u217F\u0000\u24D0\u0000\u24D1\u0000\u24D2\u0000\u24D3\u0000\u24D4\u0000\u24D5\u0000\u24D6\u0000\u24D7\u0000\u24D8\u0000\u24D9\u0000\u24DA\u0000\u24DB\u0000\u24DC\u0000\u24DD\u0000\u24DE\u0000\u24DF\u0000\u24E0\u0000\u24E1\u0000\u24E2\u0000\u24E3\u0000\u24E4\u0000\u24E5\u0000\u24E6\u0000\u24E7\u0000\u24E8\u0000\u24E9\u0000\uFF41\u0000\uFF42\u0000\uFF43\u0000\uFF44\u0000\uFF45\u0000\uFF46\u0000\uFF47\u0000\uFF48\u0000\uFF49\u0000\uFF4A\u0000\uFF4B\u0000\uFF4C\u0000\uFF4D\u0000\uFF4E\u0000\uFF4F\u0000\uFF50\u0000\uFF51\u0000\uFF52\u0000\uFF53\u0000\uFF54\u0000\uFF55\u0000\uFF56\u0000\uFF57\u0000\uFF58\u0000\uFF59\u0000\uFF5A"; private final static String UPPER = "\u0000\u0049\u0000\u00B5\u0000\u0100\u0000\u0102\u0000\u0104\u0000\u0106\u0000\u0108\u0000\u010A\u0000\u010C\u0000\u010E\u0000\u0110\u0000\u0112\u0000\u0114\u0000\u0116\u0000\u0118\u0000\u011A\u0000\u011C\u0000\u011E\u0000\u0120\u0000\u0122\u0000\u0124\u0000\u0126\u0000\u0128\u0000\u012A\u0000\u012C\u0000\u012E\u0000\u0130\u0000\u0132\u0000\u0134\u0000\u0136\u0000\u0139\u0000\u013B\u0000\u013D\u0000\u013F\u0000\u0141\u0000\u0143\u0000\u0145\u0000\u0147\u0000\u014A\u0000\u014C\u0000\u014E\u0000\u0150\u0000\u0152\u0000\u0154\u0000\u0156\u0000\u0158\u0000\u015A\u0000\u015C\u0000\u015E\u0000\u0160\u0000\u0162\u0000\u0164\u0000\u0166\u0000\u0168\u0000\u016A\u0000\u016C\u0000\u016E\u0000\u0170\u0000\u0172\u0000\u0174\u0000\u0176\u0000\u0178\u0000\u0179\u0000\u017B\u0000\u017D\u0000\u017F\u0000\u0181\u0000\u0182\u0000\u0184\u0000\u0186\u0000\u0187\u0000\u0189\u0000\u018A\u0000\u018B\u0000\u018E\u0000\u018F\u0000\u0190\u0000\u0191\u0000\u0193\u0000\u0194\u0000\u0196\u0000\u0197\u0000\u0198\u0000\u019C\u0000\u019D\u0000\u019F\u0000\u01A0\u0000\u01A2\u0000\u01A4\u0000\u01A6\u0000\u01A7\u0000\u01A9\u0000\u01AC\u0000\u01AE\u0000\u01AF\u0000\u01B1\u0000\u01B2\u0000\u01B3\u0000\u01B5\u0000\u01B7\u0000\u01B8\u0000\u01BC\u0000\u01C4\u0000\u01C5\u0000\u01C7\u0000\u01C8\u0000\u01CA\u0000\u01CB\u0000\u01CD\u0000\u01CF\u0000\u01D1\u0000\u01D3\u0000\u01D5\u0000\u01D7\u0000\u01D9\u0000\u01DB\u0000\u01DE\u0000\u01E0\u0000\u01E2\u0000\u01E4\u0000\u01E6\u0000\u01E8\u0000\u01EA\u0000\u01EC\u0000\u01EE\u0000\u01F1\u0000\u01F2\u0000\u01F4\u0000\u01F6\u0000\u01F7\u0000\u01F8\u0000\u01FA\u0000\u01FC\u0000\u01FE\u0000\u0200\u0000\u0202\u0000\u0204\u0000\u0206\u0000\u0208\u0000\u020A\u0000\u020C\u0000\u020E\u0000\u0210\u0000\u0212\u0000\u0214\u0000\u0216\u0000\u0218\u0000\u021A\u0000\u021C\u0000\u021E\u0000\u0220\u0000\u0222\u0000\u0224\u0000\u0226\u0000\u0228\u0000\u022A\u0000\u022C\u0000\u022E\u0000\u0230\u0000\u0232\u0000\u0345\u0000\u0386\u0000\u0388\u0000\u0389\u0000\u038A\u0000\u038C\u0000\u038E\u0000\u038F\u0000\u0391\u0000\u0392\u0000\u0393\u0000\u0394\u0000\u0395\u0000\u0396\u0000\u0397\u0000\u0398\u0000\u0399\u0000\u039A\u0000\u039B\u0000\u039C\u0000\u039D\u0000\u039E\u0000\u039F\u0000\u03A0\u0000\u03A1\u0000\u03A3\u0000\u03A4\u0000\u03A5\u0000\u03A6\u0000\u03A7\u0000\u03A8\u0000\u03A9\u0000\u03AA\u0000\u03AB\u0000\u03C2\u0000\u03D0\u0000\u03D1\u0000\u03D5\u0000\u03D6\u0000\u03D8\u0000\u03DA\u0000\u03DC\u0000\u03DE\u0000\u03E0\u0000\u03E2\u0000\u03E4\u0000\u03E6\u0000\u03E8\u0000\u03EA\u0000\u03EC\u0000\u03EE\u0000\u03F0\u0000\u03F1\u0000\u03F4\u0000\u03F5\u0000\u03F7\u0000\u03F9\u0000\u03FA\u0000\u0400\u0000\u0401\u0000\u0402\u0000\u0403\u0000\u0404\u0000\u0405\u0000\u0406\u0000\u0407\u0000\u0408\u0000\u0409\u0000\u040A\u0000\u040B\u0000\u040C\u0000\u040D\u0000\u040E\u0000\u040F\u0000\u0410\u0000\u0411\u0000\u0412\u0000\u0413\u0000\u0414\u0000\u0415\u0000\u0416\u0000\u0417\u0000\u0418\u0000\u0419\u0000\u041A\u0000\u041B\u0000\u041C\u0000\u041D\u0000\u041E\u0000\u041F\u0000\u0420\u0000\u0421\u0000\u0422\u0000\u0423\u0000\u0424\u0000\u0425\u0000\u0426\u0000\u0427\u0000\u0428\u0000\u0429\u0000\u042A\u0000\u042B\u0000\u042C\u0000\u042D\u0000\u042E\u0000\u042F\u0000\u0460\u0000\u0462\u0000\u0464\u0000\u0466\u0000\u0468\u0000\u046A\u0000\u046C\u0000\u046E\u0000\u0470\u0000\u0472\u0000\u0474\u0000\u0476\u0000\u0478\u0000\u047A\u0000\u047C\u0000\u047E\u0000\u0480\u0000\u048A\u0000\u048C\u0000\u048E\u0000\u0490\u0000\u0492\u0000\u0494\u0000\u0496\u0000\u0498\u0000\u049A\u0000\u049C\u0000\u049E\u0000\u04A0\u0000\u04A2\u0000\u04A4\u0000\u04A6\u0000\u04A8\u0000\u04AA\u0000\u04AC\u0000\u04AE\u0000\u04B0\u0000\u04B2\u0000\u04B4\u0000\u04B6\u0000\u04B8\u0000\u04BA\u0000\u04BC\u0000\u04BE\u0000\u04C1\u0000\u04C3\u0000\u04C5\u0000\u04C7\u0000\u04C9\u0000\u04CB\u0000\u04CD\u0000\u04D0\u0000\u04D2\u0000\u04D4\u0000\u04D6\u0000\u04D8\u0000\u04DA\u0000\u04DC\u0000\u04DE\u0000\u04E0\u0000\u04E2\u0000\u04E4\u0000\u04E6\u0000\u04E8\u0000\u04EA\u0000\u04EC\u0000\u04EE\u0000\u04F0\u0000\u04F2\u0000\u04F4\u0000\u04F8\u0000\u0500\u0000\u0502\u0000\u0504\u0000\u0506\u0000\u0508\u0000\u050A\u0000\u050C\u0000\u050E\u0000\u0531\u0000\u0532\u0000\u0533\u0000\u0534\u0000\u0535\u0000\u0536\u0000\u0537\u0000\u0538\u0000\u0539\u0000\u053A\u0000\u053B\u0000\u053C\u0000\u053D\u0000\u053E\u0000\u053F\u0000\u0540\u0000\u0541\u0000\u0542\u0000\u0543\u0000\u0544\u0000\u0545\u0000\u0546\u0000\u0547\u0000\u0548\u0000\u0549\u0000\u054A\u0000\u054B\u0000\u054C\u0000\u054D\u0000\u054E\u0000\u054F\u0000\u0550\u0000\u0551\u0000\u0552\u0000\u0553\u0000\u0554\u0000\u0555\u0000\u0556\u0000\u1E00\u0000\u1E02\u0000\u1E04\u0000\u1E06\u0000\u1E08\u0000\u1E0A\u0000\u1E0C\u0000\u1E0E\u0000\u1E10\u0000\u1E12\u0000\u1E14\u0000\u1E16\u0000\u1E18\u0000\u1E1A\u0000\u1E1C\u0000\u1E1E\u0000\u1E20\u0000\u1E22\u0000\u1E24\u0000\u1E26\u0000\u1E28\u0000\u1E2A\u0000\u1E2C\u0000\u1E2E\u0000\u1E30\u0000\u1E32\u0000\u1E34\u0000\u1E36\u0000\u1E38\u0000\u1E3A\u0000\u1E3C\u0000\u1E3E\u0000\u1E40\u0000\u1E42\u0000\u1E44\u0000\u1E46\u0000\u1E48\u0000\u1E4A\u0000\u1E4C\u0000\u1E4E\u0000\u1E50\u0000\u1E52\u0000\u1E54\u0000\u1E56\u0000\u1E58\u0000\u1E5A\u0000\u1E5C\u0000\u1E5E\u0000\u1E60\u0000\u1E62\u0000\u1E64\u0000\u1E66\u0000\u1E68\u0000\u1E6A\u0000\u1E6C\u0000\u1E6E\u0000\u1E70\u0000\u1E72\u0000\u1E74\u0000\u1E76\u0000\u1E78\u0000\u1E7A\u0000\u1E7C\u0000\u1E7E\u0000\u1E80\u0000\u1E82\u0000\u1E84\u0000\u1E86\u0000\u1E88\u0000\u1E8A\u0000\u1E8C\u0000\u1E8E\u0000\u1E90\u0000\u1E92\u0000\u1E94\u0000\u1E9B\u0000\u1EA0\u0000\u1EA2\u0000\u1EA4\u0000\u1EA6\u0000\u1EA8\u0000\u1EAA\u0000\u1EAC\u0000\u1EAE\u0000\u1EB0\u0000\u1EB2\u0000\u1EB4\u0000\u1EB6\u0000\u1EB8\u0000\u1EBA\u0000\u1EBC\u0000\u1EBE\u0000\u1EC0\u0000\u1EC2\u0000\u1EC4\u0000\u1EC6\u0000\u1EC8\u0000\u1ECA\u0000\u1ECC\u0000\u1ECE\u0000\u1ED0\u0000\u1ED2\u0000\u1ED4\u0000\u1ED6\u0000\u1ED8\u0000\u1EDA\u0000\u1EDC\u0000\u1EDE\u0000\u1EE0\u0000\u1EE2\u0000\u1EE4\u0000\u1EE6\u0000\u1EE8\u0000\u1EEA\u0000\u1EEC\u0000\u1EEE\u0000\u1EF0\u0000\u1EF2\u0000\u1EF4\u0000\u1EF6\u0000\u1EF8\u0000\u1F08\u0000\u1F09\u0000\u1F0A\u0000\u1F0B\u0000\u1F0C\u0000\u1F0D\u0000\u1F0E\u0000\u1F0F\u0000\u1F18\u0000\u1F19\u0000\u1F1A\u0000\u1F1B\u0000\u1F1C\u0000\u1F1D\u0000\u1F28\u0000\u1F29\u0000\u1F2A\u0000\u1F2B\u0000\u1F2C\u0000\u1F2D\u0000\u1F2E\u0000\u1F2F\u0000\u1F38\u0000\u1F39\u0000\u1F3A\u0000\u1F3B\u0000\u1F3C\u0000\u1F3D\u0000\u1F3E\u0000\u1F3F\u0000\u1F48\u0000\u1F49\u0000\u1F4A\u0000\u1F4B\u0000\u1F4C\u0000\u1F4D\u0000\u1F59\u0000\u1F5B\u0000\u1F5D\u0000\u1F5F\u0000\u1F68\u0000\u1F69\u0000\u1F6A\u0000\u1F6B\u0000\u1F6C\u0000\u1F6D\u0000\u1F6E\u0000\u1F6F\u0000\u1F88\u0000\u1F89\u0000\u1F8A\u0000\u1F8B\u0000\u1F8C\u0000\u1F8D\u0000\u1F8E\u0000\u1F8F\u0000\u1F98\u0000\u1F99\u0000\u1F9A\u0000\u1F9B\u0000\u1F9C\u0000\u1F9D\u0000\u1F9E\u0000\u1F9F\u0000\u1FA8\u0000\u1FA9\u0000\u1FAA\u0000\u1FAB\u0000\u1FAC\u0000\u1FAD\u0000\u1FAE\u0000\u1FAF\u0000\u1FB8\u0000\u1FB9\u0000\u1FBA\u0000\u1FBB\u0000\u1FBC\u0000\u1FBE\u0000\u1FC8\u0000\u1FC9\u0000\u1FCA\u0000\u1FCB\u0000\u1FCC\u0000\u1FD8\u0000\u1FD9\u0000\u1FDA\u0000\u1FDB\u0000\u1FE8\u0000\u1FE9\u0000\u1FEA\u0000\u1FEB\u0000\u1FEC\u0000\u1FF8\u0000\u1FF9\u0000\u1FFA\u0000\u1FFB\u0000\u1FFC\u0000\u2126\u0000\u212A\u0000\u212B\u0000\u2160\u0000\u2161\u0000\u2162\u0000\u2163\u0000\u2164\u0000\u2165\u0000\u2166\u0000\u2167\u0000\u2168\u0000\u2169\u0000\u216A\u0000\u216B\u0000\u216C\u0000\u216D\u0000\u216E\u0000\u216F\u0000\u24B6\u0000\u24B7\u0000\u24B8\u0000\u24B9\u0000\u24BA\u0000\u24BB\u0000\u24BC\u0000\u24BD\u0000\u24BE\u0000\u24BF\u0000\u24C0\u0000\u24C1\u0000\u24C2\u0000\u24C3\u0000\u24C4\u0000\u24C5\u0000\u24C6\u0000\u24C7\u0000\u24C8\u0000\u24C9\u0000\u24CA\u0000\u24CB\u0000\u24CC\u0000\u24CD\u0000\u24CE\u0000\u24CF\u0000\uFF21\u0000\uFF22\u0000\uFF23\u0000\uFF24\u0000\uFF25\u0000\uFF26\u0000\uFF27\u0000\uFF28\u0000\uFF29\u0000\uFF2A\u0000\uFF2B\u0000\uFF2C\u0000\uFF2D\u0000\uFF2E\u0000\uFF2F\u0000\uFF30\u0000\uFF31\u0000\uFF32\u0000\uFF33\u0000\uFF34\u0000\uFF35\u0000\uFF36\u0000\uFF37\u0000\uFF38\u0000\uFF39\u0000\uFF3A"; private static double []p1 = { 1.0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10,1e11,1e12,1e13,1e14,1e15, 1e16,1e17,1e18,1e19,1e20,1e21,1e22,1e23, 1e24,1e25,1e26,1e27,1e28,1e29,1e30,1e31 }; private static double []p32 = { 1.0,1e32,1e64,1e96,1e128,1e160,1e192,1e224,1e256,1e288, }; private static double []np32 = // guich@570_51 { 1e-0,1e-32,1e-64,1e-96,1e-128,1e-160,1e-192,1e-224,1e-256,1e-288,1e-320, }; private static final double LN10 = 2.30258509299404568402; private static double rounds5[] = {5e-1,5e-2,5e-3,5e-4,5e-5,5e-6,5e-7,5e-8,5e-9,5e-10,5e-11,5e-12,5e-13,5e-14,5e-15,5e-16,5e-17,5e-18}; private static char []zeros = "00000000000000000000".toCharArray(); } static class Constants4D { } static String withAcc = "������������������������������������������������������"; static char[] woutAcc = "aAaAaAaAaAaAcCeEeEeEeEiIiIiIiInNoOoOoOoOoOuUuUuUuUyYyY".toCharArray(); /** Returns the given string without accentuation characters, using the unicode range 0-255 */ public static String removeAccentuation(String s) { char[] chars = s.toCharArray(); boolean changed = false; for (int i = chars.length; --i >= 0;) { int normal = withAcc.indexOf(chars[i]); if (normal != -1) { changed = true; chars[i] = woutAcc[normal]; } } return changed ? new String(chars) : s; } public static boolean equals(byte[] b1, byte[] b2) { if (b1 != null && b2 != null && b1.length == b2.length) { for (int i = b1.length; --i >= 0;) if (b1[i] != b2[i]) return false; return true; } return b1 == b2; } //////// Native methods for device native public static boolean equals4D(byte[] b1, byte[] b2); native public static String dup4D(char c, int count); native public static String spacePad4D(String what, int count, boolean before); native public static int toInt4D(String s) throws InvalidNumberException; native public static String toString4D(char c); native public static int doubleToIntBits4D(double d); native public static double intBitsToDouble4D(int i); native public static String toString4D(int i); native public static String toString4D(double d, int precision); native public static double toDouble4D(String s) throws InvalidNumberException; native public static String toString4D(String doubleValue, int n) throws InvalidNumberException; native public static long doubleToLongBits4D(double value); native public static double longBitsToDouble4D(long bits); native public static char toLowerCase4D(char c); native public static char toUpperCase4D(char c); native public static String unsigned2hex4D(int b, int places); native public static int hashCode4D(StringBuffer sb); native public static int getBreakPos4D(totalcross.ui.font.FontMetrics fm, StringBuffer sb, int start, int width, boolean doWordWrap); native public static void insertAt4D(StringBuffer sb, int pos, char c); native public static void append4D(StringBuffer sb, char c, int count); native public static String toString4D(long l); native public static long toLong4D(String s) throws InvalidNumberException; native public static void fill4D(char[] a, int from, int to, char value); native public static void fill4D(boolean[] a, int from, int to, boolean value); native public static void fill4D(int[] a, int from, int to, int value); native public static void fill4D(double[] a, int from, int to, double value); native public static void fill4D(short[] a, int from, int to, int value); native public static void fill4D(byte[] a, int from, int to, int value); native public static void fill4D(long[] a, int from, int to, long value); native public static void fill4D(Object[] a, int from, int to, Object value); native public static String replace4D(String source, String from, String to); native public static byte[] getBytes4D(StringBuffer sb); native public static int numberOf4D(String s, char c); native public static String zeroPad4D(String s, int size); native public static String zeroPad4D(int s, int size); }