package com.mgmaps.utils;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
public class Tools {
private static final byte[] BOM = new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
public static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
// skip & read buffer size
public static final int BUFSIZE = 4096;
public static final int COORDS_D = 0;
public static final int COORDS_DM = 1;
public static final int COORDS_DMS = 2;
private Tools() {
}
/**
* Read a line from the input stream.
*
* @param is
* input stream to read from
* @throws IOException
* if an error exception occurs
*/
public static String readLine2(final InputStream is) throws IOException {
final StringBuffer sb = new StringBuffer();
while (true) {
final int c = is.read();
if (c == -1) {
break;
}
if (c == '\n' || c == '\r') {
break;
} else {
sb.append((char) c);
}
}
return sb.toString();
}
/**
* Pad a string with leading zeroes.
*
* @param s
* the string to pad
* @param chars
* minimum number of characters
* @return the padded string
*/
public static String padZero(final String s, final int chars) {
final StringBuffer result = new StringBuffer();
final int len = s.length();
for (int i = len; i < chars; i++) {
result.append('0');
}
result.append(s);
return result.toString();
}
/**
* Pad a string with trailing zeroes.
*
* @param s
* the string to pad
* @param chars
* minimum number of characters
* @return the padded string
*/
public static String padZeroEnd(final String s, final int chars) {
final StringBuffer result = new StringBuffer();
final int len = s.length();
result.append(s);
for (int i = len; i < chars; i++) {
result.append('0');
}
return result.toString();
}
/**
* Format a time (milliseconds) to a string.
*
* @param time
* time in milliseconds
* @return the time as a string
*/
public static String timeFormat(final long time) {
int millis = (int) (time % 86400000L);
final StringBuffer result = new StringBuffer();
result.append(padZero(Integer.toString(millis / 3600000), 2));
result.append(':');
millis %= 3600000;
result.append(padZero(Integer.toString(millis / 60000), 2));
result.append(':');
millis %= 60000;
result.append(padZero(Integer.toString(millis / 1000), 2));
result.append('.');
millis %= 1000;
result.append(padZero(Integer.toString(millis), 3));
return result.toString();
}
/**
* Format a floating point number represented as integer.
*
* @param number
* number to format
* @param decimals
* number of decimals after the point
* @return the formatted number
*/
public static String floatFormat(final long number, final int decimals) {
// compute the divisor
int pow10 = 1;
for (int i = 0; i < decimals; i++) {
pow10 *= 10;
}
final long trunc = Math.abs(number / pow10);
long frac = Math.abs(number % pow10);
// build the result
final StringBuffer result = new StringBuffer();
if (number < 0) {
result.append('-');
}
result.append(trunc);
result.append('.');
// tail removing
int i;
for (i = 1; (frac % 10 == 0) && (i < decimals); i++) {
frac /= 10;
}
// head padding
result.append(padZero(Long.toString(frac), decimals - i + 1));
return result.toString();
}
/**
* Word-wrap text.
*
* @param text
* the text to wrap
* @param font
* the font used
* @param fwidth
* text field width
* @param currentX
* start of first line (heading)
* @return a string array of lines containing the wrapped text
*/
public static String[] wrapText(final String text, final Font font, final int fwidth, final int currentX) {
// wrapped text lines
final Vector wt = new Vector();
final int len = text.length();
int first = 0;
int last = 0;
boolean emptylineflag = false;
for (int i = 0; i < len; i++) {
// split at '\n'
if (text.charAt(i) == '\n') {
wt.addElement(text.substring(first, i));
first = i + 1;
last = i;
emptylineflag = false;
continue;
}
// update last if necessary
if (text.charAt(i) == ' ') {
last = i;
// don't check to split - we would have splitted at the previous char
continue;
}
// check length, see if we should split
final int w = font.substringWidth(text, first, i - first + 1);
// we should not split if we printed nothing on the previous line
if ((first == 0 && w + currentX > fwidth) || (first != 0 && w > fwidth)) {
// yep, we should split, first to last, then the rest
// if last is equal to first, the first word doesn't fit, so add an
// empty string
// but set a flag to avoid adding it again
if (first < last || (first == last && !emptylineflag)) {
final String s = text.substring(first, last);
wt.addElement(s);
}
if (first < last) {
// start with last+1
emptylineflag = false;
first = last + 1;
} else {
emptylineflag = true;
}
}
}
// add the last string
wt.addElement(text.substring(first));
return toStringArray(wt);
}
/**
* Split a string.
*
* @param s
* string to split
* @param c
* character used for splitting
* @param max
* max number of fields to be returned; 0 for unlimited, non-zero to
* return all the remaining string in the last split
* @param dblquotes
* whether to ignore delimiters between double quotes
* @return an array with the tokens
*/
public static String[] split(final String s, final char c, final boolean dblquotes, final int max) {
int j = 0;
final Vector vector = new Vector();
// add first max-1 components
int num = 0;
int i = 0;
String ss = null;
int k1;
int k2;
for (i = 0; num != max - 1; i = j + 1) {
k1 = -1;
k2 = -1;
j = s.indexOf(c, i);
if (dblquotes) {
// should have k1=0
k1 = s.indexOf('"', i);
// quote found and before delimiter
if (k1 >= 0 && k1 < j) {
// next quote
k2 = s.indexOf('"', k1 + 1);
if (k2 >= 0) {
// recompute next delimiter - should have j=k2+1
j = s.indexOf(c, k2 + 1);
}
}
}
if (j >= 0) {
if (dblquotes && k1 >= 0 && k2 >= 0) {
ss = s.substring(k1 + 1, k2);
} else {
ss = s.substring(i, j);
}
vector.addElement(ss);
num++;
} else {
if (dblquotes && k1 >= 0 && k2 >= 0) {
ss = s.substring(k1 + 1, k2);
} else {
ss = s.substring(i);
}
vector.addElement(ss);
num++;
break;
}
}
// add the max-th component
k1 = -1;
k2 = -1;
if (max != 0 && j >= 0) {
if (dblquotes) {
k1 = s.indexOf('"', i);
// quote found and before delimiter
if (k1 >= 0) {
// next quote
k2 = s.indexOf('"', k1 + 1);
}
}
if (dblquotes && k1 >= 0 && k2 >= 0) {
ss = s.substring(k1 + 1, k2);
} else {
ss = s.substring(i);
}
vector.addElement(ss);
num++;
}
// convert to array
final String as[] = new String[num];
vector.copyInto(as);
// return the array
return as;
}
/**
* Convert a vector to a string array.
*
* @param v
* vector to convert
* @return the string array
*/
public static String[] toStringArray(final Vector v) {
final String[] res = new String[v.size()];
v.copyInto(res);
return res;
}
/**
* Format coordinate.
*
* @param number
* coordinate as an int
* @param isLat
* whether it's a latitude or not
* @param disp
* whether to format for display or not
* @return the formatted coord
*/
public static String formatCoord(final int number, final boolean isLat, final boolean disp) {
int ax = Math.abs(number);
final String ch = "" + (isLat ? ((number >= 0) ? 'N' : 'S') : ((number >= 0) ? 'E' : 'W'));
if (disp) {
final StringBuffer result = new StringBuffer();
switch (0) {
case Tools.COORDS_DMS:
result.append(ax / 1000000);
result.append((char) 0x00B0);
ax %= 1000000;
ax *= 60;
result.append(ax / 1000000);
result.append('\'');
ax %= 1000000;
ax *= 60;
result.append(ax / 1000000);
result.append('"');
break;
case Tools.COORDS_DM:
result.append(ax / 1000000);
result.append((char) 0x00B0);
ax %= 1000000;
ax *= 60;
result.append(Tools.floatFormat(ax / 1000, 3));
result.append('\'');
break;
case Tools.COORDS_D:
result.append(Tools.floatFormat(ax / 10, 5));
break;
}
result.append(ch);
return result.toString();
} else {
// do not translate when formatting coords for non-display
return Tools.floatFormat(ax, 6) + ch;
}
}
/**
* Scale an image down 2^n times.
*
* @param src
* image to scale
* @param dif
* logarithmic scaling level (1 = scale by 2, 2 = scale by 4, etc.)
* @return the scaled image
*/
public static Image scaleImage05(final Image src, final int dif) {
final int srcWidth = src.getWidth();
final int srcHeight = src.getHeight();
final int dstWidth = srcWidth >> dif;
final int dstHeight = srcHeight >> dif;
//TODO jaanus : this should actually be handled somewhere outside
if (dstWidth == 0 || dstHeight == 0) {
return Image.createImage(1, 1);
}
final Image dst = Image.createImage(dstWidth, dstHeight);
final Graphics g = dst.getGraphics();
final int[] lineRGB = new int[srcWidth];
final int[] srcPos = new int[dstWidth]; // cache for x positions
/*
* Pre-calculate x positions with modified bresenham algorithm
* http://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html
*/
for (int x = 1; x < dstWidth; x++) {
srcPos[x] = x << dif;
}
for (int y = 0; y < dstHeight; y++) {
src.getRGB(lineRGB, 0, srcWidth, 0, y << dif, srcWidth, 1);
for (int x = 1; x < dstWidth; x++) {
// skip pixel 0
lineRGB[x] = lineRGB[srcPos[x]];
}
g.drawRGB(lineRGB, 0, dstWidth, 0, y, dstWidth, 1, false);
}
return dst;
}
/**
* Scale an image up 2^n times.
*
* @param image
* image to scale
* @param dif
* logarithmic scaling level (1 = scale by 2, 2 = scale by 4, etc.)
* @return the scaled image
*/
public static Image scaleImage20(final Image src, final int dif) {
return scaleImage20(src, -1, -1, dif);
}
public static Image scaleImage20(final Image src, final int sourceX, final int sourceY, final int dif) {
final int dstWidth = src.getWidth();
final int dstHeight = src.getHeight();
int srcWidth = dstWidth >> dif;
int srcHeight = dstHeight >> dif;
if (srcWidth < 1) {
srcWidth = 1;
}
if (srcHeight < 1) {
srcHeight = 1;
}
final int srcX = sourceX == -1 ? (dstWidth - srcWidth) >> 1 : sourceX;
final int srcY = sourceY == -1 ? (dstHeight - srcHeight) >> 1 : sourceY;
final Image dst = Image.createImage(dstWidth, dstHeight);
final Graphics g = dst.getGraphics();
final int[] lineRGB = new int[dstWidth];
final int[] srcPos = new int[dstWidth]; // cache for x positions
final int[] lineRGB2 = new int[dstWidth];
/*
* Pre-calculate x positions with modified bresenham algorithm
* http://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html
*/
for (int x = 0; x < dstWidth; x++) {
srcPos[x] = srcX + (x >> dif);
}
for (int y = 0; y < dstHeight; y++) {
src.getRGB(lineRGB, 0, dstWidth, 0, srcY + (y >> dif), dstWidth, 1);
for (int x = 0; x < dstWidth; x++) {
lineRGB2[x] = lineRGB[srcPos[x]];
}
g.drawRGB(lineRGB2, 0, dstWidth, 0, y, dstWidth, 1, false);
}
return dst;
}
/**
* URLEncode method.
*
* @param s
* string to urlencode
* @return urlencoded string
*/
public static String urlEncode(final String s) {
if (s == null) {
return null;
}
byte[] b = null;
try {
b = s.getBytes("utf-8");
} catch (final Exception ex) {
try {
b = s.getBytes("UTF8");
} catch (final Exception ex2) {
b = s.getBytes();
}
}
final StringBuffer result = new StringBuffer();
for (int i = 0; i < b.length; i++) {
if ((b[i] >= 'A' && b[i] <= 'Z') || (b[i] >= 'a' && b[i] <= 'z') || (b[i] >= '0' && b[i] <= '9')) {
result.append((char) b[i]);
} else {
result.append('%');
result.append(HEX_ARRAY[((b[i] + 256) >> 4) & 0x0F]);
result.append(HEX_ARRAY[b[i] & 0x0F]);
}
}
return result.toString();
}
/**
* Turn a signed byte into an unsigned byte (stored as int).
*
* @param b
* byte to convert
* @return the converted byte/int
*/
public static int unsigned(final byte b) {
return (b < 0) ? (b + 256) : b;
}
/**
* Convert a byte array to a string, with UTF-8 as encoding.
*
* @param data
* byte array to convert
* @return the resulting string
*/
public static String byteArrayToString(final byte[] data) {
String sdata = null;
try {
if (startsWithBOM(data)) {
sdata = new String(data, 3, data.length - 3, "utf-8");
} else {
sdata = new String(data, "utf-8");
}
} catch (final Exception ex) {
sdata = new String(data);
}
return sdata;
}
private static boolean startsWithBOM(final byte[] data) {
return data.length >= 3 && data[0] == BOM[0] && data[1] == BOM[1] && data[2] == BOM[2];
}
}