javajs.util.PT Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmol Show documentation
Show all versions of jmol Show documentation
Jmol: an open-source Java viewer for chemical structures in 3D
/* $RCSfile$
* $Author: hansonr $
* $Date: 2007-04-26 16:57:51 -0500 (Thu, 26 Apr 2007) $
* $Revision: 7502 $
*
* Copyright (C) 2005 The Jmol Development Team
*
* Contact: [email protected]
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package javajs.util;
import java.lang.reflect.Array;
import java.util.Map;
import java.util.Map.Entry;
import javajs.J2SIgnoreImport;
import javajs.api.JSONEncodable;
/**
* a combination of Parsing and Text-related utility classes
*
* @author hansonr
*
*/
@J2SIgnoreImport(value = { java.lang.reflect.Array.class })
public class PT {
public static int parseInt(String str) {
return parseIntNext(str, new int[] {0});
}
public static int parseIntNext(String str, int[] next) {
int cch = str.length();
if (next[0] < 0 || next[0] >= cch)
return Integer.MIN_VALUE;
return parseIntChecked(str, cch, next);
}
public static int parseIntChecked(String str, int ichMax, int[] next) {
boolean digitSeen = false;
int value = 0;
int ich = next[0];
if (ich < 0)
return Integer.MIN_VALUE;
int ch;
while (ich < ichMax && isWhiteSpace(str, ich))
++ich;
boolean negative = false;
if (ich < ichMax && str.charAt(ich) == 45) { //"-"
negative = true;
++ich;
}
while (ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) {
value = value * 10 + (ch - 48);
digitSeen = true;
++ich;
}
if (!digitSeen)// || !checkTrailingText(str, ich, ichMax))
value = Integer.MIN_VALUE;
else if (negative)
value = -value;
next[0] = ich;
return value;
}
public static boolean isWhiteSpace(String str, int ich) {
char ch;
return (ich >= 0 && ((ch = str.charAt(ich)) == ' ' || ch == '\t' || ch == '\n'));
}
/**
* A float parser that is 30% faster than Float.parseFloat(x) and also accepts
* x.yD+-n
*
* @param str
* @param ichMax
* @param next
* pointer; incremented
* @param isStrict
* @return value or Float.NaN
*/
public static float parseFloatChecked(String str, int ichMax, int[] next,
boolean isStrict) {
boolean digitSeen = false;
int ich = next[0];
if (isStrict && str.indexOf('\n') != str.lastIndexOf('\n'))
return Float.NaN;
while (ich < ichMax && isWhiteSpace(str, ich))
++ich;
boolean negative = false;
if (ich < ichMax && str.charAt(ich) == '-') {
++ich;
negative = true;
}
// looks crazy, but if we don't do this, Google Closure Compiler will
// write code that Safari will misinterpret in a VERY nasty way --
// getting totally confused as to long integers and double values
// This is Safari figuring out the values of the numbers on the line (x, y, then z):
// ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C
// e=1408749273
// -e =-1408749273
// ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C
// e=-1821066134
// e=36.532
// ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C
// e=-1133871366
// e=31.576
//
// "e" values are just before and after the "value = -value" statement.
int ch = 0;
float ival = 0f;
float ival2 = 0f;
while (ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) {
ival = (ival * 10f) + (ch - 48)*1f;
++ich;
digitSeen = true;
}
boolean isDecimal = false;
int iscale = 0;
int nzero = (ival == 0 ? -1 : 0);
if (ch == '.') {
isDecimal = true;
while (++ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) {
digitSeen = true;
if (nzero < 0) {
if (ch == 48) {
nzero--;
continue;
}
nzero = -nzero;
}
if (iscale < decimalScale.length) {
ival2 = (ival2 * 10f) + (ch - 48)*1f;
iscale++;
}
}
}
float value;
// Safari breaks here intermittently converting integers to floats
if (!digitSeen) {
value = Float.NaN;
} else if (ival2 > 0) {
value = ival2 * decimalScale[iscale - 1];
if (nzero > 1) {
if (nzero - 2 < decimalScale.length) {
value *= decimalScale[nzero - 2];
} else {
value *= Math.pow(10, 1 - nzero);
}
} else {
value += ival;
}
} else {
value = ival;
}
boolean isExponent = false;
if (ich < ichMax && (ch == 69 || ch == 101 || ch == 68)) { // E e D
isExponent = true;
if (++ich >= ichMax)
return Float.NaN;
ch = str.charAt(ich);
if ((ch == '+') && (++ich >= ichMax))
return Float.NaN;
next[0] = ich;
int exponent = parseIntChecked(str, ichMax, next);
if (exponent == Integer.MIN_VALUE)
return Float.NaN;
if (exponent > 0 && exponent <= tensScale.length)
value *= tensScale[exponent - 1];
else if (exponent < 0 && -exponent <= decimalScale.length)
value *= decimalScale[-exponent - 1];
else if (exponent != 0)
value *= Math.pow(10, exponent);
} else {
next[0] = ich; // the exponent code finds its own ichNextParse
}
// believe it or not, Safari reports the long-equivalent of the
// float value here, then later the float value, after no operation!
if (negative)
value = -value;
if (value == Float.POSITIVE_INFINITY)
value = Float.MAX_VALUE;
return (!isStrict || (!isExponent || isDecimal)
&& checkTrailingText(str, next[0], ichMax) ? value : Float.NaN);
}
public final static float[] tensScale = { 10f, 100f, 1000f, 10000f, 100000f, 1000000f };
public final static float[] decimalScale = {
0.1f,
0.01f,
0.001f,
0.0001f,
0.00001f,
0.000001f,
0.0000001f,
0.00000001f,
0.000000001f
};
public static boolean checkTrailingText(String str, int ich, int ichMax) {
//number must be pure -- no additional characters other than white space or ;
char ch;
while (ich < ichMax && (isWhitespace(ch = str.charAt(ich)) || ch == ';'))
++ich;
return (ich == ichMax);
}
public static float[] parseFloatArray(String str) {
return parseFloatArrayNext(str, new int[1], null, null, null);
}
public static int parseFloatArrayInfested(String[] tokens, float[] data) {
int len = data.length;
int nTokens = tokens.length;
int n = 0;
int max = 0;
for (int i = 0; i >= 0 && i < len && n < nTokens; i++) {
float f;
while (Float.isNaN(f = parseFloat(tokens[n++]))
&& n < nTokens) {
}
if (!Float.isNaN(f))
data[(max = i)] = f;
if (n == nTokens)
break;
}
return max + 1;
}
/**
* @param str
* @param next
* @param f
* @param strStart or null
* @param strEnd or null
* @return array of float values
*
*/
public static float[] parseFloatArrayNext(String str, int[] next, float[] f,
String strStart, String strEnd) {
int n = 0;
int pt = next[0];
if (pt >= 0) {
if (strStart != null) {
int p = str.indexOf(strStart, pt);
if (p >= 0)
next[0] = p + strStart.length();
}
str = str.substring(next[0]);
pt = (strEnd == null ? -1 : str.indexOf(strEnd));
if (pt < 0)
pt = str.length();
else
str = str.substring(0, pt);
next[0] += pt + 1;
String[] tokens = getTokens(str);
if (f == null)
f = new float[tokens.length];
n = parseFloatArrayInfested(tokens, f);
}
if (f == null)
return new float[0];
for (int i = n; i < f.length; i++)
f[i] = Float.NaN;
return f;
}
public static float parseFloatRange(String str, int ichMax, int[] next) {
int cch = str.length();
if (ichMax > cch)
ichMax = cch;
if (next[0] < 0 || next[0] >= ichMax)
return Float.NaN;
return parseFloatChecked(str, ichMax, next, false);
}
public static float parseFloatNext(String str, int[] next) {
int cch = (str == null ? -1 : str.length());
return (next[0] < 0 || next[0] >= cch ? Float.NaN : parseFloatChecked(str, cch, next, false));
}
public static float parseFloatStrict(String str) {
// checks trailing characters and does not allow "1E35" to be float
int cch = str.length();
if (cch == 0)
return Float.NaN;
return parseFloatChecked(str, cch, new int[] {0}, true);
}
public static float parseFloat(String str) {
return parseFloatNext(str, new int[] {0});
}
public static int parseIntRadix(String s, int i) throws NumberFormatException {
/**
*
* JavaScript uses parseIntRadix
*
* @j2sNative
*
* return Integer.parseIntRadix(s, i);
*
*/
{
return Integer.parseInt(s, i);
}
}
public static String[] getTokens(String line) {
return getTokensAt(line, 0);
}
public static String parseToken(String str) {
return parseTokenNext(str, new int[] {0});
}
public static String parseTrimmed(String str) {
return parseTrimmedRange(str, 0, str.length());
}
public static String parseTrimmedAt(String str, int ichStart) {
return parseTrimmedRange(str, ichStart, str.length());
}
public static String parseTrimmedRange(String str, int ichStart, int ichMax) {
int cch = str.length();
if (ichMax < cch)
cch = ichMax;
if (cch < ichStart)
return "";
return parseTrimmedChecked(str, ichStart, cch);
}
public static String[] getTokensAt(String line, int ich) {
if (line == null)
return null;
int cchLine = line.length();
if (ich < 0 || ich > cchLine)
return null;
int tokenCount = countTokens(line, ich);
String[] tokens = new String[tokenCount];
int[] next = new int[1];
next[0] = ich;
for (int i = 0; i < tokenCount; ++i)
tokens[i] = parseTokenChecked(line, cchLine, next);
return tokens;
}
public static int countChar(String line, char c) {
int n = 0;
for (int i = line.lastIndexOf(c) + 1; --i >= 0;)
if (line.charAt(i) == c)
n++;
return n;
}
public static int countTokens(String line, int ich) {
int tokenCount = 0;
if (line != null) {
int ichMax = line.length();
while (true) {
while (ich < ichMax && isWhiteSpace(line, ich))
++ich;
if (ich == ichMax)
break;
++tokenCount;
do {
++ich;
} while (ich < ichMax && !isWhiteSpace(line, ich));
}
}
return tokenCount;
}
public static String parseTokenNext(String str, int[] next) {
int cch = str.length();
return (next[0] < 0 || next[0] >= cch ? null : parseTokenChecked(str, cch, next));
}
public static String parseTokenRange(String str, int ichMax, int[] next) {
int cch = str.length();
if (ichMax > cch)
ichMax = cch;
return (next[0] < 0 || next[0] >= ichMax ? null : parseTokenChecked(str, ichMax, next));
}
public static String parseTokenChecked(String str, int ichMax, int[] next) {
int ich = next[0];
while (ich < ichMax && isWhiteSpace(str, ich))
++ich;
int ichNonWhite = ich;
while (ich < ichMax && !isWhiteSpace(str, ich))
++ich;
next[0] = ich;
return (ichNonWhite == ich ? null : str.substring(ichNonWhite, ich));
}
public static String parseTrimmedChecked(String str, int ich, int ichMax) {
while (ich < ichMax && isWhiteSpace(str, ich))
++ich;
int ichLast = ichMax - 1;
while (ichLast >= ich && isWhiteSpace(str, ichLast))
--ichLast;
return (ichLast < ich ? "" : str.substring(ich, ichLast + 1));
}
public static double dVal(String s) throws NumberFormatException {
/**
* @j2sNative
*
* if(s==null)
* throw new NumberFormatException("null");
* var d=parseFloat(s);
* if(isNaN(d))
* throw new NumberFormatException("Not a Number : "+s);
* return d
*
*/
{
return Double.valueOf(s).doubleValue();
}
}
public static float fVal(String s) throws NumberFormatException {
/**
* @j2sNative
*
* return this.dVal(s);
*/
{
return Float.parseFloat(s);
}
}
public static int parseIntRange(String str, int ichMax, int[] next) {
int cch = str.length();
if (ichMax > cch)
ichMax = cch;
return (next[0] < 0 || next[0] >= ichMax ? Integer.MIN_VALUE : parseIntChecked(str, ichMax, next));
}
/**
* parses a string array for floats. Returns NaN for nonfloats.
*
* @param tokens the strings to parse
* @param data the array to fill
*/
public static void parseFloatArrayData(String[] tokens, float[] data) {
parseFloatArrayDataN(tokens, data, data.length);
}
/**
* parses a string array for floats. Returns NaN for nonfloats or missing data.
*
* @param tokens the strings to parse
* @param data the array to fill
* @param nData the number of elements
*/
public static void parseFloatArrayDataN(String[] tokens, float[] data, int nData) {
for (int i = nData; --i >= 0;)
data[i] = (i >= tokens.length ? Float.NaN : parseFloat(tokens[i]));
}
/**
*
* proper splitting, even for Java 1.3 -- if the text ends in the run,
* no new line is appended.
*
* @param text
* @param run
* @return String array
*/
public static String[] split(String text, String run) {
if (text.length() == 0)
return new String[0];
int n = 1;
int i = text.indexOf(run);
String[] lines;
int runLen = run.length();
if (i < 0 || runLen == 0) {
lines = new String[1];
lines[0] = text;
return lines;
}
int len = text.length() - runLen;
for (; i >= 0 && i < len; n++)
i = text.indexOf(run, i + runLen);
lines = new String[n];
i = 0;
int ipt = 0;
int pt = 0;
for (; (ipt = text.indexOf(run, i)) >= 0 && pt + 1 < n;) {
lines[pt++] = text.substring(i, ipt);
i = ipt + runLen;
}
if (text.indexOf(run, len) != len)
len += runLen;
lines[pt] = text.substring(i, len);
return lines;
}
public final static float FLOAT_MIN_SAFE = 2E-45f;
// Float.MIN_VALUE (1.45E-45) is not reliable with JavaScript because of the float/double difference there
/// general static string-parsing class ///
// next[0] tracks the pointer within the string so these can all be static.
// but the methods parseFloat, parseInt, parseToken, parseTrimmed, and getTokens do not require this.
// public static String concatTokens(String[] tokens, int iFirst, int iEnd) {
// String str = "";
// String sep = "";
// for (int i = iFirst; i < iEnd; i++) {
// if (i < tokens.length) {
// str += sep + tokens[i];
// sep = " ";
// }
// }
// return str;
// }
public static String getQuotedStringAt(String line, int ipt0) {
int[] next = new int[] { ipt0 };
return getQuotedStringNext(line, next);
}
/**
*
* @param line
* @param next passes [current pointer]
* @return quoted string -- does NOT unescape characters
*/
public static String getQuotedStringNext(String line, int[] next) {
int i = next[0];
if (i < 0 || (i = line.indexOf("\"", i)) < 0)
return "";
int pt = i + 1;
int len = line.length();
while (++i < len && line.charAt(i) != '"')
if (line.charAt(i) == '\\')
i++;
next[0] = i + 1;
return line.substring(pt, i);
}
/**
* CSV format -- escaped quote is "" WITHIN "..."
*
*
* @param line
* @param next int[2] filled with [ptrQuote1, ptrAfterQuote2]
* next[1] will be -1 if unmatched quotes are found (continuation on next line)
* @return unescaped string or null
*/
public static String getCSVString(String line, int[] next) {
int i = next[1];
if (i < 0 || (i = line.indexOf("\"", i)) < 0)
return null;
int pt = next[0] = i;
int len = line.length();
boolean escaped = false;
boolean haveEscape = false;
while (++i < len
&& (line.charAt(i) != '"' || (escaped = (i + 1 < len && line.charAt(i + 1) == '"'))))
if (escaped) {
escaped = false;
haveEscape = true;
i++;
}
if (i >= len) {
next[1] = -1;
return null; // unmatched
}
next[1] = i + 1;
String s = line.substring(pt + 1, i);
return (haveEscape ? rep(rep(s, "\"\"", "\0"), "\0","\"") : s);
}
public static boolean isOneOf(String key, String semiList) {
if (semiList.length() == 0)
return false;
if (semiList.charAt(0) != ';')
semiList = ";" + semiList + ";";
return key.indexOf(";") < 0 && semiList.indexOf(';' + key + ';') >= 0;
}
public static String getQuotedAttribute(String info, String name) {
int i = info.indexOf(name + "=");
return (i < 0 ? null : getQuotedStringAt(info, i));
}
public static float approx(float f, float n) {
return Math.round (f * n) / n;
}
/**
* Does a clean ITERATIVE replace of strFrom in str with strTo.
* Thus, rep("Testttt", "tt","t") becomes "Test".
*
* @param str
* @param strFrom
* @param strTo
* @return replaced string
*/
public static String rep(String str, String strFrom, String strTo) {
if (str == null || strFrom.length() == 0 || str.indexOf(strFrom) < 0)
return str;
boolean isOnce = (strTo.indexOf(strFrom) >= 0);
do {
str = str.replace(strFrom, strTo);
} while (!isOnce && str.indexOf(strFrom) >= 0);
return str;
}
public static String formatF(float value, int width, int precision,
boolean alignLeft, boolean zeroPad) {
return formatS(DF.formatDecimal(value, precision), width, 0, alignLeft, zeroPad);
}
/**
*
* @param value
* @param width
* @param precision
* @param alignLeft
* @param zeroPad
* @param allowOverflow IGNORED
* @return formatted string
*/
public static String formatD(double value, int width, int precision,
boolean alignLeft, boolean zeroPad, boolean allowOverflow) {
return formatS(DF.formatDecimal((float)value, -1 - precision), width, 0, alignLeft, zeroPad);
}
/**
*
* @param value
* @param width number of columns
* @param precision precision > 0 ==> precision = number of characters max from left
* precision < 0 ==> -1 - precision = number of char. max from right
* @param alignLeft
* @param zeroPad generally for numbers turned strings
* @return formatted string
*/
public static String formatS(String value, int width, int precision,
boolean alignLeft, boolean zeroPad) {
if (value == null)
return "";
int len = value.length();
if (precision != Integer.MAX_VALUE && precision > 0
&& precision < len)
value = value.substring(0, precision);
else if (precision < 0 && len + precision >= 0)
value = value.substring(len + precision + 1);
int padLength = width - value.length();
if (padLength <= 0)
return value;
boolean isNeg = (zeroPad && !alignLeft && value.charAt(0) == '-');
char padChar = (zeroPad ? '0' : ' ');
char padChar0 = (isNeg ? '-' : padChar);
SB sb = new SB();
if (alignLeft)
sb.append(value);
sb.appendC(padChar0);
for (int i = padLength; --i > 0;)
// this is correct, not >= 0
sb.appendC(padChar);
if (!alignLeft)
sb.append(isNeg ? padChar + value.substring(1) : value);
return sb.toString();
}
/**
* Does a clean replace of any of the characters in str with chrTo
* If strTo contains strFrom, then only a single pass is done.
* Otherwise, multiple passes are made until no more replacements can be made.
*
* @param str
* @param strFrom
* @param chTo
* @return replaced string
*/
public static String replaceWithCharacter(String str, String strFrom,
char chTo) {
if (str == null)
return null;
for (int i = strFrom.length(); --i >= 0;)
str = str.replace(strFrom.charAt(i), chTo);
return str;
}
/**
* Does a clean replace of any of the characters in str with strTo
* If strTo contains strFrom, then only a single pass is done.
* Otherwise, multiple passes are made until no more replacements can be made.
*
* @param str
* @param strFrom
* @param strTo
* @return replaced string
*/
public static String replaceAllCharacters(String str, String strFrom,
String strTo) {
for (int i = strFrom.length(); --i >= 0;) {
String chFrom = strFrom.substring(i, i + 1);
str = rep(str, chFrom, strTo);
}
return str;
}
public static String trim(String str, String chars) {
if (str == null || str.length() == 0)
return str;
if (chars.length() == 0)
return str.trim();
int len = str.length();
int k = 0;
while (k < len && chars.indexOf(str.charAt(k)) >= 0)
k++;
int m = str.length() - 1;
while (m > k && chars.indexOf(str.charAt(m)) >= 0)
m--;
return str.substring(k, m + 1);
}
public static String trimQuotes(String value) {
return (value != null && value.length() > 1 && value.startsWith("\"")
&& value.endsWith("\"") ? value.substring(1, value.length() - 1)
: value);
}
public static boolean isNonStringPrimitive(Object info) {
// note that we don't use Double, Float, or Integer here
// because in JavaScript those would be false for unwrapped primitives
// coming from equivalent of Array.get()
// Strings will need their own escaped processing
return info instanceof Number || info instanceof Boolean;
}
private static Object arrayGet(Object info, int i) {
/**
*
* Note that info will be a primitive in JavaScript
* but a wrapped primitive in Java.
*
* @j2sNative
*
* return info[i];
*/
{
return Array.get(info, i);
}
}
@SuppressWarnings("unchecked")
public static String toJSON(String infoType, Object info) {
if (info == null)
return packageJSON(infoType, null);
if (isNonStringPrimitive(info))
return packageJSON(infoType, info.toString());
String s = null;
SB sb = null;
while (true) {
if (info instanceof String) {
s = (String) info;
/**
* @j2sNative
*
* if (typeof s == "undefined") s = "null"
*
*/
{}
if (s.indexOf("{\"") != 0) {
//don't doubly fix JSON strings when retrieving status
// what about \1 \2 \3 etc.?
s = esc(s);
}
break;
}
if (info instanceof JSONEncodable) {
// includes javajs.util.BS, org.jmol.script.SV
if ((s = ((JSONEncodable) info).toJSON()) == null)
s = "null"; // perhaps a list has a null value (group3List, for example)
break;
}
sb = new SB();
if (info instanceof Map) {
sb.append("{ ");
String sep = "";
for (String key : ((Map) info).keySet()) {
sb.append(sep).append(
packageJSON(key, toJSON(null, ((Map, ?>) info).get(key))));
sep = ",";
}
sb.append(" }");
break;
}
if (info instanceof Lst) {
sb.append("[ ");
int n = ((Lst>) info).size();
for (int i = 0; i < n; i++) {
if (i > 0)
sb.appendC(',');
sb.append(toJSON(null, ((Lst>) info).get(i)));
}
sb.append(" ]");
break;
}
if (info instanceof M34) {
// M4 extends M3
int len = (info instanceof M4 ? 4 : 3);
float[] x = new float[len];
M34 m = (M34) info;
sb.appendC('[');
for (int i = 0; i < len; i++) {
if (i > 0)
sb.appendC(',');
m.getRow(i, x);
sb.append(toJSON(null, x));
}
sb.appendC(']');
break;
}
s = nonArrayString(info);
if (s == null) {
sb.append("[");
int n = AU.getLength(info);
for (int i = 0; i < n; i++) {
if (i > 0)
sb.appendC(',');
sb.append(toJSON(null, arrayGet(info, i)));
}
sb.append("]");
break;
}
info = info.toString();
}
return packageJSON(infoType, (s == null ? sb.toString() : s));
}
/**
* Checks to see if an object is an array (including typed arrays), and if it is, returns null;
* otherwise it returns the string equivalent of that object.
*
* @param x
* @return String or null
*/
public static String nonArrayString(Object x) {
/**
* @j2sNative
*
* return (x.constructor == Array || x.BYTES_PER_ELEMENT ? null : x.toString());
*
*/
{
try {
Array.getLength(x);
return null;
} catch (Exception e) {
return x.toString();
}
}
}
public static String byteArrayToJSON(byte[] data) {
SB sb = new SB();
sb.append("[");
int n = data.length;
for (int i = 0; i < n; i++) {
if (i > 0)
sb.appendC(',');
sb.appendI(data[i] & 0xFF);
}
sb.append("]");
return sb.toString();
}
public static String packageJSON(String infoType, String info) {
return (infoType == null ? info : "\"" + infoType + "\": " + info);
}
public static String escapeUrl(String url) {
url = rep(url, "\n", "");
url = rep(url, "%", "%25");
url = rep(url, "#", "%23");
url = rep(url, "[", "%5B");
url = rep(url, "\\", "%5C");
url = rep(url, "]", "%5D");
url = rep(url, " ", "%20");
return url;
}
private final static String escapable = "\\\\\tt\rr\nn\"\"";
public static String esc(String str) {
if (str == null || str.length() == 0)
return "\"\"";
boolean haveEscape = false;
int i = 0;
for (; i < escapable.length(); i += 2)
if (str.indexOf(escapable.charAt(i)) >= 0) {
haveEscape = true;
break;
}
if (haveEscape)
while (i < escapable.length()) {
int pt = -1;
char ch = escapable.charAt(i++);
char ch2 = escapable.charAt(i++);
SB sb = new SB();
int pt0 = 0;
while ((pt = str.indexOf(ch, pt + 1)) >= 0) {
sb.append(str.substring(pt0, pt)).appendC('\\').appendC(ch2);
pt0 = pt + 1;
}
sb.append(str.substring(pt0, str.length()));
str = sb.toString();
}
return "\"" + escUnicode(str) + "\"";
}
public static String escUnicode(String str) {
for (int i = str.length(); --i >= 0;)
if (str.charAt(i) > 0x7F) {
String s = "0000" + Integer.toHexString(str.charAt(i));
str = str.substring(0, i) + "\\u" + s.substring(s.length() - 4)
+ str.substring(i + 1);
}
return str;
}
/**
* ensures that a float turned to string has a decimal point
*
* @param f
* @return string version of float
*/
public static String escF(float f) {
String sf = "" + f;
/**
* @j2sNative
*
* if (sf.indexOf(".") < 0 && sf.indexOf("e") < 0)
* sf += ".0";
*/
{
}
return sf;
}
public static String join(String[] s, char c, int i0) {
if (s.length < i0)
return null;
SB sb = new SB();
sb.append(s[i0++]);
for (int i = i0; i < s.length; i++)
sb.appendC(c).append(s[i]);
return sb.toString();
}
/**
* a LIKE "x" a is a string and equals x
*
* a LIKE "*x" a is a string and ends with x
*
* a LIKE "x*" a is a string and starts with x
*
* a LIKE "*x*" a is a string and contains x
*
* @param a
* @param b
* @return a LIKE b
*/
public static boolean isLike(String a, String b) {
boolean areEqual = a.equals(b);
if (areEqual)
return true;
boolean isStart = b.startsWith("*");
boolean isEnd = b.endsWith("*");
return (!isStart && !isEnd) ? areEqual
: isStart && isEnd ? b.length() == 1 || a.contains(b.substring(1, b.length() - 1))
: isStart ? a.endsWith(b.substring(1))
: a.startsWith(b.substring(0, b.length() - 1));
}
public static Object getMapValueNoCase(Map h, String key) {
if ("this".equals(key))
return h;
Object val = h.get(key);
if (val == null)
for (Entry e : h.entrySet())
if (e.getKey().equalsIgnoreCase(key))
return e.getValue();
return val;
}
public static String clean(String s) {
return rep(replaceAllCharacters(s, " \t\n\r", " "), " ", " ").trim();
}
/**
*
* fdup duplicates p or q formats for formatCheck
* and the format() function.
*
* @param f
* @param pt
* @param n
* @return %3.5q%3.5q%3.5q%3.5q or %3.5p%3.5p%3.5p
*/
public static String fdup(String f, int pt, int n) {
char ch;
int count = 0;
for (int i = pt; --i >= 1; ) {
if (isDigit(ch = f.charAt(i)))
continue;
switch (ch) {
case '.':
if (count++ != 0)
return f;
continue;
case '-':
if (i != 1 && f.charAt(i - 1) != '.')
return f;
continue;
default:
return f;
}
}
String s = f.substring(0, pt + 1);
SB sb = new SB();
for (int i = 0; i < n; i++)
sb.append(s);
sb.append(f.substring(pt + 1));
return sb.toString();
}
/**
* generic string formatter based on formatLabel in Atom
*
*
* @param strFormat .... %width.precisionKEY....
* @param key any string to match
* @param strT replacement string or null
* @param floatT replacement float or Float.NaN
* @param doubleT replacement double or Double.NaN -- for exponential
* @param doOne mimic sprintf
* @return formatted string
*/
private static String formatString(String strFormat, String key, String strT,
float floatT, double doubleT, boolean doOne) {
if (strFormat == null)
return null;
if ("".equals(strFormat))
return "";
int len = key.length();
if (strFormat.indexOf("%") < 0 || len == 0 || strFormat.indexOf(key) < 0)
return strFormat;
String strLabel = "";
int ich, ichPercent, ichKey;
for (ich = 0; (ichPercent = strFormat.indexOf('%', ich)) >= 0
&& (ichKey = strFormat.indexOf(key, ichPercent + 1)) >= 0;) {
if (ich != ichPercent)
strLabel += strFormat.substring(ich, ichPercent);
ich = ichPercent + 1;
if (ichKey > ichPercent + 6) {
strLabel += '%';
continue;//%12.10x
}
try {
boolean alignLeft = false;
if (strFormat.charAt(ich) == '-') {
alignLeft = true;
++ich;
}
boolean zeroPad = false;
if (strFormat.charAt(ich) == '0') {
zeroPad = true;
++ich;
}
char ch;
int width = 0;
while ((ch = strFormat.charAt(ich)) >= '0' && (ch <= '9')) {
width = (10 * width) + (ch - '0');
++ich;
}
int precision = Integer.MAX_VALUE;
boolean isExponential = false;
if (strFormat.charAt(ich) == '.') {
++ich;
if ((ch = strFormat.charAt(ich)) == '-') {
isExponential = (strT == null);
++ich;
}
if ((ch = strFormat.charAt(ich)) >= '0' && ch <= '9') {
precision = ch - '0';
++ich;
}
if (isExponential)
precision = -precision;
}
String st = strFormat.substring(ich, ich + len);
if (!st.equals(key)) {
ich = ichPercent + 1;
strLabel += '%';
continue;
}
ich += len;
if (!Float.isNaN(floatT)) // 'f'
strLabel += formatF(floatT, width, precision, alignLeft,
zeroPad);
else if (strT != null) // 'd' 'i' or 's'
strLabel += formatS(strT, width, precision, alignLeft,
zeroPad);
else if (!Double.isNaN(doubleT)) // 'e'
strLabel += formatD(doubleT, width, precision - 1, alignLeft,
zeroPad, true);
if (doOne)
break;
} catch (IndexOutOfBoundsException ioobe) {
ich = ichPercent;
break;
}
}
strLabel += strFormat.substring(ich);
//if (strLabel.length() == 0)
//return null;
return strLabel;
}
public static String formatStringS(String strFormat, String key, String strT) {
return formatString(strFormat, key, strT, Float.NaN, Double.NaN, false);
}
public static String formatStringF(String strFormat, String key, float floatT) {
return formatString(strFormat, key, null, floatT, Double.NaN, false);
}
public static String formatStringI(String strFormat, String key, int intT) {
return formatString(strFormat, key, "" + intT, Float.NaN, Double.NaN, false);
}
/**
* sprintf emulation uses (almost) c++ standard string formats
*
* 's' string 'i' or 'd' integer, 'e' double, 'f' float, 'p' point3f 'q'
* quaternion/plane/axisangle with added "i" (equal to the insipid "d" --
* digits?)
*
* @param strFormat
* @param list
* a listing of what sort of data will be found in Object[] values, in
* order: s string, f float, i integer, d double, p point3f, q
* quaternion/point4f, S String[], F float[], I int[], and D double[]
* @param values
* Object[] containing above types
* @return formatted string
*/
public static String sprintf(String strFormat, String list, Object[] values) {
if (values == null)
return strFormat;
int n = list.length();
if (n == values.length)
try {
for (int o = 0; o < n; o++) {
if (values[o] == null)
continue;
switch (list.charAt(o)) {
case 's':
strFormat = formatString(strFormat, "s", (String) values[o],
Float.NaN, Double.NaN, true);
break;
case 'f':
strFormat = formatString(strFormat, "f", null, ((Float) values[o])
.floatValue(), Double.NaN, true);
break;
case 'i':
strFormat = formatString(strFormat, "d", "" + values[o], Float.NaN,
Double.NaN, true);
strFormat = formatString(strFormat, "i", "" + values[o], Float.NaN,
Double.NaN, true);
break;
case 'd':
strFormat = formatString(strFormat, "e", null, Float.NaN,
((Double) values[o]).doubleValue(), true);
break;
case 'p':
T3 pVal = (T3) values[o];
strFormat = formatString(strFormat, "p", null, pVal.x, Double.NaN,
true);
strFormat = formatString(strFormat, "p", null, pVal.y, Double.NaN,
true);
strFormat = formatString(strFormat, "p", null, pVal.z, Double.NaN,
true);
break;
case 'q':
T4 qVal = (T4) values[o];
strFormat = formatString(strFormat, "q", null, qVal.x, Double.NaN,
true);
strFormat = formatString(strFormat, "q", null, qVal.y, Double.NaN,
true);
strFormat = formatString(strFormat, "q", null, qVal.z, Double.NaN,
true);
strFormat = formatString(strFormat, "q", null, qVal.w, Double.NaN,
true);
break;
case 'S':
String[] sVal = (String[]) values[o];
for (int i = 0; i < sVal.length; i++)
strFormat = formatString(strFormat, "s", sVal[i], Float.NaN,
Double.NaN, true);
break;
case 'F':
float[] fVal = (float[]) values[o];
for (int i = 0; i < fVal.length; i++)
strFormat = formatString(strFormat, "f", null, fVal[i],
Double.NaN, true);
break;
case 'I':
int[] iVal = (int[]) values[o];
for (int i = 0; i < iVal.length; i++)
strFormat = formatString(strFormat, "d", "" + iVal[i], Float.NaN,
Double.NaN, true);
for (int i = 0; i < iVal.length; i++)
strFormat = formatString(strFormat, "i", "" + iVal[i], Float.NaN,
Double.NaN, true);
break;
case 'D':
double[] dVal = (double[]) values[o];
for (int i = 0; i < dVal.length; i++)
strFormat = formatString(strFormat, "e", null, Float.NaN,
dVal[i], true);
}
}
return rep(strFormat, "%%", "%");
} catch (Exception e) {
//
}
System.out.println("TextFormat.sprintf error " + list + " " + strFormat);
return rep(strFormat, "%", "?");
}
/**
*
* formatCheck checks p and q formats and duplicates if necessary
* "%10.5p xxxx" ==> "%10.5p%10.5p%10.5p xxxx"
*
* @param strFormat
* @return f or dupicated format
*/
public static String formatCheck(String strFormat) {
if (strFormat == null || strFormat.indexOf('p') < 0 && strFormat.indexOf('q') < 0)
return strFormat;
strFormat = rep(strFormat, "%%", "\1");
strFormat = rep(strFormat, "%p", "%6.2p");
strFormat = rep(strFormat, "%q", "%6.2q");
String[] format = split(strFormat, "%");
SB sb = new SB();
sb.append(format[0]);
for (int i = 1; i < format.length; i++) {
String f = "%" + format[i];
int pt;
if (f.length() >= 3) {
if ((pt = f.indexOf('p')) >= 0)
f = fdup(f, pt, 3);
if ((pt = f.indexOf('q')) >= 0)
f = fdup(f, pt, 4);
}
sb.append(f);
}
return sb.toString().replace('\1', '%');
}
public static void leftJustify(SB s, String s1, String s2) {
s.append(s2);
int n = s1.length() - s2.length();
if (n > 0)
s.append(s1.substring(0, n));
}
public static void rightJustify(SB s, String s1, String s2) {
int n = s1.length() - s2.length();
if (n > 0)
s.append(s1.substring(0, n));
s.append(s2);
}
public static String safeTruncate(float f, int n) {
if (f > -0.001 && f < 0.001)
f = 0;
return (f + " ").substring(0,n);
}
public static boolean isWild(String s) {
return s != null && (s.indexOf("*") >= 0 || s.indexOf("?") >= 0);
}
/**
* A general non-regex (for performance) text matcher that utilizes ? and *.
*
* ??? means "at most three" characters if at beginning or end;
* "exactly three" otherwise
* \1 in search is a stand-in for actual ?
*
* @param search
* the string to search
* @param match
* the match string
* @param checkStar
* @param allowInitialStar
* @return true if found
*/
public static boolean isMatch(String search, String match, boolean checkStar,
boolean allowInitialStar) {
// search == match --> true
if (search.equals(match))
return true;
int mLen = match.length();
// match == "" --> false
if (mLen == 0)
return false;
boolean isStar0 = (checkStar && allowInitialStar ? match.charAt(0) == '*'
: false);
// match == "*" --> true
if (mLen == 1 && isStar0)
return true;
boolean isStar1 = (checkStar && match.endsWith("*"));
boolean haveQ = (match.indexOf('?') >= 0);
// match == "**" --> true
// match == "*xxx*" --> search contains "xxx"
// match == "*xxx" --> search ends with "xxx"
// match == "xxx*" --> search starts with "xxx"
if (!haveQ) {
if (isStar0)
return (isStar1 ? (mLen < 3 || search.indexOf(match.substring(1,
mLen - 1)) >= 0) : search.endsWith(match.substring(1)));
else if (isStar1)
return search.startsWith(match.substring(0, mLen - 1));
}
int sLen = search.length();
// pad match with "?" -- same as *
String qqqq = "????";
int nq = 4;
while (nq < sLen) {
qqqq += qqqq;
nq += 4;
}
if (checkStar) {
if (isStar0) {
match = qqqq + match.substring(1);
mLen += nq - 1;
}
if (isStar1) {
match = match.substring(0, mLen - 1) + qqqq;
mLen += nq - 1;
}
}
// length of match < length of search --> false
if (mLen < sLen)
return false;
// -- each ? matches ONE character if not at end
// -- extra ? at end ignored
// (allowInitialStar == true)
// -- extra ? at beginning reduced to match length
int ich = 0;
while (mLen > sLen) {
if (allowInitialStar && match.charAt(ich) == '?') {
++ich;
} else if (match.charAt(ich + mLen - 1) != '?') {
return false;
}
--mLen;
}
// both are effectively same length now.
// \1 is stand-in for "?"
for (int i = sLen; --i >= 0;) {
char chm = match.charAt(ich + i);
if (chm == '?')
continue;
char chs = search.charAt(i);
if (chm != chs && (chm != '\1' || chs != '?'))
return false;
}
return true;
}
public static String replaceQuotedStrings(String s, Lst list,
Lst newList) {
int n = list.size();
for (int i = 0; i < n; i++) {
String name = list.get(i);
String newName = newList.get(i);
if (!newName.equals(name))
s = rep(s, "\"" + name + "\"", "\"" + newName
+ "\"");
}
return s;
}
public static String replaceStrings(String s, Lst list,
Lst newList) {
int n = list.size();
for (int i = 0; i < n; i++) {
String name = list.get(i);
String newName = newList.get(i);
if (!newName.equals(name))
s = rep(s, name, newName);
}
return s;
}
public static boolean isDigit(char ch) {
// just way simpler code than Character.isDigit(ch);
int c = ch;
return (48 <= c && c <= 57);
}
public static boolean isUpperCase(char ch) {
int c = ch;
return (65 <= c && c <= 90);
}
public static boolean isLowerCase(char ch) {
int c = ch;
return (97 <= c && c <= 122);
}
public static boolean isLetter(char ch) {
// just way simpler code than Character.isLetter(ch);
int c = ch;
return (65 <= c && c <= 90 || 97 <= c && c <= 122);
}
public static boolean isLetterOrDigit(char ch) {
// just way simpler code than Character.isLetterOrDigit(ch);
int c = ch;
return (65 <= c && c <= 90 || 97 <= c && c <= 122 || 48 <= c && c <= 57);
}
public static boolean isWhitespace(char ch) {
int c = ch;
return (c >= 0x1c && c <= 0x20 || c >= 0x9 && c <= 0xd);
}
public static final float FRACTIONAL_PRECISION = 100000f;
public static final float CARTESIAN_PRECISION = 10000f;
public static void fixPtFloats(T3 pt, float f) {
//this will equate float and double as long as -256 <= x <= 256
pt.x = Math.round(pt.x * f) / f;
pt.y = Math.round(pt.y * f) / f;
pt.z = Math.round(pt.z * f) / f;
}
public static double fixDouble(double d, double f) {
return Math.round(d * f) / f;
}
/**
* parse a float or "float/float"
* @param s
* @return a/b
*/
public static float parseFloatFraction(String s) {
int pt = s.indexOf("/");
return (pt < 0 ? parseFloat(s) : parseFloat(s.substring(0, pt))
/ parseFloat(s.substring(pt + 1)));
}
//static {
//
// double d = 790.8999998888;
// float x = 790.8999998888f;
// for (int i = 0; i < 50; i++) {
// System.out.println(x + " " + d);
// System.out.println(Math.round(x * 100000) / 100000f);
// System.out.println(Math.round(d * 100000) / 100000.);
// System.out.println(Math.round(x * 10000) / 10000f);
// System.out.println(Math.round(d * 10000) / 10000.);
// x+=1;
// d+=1;
// }
// System.out.println(100.123456789f);
//}
// static {
// long t;
// char c = '0';
// t = System.currentTimeMillis();
// for (int i = 0; i < 10000000; i++) {
// boolean b = PT.isUpperCase(c);
// }
// System.out.println(System.currentTimeMillis() - t);
//
// t = System.currentTimeMillis();
// for (int i = 0; i < 10000000; i++) {
// boolean b = Character.isUpperCase(c);
// }
// System.out.println(System.currentTimeMillis() - t);
//
// t = System.currentTimeMillis();
// for (int i = 0; i < 10000000; i++) {
// boolean b = PT.isUpperCase(c);
// }
// System.out.println(System.currentTimeMillis() - t);
//
// System.out.println("PT test");
// }
}