3 * $Date: 2007-04-26 16:57:51 -0500 (Thu, 26 Apr 2007) $
\r
6 * Copyright (C) 2005 The Jmol Development Team
\r
8 * Contact: jmol-developers@lists.sf.net
\r
10 * This library is free software; you can redistribute it and/or
\r
11 * modify it under the terms of the GNU Lesser General Public
\r
12 * License as published by the Free Software Foundation; either
\r
13 * version 2.1 of the License, or (at your option) any later version.
\r
15 * This library is distributed in the hope that it will be useful,
\r
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
\r
18 * Lesser General Public License for more details.
\r
20 * You should have received a copy of the GNU Lesser General Public
\r
21 * License along with this library; if not, write to the Free Software
\r
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
\r
25 package javajs.util;
\r
27 import java.lang.reflect.Array;
\r
28 import java.util.Map;
\r
29 import java.util.Map.Entry;
\r
31 import javajs.J2SIgnoreImport;
\r
32 import javajs.api.JSONEncodable;
\r
35 * a combination of Parsing and Text-related utility classes
\r
41 @J2SIgnoreImport(value = { java.lang.reflect.Array.class })
\r
44 public static int parseInt(String str) {
\r
45 return parseIntNext(str, new int[] {0});
\r
48 public static int parseIntNext(String str, int[] next) {
\r
49 int cch = str.length();
\r
50 if (next[0] < 0 || next[0] >= cch)
\r
51 return Integer.MIN_VALUE;
\r
52 return parseIntChecked(str, cch, next);
\r
55 public static int parseIntChecked(String str, int ichMax, int[] next) {
\r
56 boolean digitSeen = false;
\r
60 return Integer.MIN_VALUE;
\r
62 while (ich < ichMax && isWhiteSpace(str, ich))
\r
64 boolean negative = false;
\r
65 if (ich < ichMax && str.charAt(ich) == 45) { //"-"
\r
69 while (ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) {
\r
70 value = value * 10 + (ch - 48);
\r
74 if (!digitSeen)// || !checkTrailingText(str, ich, ichMax))
\r
75 value = Integer.MIN_VALUE;
\r
82 public static boolean isWhiteSpace(String str, int ich) {
\r
84 return (ich >= 0 && ((ch = str.charAt(ich)) == ' ' || ch == '\t' || ch == '\n'));
\r
88 * A float parser that is 30% faster than Float.parseFloat(x) and also accepts
\r
94 * pointer; incremented
\r
96 * @return value or Float.NaN
\r
98 public static float parseFloatChecked(String str, int ichMax, int[] next,
\r
100 boolean digitSeen = false;
\r
102 if (isStrict && str.indexOf('\n') != str.lastIndexOf('\n'))
\r
104 while (ich < ichMax && isWhiteSpace(str, ich))
\r
106 boolean negative = false;
\r
107 if (ich < ichMax && str.charAt(ich) == '-') {
\r
111 // looks crazy, but if we don't do this, Google Closure Compiler will
\r
112 // write code that Safari will misinterpret in a VERY nasty way --
\r
113 // getting totally confused as to long integers and double values
\r
115 // This is Safari figuring out the values of the numbers on the line (x, y, then z):
\r
117 // ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C
\r
120 // ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C
\r
123 // ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C
\r
127 // "e" values are just before and after the "value = -value" statement.
\r
132 while (ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) {
\r
133 ival = (ival * 10f) + (ch - 48)*1f;
\r
137 boolean isDecimal = false;
\r
139 int nzero = (ival == 0 ? -1 : 0);
\r
142 while (++ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) {
\r
151 if (iscale < decimalScale.length) {
\r
152 ival2 = (ival2 * 10f) + (ch - 48)*1f;
\r
159 // Safari breaks here intermittently converting integers to floats
\r
163 } else if (ival2 > 0) {
\r
164 value = ival2 * decimalScale[iscale - 1];
\r
166 if (nzero - 2 < decimalScale.length) {
\r
167 value *= decimalScale[nzero - 2];
\r
169 value *= Math.pow(10, 1 - nzero);
\r
177 boolean isExponent = false;
\r
178 if (ich < ichMax && (ch == 69 || ch == 101 || ch == 68)) { // E e D
\r
180 if (++ich >= ichMax)
\r
182 ch = str.charAt(ich);
\r
183 if ((ch == '+') && (++ich >= ichMax))
\r
186 int exponent = parseIntChecked(str, ichMax, next);
\r
187 if (exponent == Integer.MIN_VALUE)
\r
189 if (exponent > 0 && exponent <= tensScale.length)
\r
190 value *= tensScale[exponent - 1];
\r
191 else if (exponent < 0 && -exponent <= decimalScale.length)
\r
192 value *= decimalScale[-exponent - 1];
\r
193 else if (exponent != 0)
\r
194 value *= Math.pow(10, exponent);
\r
196 next[0] = ich; // the exponent code finds its own ichNextParse
\r
198 // believe it or not, Safari reports the long-equivalent of the
\r
199 // float value here, then later the float value, after no operation!
\r
202 if (value == Float.POSITIVE_INFINITY)
\r
203 value = Float.MAX_VALUE;
\r
204 return (!isStrict || (!isExponent || isDecimal)
\r
205 && checkTrailingText(str, next[0], ichMax) ? value : Float.NaN);
\r
208 public final static float[] tensScale = { 10f, 100f, 1000f, 10000f, 100000f, 1000000f };
\r
209 public final static float[] decimalScale = {
\r
220 public static boolean checkTrailingText(String str, int ich, int ichMax) {
\r
221 //number must be pure -- no additional characters other than white space or ;
\r
223 while (ich < ichMax && (isWhitespace(ch = str.charAt(ich)) || ch == ';'))
\r
225 return (ich == ichMax);
\r
228 public static float[] parseFloatArray(String str) {
\r
229 return parseFloatArrayNext(str, new int[1], null, null, null);
\r
232 public static int parseFloatArrayInfested(String[] tokens, float[] data) {
\r
233 int len = data.length;
\r
234 int nTokens = tokens.length;
\r
237 for (int i = 0; i >= 0 && i < len && n < nTokens; i++) {
\r
239 while (Float.isNaN(f = parseFloat(tokens[n++]))
\r
242 if (!Float.isNaN(f))
\r
243 data[(max = i)] = f;
\r
254 * @param strStart or null
\r
255 * @param strEnd or null
\r
256 * @return array of float values
\r
259 public static float[] parseFloatArrayNext(String str, int[] next, float[] f,
\r
260 String strStart, String strEnd) {
\r
264 if (strStart != null) {
\r
265 int p = str.indexOf(strStart, pt);
\r
267 next[0] = p + strStart.length();
\r
269 str = str.substring(next[0]);
\r
270 pt = (strEnd == null ? -1 : str.indexOf(strEnd));
\r
274 str = str.substring(0, pt);
\r
276 String[] tokens = getTokens(str);
\r
278 f = new float[tokens.length];
\r
279 n = parseFloatArrayInfested(tokens, f);
\r
282 return new float[0];
\r
283 for (int i = n; i < f.length; i++)
\r
288 public static float parseFloatRange(String str, int ichMax, int[] next) {
\r
289 int cch = str.length();
\r
292 if (next[0] < 0 || next[0] >= ichMax)
\r
294 return parseFloatChecked(str, ichMax, next, false);
\r
297 public static float parseFloatNext(String str, int[] next) {
\r
298 int cch = (str == null ? -1 : str.length());
\r
299 return (next[0] < 0 || next[0] >= cch ? Float.NaN : parseFloatChecked(str, cch, next, false));
\r
302 public static float parseFloatStrict(String str) {
\r
303 // checks trailing characters and does not allow "1E35" to be float
\r
304 int cch = str.length();
\r
307 return parseFloatChecked(str, cch, new int[] {0}, true);
\r
310 public static float parseFloat(String str) {
\r
311 return parseFloatNext(str, new int[] {0});
\r
314 public static int parseIntRadix(String s, int i) throws NumberFormatException {
\r
317 * JavaScript uses parseIntRadix
\r
321 * return Integer.parseIntRadix(s, i);
\r
325 return Integer.parseInt(s, i);
\r
329 public static String[] getTokens(String line) {
\r
330 return getTokensAt(line, 0);
\r
333 public static String parseToken(String str) {
\r
334 return parseTokenNext(str, new int[] {0});
\r
337 public static String parseTrimmed(String str) {
\r
338 return parseTrimmedRange(str, 0, str.length());
\r
341 public static String parseTrimmedAt(String str, int ichStart) {
\r
342 return parseTrimmedRange(str, ichStart, str.length());
\r
345 public static String parseTrimmedRange(String str, int ichStart, int ichMax) {
\r
346 int cch = str.length();
\r
349 if (cch < ichStart)
\r
351 return parseTrimmedChecked(str, ichStart, cch);
\r
354 public static String[] getTokensAt(String line, int ich) {
\r
357 int cchLine = line.length();
\r
358 if (ich < 0 || ich > cchLine)
\r
360 int tokenCount = countTokens(line, ich);
\r
361 String[] tokens = new String[tokenCount];
\r
362 int[] next = new int[1];
\r
364 for (int i = 0; i < tokenCount; ++i)
\r
365 tokens[i] = parseTokenChecked(line, cchLine, next);
\r
369 public static int countChar(String line, char c) {
\r
370 int tokenCount = 0;
\r
372 while ((pt = line.indexOf(c, pt + 1)) >= 0)
\r
377 public static int countTokens(String line, int ich) {
\r
378 int tokenCount = 0;
\r
379 if (line != null) {
\r
380 int ichMax = line.length();
\r
382 while (ich < ichMax && isWhiteSpace(line, ich))
\r
389 } while (ich < ichMax && !isWhiteSpace(line, ich));
\r
395 public static String parseTokenNext(String str, int[] next) {
\r
396 int cch = str.length();
\r
397 return (next[0] < 0 || next[0] >= cch ? null : parseTokenChecked(str, cch, next));
\r
400 public static String parseTokenRange(String str, int ichMax, int[] next) {
\r
401 int cch = str.length();
\r
404 return (next[0] < 0 || next[0] >= ichMax ? null : parseTokenChecked(str, ichMax, next));
\r
407 public static String parseTokenChecked(String str, int ichMax, int[] next) {
\r
409 while (ich < ichMax && isWhiteSpace(str, ich))
\r
411 int ichNonWhite = ich;
\r
412 while (ich < ichMax && !isWhiteSpace(str, ich))
\r
415 return (ichNonWhite == ich ? null : str.substring(ichNonWhite, ich));
\r
418 public static String parseTrimmedChecked(String str, int ich, int ichMax) {
\r
419 while (ich < ichMax && isWhiteSpace(str, ich))
\r
421 int ichLast = ichMax - 1;
\r
422 while (ichLast >= ich && isWhiteSpace(str, ichLast))
\r
424 return (ichLast < ich ? "" : str.substring(ich, ichLast + 1));
\r
427 public static double dVal(String s) throws NumberFormatException {
\r
432 * throw new NumberFormatException("null");
\r
433 * var d=parseFloat(s);
\r
435 * throw new NumberFormatException("Not a Number : "+s);
\r
440 return Double.valueOf(s).doubleValue();
\r
444 public static float fVal(String s) throws NumberFormatException {
\r
448 * return this.dVal(s);
\r
451 return Float.parseFloat(s);
\r
455 public static int parseIntRange(String str, int ichMax, int[] next) {
\r
456 int cch = str.length();
\r
459 return (next[0] < 0 || next[0] >= ichMax ? Integer.MIN_VALUE : parseIntChecked(str, ichMax, next));
\r
463 * parses a string array for floats. Returns NaN for nonfloats.
\r
465 * @param tokens the strings to parse
\r
466 * @param data the array to fill
\r
468 public static void parseFloatArrayData(String[] tokens, float[] data) {
\r
469 parseFloatArrayDataN(tokens, data, data.length);
\r
473 * parses a string array for floats. Returns NaN for nonfloats or missing data.
\r
475 * @param tokens the strings to parse
\r
476 * @param data the array to fill
\r
477 * @param nData the number of elements
\r
479 public static void parseFloatArrayDataN(String[] tokens, float[] data, int nData) {
\r
480 for (int i = nData; --i >= 0;)
\r
481 data[i] = (i >= tokens.length ? Float.NaN : parseFloat(tokens[i]));
\r
486 * proper splitting, even for Java 1.3 -- if the text ends in the run,
\r
487 * no new line is appended.
\r
491 * @return String array
\r
493 public static String[] split(String text, String run) {
\r
494 if (text.length() == 0)
\r
495 return new String[0];
\r
497 int i = text.indexOf(run);
\r
499 int runLen = run.length();
\r
500 if (i < 0 || runLen == 0) {
\r
501 lines = new String[1];
\r
505 int len = text.length() - runLen;
\r
506 for (; i >= 0 && i < len; n++)
\r
507 i = text.indexOf(run, i + runLen);
\r
508 lines = new String[n];
\r
512 for (; (ipt = text.indexOf(run, i)) >= 0 && pt + 1 < n;) {
\r
513 lines[pt++] = text.substring(i, ipt);
\r
516 if (text.indexOf(run, len) != len)
\r
518 lines[pt] = text.substring(i, len);
\r
522 public final static float FLOAT_MIN_SAFE = 2E-45f;
\r
523 // Float.MIN_VALUE (1.45E-45) is not reliable with JavaScript because of the float/double difference there
\r
525 /// general static string-parsing class ///
\r
527 // next[0] tracks the pointer within the string so these can all be static.
\r
528 // but the methods parseFloat, parseInt, parseToken, parseTrimmed, and getTokens do not require this.
\r
530 // public static String concatTokens(String[] tokens, int iFirst, int iEnd) {
\r
531 // String str = "";
\r
532 // String sep = "";
\r
533 // for (int i = iFirst; i < iEnd; i++) {
\r
534 // if (i < tokens.length) {
\r
535 // str += sep + tokens[i];
\r
542 public static String getQuotedStringAt(String line, int ipt0) {
\r
543 int[] next = new int[] { ipt0 };
\r
544 return getQuotedStringNext(line, next);
\r
550 * @param next passes [current pointer]
\r
551 * @return quoted string -- does NOT unescape characters
\r
553 public static String getQuotedStringNext(String line, int[] next) {
\r
555 if (i < 0 || (i = line.indexOf("\"", i)) < 0)
\r
558 int len = line.length();
\r
559 while (++i < len && line.charAt(i) != '"')
\r
560 if (line.charAt(i) == '\\')
\r
563 return line.substring(pt, i);
\r
567 * CSV format -- escaped quote is "" WITHIN "..."
\r
571 * @param next int[2] filled with [ptrQuote1, ptrAfterQuote2]
\r
572 * next[1] will be -1 if unmatched quotes are found (continuation on next line)
\r
573 * @return unescaped string or null
\r
575 public static String getCSVString(String line, int[] next) {
\r
577 if (i < 0 || (i = line.indexOf("\"", i)) < 0)
\r
579 int pt = next[0] = i;
\r
580 int len = line.length();
\r
581 boolean escaped = false;
\r
582 boolean haveEscape = false;
\r
584 && (line.charAt(i) != '"' || (escaped = (i + 1 < len && line.charAt(i + 1) == '"'))))
\r
592 return null; // unmatched
\r
595 String s = line.substring(pt + 1, i);
\r
596 return (haveEscape ? rep(rep(s, "\"\"", "\0"), "\0","\"") : s);
\r
599 public static boolean isOneOf(String key, String semiList) {
\r
600 if (semiList.length() == 0)
\r
602 if (semiList.charAt(0) != ';')
\r
603 semiList = ";" + semiList + ";";
\r
604 return key.indexOf(";") < 0 && semiList.indexOf(';' + key + ';') >= 0;
\r
607 public static String getQuotedAttribute(String info, String name) {
\r
608 int i = info.indexOf(name + "=");
\r
609 return (i < 0 ? null : getQuotedStringAt(info, i));
\r
612 public static float approx(float f, float n) {
\r
613 return Math.round (f * n) / n;
\r
617 * Does a clean ITERATIVE replace of strFrom in str with strTo.
\r
618 * Thus, rep("Testttt", "tt","t") becomes "Test".
\r
623 * @return replaced string
\r
625 public static String rep(String str, String strFrom, String strTo) {
\r
626 if (str == null || strFrom.length() == 0 || str.indexOf(strFrom) < 0)
\r
628 boolean isOnce = (strTo.indexOf(strFrom) >= 0);
\r
630 str = str.replace(strFrom, strTo);
\r
631 } while (!isOnce && str.indexOf(strFrom) >= 0);
\r
635 public static String formatF(float value, int width, int precision,
\r
636 boolean alignLeft, boolean zeroPad) {
\r
637 return formatS(DF.formatDecimal(value, precision), width, 0, alignLeft, zeroPad);
\r
647 * @param allowOverflow IGNORED
\r
648 * @return formatted string
\r
650 public static String formatD(double value, int width, int precision,
\r
651 boolean alignLeft, boolean zeroPad, boolean allowOverflow) {
\r
652 return formatS(DF.formatDecimal((float)value, -1 - precision), width, 0, alignLeft, zeroPad);
\r
658 * @param width number of columns
\r
659 * @param precision precision > 0 ==> precision = number of characters max from left
\r
660 * precision < 0 ==> -1 - precision = number of char. max from right
\r
662 * @param zeroPad generally for numbers turned strings
\r
663 * @return formatted string
\r
665 public static String formatS(String value, int width, int precision,
\r
666 boolean alignLeft, boolean zeroPad) {
\r
669 int len = value.length();
\r
670 if (precision != Integer.MAX_VALUE && precision > 0
\r
671 && precision < len)
\r
672 value = value.substring(0, precision);
\r
673 else if (precision < 0 && len + precision >= 0)
\r
674 value = value.substring(len + precision + 1);
\r
676 int padLength = width - value.length();
\r
677 if (padLength <= 0)
\r
679 boolean isNeg = (zeroPad && !alignLeft && value.charAt(0) == '-');
\r
680 char padChar = (zeroPad ? '0' : ' ');
\r
681 char padChar0 = (isNeg ? '-' : padChar);
\r
686 sb.appendC(padChar0);
\r
687 for (int i = padLength; --i > 0;)
\r
688 // this is correct, not >= 0
\r
689 sb.appendC(padChar);
\r
691 sb.append(isNeg ? padChar + value.substring(1) : value);
\r
692 return sb.toString();
\r
696 * Does a clean replace of any of the characters in str with chrTo
\r
697 * If strTo contains strFrom, then only a single pass is done.
\r
698 * Otherwise, multiple passes are made until no more replacements can be made.
\r
703 * @return replaced string
\r
705 public static String replaceWithCharacter(String str, String strFrom,
\r
709 for (int i = strFrom.length(); --i >= 0;)
\r
710 str = str.replace(strFrom.charAt(i), chTo);
\r
715 * Does a clean replace of any of the characters in str with strTo
\r
716 * If strTo contains strFrom, then only a single pass is done.
\r
717 * Otherwise, multiple passes are made until no more replacements can be made.
\r
722 * @return replaced string
\r
724 public static String replaceAllCharacters(String str, String strFrom,
\r
726 for (int i = strFrom.length(); --i >= 0;) {
\r
727 String chFrom = strFrom.substring(i, i + 1);
\r
728 str = rep(str, chFrom, strTo);
\r
733 public static String trim(String str, String chars) {
\r
734 if (str == null || str.length() == 0)
\r
736 if (chars.length() == 0)
\r
738 int len = str.length();
\r
740 while (k < len && chars.indexOf(str.charAt(k)) >= 0)
\r
742 int m = str.length() - 1;
\r
743 while (m > k && chars.indexOf(str.charAt(m)) >= 0)
\r
745 return str.substring(k, m + 1);
\r
748 public static String trimQuotes(String value) {
\r
749 return (value != null && value.length() > 1 && value.startsWith("\"")
\r
750 && value.endsWith("\"") ? value.substring(1, value.length() - 1)
\r
754 public static boolean isNonStringPrimitive(Object info) {
\r
755 // note that we don't use Double, Float, or Integer here
\r
756 // because in JavaScript those would be false for unwrapped primitives
\r
757 // coming from equivalent of Array.get()
\r
758 // Strings will need their own escaped processing
\r
760 return info instanceof Number || info instanceof Boolean;
\r
763 private static Object arrayGet(Object info, int i) {
\r
766 * Note that info will be a primitive in JavaScript
\r
767 * but a wrapped primitive in Java.
\r
774 return Array.get(info, i);
\r
778 @SuppressWarnings("unchecked")
\r
779 public static String toJSON(String infoType, Object info) {
\r
781 return packageJSON(infoType, null);
\r
782 if (isNonStringPrimitive(info))
\r
783 return packageJSON(infoType, info.toString());
\r
787 if (info instanceof String) {
\r
792 * if (typeof s == "undefined") s = "null"
\r
796 if (s.indexOf("{\"") != 0) {
\r
797 //don't doubly fix JSON strings when retrieving status
\r
798 s = rep(s, "\"", "\\\"");
\r
799 s = rep(s, "\n", "\\n");
\r
800 s = "\"" + s + "\"";
\r
804 if (info instanceof JSONEncodable) {
\r
805 // includes javajs.util.BS, org.jmol.script.SV
\r
806 if ((s = ((JSONEncodable) info).toJSON()) == null)
\r
807 s = "null"; // perhaps a list has a null value (group3List, for example)
\r
811 if (info instanceof Map) {
\r
814 for (String key : ((Map<String, ?>) info).keySet()) {
\r
815 sb.append(sep).append(
\r
816 packageJSON(key, toJSON(null, ((Map<?, ?>) info).get(key))));
\r
822 if (info instanceof Lst) {
\r
824 int n = ((Lst<?>) info).size();
\r
825 for (int i = 0; i < n; i++) {
\r
828 sb.append(toJSON(null, ((Lst<?>) info).get(i)));
\r
833 if (info instanceof M34) {
\r
835 int len = (info instanceof M4 ? 4 : 3);
\r
836 float[] x = new float[len];
\r
837 M34 m = (M34) info;
\r
839 for (int i = 0; i < len; i++) {
\r
843 sb.append(toJSON(null, x));
\r
848 s = nonArrayString(info);
\r
851 int n = AU.getLength(info);
\r
852 for (int i = 0; i < n; i++) {
\r
855 sb.append(toJSON(null, arrayGet(info, i)));
\r
860 info = info.toString();
\r
862 return packageJSON(infoType, (s == null ? sb.toString() : s));
\r
866 * Checks to see if an object is an array, and if it is, returns null;
\r
867 * otherwise it returns the string equivalent of that object.
\r
870 * @return String or null
\r
872 public static String nonArrayString(Object x) {
\r
876 * var s = x.toString(); return (s.startsWith("[object") &&
\r
877 * s.endsWith("Array]") ? null : s);
\r
882 Array.getLength(x);
\r
884 } catch (Exception e) {
\r
885 return x.toString();
\r
890 public static String byteArrayToJSON(byte[] data) {
\r
893 int n = data.length;
\r
894 for (int i = 0; i < n; i++) {
\r
897 sb.appendI(data[i] & 0xFF);
\r
900 return sb.toString();
\r
903 public static String packageJSON(String infoType, String info) {
\r
904 return (infoType == null ? info : "\"" + infoType + "\": " + info);
\r
907 public static String escapeUrl(String url) {
\r
908 url = rep(url, "\n", "");
\r
909 url = rep(url, "%", "%25");
\r
910 url = rep(url, "#", "%23");
\r
911 url = rep(url, "[", "%5B");
\r
912 url = rep(url, "]", "%5D");
\r
913 url = rep(url, " ", "%20");
\r
917 private final static String escapable = "\\\\\tt\rr\nn\"\"";
\r
919 public static String esc(String str) {
\r
920 if (str == null || str.length() == 0)
\r
922 boolean haveEscape = false;
\r
924 for (; i < escapable.length(); i += 2)
\r
925 if (str.indexOf(escapable.charAt(i)) >= 0) {
\r
930 while (i < escapable.length()) {
\r
932 char ch = escapable.charAt(i++);
\r
933 char ch2 = escapable.charAt(i++);
\r
936 while ((pt = str.indexOf(ch, pt + 1)) >= 0) {
\r
937 sb.append(str.substring(pt0, pt)).appendC('\\').appendC(ch2);
\r
940 sb.append(str.substring(pt0, str.length()));
\r
941 str = sb.toString();
\r
943 return "\"" + escUnicode(str) + "\"";
\r
946 public static String escUnicode(String str) {
\r
947 for (int i = str.length(); --i >= 0;)
\r
948 if (str.charAt(i) > 0x7F) {
\r
949 String s = "0000" + Integer.toHexString(str.charAt(i));
\r
950 str = str.substring(0, i) + "\\u" + s.substring(s.length() - 4)
\r
951 + str.substring(i + 1);
\r
957 * ensures that a float turned to string has a decimal point
\r
960 * @return string version of float
\r
962 public static String escF(float f) {
\r
963 String sf = "" + f;
\r
967 * if (sf.indexOf(".") < 0 && sf.indexOf("e") < 0)
\r
974 public static String join(String[] s, char c, int i0) {
\r
978 sb.append(s[i0++]);
\r
979 for (int i = i0; i < s.length; i++)
\r
980 sb.appendC(c).append(s[i]);
\r
981 return sb.toString();
\r
985 * a LIKE "x" a is a string and equals x
\r
987 * a LIKE "*x" a is a string and ends with x
\r
989 * a LIKE "x*" a is a string and starts with x
\r
991 * a LIKE "*x*" a is a string and contains x
\r
997 public static boolean isLike(String a, String b) {
\r
998 boolean areEqual = a.equals(b);
\r
1001 boolean isStart = b.startsWith("*");
\r
1002 boolean isEnd = b.endsWith("*");
\r
1003 return (!isStart && !isEnd) ? areEqual
\r
1004 : isStart && isEnd ? b.length() == 1 || a.contains(b.substring(1, b.length() - 1))
\r
1005 : isStart ? a.endsWith(b.substring(1))
\r
1006 : a.startsWith(b.substring(0, b.length() - 1));
\r
1009 public static Object getMapValueNoCase(Map<String, ?> h, String key) {
\r
1010 if ("this".equals(key))
\r
1012 Object val = h.get(key);
\r
1014 for (Entry<String, ?> e : h.entrySet())
\r
1015 if (e.getKey().equalsIgnoreCase(key))
\r
1016 return e.getValue();
\r
1020 public static String clean(String s) {
\r
1021 return rep(replaceAllCharacters(s, " \t\n\r", " "), " ", " ").trim();
\r
1026 * fdup duplicates p or q formats for formatCheck
\r
1027 * and the format() function.
\r
1032 * @return %3.5q%3.5q%3.5q%3.5q or %3.5p%3.5p%3.5p
\r
1034 public static String fdup(String f, int pt, int n) {
\r
1037 for (int i = pt; --i >= 1; ) {
\r
1038 if (isDigit(ch = f.charAt(i)))
\r
1046 if (i != 1 && f.charAt(i - 1) != '.')
\r
1053 String s = f.substring(0, pt + 1);
\r
1055 for (int i = 0; i < n; i++)
\r
1057 sb.append(f.substring(pt + 1));
\r
1058 return sb.toString();
\r
1062 * generic string formatter based on formatLabel in Atom
\r
1065 * @param strFormat .... %width.precisionKEY....
\r
1066 * @param key any string to match
\r
1067 * @param strT replacement string or null
\r
1068 * @param floatT replacement float or Float.NaN
\r
1069 * @param doubleT replacement double or Double.NaN -- for exponential
\r
1070 * @param doOne mimic sprintf
\r
1071 * @return formatted string
\r
1074 public static String formatString(String strFormat, String key, String strT,
\r
1075 float floatT, double doubleT, boolean doOne) {
\r
1076 if (strFormat == null)
\r
1078 if ("".equals(strFormat))
\r
1080 int len = key.length();
\r
1081 if (strFormat.indexOf("%") < 0 || len == 0 || strFormat.indexOf(key) < 0)
\r
1084 String strLabel = "";
\r
1085 int ich, ichPercent, ichKey;
\r
1086 for (ich = 0; (ichPercent = strFormat.indexOf('%', ich)) >= 0
\r
1087 && (ichKey = strFormat.indexOf(key, ichPercent + 1)) >= 0;) {
\r
1088 if (ich != ichPercent)
\r
1089 strLabel += strFormat.substring(ich, ichPercent);
\r
1090 ich = ichPercent + 1;
\r
1091 if (ichKey > ichPercent + 6) {
\r
1093 continue;//%12.10x
\r
1096 boolean alignLeft = false;
\r
1097 if (strFormat.charAt(ich) == '-') {
\r
1101 boolean zeroPad = false;
\r
1102 if (strFormat.charAt(ich) == '0') {
\r
1108 while ((ch = strFormat.charAt(ich)) >= '0' && (ch <= '9')) {
\r
1109 width = (10 * width) + (ch - '0');
\r
1112 int precision = Integer.MAX_VALUE;
\r
1113 boolean isExponential = false;
\r
1114 if (strFormat.charAt(ich) == '.') {
\r
1116 if ((ch = strFormat.charAt(ich)) == '-') {
\r
1117 isExponential = true;
\r
1120 if ((ch = strFormat.charAt(ich)) >= '0' && ch <= '9') {
\r
1121 precision = ch - '0';
\r
1124 if (isExponential)
\r
1125 precision = -precision - (strT == null ? 1 : 0);
\r
1127 String st = strFormat.substring(ich, ich + len);
\r
1128 if (!st.equals(key)) {
\r
1129 ich = ichPercent + 1;
\r
1134 if (!Float.isNaN(floatT))
\r
1135 strLabel += formatF(floatT, width, precision, alignLeft,
\r
1137 else if (strT != null)
\r
1138 strLabel += formatS(strT, width, precision, alignLeft,
\r
1140 else if (!Double.isNaN(doubleT))
\r
1141 strLabel += formatD(doubleT, width, precision, alignLeft,
\r
1145 } catch (IndexOutOfBoundsException ioobe) {
\r
1150 strLabel += strFormat.substring(ich);
\r
1151 //if (strLabel.length() == 0)
\r
1156 public static String formatStringS(String strFormat, String key, String strT) {
\r
1157 return formatString(strFormat, key, strT, Float.NaN, Double.NaN, false);
\r
1160 public static String formatStringF(String strFormat, String key, float floatT) {
\r
1161 return formatString(strFormat, key, null, floatT, Double.NaN, false);
\r
1164 public static String formatStringI(String strFormat, String key, int intT) {
\r
1165 return formatString(strFormat, key, "" + intT, Float.NaN, Double.NaN, false);
\r
1169 * sprintf emulation uses (almost) c++ standard string formats
\r
1171 * 's' string 'i' or 'd' integer, 'e' double, 'f' float, 'p' point3f 'q'
\r
1172 * quaternion/plane/axisangle with added "i" (equal to the insipid "d" --
\r
1175 * @param strFormat
\r
1177 * a listing of what sort of data will be found in Object[] values, in
\r
1178 * order: s string, f float, i integer, d double, p point3f, q
\r
1179 * quaternion/point4f, S String[], F float[], I int[], and D double[]
\r
1181 * Object[] containing above types
\r
1182 * @return formatted string
\r
1184 public static String sprintf(String strFormat, String list, Object[] values) {
\r
1185 if (values == null)
\r
1187 int n = list.length();
\r
1188 if (n == values.length)
\r
1190 for (int o = 0; o < n; o++) {
\r
1191 if (values[o] == null)
\r
1193 switch (list.charAt(o)) {
\r
1195 strFormat = formatString(strFormat, "s", (String) values[o],
\r
1196 Float.NaN, Double.NaN, true);
\r
1199 strFormat = formatString(strFormat, "f", null, ((Float) values[o])
\r
1200 .floatValue(), Double.NaN, true);
\r
1203 strFormat = formatString(strFormat, "d", "" + values[o], Float.NaN,
\r
1204 Double.NaN, true);
\r
1205 strFormat = formatString(strFormat, "i", "" + values[o], Float.NaN,
\r
1206 Double.NaN, true);
\r
1209 strFormat = formatString(strFormat, "e", null, Float.NaN,
\r
1210 ((Double) values[o]).doubleValue(), true);
\r
1213 T3 pVal = (T3) values[o];
\r
1214 strFormat = formatString(strFormat, "p", null, pVal.x, Double.NaN,
\r
1216 strFormat = formatString(strFormat, "p", null, pVal.y, Double.NaN,
\r
1218 strFormat = formatString(strFormat, "p", null, pVal.z, Double.NaN,
\r
1222 T4 qVal = (T4) values[o];
\r
1223 strFormat = formatString(strFormat, "q", null, qVal.x, Double.NaN,
\r
1225 strFormat = formatString(strFormat, "q", null, qVal.y, Double.NaN,
\r
1227 strFormat = formatString(strFormat, "q", null, qVal.z, Double.NaN,
\r
1229 strFormat = formatString(strFormat, "q", null, qVal.w, Double.NaN,
\r
1233 String[] sVal = (String[]) values[o];
\r
1234 for (int i = 0; i < sVal.length; i++)
\r
1235 strFormat = formatString(strFormat, "s", sVal[i], Float.NaN,
\r
1236 Double.NaN, true);
\r
1239 float[] fVal = (float[]) values[o];
\r
1240 for (int i = 0; i < fVal.length; i++)
\r
1241 strFormat = formatString(strFormat, "f", null, fVal[i],
\r
1242 Double.NaN, true);
\r
1245 int[] iVal = (int[]) values[o];
\r
1246 for (int i = 0; i < iVal.length; i++)
\r
1247 strFormat = formatString(strFormat, "d", "" + iVal[i], Float.NaN,
\r
1248 Double.NaN, true);
\r
1249 for (int i = 0; i < iVal.length; i++)
\r
1250 strFormat = formatString(strFormat, "i", "" + iVal[i], Float.NaN,
\r
1251 Double.NaN, true);
\r
1254 double[] dVal = (double[]) values[o];
\r
1255 for (int i = 0; i < dVal.length; i++)
\r
1256 strFormat = formatString(strFormat, "e", null, Float.NaN,
\r
1261 return rep(strFormat, "%%", "%");
\r
1262 } catch (Exception e) {
\r
1265 System.out.println("TextFormat.sprintf error " + list + " " + strFormat);
\r
1266 return rep(strFormat, "%", "?");
\r
1271 * formatCheck checks p and q formats and duplicates if necessary
\r
1272 * "%10.5p xxxx" ==> "%10.5p%10.5p%10.5p xxxx"
\r
1274 * @param strFormat
\r
1275 * @return f or dupicated format
\r
1277 public static String formatCheck(String strFormat) {
\r
1278 if (strFormat == null || strFormat.indexOf('p') < 0 && strFormat.indexOf('q') < 0)
\r
1280 strFormat = rep(strFormat, "%%", "\1");
\r
1281 strFormat = rep(strFormat, "%p", "%6.2p");
\r
1282 strFormat = rep(strFormat, "%q", "%6.2q");
\r
1283 String[] format = split(strFormat, "%");
\r
1285 sb.append(format[0]);
\r
1286 for (int i = 1; i < format.length; i++) {
\r
1287 String f = "%" + format[i];
\r
1289 if (f.length() >= 3) {
\r
1290 if ((pt = f.indexOf('p')) >= 0)
\r
1291 f = fdup(f, pt, 3);
\r
1292 if ((pt = f.indexOf('q')) >= 0)
\r
1293 f = fdup(f, pt, 4);
\r
1297 return sb.toString().replace('\1', '%');
\r
1300 public static void leftJustify(SB s, String s1, String s2) {
\r
1302 int n = s1.length() - s2.length();
\r
1304 s.append(s1.substring(0, n));
\r
1307 public static void rightJustify(SB s, String s1, String s2) {
\r
1308 int n = s1.length() - s2.length();
\r
1310 s.append(s1.substring(0, n));
\r
1314 public static String safeTruncate(float f, int n) {
\r
1315 if (f > -0.001 && f < 0.001)
\r
1317 return (f + " ").substring(0,n);
\r
1320 public static boolean isWild(String s) {
\r
1321 return s != null && (s.indexOf("*") >= 0 || s.indexOf("?") >= 0);
\r
1325 * A general non-regex (for performance) text matcher that utilizes ? and *.
\r
1327 * ??? means "at most three" characters if at beginning or end;
\r
1328 * "exactly three" otherwise
\r
1329 * \1 in search is a stand-in for actual ?
\r
1332 * the string to search
\r
1334 * the match string
\r
1335 * @param checkStar
\r
1336 * @param allowInitialStar
\r
1337 * @return true if found
\r
1339 public static boolean isMatch(String search, String match, boolean checkStar,
\r
1340 boolean allowInitialStar) {
\r
1341 // search == match --> true
\r
1342 if (search.equals(match))
\r
1344 int mLen = match.length();
\r
1345 // match == "" --> false
\r
1348 boolean isStar0 = (checkStar && allowInitialStar ? match.charAt(0) == '*'
\r
1350 // match == "*" --> true
\r
1351 if (mLen == 1 && isStar0)
\r
1353 boolean isStar1 = (checkStar && match.endsWith("*"));
\r
1354 boolean haveQ = (match.indexOf('?') >= 0);
\r
1355 // match == "**" --> true
\r
1356 // match == "*xxx*" --> search contains "xxx"
\r
1357 // match == "*xxx" --> search ends with "xxx"
\r
1358 // match == "xxx*" --> search starts with "xxx"
\r
1361 return (isStar1 ? (mLen < 3 || search.indexOf(match.substring(1,
\r
1362 mLen - 1)) >= 0) : search.endsWith(match.substring(1)));
\r
1364 return search.startsWith(match.substring(0, mLen - 1));
\r
1366 int sLen = search.length();
\r
1367 // pad match with "?" -- same as *
\r
1368 String qqqq = "????";
\r
1370 while (nq < sLen) {
\r
1376 match = qqqq + match.substring(1);
\r
1380 match = match.substring(0, mLen - 1) + qqqq;
\r
1384 // length of match < length of search --> false
\r
1388 // -- each ? matches ONE character if not at end
\r
1389 // -- extra ? at end ignored
\r
1391 // (allowInitialStar == true)
\r
1392 // -- extra ? at beginning reduced to match length
\r
1395 while (mLen > sLen) {
\r
1396 if (allowInitialStar && match.charAt(ich) == '?') {
\r
1398 } else if (match.charAt(ich + mLen - 1) != '?') {
\r
1404 // both are effectively same length now.
\r
1405 // \1 is stand-in for "?"
\r
1407 for (int i = sLen; --i >= 0;) {
\r
1408 char chm = match.charAt(ich + i);
\r
1411 char chs = search.charAt(i);
\r
1412 if (chm != chs && (chm != '\1' || chs != '?'))
\r
1418 public static String replaceQuotedStrings(String s, Lst<String> list,
\r
1419 Lst<String> newList) {
\r
1420 int n = list.size();
\r
1421 for (int i = 0; i < n; i++) {
\r
1422 String name = list.get(i);
\r
1423 String newName = newList.get(i);
\r
1424 if (!newName.equals(name))
\r
1425 s = rep(s, "\"" + name + "\"", "\"" + newName
\r
1431 public static String replaceStrings(String s, Lst<String> list,
\r
1432 Lst<String> newList) {
\r
1433 int n = list.size();
\r
1434 for (int i = 0; i < n; i++) {
\r
1435 String name = list.get(i);
\r
1436 String newName = newList.get(i);
\r
1437 if (!newName.equals(name))
\r
1438 s = rep(s, name, newName);
\r
1443 public static boolean isDigit(char ch) {
\r
1444 // just way simpler code than Character.isDigit(ch);
\r
1446 return (48 <= c && c <= 57);
\r
1449 public static boolean isUpperCase(char ch) {
\r
1451 return (65 <= c && c <= 90);
\r
1454 public static boolean isLowerCase(char ch) {
\r
1456 return (97 <= c && c <= 122);
\r
1459 public static boolean isLetter(char ch) {
\r
1460 // just way simpler code than Character.isLetter(ch);
\r
1462 return (65 <= c && c <= 90 || 97 <= c && c <= 122);
\r
1465 public static boolean isLetterOrDigit(char ch) {
\r
1466 // just way simpler code than Character.isLetterOrDigit(ch);
\r
1468 return (65 <= c && c <= 90 || 97 <= c && c <= 122 || 48 <= c && c <= 57);
\r
1471 public static boolean isWhitespace(char ch) {
\r
1473 return (c >= 0x1c && c <= 0x20 || c >= 0x9 && c <= 0xd);
\r
1476 public static final float FRACTIONAL_PRECISION = 100000f;
\r
1477 public static final float CARTESIAN_PRECISION = 10000f;
\r
1479 public static void fixPtFloats(T3 pt, float f) {
\r
1480 //this will equate float and double as long as -256 <= x <= 256
\r
1481 pt.x = Math.round(pt.x * f) / f;
\r
1482 pt.y = Math.round(pt.y * f) / f;
\r
1483 pt.z = Math.round(pt.z * f) / f;
\r
1486 public static double fixDouble(double d, double f) {
\r
1487 return Math.round(d * f) / f;
\r
1491 * parse a float or "float/float"
\r
1495 public static float parseFloatFraction(String s) {
\r
1496 int pt = s.indexOf("/");
\r
1497 return (pt < 0 ? parseFloat(s) : parseFloat(s.substring(0, pt))
\r
1498 / parseFloat(s.substring(pt + 1)));
\r
1503 // double d = 790.8999998888;
\r
1504 // float x = 790.8999998888f;
\r
1505 // for (int i = 0; i < 50; i++) {
\r
1506 // System.out.println(x + " " + d);
\r
1507 // System.out.println(Math.round(x * 100000) / 100000f);
\r
1508 // System.out.println(Math.round(d * 100000) / 100000.);
\r
1509 // System.out.println(Math.round(x * 10000) / 10000f);
\r
1510 // System.out.println(Math.round(d * 10000) / 10000.);
\r
1514 // System.out.println(100.123456789f);
\r
1520 // t = System.currentTimeMillis();
\r
1521 // for (int i = 0; i < 10000000; i++) {
\r
1522 // boolean b = PT.isUpperCase(c);
\r
1524 // System.out.println(System.currentTimeMillis() - t);
\r
1526 // t = System.currentTimeMillis();
\r
1527 // for (int i = 0; i < 10000000; i++) {
\r
1528 // boolean b = Character.isUpperCase(c);
\r
1530 // System.out.println(System.currentTimeMillis() - t);
\r
1532 // t = System.currentTimeMillis();
\r
1533 // for (int i = 0; i < 10000000; i++) {
\r
1534 // boolean b = PT.isUpperCase(c);
\r
1536 // System.out.println(System.currentTimeMillis() - t);
\r
1538 // System.out.println("PT test");
\r