//
// Convert.java
//
/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package visad.browser;
/**
* Utility methods for various conversions between primitive data types.
*/
public class Convert {
/**
* Converts an array of ints to an array of bytes. Each integer is cut into
* four byte-size pieces, making the resulting byte array four times the
* length of the input int array.
*
* @param ints The array of ints to be converted to a byte array
*
* @return An array of bytes corresponding to the original int array.
*/
public static byte[] intToBytes(int[] ints) {
int len = ints.length;
byte[] bytes = new byte[4 * len];
for (int i = 0; i < len; i++) {
int q = ints[i];
bytes[4 * i] = (byte) (q & 0x000000ff);
bytes[4 * i + 1] = (byte) ((q & 0x0000ff00) >> 8);
bytes[4 * i + 2] = (byte) ((q & 0x00ff0000) >> 16);
bytes[4 * i + 3] = (byte) ((q & 0xff000000) >> 24);
}
return bytes;
}
/**
* Converts an array of bytes to an array of ints. Each group of four bytes
* form a single int, making the resulting int array one fourth the length
* of the input byte array. Note that trailing elements of the bytes array
* will be ignored.
*
* @param bytes The array of bytes to be converted to an int array
*
* @return An array of ints corresponding to the original byte array.
*/
public static int[] bytesToInt(byte[] bytes) {
int len = bytes.length / 4;
int[] ints = new int[len];
for (int i = 0; i < len; i++) {
// This byte decoding method is not very good; is there a better way?
int q3 = bytes[4 * i + 3] << 24;
int q2 = bytes[4 * i + 2] << 16;
int q1 = bytes[4 * i + 1] << 8;
int q0 = bytes[4 * i];
if (q2 < 0) q2 += 16777216;
if (q1 < 0) q1 += 65536;
if (q0 < 0) q0 += 256;
ints[i] = q3 | q2 | q1 | q0;
}
return ints;
}
/**
* Escape value for an RLE encoded sequence.
*/
private static final int RLE_ESCAPE = Integer.MIN_VALUE;
/**
* Encodes the given array of ints using a run-length scheme.
*
* @param array The array of ints to RLE-encode
*
* @return An RLE-encoded array of ints.
*/
public static int[] encodeRLE(int[] array) {
int len = array.length;
int[] temp = new int[len];
int p = 0;
for (int i = 0; i < len;) {
int q = array[i];
int count = 0;
while (i < len && q == array[i]) {
count++;
i++;
}
if (count < 4) {
// no gain from RLE; save values directly
for (int z = 0; z < count; z++) temp[p++] = q;
}
else {
// compress data using RLE
temp[p++] = RLE_ESCAPE;
temp[p++] = q;
temp[p++] = count;
}
}
// trim encoded array
int[] encoded = new int[p];
System.arraycopy(temp, 0, encoded, 0, p);
return encoded;
}
/**
* Decodes the given array of ints from a run-length encoding.
*
* @param array The RLE-encoded array of ints to decode
*
* @return A decoded array of ints.
*/
public static int[] decodeRLE(int[] array) {
// compute size of decoded array
int count = 0;
int i = 0;
while (i < array.length) {
if (array[i] == RLE_ESCAPE) {
count += array[i + 2];
i += 3;
}
else {
count++;
i++;
}
}
// allocate decoded array
int[] decoded = new int[count];
int p = 0;
// decode RLE sequence
for (i = 0; i < array.length; i++) {
int q = array[i];
if (q == RLE_ESCAPE) {
int val = array[++i];
int cnt = array[++i];
for (int z = 0; z < cnt; z++) decoded[p++] = val;
}
else decoded[p++] = q;
}
return decoded;
}
/**
* Extracts a double from a string.
*/
public static double getDouble(String s) {
double d = Double.NaN;
try {
d = Double.valueOf(s).doubleValue();
}
catch (NumberFormatException exc) { }
return d;
}
/**
* Extracts a float from a string.
*/
public static float getFloat(String s) {
float f = Float.NaN;
if (s != null) {
try {
f = Float.valueOf(s).floatValue();
}
catch (NumberFormatException exc) { }
}
return f;
}
/**
* Extracts a boolean from a string.
*/
public static boolean getBoolean(String s) {
if (s == null) return false;
char c = s.trim().charAt(0);
return c == 'T' || c == 't';
}
/**
* Extracts an integer from a string.
*/
public static int getInt(String s) {
int i = 0;
if (s != null) {
try {
i = Integer.parseInt(s);
}
catch (NumberFormatException exc) { }
}
return i;
}
/**
* Number of significant digits after the decimal point.
*/
public static final int PLACES = 3;
/** Mode where shortString rounds to the nearest value. */
public static final int ROUND_NEAREST = 1;
/** Mode where shortString rounds to the lower value. */
public static final int ROUND_DOWN = 2;
/** Mode where shortString rounds to the higher value. */
public static final int ROUND_UP = 3;
/**
* Gets a reasonably short string representation of a double
* for use in a graphical user interface. The number will be rounded
* to the nearest integer.
*/
public static String shortString(double val) {
return shortString(val, ROUND_NEAREST);
}
/**
* Gets a reasonably short string representation of a double
* for use in a graphical user interface. Mode indicates the method
* used to deal insignificant digits: ROUND_NEAREST, ROUND_UP or ROUND_DOWN.
*/
public static String shortString(double val, int mode) {
// remember whether or not the number is negative
boolean negative = (val < 0.0);
double origVal = val;
// now we only need to deal with a positive number
val = Math.abs(val);
if (val < 0.001) {
for (int i = 1; i < 30; i++) {
val *= 10.0;
origVal *= 10.0;
if (val >= 1.0) {
return shortString(origVal) + "E-" + i;
}
}
}
// build multiplier for saving significant digits
// also build value used to round up insignificant digits
int mult = 1;
float round;
if (mode == ROUND_DOWN) round = negative ? 1 : 0;
else if (mode == ROUND_UP) round = negative ? 0 : 1;
else round = 0.5f; // mode == ROUND_NEAREST (default)
for (int p = PLACES; p > 0; p--) {
mult *= 10;
round /= 10;
}
// break into digits before (preDot) and after (postDot) the decimal point
long l = (long) ((val + round) * mult);
long preDot = l / mult;
int postDot = (int )(l % mult);
// format the pre-decimal point number
// Integer.toString() is faster than Long.toString(); use it if possible
String num;
if (preDot <= Integer.MAX_VALUE) {
num = Integer.toString((int )preDot);
} else {
num = Long.toString(preDot);
}
// if there's nothing after the decimal point, use the whole number
if (postDot == 0) {
// make sure we don't return "-0"
if (negative && preDot != 0) {
return "-" + num;
}
return num;
}
// start building the string
StringBuffer buf = new StringBuffer(num.length() + 5);
// add sign (if necessary), pre-decimal point digits and decimal point
if (negative) {
buf.append('-');
}
buf.append(num);
buf.append('.');
// format the post-decimal point digits
num = Integer.toString(postDot);
// add leading zeros if necessary
int nlen = num.length();
for (int p = PLACES; p > nlen; p--) {
buf.append('0');
}
// add actual digits
buf.append(num);
// return the final string
return buf.toString();
}
}