1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2007-04-26 16:57:51 -0500 (Thu, 26 Apr 2007) $ 4 * $Revision: 7502 $ 5 * 6 * Copyright (C) 2005 The Jmol Development Team 7 * 8 * Contact: jmol-developers@lists.sf.net 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 23 */ 24 25 package javajs.util; 26 27 import java.lang.reflect.Array; 28 import java.util.Map; 29 import java.util.Map.Entry; 30 31 import javajs.J2SIgnoreImport; 32 import javajs.api.JSONEncodable; 33 34 /** 35 * a combination of Parsing and Text-related utility classes 36 * 37 * @author hansonr 38 * 39 */ 40 41 @J2SIgnoreImport(value = { java.lang.reflect.Array.class }) 42 public class PT { 43 parseInt(String str)44 public static int parseInt(String str) { 45 return parseIntNext(str, new int[] {0}); 46 } 47 parseIntNext(String str, int[] next)48 public static int parseIntNext(String str, int[] next) { 49 int cch = str.length(); 50 if (next[0] < 0 || next[0] >= cch) 51 return Integer.MIN_VALUE; 52 return parseIntChecked(str, cch, next); 53 } 54 parseIntChecked(String str, int ichMax, int[] next)55 public static int parseIntChecked(String str, int ichMax, int[] next) { 56 boolean digitSeen = false; 57 int value = 0; 58 int ich = next[0]; 59 if (ich < 0) 60 return Integer.MIN_VALUE; 61 int ch; 62 while (ich < ichMax && isWhiteSpace(str, ich)) 63 ++ich; 64 boolean negative = false; 65 if (ich < ichMax && str.charAt(ich) == 45) { //"-" 66 negative = true; 67 ++ich; 68 } 69 while (ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) { 70 value = value * 10 + (ch - 48); 71 digitSeen = true; 72 ++ich; 73 } 74 if (!digitSeen)// || !checkTrailingText(str, ich, ichMax)) 75 value = Integer.MIN_VALUE; 76 else if (negative) 77 value = -value; 78 next[0] = ich; 79 return value; 80 } 81 isWhiteSpace(String str, int ich)82 public static boolean isWhiteSpace(String str, int ich) { 83 char ch; 84 return (ich >= 0 && ((ch = str.charAt(ich)) == ' ' || ch == '\t' || ch == '\n')); 85 } 86 87 /** 88 * A float parser that is 30% faster than Float.parseFloat(x) and also accepts 89 * x.yD+-n 90 * 91 * @param str 92 * @param ichMax 93 * @param next 94 * pointer; incremented 95 * @param isStrict 96 * @return value or Float.NaN 97 */ parseFloatChecked(String str, int ichMax, int[] next, boolean isStrict)98 public static float parseFloatChecked(String str, int ichMax, int[] next, 99 boolean isStrict) { 100 boolean digitSeen = false; 101 int ich = next[0]; 102 if (isStrict && str.indexOf('\n') != str.lastIndexOf('\n')) 103 return Float.NaN; 104 while (ich < ichMax && isWhiteSpace(str, ich)) 105 ++ich; 106 boolean negative = false; 107 if (ich < ichMax && str.charAt(ich) == '-') { 108 ++ich; 109 negative = true; 110 } 111 // looks crazy, but if we don't do this, Google Closure Compiler will 112 // write code that Safari will misinterpret in a VERY nasty way -- 113 // getting totally confused as to long integers and double values 114 115 // This is Safari figuring out the values of the numbers on the line (x, y, then z): 116 117 // ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C 118 // e=1408749273 119 // -e =-1408749273 120 // ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C 121 // e=-1821066134 122 // e=36.532 123 // ATOM 1241 CD1 LEU A 64 -2.206 36.532 31.576 1.00 60.60 C 124 // e=-1133871366 125 // e=31.576 126 // 127 // "e" values are just before and after the "value = -value" statement. 128 129 int ch = 0; 130 float ival = 0f; 131 float ival2 = 0f; 132 while (ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) { 133 ival = (ival * 10f) + (ch - 48)*1f; 134 ++ich; 135 digitSeen = true; 136 } 137 boolean isDecimal = false; 138 int iscale = 0; 139 int nzero = (ival == 0 ? -1 : 0); 140 if (ch == '.') { 141 isDecimal = true; 142 while (++ich < ichMax && (ch = str.charAt(ich)) >= 48 && ch <= 57) { 143 digitSeen = true; 144 if (nzero < 0) { 145 if (ch == 48) { 146 nzero--; 147 continue; 148 } 149 nzero = -nzero; 150 } 151 if (iscale < decimalScale.length) { 152 ival2 = (ival2 * 10f) + (ch - 48)*1f; 153 iscale++; 154 } 155 } 156 } 157 float value; 158 159 // Safari breaks here intermittently converting integers to floats 160 161 if (!digitSeen) { 162 value = Float.NaN; 163 } else if (ival2 > 0) { 164 value = ival2 * decimalScale[iscale - 1]; 165 if (nzero > 1) { 166 if (nzero - 2 < decimalScale.length) { 167 value *= decimalScale[nzero - 2]; 168 } else { 169 value *= Math.pow(10, 1 - nzero); 170 } 171 } else { 172 value += ival; 173 } 174 } else { 175 value = ival; 176 } 177 boolean isExponent = false; 178 if (ich < ichMax && (ch == 69 || ch == 101 || ch == 68)) { // E e D 179 isExponent = true; 180 if (++ich >= ichMax) 181 return Float.NaN; 182 ch = str.charAt(ich); 183 if ((ch == '+') && (++ich >= ichMax)) 184 return Float.NaN; 185 next[0] = ich; 186 int exponent = parseIntChecked(str, ichMax, next); 187 if (exponent == Integer.MIN_VALUE) 188 return Float.NaN; 189 if (exponent > 0 && exponent <= tensScale.length) 190 value *= tensScale[exponent - 1]; 191 else if (exponent < 0 && -exponent <= decimalScale.length) 192 value *= decimalScale[-exponent - 1]; 193 else if (exponent != 0) 194 value *= Math.pow(10, exponent); 195 } else { 196 next[0] = ich; // the exponent code finds its own ichNextParse 197 } 198 // believe it or not, Safari reports the long-equivalent of the 199 // float value here, then later the float value, after no operation! 200 if (negative) 201 value = -value; 202 if (value == Float.POSITIVE_INFINITY) 203 value = Float.MAX_VALUE; 204 return (!isStrict || (!isExponent || isDecimal) 205 && checkTrailingText(str, next[0], ichMax) ? value : Float.NaN); 206 } 207 208 public final static float[] tensScale = { 10f, 100f, 1000f, 10000f, 100000f, 1000000f }; 209 public final static float[] decimalScale = { 210 0.1f, 211 0.01f, 212 0.001f, 213 0.0001f, 214 0.00001f, 215 0.000001f, 216 0.0000001f, 217 0.00000001f, 218 0.000000001f 219 }; checkTrailingText(String str, int ich, int ichMax)220 public static boolean checkTrailingText(String str, int ich, int ichMax) { 221 //number must be pure -- no additional characters other than white space or ; 222 char ch; 223 while (ich < ichMax && (isWhitespace(ch = str.charAt(ich)) || ch == ';')) 224 ++ich; 225 return (ich == ichMax); 226 } 227 parseFloatArray(String str)228 public static float[] parseFloatArray(String str) { 229 return parseFloatArrayNext(str, new int[1], null, null, null); 230 } 231 parseFloatArrayInfested(String[] tokens, float[] data)232 public static int parseFloatArrayInfested(String[] tokens, float[] data) { 233 int len = data.length; 234 int nTokens = tokens.length; 235 int n = 0; 236 int max = 0; 237 for (int i = 0; i >= 0 && i < len && n < nTokens; i++) { 238 float f; 239 while (Float.isNaN(f = parseFloat(tokens[n++])) 240 && n < nTokens) { 241 } 242 if (!Float.isNaN(f)) 243 data[(max = i)] = f; 244 if (n == nTokens) 245 break; 246 } 247 return max + 1; 248 } 249 250 /** 251 * @param str 252 * @param next 253 * @param f 254 * @param strStart or null 255 * @param strEnd or null 256 * @return array of float values 257 * 258 */ parseFloatArrayNext(String str, int[] next, float[] f, String strStart, String strEnd)259 public static float[] parseFloatArrayNext(String str, int[] next, float[] f, 260 String strStart, String strEnd) { 261 int n = 0; 262 int pt = next[0]; 263 if (pt >= 0) { 264 if (strStart != null) { 265 int p = str.indexOf(strStart, pt); 266 if (p >= 0) 267 next[0] = p + strStart.length(); 268 } 269 str = str.substring(next[0]); 270 pt = (strEnd == null ? -1 : str.indexOf(strEnd)); 271 if (pt < 0) 272 pt = str.length(); 273 else 274 str = str.substring(0, pt); 275 next[0] += pt + 1; 276 String[] tokens = getTokens(str); 277 if (f == null) 278 f = new float[tokens.length]; 279 n = parseFloatArrayInfested(tokens, f); 280 } 281 if (f == null) 282 return new float[0]; 283 for (int i = n; i < f.length; i++) 284 f[i] = Float.NaN; 285 return f; 286 } 287 parseFloatRange(String str, int ichMax, int[] next)288 public static float parseFloatRange(String str, int ichMax, int[] next) { 289 int cch = str.length(); 290 if (ichMax > cch) 291 ichMax = cch; 292 if (next[0] < 0 || next[0] >= ichMax) 293 return Float.NaN; 294 return parseFloatChecked(str, ichMax, next, false); 295 } 296 parseFloatNext(String str, int[] next)297 public static float parseFloatNext(String str, int[] next) { 298 int cch = (str == null ? -1 : str.length()); 299 return (next[0] < 0 || next[0] >= cch ? Float.NaN : parseFloatChecked(str, cch, next, false)); 300 } 301 parseFloatStrict(String str)302 public static float parseFloatStrict(String str) { 303 // checks trailing characters and does not allow "1E35" to be float 304 int cch = str.length(); 305 if (cch == 0) 306 return Float.NaN; 307 return parseFloatChecked(str, cch, new int[] {0}, true); 308 } 309 parseFloat(String str)310 public static float parseFloat(String str) { 311 return parseFloatNext(str, new int[] {0}); 312 } 313 parseIntRadix(String s, int i)314 public static int parseIntRadix(String s, int i) throws NumberFormatException { 315 /** 316 * 317 * JavaScript uses parseIntRadix 318 * 319 * @j2sNative 320 * 321 * return Integer.parseIntRadix(s, i); 322 * 323 */ 324 { 325 return Integer.parseInt(s, i); 326 } 327 } 328 getTokens(String line)329 public static String[] getTokens(String line) { 330 return getTokensAt(line, 0); 331 } 332 parseToken(String str)333 public static String parseToken(String str) { 334 return parseTokenNext(str, new int[] {0}); 335 } 336 parseTrimmed(String str)337 public static String parseTrimmed(String str) { 338 return parseTrimmedRange(str, 0, str.length()); 339 } 340 parseTrimmedAt(String str, int ichStart)341 public static String parseTrimmedAt(String str, int ichStart) { 342 return parseTrimmedRange(str, ichStart, str.length()); 343 } 344 parseTrimmedRange(String str, int ichStart, int ichMax)345 public static String parseTrimmedRange(String str, int ichStart, int ichMax) { 346 int cch = str.length(); 347 if (ichMax < cch) 348 cch = ichMax; 349 if (cch < ichStart) 350 return ""; 351 return parseTrimmedChecked(str, ichStart, cch); 352 } 353 getTokensAt(String line, int ich)354 public static String[] getTokensAt(String line, int ich) { 355 if (line == null) 356 return null; 357 int cchLine = line.length(); 358 if (ich < 0 || ich > cchLine) 359 return null; 360 int tokenCount = countTokens(line, ich); 361 String[] tokens = new String[tokenCount]; 362 int[] next = new int[1]; 363 next[0] = ich; 364 for (int i = 0; i < tokenCount; ++i) 365 tokens[i] = parseTokenChecked(line, cchLine, next); 366 return tokens; 367 } 368 countChar(String line, char c)369 public static int countChar(String line, char c) { 370 int n = 0; 371 for (int i = line.lastIndexOf(c) + 1; --i >= 0;) 372 if (line.charAt(i) == c) 373 n++; 374 return n; 375 } 376 countTokens(String line, int ich)377 public static int countTokens(String line, int ich) { 378 int tokenCount = 0; 379 if (line != null) { 380 int ichMax = line.length(); 381 while (true) { 382 while (ich < ichMax && isWhiteSpace(line, ich)) 383 ++ich; 384 if (ich == ichMax) 385 break; 386 ++tokenCount; 387 do { 388 ++ich; 389 } while (ich < ichMax && !isWhiteSpace(line, ich)); 390 } 391 } 392 return tokenCount; 393 } 394 parseTokenNext(String str, int[] next)395 public static String parseTokenNext(String str, int[] next) { 396 int cch = str.length(); 397 return (next[0] < 0 || next[0] >= cch ? null : parseTokenChecked(str, cch, next)); 398 } 399 parseTokenRange(String str, int ichMax, int[] next)400 public static String parseTokenRange(String str, int ichMax, int[] next) { 401 int cch = str.length(); 402 if (ichMax > cch) 403 ichMax = cch; 404 return (next[0] < 0 || next[0] >= ichMax ? null : parseTokenChecked(str, ichMax, next)); 405 } 406 parseTokenChecked(String str, int ichMax, int[] next)407 public static String parseTokenChecked(String str, int ichMax, int[] next) { 408 int ich = next[0]; 409 while (ich < ichMax && isWhiteSpace(str, ich)) 410 ++ich; 411 int ichNonWhite = ich; 412 while (ich < ichMax && !isWhiteSpace(str, ich)) 413 ++ich; 414 next[0] = ich; 415 return (ichNonWhite == ich ? null : str.substring(ichNonWhite, ich)); 416 } 417 parseTrimmedChecked(String str, int ich, int ichMax)418 public static String parseTrimmedChecked(String str, int ich, int ichMax) { 419 while (ich < ichMax && isWhiteSpace(str, ich)) 420 ++ich; 421 int ichLast = ichMax - 1; 422 while (ichLast >= ich && isWhiteSpace(str, ichLast)) 423 --ichLast; 424 return (ichLast < ich ? "" : str.substring(ich, ichLast + 1)); 425 } 426 dVal(String s)427 public static double dVal(String s) throws NumberFormatException { 428 /** 429 * @j2sNative 430 * 431 * if(s==null) 432 * throw new NumberFormatException("null"); 433 * var d=parseFloat(s); 434 * if(isNaN(d)) 435 * throw new NumberFormatException("Not a Number : "+s); 436 * return d 437 * 438 */ 439 { 440 return Double.valueOf(s).doubleValue(); 441 } 442 } 443 fVal(String s)444 public static float fVal(String s) throws NumberFormatException { 445 /** 446 * @j2sNative 447 * 448 * return this.dVal(s); 449 */ 450 { 451 return Float.parseFloat(s); 452 } 453 } 454 parseIntRange(String str, int ichMax, int[] next)455 public static int parseIntRange(String str, int ichMax, int[] next) { 456 int cch = str.length(); 457 if (ichMax > cch) 458 ichMax = cch; 459 return (next[0] < 0 || next[0] >= ichMax ? Integer.MIN_VALUE : parseIntChecked(str, ichMax, next)); 460 } 461 462 /** 463 * parses a string array for floats. Returns NaN for nonfloats. 464 * 465 * @param tokens the strings to parse 466 * @param data the array to fill 467 */ parseFloatArrayData(String[] tokens, float[] data)468 public static void parseFloatArrayData(String[] tokens, float[] data) { 469 parseFloatArrayDataN(tokens, data, data.length); 470 } 471 472 /** 473 * parses a string array for floats. Returns NaN for nonfloats or missing data. 474 * 475 * @param tokens the strings to parse 476 * @param data the array to fill 477 * @param nData the number of elements 478 */ parseFloatArrayDataN(String[] tokens, float[] data, int nData)479 public static void parseFloatArrayDataN(String[] tokens, float[] data, int nData) { 480 for (int i = nData; --i >= 0;) 481 data[i] = (i >= tokens.length ? Float.NaN : parseFloat(tokens[i])); 482 } 483 484 /** 485 * 486 * proper splitting, even for Java 1.3 -- if the text ends in the run, 487 * no new line is appended. 488 * 489 * @param text 490 * @param run 491 * @return String array 492 */ split(String text, String run)493 public static String[] split(String text, String run) { 494 if (text.length() == 0) 495 return new String[0]; 496 int n = 1; 497 int i = text.indexOf(run); 498 String[] lines; 499 int runLen = run.length(); 500 if (i < 0 || runLen == 0) { 501 lines = new String[1]; 502 lines[0] = text; 503 return lines; 504 } 505 int len = text.length() - runLen; 506 for (; i >= 0 && i < len; n++) 507 i = text.indexOf(run, i + runLen); 508 lines = new String[n]; 509 i = 0; 510 int ipt = 0; 511 int pt = 0; 512 for (; (ipt = text.indexOf(run, i)) >= 0 && pt + 1 < n;) { 513 lines[pt++] = text.substring(i, ipt); 514 i = ipt + runLen; 515 } 516 if (text.indexOf(run, len) != len) 517 len += runLen; 518 lines[pt] = text.substring(i, len); 519 return lines; 520 } 521 522 public final static float FLOAT_MIN_SAFE = 2E-45f; 523 // Float.MIN_VALUE (1.45E-45) is not reliable with JavaScript because of the float/double difference there 524 525 /// general static string-parsing class /// 526 527 // next[0] tracks the pointer within the string so these can all be static. 528 // but the methods parseFloat, parseInt, parseToken, parseTrimmed, and getTokens do not require this. 529 530 // public static String concatTokens(String[] tokens, int iFirst, int iEnd) { 531 // String str = ""; 532 // String sep = ""; 533 // for (int i = iFirst; i < iEnd; i++) { 534 // if (i < tokens.length) { 535 // str += sep + tokens[i]; 536 // sep = " "; 537 // } 538 // } 539 // return str; 540 // } 541 getQuotedStringAt(String line, int ipt0)542 public static String getQuotedStringAt(String line, int ipt0) { 543 int[] next = new int[] { ipt0 }; 544 return getQuotedStringNext(line, next); 545 } 546 547 /** 548 * 549 * @param line 550 * @param next passes [current pointer] 551 * @return quoted string -- does NOT unescape characters 552 */ getQuotedStringNext(String line, int[] next)553 public static String getQuotedStringNext(String line, int[] next) { 554 int i = next[0]; 555 if (i < 0 || (i = line.indexOf("\"", i)) < 0) 556 return ""; 557 int pt = i + 1; 558 int len = line.length(); 559 while (++i < len && line.charAt(i) != '"') 560 if (line.charAt(i) == '\\') 561 i++; 562 next[0] = i + 1; 563 return line.substring(pt, i); 564 } 565 566 /** 567 * single- or double-quoted string or up to the first space -- like HTML5 568 * not case-sensitive 569 * 570 * @param line 571 * @param key 572 * @return attribute 573 */ getQuotedOrUnquotedAttribute(String line, String key)574 public static String getQuotedOrUnquotedAttribute(String line, String key) { 575 if (line == null || key == null) 576 return null; 577 int pt = line.toLowerCase().indexOf(key.toLowerCase() + "="); 578 if (pt < 0 || (pt = pt + key.length() + 1) >= line.length()) 579 return ""; 580 char c = line.charAt(pt); 581 switch (c) { 582 case '\'': 583 case '"': 584 pt++; 585 break; 586 default: 587 c = ' '; 588 line += " "; 589 } 590 int pt1 = line.indexOf(c, pt); 591 return (pt1 < 0 ? null : line.substring(pt, pt1)); 592 } 593 594 /** 595 * CSV format -- escaped quote is "" WITHIN "..." 596 * 597 * 598 * @param line 599 * @param next int[2] filled with [ptrQuote1, ptrAfterQuote2] 600 * next[1] will be -1 if unmatched quotes are found (continuation on next line) 601 * @return unescaped string or null 602 */ getCSVString(String line, int[] next)603 public static String getCSVString(String line, int[] next) { 604 int i = next[1]; 605 if (i < 0 || (i = line.indexOf("\"", i)) < 0) 606 return null; 607 int pt = next[0] = i; 608 int len = line.length(); 609 boolean escaped = false; 610 boolean haveEscape = false; 611 while (++i < len 612 && (line.charAt(i) != '"' || (escaped = (i + 1 < len && line.charAt(i + 1) == '"')))) 613 if (escaped) { 614 escaped = false; 615 haveEscape = true; 616 i++; 617 } 618 if (i >= len) { 619 next[1] = -1; 620 return null; // unmatched 621 } 622 next[1] = i + 1; 623 String s = line.substring(pt + 1, i); 624 return (haveEscape ? rep(rep(s, "\"\"", "\0"), "\0","\"") : s); 625 } 626 isOneOf(String key, String semiList)627 public static boolean isOneOf(String key, String semiList) { 628 if (semiList.length() == 0) 629 return false; 630 if (semiList.charAt(0) != ';') 631 semiList = ";" + semiList + ";"; 632 return key.indexOf(";") < 0 && semiList.indexOf(';' + key + ';') >= 0; 633 } 634 getQuotedAttribute(String info, String name)635 public static String getQuotedAttribute(String info, String name) { 636 int i = info.indexOf(name + "="); 637 return (i < 0 ? null : getQuotedStringAt(info, i)); 638 } 639 approx(float f, float n)640 public static float approx(float f, float n) { 641 return Math.round (f * n) / n; 642 } 643 644 /** 645 * Does a clean ITERATIVE replace of strFrom in str with strTo. 646 * Thus, rep("Testttt", "tt","t") becomes "Test". 647 * 648 * @param str 649 * @param strFrom 650 * @param strTo 651 * @return replaced string 652 */ rep(String str, String strFrom, String strTo)653 public static String rep(String str, String strFrom, String strTo) { 654 if (str == null || strFrom.length() == 0 || str.indexOf(strFrom) < 0) 655 return str; 656 boolean isOnce = (strTo.indexOf(strFrom) >= 0); 657 do { 658 str = str.replace(strFrom, strTo); 659 } while (!isOnce && str.indexOf(strFrom) >= 0); 660 return str; 661 } 662 formatF(float value, int width, int precision, boolean alignLeft, boolean zeroPad)663 public static String formatF(float value, int width, int precision, 664 boolean alignLeft, boolean zeroPad) { 665 return formatS(DF.formatDecimal(value, precision), width, 0, alignLeft, zeroPad); 666 } 667 668 /** 669 * 670 * @param value 671 * @param width 672 * @param precision 673 * @param alignLeft 674 * @param zeroPad 675 * @param allowOverflow IGNORED 676 * @return formatted string 677 */ formatD(double value, int width, int precision, boolean alignLeft, boolean zeroPad, boolean allowOverflow)678 public static String formatD(double value, int width, int precision, 679 boolean alignLeft, boolean zeroPad, boolean allowOverflow) { 680 return formatS(DF.formatDecimal((float)value, -1 - precision), width, 0, alignLeft, zeroPad); 681 } 682 683 /** 684 * 685 * @param value 686 * @param width number of columns 687 * @param precision precision > 0 ==> precision = number of characters max from left 688 * precision < 0 ==> -1 - precision = number of char. max from right 689 * @param alignLeft 690 * @param zeroPad generally for numbers turned strings 691 * @return formatted string 692 */ formatS(String value, int width, int precision, boolean alignLeft, boolean zeroPad)693 public static String formatS(String value, int width, int precision, 694 boolean alignLeft, boolean zeroPad) { 695 if (value == null) 696 return ""; 697 int len = value.length(); 698 if (precision != Integer.MAX_VALUE && precision > 0 699 && precision < len) 700 value = value.substring(0, precision); 701 else if (precision < 0 && len + precision >= 0) 702 value = value.substring(len + precision + 1); 703 704 int padLength = width - value.length(); 705 if (padLength <= 0) 706 return value; 707 boolean isNeg = (zeroPad && !alignLeft && value.charAt(0) == '-'); 708 char padChar = (zeroPad ? '0' : ' '); 709 char padChar0 = (isNeg ? '-' : padChar); 710 711 SB sb = new SB(); 712 if (alignLeft) 713 sb.append(value); 714 sb.appendC(padChar0); 715 for (int i = padLength; --i > 0;) 716 // this is correct, not >= 0 717 sb.appendC(padChar); 718 if (!alignLeft) 719 sb.append(isNeg ? padChar + value.substring(1) : value); 720 return sb.toString(); 721 } 722 723 /** 724 * Does a clean replace of any of the characters in str with chrTo 725 * If strTo contains strFrom, then only a single pass is done. 726 * Otherwise, multiple passes are made until no more replacements can be made. 727 * 728 * @param str 729 * @param strFrom 730 * @param chTo 731 * @return replaced string 732 */ replaceWithCharacter(String str, String strFrom, char chTo)733 public static String replaceWithCharacter(String str, String strFrom, 734 char chTo) { 735 if (str == null) 736 return null; 737 for (int i = strFrom.length(); --i >= 0;) 738 str = str.replace(strFrom.charAt(i), chTo); 739 return str; 740 } 741 742 /** 743 * Does a clean replace of any of the characters in str with strTo 744 * If strTo contains strFrom, then only a single pass is done. 745 * Otherwise, multiple passes are made until no more replacements can be made. 746 * 747 * @param str 748 * @param strFrom 749 * @param strTo 750 * @return replaced string 751 */ replaceAllCharacters(String str, String strFrom, String strTo)752 public static String replaceAllCharacters(String str, String strFrom, 753 String strTo) { 754 for (int i = strFrom.length(); --i >= 0;) { 755 String chFrom = strFrom.substring(i, i + 1); 756 str = rep(str, chFrom, strTo); 757 } 758 return str; 759 } 760 trim(String str, String chars)761 public static String trim(String str, String chars) { 762 if (str == null || str.length() == 0) 763 return str; 764 if (chars.length() == 0) 765 return str.trim(); 766 int len = str.length(); 767 int k = 0; 768 while (k < len && chars.indexOf(str.charAt(k)) >= 0) 769 k++; 770 int m = str.length() - 1; 771 while (m > k && chars.indexOf(str.charAt(m)) >= 0) 772 m--; 773 return str.substring(k, m + 1); 774 } 775 trimQuotes(String value)776 public static String trimQuotes(String value) { 777 return (value != null && value.length() > 1 && value.startsWith("\"") 778 && value.endsWith("\"") ? value.substring(1, value.length() - 1) 779 : value); 780 } 781 isNonStringPrimitive(Object info)782 public static boolean isNonStringPrimitive(Object info) { 783 // note that we don't use Double, Float, or Integer here 784 // because in JavaScript those would be false for unwrapped primitives 785 // coming from equivalent of Array.get() 786 // Strings will need their own escaped processing 787 /** 788 * @j2sNative 789 * 790 * if(typeof info == "number" || typeof info == "boolean") { 791 * return true; 792 * } 793 * 794 * 795 */ 796 {} 797 return info instanceof Number || info instanceof Boolean; 798 } 799 800 @SuppressWarnings({ "null", "unused", "unchecked" }) toJSON(String infoType, Object info)801 public static String toJSON(String infoType, Object info) { 802 if (info == null) 803 return packageJSON(infoType, null); 804 if (isNonStringPrimitive(info)) 805 return packageJSON(infoType, info.toString()); 806 String s = null; 807 SB sb = null; 808 while (true) { 809 if (info instanceof String) { 810 s = (String) info; 811 /** 812 * @j2sNative 813 * 814 * if (typeof s == "undefined") s = "null" 815 * 816 */ 817 { 818 } 819 820 if (s.indexOf("{\"") != 0) { 821 //don't doubly fix JSON strings when retrieving status 822 // what about \1 \2 \3 etc.? 823 s = esc(s); 824 } 825 break; 826 } 827 if (info instanceof JSONEncodable) { 828 // includes javajs.util.BS, org.jmol.script.SV 829 if ((s = ((JSONEncodable) info).toJSON()) == null) 830 s = "null"; // perhaps a list has a null value (group3List, for example) 831 break; 832 } 833 sb = new SB(); 834 if (info instanceof Map) { 835 sb.append("{ "); 836 String sep = ""; 837 for (String key : ((Map<String, ?>) info).keySet()) { 838 if (key == null) 839 key = "null"; 840 sb.append(sep).append( 841 packageJSON(key, toJSON(null, ((Map<?, ?>) info).get(key)))); 842 sep = ","; 843 } 844 sb.append(" }"); 845 break; 846 } 847 if (info instanceof Lst) { 848 sb.append("[ "); 849 int n = ((Lst<?>) info).size(); 850 for (int i = 0; i < n; i++) { 851 if (i > 0) 852 sb.appendC(','); 853 sb.append(toJSON(null, ((Lst<?>) info).get(i))); 854 } 855 sb.append(" ]"); 856 break; 857 } 858 if (info instanceof M34) { 859 // M4 extends M3 860 int len = (info instanceof M4 ? 4 : 3); 861 float[] x = new float[len]; 862 M34 m = (M34) info; 863 sb.appendC('['); 864 for (int i = 0; i < len; i++) { 865 if (i > 0) 866 sb.appendC(','); 867 m.getRow(i, x); 868 sb.append(toJSON(null, x)); 869 } 870 sb.appendC(']'); 871 break; 872 } 873 s = nonArrayString(info); 874 if (s == null) { 875 sb.append("["); 876 int n = AU.getLength(info); 877 Object o = null; 878 /** @j2sNative 879 * o = info[0]; 880 * typeof o != "number" && typeof 0 != "boolean" && (o = null); 881 */ 882 {} 883 if (o != null) { 884 sb.appendO(info); 885 } else { 886 for (int i = 0; i < n; i++) { 887 if (i > 0) 888 sb.appendC(','); 889 sb.append(toJSON(null, Array.get(info, i))); 890 } 891 } 892 sb.append("]"); 893 break; 894 } 895 info = info.toString(); 896 } 897 return packageJSON(infoType, (s == null ? sb.toString() : s)); 898 } 899 900 /** 901 * Checks to see if an object is an array (including typed arrays), and if it is, returns null; 902 * otherwise it returns the string equivalent of that object. 903 * 904 * @param x 905 * @return String or null 906 */ nonArrayString(Object x)907 public static String nonArrayString(Object x) { 908 /** 909 * @j2sNative 910 * 911 * return (x.constructor == Array || x.BYTES_PER_ELEMENT ? null : x.toString()); 912 * 913 */ 914 { 915 try { 916 Array.getLength(x); 917 return null; 918 } catch (Exception e) { 919 return x.toString(); 920 } 921 } 922 } 923 byteArrayToJSON(byte[] data)924 public static String byteArrayToJSON(byte[] data) { 925 SB sb = new SB(); 926 sb.append("["); 927 int n = data.length; 928 for (int i = 0; i < n; i++) { 929 if (i > 0) 930 sb.appendC(','); 931 sb.appendI(data[i] & 0xFF); 932 } 933 sb.append("]"); 934 return sb.toString(); 935 } 936 packageJSON(String infoType, String info)937 public static String packageJSON(String infoType, String info) { 938 return (infoType == null ? info : "\"" + infoType + "\": " + info); 939 } 940 escapeUrl(String url)941 public static String escapeUrl(String url) { 942 url = rep(url, "\n", ""); 943 url = rep(url, "%", "%25"); 944 url = rep(url, "#", "%23"); 945 url = rep(url, "[", "%5B"); 946 url = rep(url, "\\", "%5C"); 947 url = rep(url, "]", "%5D"); 948 url = rep(url, " ", "%20"); 949 return url; 950 } 951 952 private final static String escapable = "\\\\\tt\rr\nn\"\""; 953 esc(String str)954 public static String esc(String str) { 955 if (str == null || str.length() == 0) 956 return "\"\""; 957 boolean haveEscape = false; 958 int i = 0; 959 for (; i < escapable.length(); i += 2) 960 if (str.indexOf(escapable.charAt(i)) >= 0) { 961 haveEscape = true; 962 break; 963 } 964 if (haveEscape) 965 while (i < escapable.length()) { 966 int pt = -1; 967 char ch = escapable.charAt(i++); 968 char ch2 = escapable.charAt(i++); 969 SB sb = new SB(); 970 int pt0 = 0; 971 while ((pt = str.indexOf(ch, pt + 1)) >= 0) { 972 sb.append(str.substring(pt0, pt)).appendC('\\').appendC(ch2); 973 pt0 = pt + 1; 974 } 975 sb.append(str.substring(pt0, str.length())); 976 str = sb.toString(); 977 } 978 return "\"" + escUnicode(str) + "\""; 979 } 980 escUnicode(String str)981 public static String escUnicode(String str) { 982 for (int i = str.length(); --i >= 0;) 983 if (str.charAt(i) > 0x7F) { 984 String s = "0000" + Integer.toHexString(str.charAt(i)); 985 str = str.substring(0, i) + "\\u" + s.substring(s.length() - 4) 986 + str.substring(i + 1); 987 } 988 return str; 989 } 990 991 /** 992 * ensures that a float turned to string has a decimal point 993 * 994 * @param f 995 * @return string version of float 996 */ escF(float f)997 public static String escF(float f) { 998 String sf = "" + f; 999 /** 1000 * @j2sNative 1001 * 1002 * if (sf.indexOf(".") < 0 && sf.indexOf("e") < 0) 1003 * sf += ".0"; 1004 */ 1005 { 1006 } 1007 return sf; 1008 } join(String[] s, char c, int i0)1009 public static String join(String[] s, char c, int i0) { 1010 if (s.length < i0) 1011 return null; 1012 SB sb = new SB(); 1013 sb.append(s[i0++]); 1014 for (int i = i0; i < s.length; i++) 1015 sb.appendC(c).append(s[i]); 1016 return sb.toString(); 1017 } 1018 1019 /** 1020 * a LIKE "x" a is a string and equals x 1021 * 1022 * a LIKE "*x" a is a string and ends with x 1023 * 1024 * a LIKE "x*" a is a string and starts with x 1025 * 1026 * a LIKE "*x*" a is a string and contains x 1027 * 1028 * @param a 1029 * @param b 1030 * @return a LIKE b 1031 */ isLike(String a, String b)1032 public static boolean isLike(String a, String b) { 1033 boolean areEqual = a.equals(b); 1034 if (areEqual) 1035 return true; 1036 boolean isStart = b.startsWith("*"); 1037 boolean isEnd = b.endsWith("*"); 1038 return (!isStart && !isEnd) ? areEqual 1039 : isStart && isEnd ? b.length() == 1 || a.contains(b.substring(1, b.length() - 1)) 1040 : isStart ? a.endsWith(b.substring(1)) 1041 : a.startsWith(b.substring(0, b.length() - 1)); 1042 } 1043 getMapValueNoCase(Map<String, ?> h, String key)1044 public static Object getMapValueNoCase(Map<String, ?> h, String key) { 1045 if ("this".equals(key)) 1046 return h; 1047 Object val = h.get(key); 1048 if (val == null) 1049 for (Entry<String, ?> e : h.entrySet()) 1050 if (e.getKey().equalsIgnoreCase(key)) 1051 return e.getValue(); 1052 return val; 1053 } 1054 clean(String s)1055 public static String clean(String s) { 1056 return rep(replaceAllCharacters(s, " \t\n\r", " "), " ", " ").trim(); 1057 } 1058 1059 /** 1060 * 1061 * fdup duplicates p or q formats for formatCheck 1062 * and the format() function. 1063 * 1064 * @param f 1065 * @param pt 1066 * @param n 1067 * @return %3.5q%3.5q%3.5q%3.5q or %3.5p%3.5p%3.5p 1068 */ fdup(String f, int pt, int n)1069 public static String fdup(String f, int pt, int n) { 1070 char ch; 1071 int count = 0; 1072 for (int i = pt; --i >= 1; ) { 1073 if (isDigit(ch = f.charAt(i))) 1074 continue; 1075 switch (ch) { 1076 case '.': 1077 if (count++ != 0) 1078 return f; 1079 continue; 1080 case '-': 1081 if (i != 1 && f.charAt(i - 1) != '.') 1082 return f; 1083 continue; 1084 default: 1085 return f; 1086 } 1087 } 1088 String s = f.substring(0, pt + 1); 1089 SB sb = new SB(); 1090 for (int i = 0; i < n; i++) 1091 sb.append(s); 1092 sb.append(f.substring(pt + 1)); 1093 return sb.toString(); 1094 } 1095 1096 /** 1097 * generic string formatter based on formatLabel in Atom 1098 * 1099 * 1100 * @param strFormat .... %width.precisionKEY.... 1101 * @param key any string to match 1102 * @param strT replacement string or null 1103 * @param floatT replacement float or Float.NaN 1104 * @param doubleT replacement double or Double.NaN -- for exponential 1105 * @param doOne mimic sprintf 1106 * @return formatted string 1107 */ 1108 formatString(String strFormat, String key, String strT, float floatT, double doubleT, boolean doOne)1109 private static String formatString(String strFormat, String key, String strT, 1110 float floatT, double doubleT, boolean doOne) { 1111 if (strFormat == null) 1112 return null; 1113 if ("".equals(strFormat)) 1114 return ""; 1115 int len = key.length(); 1116 if (strFormat.indexOf("%") < 0 || len == 0 || strFormat.indexOf(key) < 0) 1117 return strFormat; 1118 1119 String strLabel = ""; 1120 int ich, ichPercent, ichKey; 1121 for (ich = 0; (ichPercent = strFormat.indexOf('%', ich)) >= 0 1122 && (ichKey = strFormat.indexOf(key, ichPercent + 1)) >= 0;) { 1123 if (ich != ichPercent) 1124 strLabel += strFormat.substring(ich, ichPercent); 1125 ich = ichPercent + 1; 1126 if (ichKey > ichPercent + 6) { 1127 strLabel += '%'; 1128 continue;//%12.10x 1129 } 1130 try { 1131 boolean alignLeft = false; 1132 if (strFormat.charAt(ich) == '-') { 1133 alignLeft = true; 1134 ++ich; 1135 } 1136 boolean zeroPad = false; 1137 if (strFormat.charAt(ich) == '0') { 1138 zeroPad = true; 1139 ++ich; 1140 } 1141 char ch; 1142 int width = 0; 1143 while ((ch = strFormat.charAt(ich)) >= '0' && (ch <= '9')) { 1144 width = (10 * width) + (ch - '0'); 1145 ++ich; 1146 } 1147 int precision = Integer.MAX_VALUE; 1148 boolean isExponential = false; 1149 if (strFormat.charAt(ich) == '.') { 1150 ++ich; 1151 if ((ch = strFormat.charAt(ich)) == '-') { 1152 isExponential = (strT == null); 1153 ++ich; 1154 } 1155 if ((ch = strFormat.charAt(ich)) >= '0' && ch <= '9') { 1156 precision = ch - '0'; 1157 ++ich; 1158 } 1159 if (isExponential) 1160 precision = -precision; 1161 } 1162 String st = strFormat.substring(ich, ich + len); 1163 if (!st.equals(key)) { 1164 ich = ichPercent + 1; 1165 strLabel += '%'; 1166 continue; 1167 } 1168 ich += len; 1169 if (!Float.isNaN(floatT)) // 'f' 1170 strLabel += formatF(floatT, width, precision, alignLeft, 1171 zeroPad); 1172 else if (strT != null) // 'd' 'i' or 's' 1173 strLabel += formatS(strT, width, precision, alignLeft, 1174 zeroPad); 1175 else if (!Double.isNaN(doubleT)) // 'e' 1176 strLabel += formatD(doubleT, width, precision - 1, alignLeft, 1177 zeroPad, true); 1178 if (doOne) 1179 break; 1180 } catch (IndexOutOfBoundsException ioobe) { 1181 ich = ichPercent; 1182 break; 1183 } 1184 } 1185 strLabel += strFormat.substring(ich); 1186 //if (strLabel.length() == 0) 1187 //return null; 1188 return strLabel; 1189 } 1190 formatStringS(String strFormat, String key, String strT)1191 public static String formatStringS(String strFormat, String key, String strT) { 1192 return formatString(strFormat, key, strT, Float.NaN, Double.NaN, false); 1193 } 1194 formatStringF(String strFormat, String key, float floatT)1195 public static String formatStringF(String strFormat, String key, float floatT) { 1196 return formatString(strFormat, key, null, floatT, Double.NaN, false); 1197 } 1198 formatStringI(String strFormat, String key, int intT)1199 public static String formatStringI(String strFormat, String key, int intT) { 1200 return formatString(strFormat, key, "" + intT, Float.NaN, Double.NaN, false); 1201 } 1202 1203 /** 1204 * sprintf emulation uses (almost) c++ standard string formats 1205 * 1206 * 's' string 'i' or 'd' integer, 'e' double, 'f' float, 'p' point3f 'q' 1207 * quaternion/plane/axisangle with added "i" (equal to the insipid "d" -- 1208 * digits?) 1209 * 1210 * @param strFormat 1211 * @param list 1212 * a listing of what sort of data will be found in Object[] values, in 1213 * order: s string, f float, i integer, d double, p point3f, q 1214 * quaternion/point4f, S String[], F float[], I int[], and D double[] 1215 * @param values 1216 * Object[] containing above types 1217 * @return formatted string 1218 */ sprintf(String strFormat, String list, Object[] values)1219 public static String sprintf(String strFormat, String list, Object[] values) { 1220 if (values == null) 1221 return strFormat; 1222 int n = list.length(); 1223 if (n == values.length) 1224 try { 1225 for (int o = 0; o < n; o++) { 1226 if (values[o] == null) 1227 continue; 1228 switch (list.charAt(o)) { 1229 case 's': 1230 strFormat = formatString(strFormat, "s", (String) values[o], 1231 Float.NaN, Double.NaN, true); 1232 break; 1233 case 'f': 1234 strFormat = formatString(strFormat, "f", null, ((Float) values[o]) 1235 .floatValue(), Double.NaN, true); 1236 break; 1237 case 'i': 1238 strFormat = formatString(strFormat, "d", "" + values[o], Float.NaN, 1239 Double.NaN, true); 1240 strFormat = formatString(strFormat, "i", "" + values[o], Float.NaN, 1241 Double.NaN, true); 1242 break; 1243 case 'd': 1244 strFormat = formatString(strFormat, "e", null, Float.NaN, 1245 ((Double) values[o]).doubleValue(), true); 1246 break; 1247 case 'p': 1248 T3 pVal = (T3) values[o]; 1249 strFormat = formatString(strFormat, "p", null, pVal.x, Double.NaN, 1250 true); 1251 strFormat = formatString(strFormat, "p", null, pVal.y, Double.NaN, 1252 true); 1253 strFormat = formatString(strFormat, "p", null, pVal.z, Double.NaN, 1254 true); 1255 break; 1256 case 'q': 1257 T4 qVal = (T4) values[o]; 1258 strFormat = formatString(strFormat, "q", null, qVal.x, Double.NaN, 1259 true); 1260 strFormat = formatString(strFormat, "q", null, qVal.y, Double.NaN, 1261 true); 1262 strFormat = formatString(strFormat, "q", null, qVal.z, Double.NaN, 1263 true); 1264 strFormat = formatString(strFormat, "q", null, qVal.w, Double.NaN, 1265 true); 1266 break; 1267 case 'S': 1268 String[] sVal = (String[]) values[o]; 1269 for (int i = 0; i < sVal.length; i++) 1270 strFormat = formatString(strFormat, "s", sVal[i], Float.NaN, 1271 Double.NaN, true); 1272 break; 1273 case 'F': 1274 float[] fVal = (float[]) values[o]; 1275 for (int i = 0; i < fVal.length; i++) 1276 strFormat = formatString(strFormat, "f", null, fVal[i], 1277 Double.NaN, true); 1278 break; 1279 case 'I': 1280 int[] iVal = (int[]) values[o]; 1281 for (int i = 0; i < iVal.length; i++) 1282 strFormat = formatString(strFormat, "d", "" + iVal[i], Float.NaN, 1283 Double.NaN, true); 1284 for (int i = 0; i < iVal.length; i++) 1285 strFormat = formatString(strFormat, "i", "" + iVal[i], Float.NaN, 1286 Double.NaN, true); 1287 break; 1288 case 'D': 1289 double[] dVal = (double[]) values[o]; 1290 for (int i = 0; i < dVal.length; i++) 1291 strFormat = formatString(strFormat, "e", null, Float.NaN, 1292 dVal[i], true); 1293 } 1294 1295 } 1296 return rep(strFormat, "%%", "%"); 1297 } catch (Exception e) { 1298 // 1299 } 1300 System.out.println("TextFormat.sprintf error " + list + " " + strFormat); 1301 return rep(strFormat, "%", "?"); 1302 } 1303 1304 /** 1305 * 1306 * formatCheck checks p and q formats and duplicates if necessary 1307 * "%10.5p xxxx" ==> "%10.5p%10.5p%10.5p xxxx" 1308 * 1309 * @param strFormat 1310 * @return f or dupicated format 1311 */ formatCheck(String strFormat)1312 public static String formatCheck(String strFormat) { 1313 if (strFormat == null || strFormat.indexOf('p') < 0 && strFormat.indexOf('q') < 0) 1314 return strFormat; 1315 strFormat = rep(strFormat, "%%", "\1"); 1316 strFormat = rep(strFormat, "%p", "%6.2p"); 1317 strFormat = rep(strFormat, "%q", "%6.2q"); 1318 String[] format = split(strFormat, "%"); 1319 SB sb = new SB(); 1320 sb.append(format[0]); 1321 for (int i = 1; i < format.length; i++) { 1322 String f = "%" + format[i]; 1323 int pt; 1324 if (f.length() >= 3) { 1325 if ((pt = f.indexOf('p')) >= 0) 1326 f = fdup(f, pt, 3); 1327 if ((pt = f.indexOf('q')) >= 0) 1328 f = fdup(f, pt, 4); 1329 } 1330 sb.append(f); 1331 } 1332 return sb.toString().replace('\1', '%'); 1333 } 1334 leftJustify(SB s, String s1, String s2)1335 public static void leftJustify(SB s, String s1, String s2) { 1336 s.append(s2); 1337 int n = s1.length() - s2.length(); 1338 if (n > 0) 1339 s.append(s1.substring(0, n)); 1340 } 1341 rightJustify(SB s, String s1, String s2)1342 public static void rightJustify(SB s, String s1, String s2) { 1343 int n = s1.length() - s2.length(); 1344 if (n > 0) 1345 s.append(s1.substring(0, n)); 1346 s.append(s2); 1347 } 1348 safeTruncate(float f, int n)1349 public static String safeTruncate(float f, int n) { 1350 if (f > -0.001 && f < 0.001) 1351 f = 0; 1352 return (f + " ").substring(0,n); 1353 } 1354 isWild(String s)1355 public static boolean isWild(String s) { 1356 return s != null && (s.indexOf("*") >= 0 || s.indexOf("?") >= 0); 1357 } 1358 1359 /** 1360 * A general non-regex (for performance) text matcher that utilizes ? and *. 1361 * 1362 * ??? means "at most three" characters if at beginning or end; 1363 * "exactly three" otherwise 1364 * \1 in search is a stand-in for actual ? 1365 * 1366 * @param search 1367 * the string to search 1368 * @param match 1369 * the match string 1370 * @param checkStar 1371 * @param allowInitialStar 1372 * @return true if found 1373 */ isMatch(String search, String match, boolean checkStar, boolean allowInitialStar)1374 public static boolean isMatch(String search, String match, boolean checkStar, 1375 boolean allowInitialStar) { 1376 // search == match --> true 1377 if (search.equals(match)) 1378 return true; 1379 int mLen = match.length(); 1380 // match == "" --> false 1381 if (mLen == 0) 1382 return false; 1383 boolean isStar0 = (checkStar && allowInitialStar ? match.charAt(0) == '*' 1384 : false); 1385 // match == "*" --> true 1386 if (mLen == 1 && isStar0) 1387 return true; 1388 boolean isStar1 = (checkStar && match.endsWith("*")); 1389 boolean haveQ = (match.indexOf('?') >= 0); 1390 // match == "**" --> true 1391 // match == "*xxx*" --> search contains "xxx" 1392 // match == "*xxx" --> search ends with "xxx" 1393 // match == "xxx*" --> search starts with "xxx" 1394 if (!haveQ) { 1395 if (isStar0) 1396 return (isStar1 ? (mLen < 3 || search.indexOf(match.substring(1, 1397 mLen - 1)) >= 0) : search.endsWith(match.substring(1))); 1398 else if (isStar1) 1399 return search.startsWith(match.substring(0, mLen - 1)); 1400 } 1401 int sLen = search.length(); 1402 // pad match with "?" -- same as * 1403 String qqqq = "????"; 1404 int nq = 4; 1405 while (nq < sLen) { 1406 qqqq += qqqq; 1407 nq += 4; 1408 } 1409 if (checkStar) { 1410 if (isStar0) { 1411 match = qqqq + match.substring(1); 1412 mLen += nq - 1; 1413 } 1414 if (isStar1) { 1415 match = match.substring(0, mLen - 1) + qqqq; 1416 mLen += nq - 1; 1417 } 1418 } 1419 // length of match < length of search --> false 1420 if (mLen < sLen) 1421 return false; 1422 1423 // -- each ? matches ONE character if not at end 1424 // -- extra ? at end ignored 1425 1426 // (allowInitialStar == true) 1427 // -- extra ? at beginning reduced to match length 1428 1429 int ich = 0; 1430 while (mLen > sLen) { 1431 if (allowInitialStar && match.charAt(ich) == '?') { 1432 ++ich; 1433 } else if (match.charAt(ich + mLen - 1) != '?') { 1434 return false; 1435 } 1436 --mLen; 1437 } 1438 1439 // both are effectively same length now. 1440 // \1 is stand-in for "?" 1441 1442 for (int i = sLen; --i >= 0;) { 1443 char chm = match.charAt(ich + i); 1444 if (chm == '?') 1445 continue; 1446 char chs = search.charAt(i); 1447 if (chm != chs && (chm != '\1' || chs != '?')) 1448 return false; 1449 } 1450 return true; 1451 } 1452 replaceQuotedStrings(String s, Lst<String> list, Lst<String> newList)1453 public static String replaceQuotedStrings(String s, Lst<String> list, 1454 Lst<String> newList) { 1455 int n = list.size(); 1456 for (int i = 0; i < n; i++) { 1457 String name = list.get(i); 1458 String newName = newList.get(i); 1459 if (!newName.equals(name)) 1460 s = rep(s, "\"" + name + "\"", "\"" + newName 1461 + "\""); 1462 } 1463 return s; 1464 } 1465 replaceStrings(String s, Lst<String> list, Lst<String> newList)1466 public static String replaceStrings(String s, Lst<String> list, 1467 Lst<String> newList) { 1468 int n = list.size(); 1469 for (int i = 0; i < n; i++) { 1470 String name = list.get(i); 1471 String newName = newList.get(i); 1472 if (!newName.equals(name)) 1473 s = rep(s, name, newName); 1474 } 1475 return s; 1476 } 1477 isDigit(char ch)1478 public static boolean isDigit(char ch) { 1479 // just way simpler code than Character.isDigit(ch); 1480 int c = ch; 1481 return (48 <= c && c <= 57); 1482 } 1483 isUpperCase(char ch)1484 public static boolean isUpperCase(char ch) { 1485 int c = ch; 1486 return (65 <= c && c <= 90); 1487 } 1488 isLowerCase(char ch)1489 public static boolean isLowerCase(char ch) { 1490 int c = ch; 1491 return (97 <= c && c <= 122); 1492 } 1493 isLetter(char ch)1494 public static boolean isLetter(char ch) { 1495 // just way simpler code than Character.isLetter(ch); 1496 int c = ch; 1497 return (65 <= c && c <= 90 || 97 <= c && c <= 122); 1498 } 1499 isLetterOrDigit(char ch)1500 public static boolean isLetterOrDigit(char ch) { 1501 // just way simpler code than Character.isLetterOrDigit(ch); 1502 int c = ch; 1503 return (65 <= c && c <= 90 || 97 <= c && c <= 122 || 48 <= c && c <= 57); 1504 } 1505 isWhitespace(char ch)1506 public static boolean isWhitespace(char ch) { 1507 int c = ch; 1508 return (c >= 0x1c && c <= 0x20 || c >= 0x9 && c <= 0xd); 1509 } 1510 1511 public static final float FRACTIONAL_PRECISION = 100000f; 1512 public static final float CARTESIAN_PRECISION = 10000f; 1513 fixPtFloats(T3 pt, float f)1514 public static void fixPtFloats(T3 pt, float f) { 1515 //this will equate float and double as long as -256 <= x <= 256 1516 pt.x = Math.round(pt.x * f) / f; 1517 pt.y = Math.round(pt.y * f) / f; 1518 pt.z = Math.round(pt.z * f) / f; 1519 } 1520 fixDouble(double d, double f)1521 public static double fixDouble(double d, double f) { 1522 return Math.round(d * f) / f; 1523 } 1524 1525 /** 1526 * parse a float or "float/float" 1527 * @param s 1528 * @return a/b 1529 */ parseFloatFraction(String s)1530 public static float parseFloatFraction(String s) { 1531 int pt = s.indexOf("/"); 1532 return (pt < 0 ? parseFloat(s) : parseFloat(s.substring(0, pt)) 1533 / parseFloat(s.substring(pt + 1))); 1534 } 1535 1536 //static { 1537 // 1538 // double d = 790.8999998888; 1539 // float x = 790.8999998888f; 1540 // for (int i = 0; i < 50; i++) { 1541 // System.out.println(x + " " + d); 1542 // System.out.println(Math.round(x * 100000) / 100000f); 1543 // System.out.println(Math.round(d * 100000) / 100000.); 1544 // System.out.println(Math.round(x * 10000) / 10000f); 1545 // System.out.println(Math.round(d * 10000) / 10000.); 1546 // x+=1; 1547 // d+=1; 1548 // } 1549 // System.out.println(100.123456789f); 1550 //} 1551 1552 // static { 1553 // long t; 1554 // char c = '0'; 1555 // t = System.currentTimeMillis(); 1556 // for (int i = 0; i < 10000000; i++) { 1557 // boolean b = PT.isUpperCase(c); 1558 // } 1559 // System.out.println(System.currentTimeMillis() - t); 1560 // 1561 // t = System.currentTimeMillis(); 1562 // for (int i = 0; i < 10000000; i++) { 1563 // boolean b = Character.isUpperCase(c); 1564 // } 1565 // System.out.println(System.currentTimeMillis() - t); 1566 // 1567 // t = System.currentTimeMillis(); 1568 // for (int i = 0; i < 10000000; i++) { 1569 // boolean b = PT.isUpperCase(c); 1570 // } 1571 // System.out.println(System.currentTimeMillis() - t); 1572 // 1573 // System.out.println("PT test"); 1574 // } 1575 } 1576