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