1 /* $RCSfile$ 2 * $Author: hansonr $ 3 * $Date: 2009-06-05 07:42:12 -0500 (Fri, 05 Jun 2009) $ 4 * $Revision: 10958 $ 5 * 6 * Copyright (C) 2003-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 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 org.jmol.script; 26 27 import java.util.Arrays; 28 import java.util.Collections; 29 import java.util.Comparator; 30 import java.util.Hashtable; 31 import java.util.Map; 32 import java.util.Set; 33 import java.util.Map.Entry; 34 35 36 import javajs.util.BS; 37 import org.jmol.modelset.BondSet; 38 import org.jmol.util.BSUtil; 39 import org.jmol.util.Escape; 40 import org.jmol.viewer.Viewer; 41 42 import javajs.api.JSONEncodable; 43 import javajs.util.Lst; 44 import javajs.util.SB; 45 46 47 import javajs.util.AU; 48 import javajs.util.BArray; 49 import javajs.util.Base64; 50 import javajs.util.M3; 51 import javajs.util.M34; 52 import javajs.util.M4; 53 import javajs.util.Measure; 54 import javajs.util.P3; 55 import javajs.util.P4; 56 import javajs.util.PT; 57 import javajs.util.Quat; 58 import javajs.util.T3; 59 60 import javajs.util.V3; 61 62 63 /** 64 * ScriptVariable class 65 * 66 */ 67 public class SV extends T implements JSONEncodable { 68 69 public final static SV vT = newSV(on, 1, "true"); 70 public final static SV vF = newSV(off, 0, "false"); 71 72 public int index = Integer.MAX_VALUE; 73 74 public String myName; 75 newV(int tok, Object value)76 public static SV newV(int tok, Object value) { 77 SV sv = new SV(); 78 sv.tok = tok; 79 sv.value = value; 80 return sv; 81 } 82 newI(int i)83 public static SV newI(int i) { 84 SV sv = new SV(); 85 sv.tok = integer; 86 sv.intValue = i; 87 return sv; 88 } 89 newF(float f)90 public static SV newF(float f) { 91 SV sv = new SV(); 92 sv.tok = decimal; 93 sv.value = Float.valueOf(f); 94 return sv; 95 } 96 newS(String s)97 public static SV newS(String s) { 98 return newV(string, s); 99 } 100 newT(T x)101 public static SV newT(T x) { 102 return newSV(x.tok, x.intValue, x.value); 103 } 104 newSV(int tok, int intValue, Object value)105 static SV newSV(int tok, int intValue, Object value) { 106 SV sv = newV(tok, value); 107 sv.intValue = intValue; 108 return sv; 109 } 110 111 /** 112 * 113 * Creates a NEW version of the variable. 114 * Object values are not copied. (Just found no 115 * use for that.) 116 * 117 * 118 * @param v 119 * @return new ScriptVariable 120 */ setv(SV v)121 SV setv(SV v) { 122 index = v.index; 123 intValue = v.intValue; 124 tok = v.tok; 125 value = v.value; 126 return this; 127 } 128 129 @SuppressWarnings("unchecked") sizeOf(T x)130 static int sizeOf(T x) { 131 switch (x == null ? nada : x.tok) { 132 case bitset: 133 return bsSelectToken(x).cardinality(); 134 case on: 135 case off: 136 return -1; 137 case integer: 138 return -2; 139 case decimal: 140 return -4; 141 case point3f: 142 return -8; 143 case point4f: 144 return -16; 145 case matrix3f: 146 return -32; 147 case matrix4f: 148 return -64; 149 case barray: 150 return ((BArray) x.value).data.length; 151 case string: 152 return ((String) x.value).length(); 153 case varray: 154 return x.intValue == Integer.MAX_VALUE ? ((SV)x).getList().size() 155 : sizeOf(selectItemTok(x, Integer.MIN_VALUE)); 156 case hash: 157 return ((Map<String, SV>) x.value).size(); 158 case context: 159 return ((ScriptContext) x.value).getFullMap().size(); 160 default: 161 return 0; 162 } 163 } 164 165 /** 166 * Must be updated if getVariable is updated! 167 * 168 * @param x 169 * @return if we recognize this as a variable 170 * 171 */ isVariableType(Object x)172 public static boolean isVariableType(Object x) { 173 return (x instanceof SV 174 || x instanceof Boolean 175 || x instanceof Integer 176 || x instanceof Float 177 || x instanceof String 178 || x instanceof T3 // stored as point3f 179 || x instanceof BS 180 || x instanceof P4 // stored as point4f 181 || x instanceof Quat // stored as point4f 182 || x instanceof M34 183 || x instanceof Map<?, ?> // stored as Map<String, ScriptVariable> 184 || x instanceof Lst<?> 185 || x instanceof BArray 186 || x instanceof ScriptContext 187 // in JavaScript, all these will be "Array" which is fine; 188 || isArray(x)); // stored as list 189 } 190 191 /** 192 * Must be updated if getVariable is updated! 193 * 194 * @param x 195 * @return if we recognize this as an primitive array type 196 * 197 */ isArray(Object x)198 private static boolean isArray(Object x) { 199 /** 200 * @j2sNative 201 * 202 * return Clazz.instanceOf(x, Array); 203 */ 204 { 205 return x instanceof SV[] 206 || x instanceof int[] 207 || x instanceof byte[] 208 || x instanceof float[] 209 || x instanceof double[] 210 || x instanceof String[] 211 || x instanceof T3[] 212 || x instanceof int[][] 213 || x instanceof float[][] 214 || x instanceof String[][] 215 || x instanceof double[][] 216 || x instanceof Float[] 217 || x instanceof Object[]; 218 } 219 } 220 221 222 /** 223 * @param x 224 * @return a ScriptVariable of the input type, or if x is null, then a new 225 * ScriptVariable, or, if the type is not found, a string version 226 */ 227 @SuppressWarnings("unchecked") getVariable(Object x)228 public static SV getVariable(Object x) { 229 if (x == null) 230 return newS(""); 231 if (x instanceof SV) 232 return (SV) x; 233 234 // the eight basic types are: 235 // boolean, integer, decimal, string, point3f, point4f, bitset, and list 236 // listf is a special temporary type for storing results 237 // of .all in preparation for .bin in the case of xxx.all.bin 238 // but with some work, this could be developed into a storage class 239 240 if (x instanceof Boolean) 241 return getBoolean(((Boolean) x).booleanValue()); 242 if (x instanceof Integer) 243 return newI(((Integer) x).intValue()); 244 if (x instanceof Float) 245 return newV(decimal, x); 246 if (x instanceof String) { 247 x = unescapePointOrBitsetAsVariable(x); 248 if (x instanceof SV) 249 return (SV) x; 250 return newV(string, x); 251 } 252 if (x instanceof P3) 253 return newV(point3f, x); 254 if (x instanceof V3) // point3f is not mutable anyway 255 return newV(point3f, P3.newP((V3) x)); 256 if (x instanceof BS) 257 return newV(bitset, x); 258 if (x instanceof P4) 259 return newV(point4f, x); 260 // note: for quaternions, we save them {q1, q2, q3, q0} 261 // While this may seem odd, it is so that for any point4 -- 262 // planes, axisangles, and quaternions -- we can use the 263 // first three coordinates to determine the relavent axis 264 // the fourth then gives us offset to {0,0,0} (plane), 265 // rotation angle (axisangle), and cos(theta/2) (quaternion). 266 if (x instanceof Quat) 267 return newV(point4f, ((Quat) x).toPoint4f()); 268 if (x instanceof M34) 269 return newV(x instanceof M4 ? matrix4f : matrix3f, x); 270 if (x instanceof Map) 271 return getVariableMap((Map<String, ?>)x); 272 if (x instanceof Lst) 273 return getVariableList((Lst<?>) x); 274 if (x instanceof BArray) 275 return newV(barray, x); 276 if (x instanceof ScriptContext) 277 return newV(context, x); 278 if (isASV(x)) 279 return getVariableAV((SV[]) x); 280 // rest are specific array types supported 281 // DO NOT ADD MORE UNLESS YOU CHANGE isArray(), above 282 if (AU.isAI(x)) 283 return getVariableAI((int[]) x); 284 if (AU.isAB(x)) 285 return getVariableAB((byte[]) x); 286 if (AU.isAF(x)) 287 return getVariableAF((float[]) x); 288 if (AU.isAD(x)) 289 return getVariableAD((double[]) x); 290 if (AU.isAS(x)) 291 return getVariableAS((String[]) x); 292 //NOTE: isAP(x) does not return TRUE for Atom[] in JavaScript 293 // only occurrence of this was for Polyhedron.getInfo() 294 if (AU.isAP(x)) 295 return getVariableAP((T3[]) x); 296 if (AU.isAII(x)) 297 return getVariableAII((int[][]) x); 298 if (AU.isAFF(x)) 299 return getVariableAFF((float[][]) x); 300 if (AU.isASS(x)) 301 return getVariableASS((String[][]) x); 302 if (AU.isADD(x)) 303 return getVariableADD((double[][]) x); 304 if (AU.isAFloat(x)) 305 return newV(listf, x); 306 return newJSVar(x); 307 } 308 isASV(Object x)309 private static boolean isASV(Object x) { 310 if (!Viewer.isSwingJS) { 311 /** 312 * @j2sNative 313 * 314 * return x && x[0] && x[0].__CLASS_NAME__ == "JS.SV"; 315 * 316 */ 317 {} 318 } 319 return x instanceof SV[]; 320 } 321 322 /** 323 * Conversion to Jmol variables of JavaScript variables using 324 * y = javascript("x") 325 * 326 * @param x a JavaScript variable, perhaps 327 * @return SV 328 */ 329 @SuppressWarnings("unused") newJSVar(Object x)330 private static SV newJSVar(Object x) { 331 // JavaScript only 332 int itype; 333 boolean itest; 334 float inum; 335 Object[] array; 336 String[] keys; 337 /** 338 * @j2sNative 339 * 340 * switch(x.BYTES_PER_ELEMENT ? Array : x.constructor) { 341 * case Boolean: 342 * itype = 0; 343 * itest = x; 344 * break; 345 * case Number: 346 * itype = 1; 347 * inum = x; 348 * break; 349 * case Array: 350 * itype = 2; 351 * array = x; 352 * break; 353 * case Object: 354 * itype = 3; 355 * array = x; 356 * keys = Object.keys(x); 357 * break; 358 * } 359 * 360 */ 361 { 362 if (x instanceof Object[]) 363 return getVariableAO((Object[]) x); 364 if (true) 365 return newS(x.toString()); 366 } 367 368 // JavaScript only 369 switch (itype) { 370 case 0: 371 return (itest ? vT : vF); 372 case 1: 373 return (inum > Integer.MAX_VALUE || inum != Math.floor(inum) 374 ? SV.newF(inum) : newI((int) inum)); 375 case 2: 376 Lst<SV> v = new javajs.util.Lst<SV>(); 377 for (int i = 0, n = array.length; i < n; i++) 378 v.addLast(newJSVar(array[i])); 379 return getVariableList(v); 380 case 3: 381 Map<String, Object> map = new Hashtable<String, Object>(); 382 for (int i = keys.length; --i >= 0;) { 383 Object o = null; 384 /** 385 * @j2sNative 386 * 387 * o = array[keys[i]]; 388 * 389 */ 390 { 391 } 392 map.put(keys[i], newJSVar(o)); 393 } 394 return SV.getVariableMap(map); 395 } 396 return newS(x.toString()); 397 } 398 399 @SuppressWarnings("unchecked") getVariableMap(Map<String, ?> x)400 public static SV getVariableMap(Map<String, ?> x) { 401 Map<String, Object> ht = (Map<String, Object>) x; 402 Object o = null; 403 for (Object oo : ht.values()) { 404 o = oo; 405 break; 406 } 407 if (!(o instanceof SV)) { 408 Map<String, SV> x2 = new Hashtable<String, SV>(); 409 for (Map.Entry<String, Object> entry : ht.entrySet()) 410 x2.put(entry.getKey(), getVariable(entry.getValue())); 411 x = x2; 412 } 413 return newV(hash, x); 414 } 415 getVariableList(Lst<?> v)416 public static SV getVariableList(Lst<?> v) { 417 int len = v.size(); 418 if (len > 0 && v.get(0) instanceof SV) 419 return newV(varray, v); 420 Lst<SV> objects = new Lst<SV>(); 421 for (int i = 0; i < len; i++) 422 objects.addLast(getVariable(v.get(i))); 423 return newV(varray, objects); 424 } 425 getVariableAV(SV[] v)426 static SV getVariableAV(SV[] v) { 427 Lst<SV> objects = new Lst<SV>(); 428 for (int i = 0; i < v.length; i++) 429 objects.addLast(v[i]); 430 return newV(varray, objects); 431 } 432 getVariableAD(double[] f)433 public static SV getVariableAD(double[] f) { 434 Lst<SV> objects = new Lst<SV>(); 435 for (int i = 0; i < f.length; i++) 436 objects.addLast(newV(decimal, Float.valueOf((float) f[i]))); 437 return newV(varray, objects); 438 } 439 getVariableAO(Object[] o)440 static SV getVariableAO(Object[] o) { 441 Lst<SV> objects = new Lst<SV>(); 442 for (int i = 0; i < o.length; i++) 443 objects.addLast(getVariable(o[i])); 444 return newV(varray, objects); 445 } 446 getVariableAS(String[] s)447 static SV getVariableAS(String[] s) { 448 Lst<SV> objects = new Lst<SV>(); 449 for (int i = 0; i < s.length; i++) 450 objects.addLast(newV(string, s[i])); 451 return newV(varray, objects); 452 } 453 getVariableAP(T3[] p)454 static SV getVariableAP(T3[] p) { 455 Lst<SV> objects = new Lst<SV>(); 456 for (int i = 0; i < p.length; i++) 457 objects.addLast(newV(point3f, p[i])); 458 return newV(varray, objects); 459 } 460 getVariableAFF(float[][] fx)461 static SV getVariableAFF(float[][] fx) { 462 Lst<SV> objects = new Lst<SV>(); 463 for (int i = 0; i < fx.length; i++) 464 objects.addLast(getVariableAF(fx[i])); 465 return newV(varray, objects); 466 } 467 getVariableADD(double[][] fx)468 static SV getVariableADD(double[][] fx) { 469 Lst<SV> objects = new Lst<SV>(); 470 for (int i = 0; i < fx.length; i++) 471 objects.addLast(getVariableAD(fx[i])); 472 return newV(varray, objects); 473 } 474 getVariableASS(String[][] fx)475 static SV getVariableASS(String[][] fx) { 476 Lst<SV> objects = new Lst<SV>(); 477 for (int i = 0; i < fx.length; i++) 478 objects.addLast(getVariableAS(fx[i])); 479 return newV(varray, objects); 480 } 481 getVariableAII(int[][] ix)482 static SV getVariableAII(int[][] ix) { 483 Lst<SV> objects = new Lst<SV>(); 484 for (int i = 0; i < ix.length; i++) 485 objects.addLast(getVariableAI(ix[i])); 486 return newV(varray, objects); 487 } 488 getVariableAF(float[] f)489 static SV getVariableAF(float[] f) { 490 Lst<SV> objects = new Lst<SV>(); 491 for (int i = 0; i < f.length; i++) 492 objects.addLast(newV(decimal, Float.valueOf(f[i]))); 493 return newV(varray, objects); 494 } 495 getVariableAI(int[] ix)496 static SV getVariableAI(int[] ix) { 497 Lst<SV> objects = new Lst<SV>(); 498 for (int i = 0; i < ix.length; i++) 499 objects.addLast(newI(ix[i])); 500 return newV(varray, objects); 501 } 502 getVariableAB(byte[] ix)503 static SV getVariableAB(byte[] ix) { 504 Lst<SV> objects = new Lst<SV>(); 505 for (int i = 0; i < ix.length; i++) 506 objects.addLast(newI(ix[i])); 507 return newV(varray, objects); 508 } 509 setName(String name)510 public SV setName(String name) { 511 this.myName = name; 512 //System.out.println("Variable: " + name + " " + intValue + " " + value); 513 return this; 514 } 515 canIncrement()516 boolean canIncrement() { 517 switch (tok) { 518 case integer: 519 case decimal: 520 return true; 521 default: 522 return false; 523 } 524 } 525 increment(int n)526 boolean increment(int n) { 527 switch (tok) { 528 case integer: 529 intValue += n; 530 return true; 531 case decimal: 532 value = Float.valueOf(((Float) value).floatValue() + n); 533 return true; 534 default: 535 return false; 536 } 537 } 538 asBoolean()539 public boolean asBoolean() { 540 return bValue(this); 541 } 542 asInt()543 public int asInt() { 544 return iValue(this); 545 } 546 asFloat()547 public float asFloat() { 548 return fValue(this); 549 } 550 asString()551 public String asString() { 552 return sValue(this); 553 } 554 555 // math-related Token static methods 556 557 private final static P3 pt0 = new P3(); 558 559 /** 560 * 561 * @param xx 562 * @return Object-wrapped value 563 */ 564 oValue(Object xx)565 public static Object oValue(Object xx) { 566 if (!(xx instanceof SV)) 567 return xx; 568 SV x = (SV) xx; 569 switch (x.tok) { 570 case on: 571 return Boolean.TRUE; 572 case nada: 573 case off: 574 return Boolean.FALSE; 575 case integer: 576 return Integer.valueOf(x.intValue); 577 case bitset: 578 case array: 579 return selectItemVar(x).value; // TODO: matrix3f?? 580 default: 581 return x.value; 582 } 583 584 } 585 586 /** 587 * 588 * @param x 589 * @return numeric value -- integer or decimal 590 */ nValue(T x)591 static Object nValue(T x) { 592 int iValue; 593 switch (x == null ? nada : x.tok) { 594 case decimal: 595 return x.value; 596 case integer: 597 iValue = x.intValue; 598 break; 599 case string: 600 if (((String) x.value).indexOf(".") >= 0) 601 return Float.valueOf(toFloat((String) x.value)); 602 iValue = (int) toFloat((String) x.value); 603 break; 604 case point3f: 605 return Float.valueOf(((T3) x.value).length()); 606 default: 607 iValue = 0; 608 } 609 return Integer.valueOf(iValue); 610 } 611 612 // there are reasons to use Token here rather than ScriptVariable 613 // for some of these functions, in particular iValue, fValue, and sValue 614 bValue(T x)615 public static boolean bValue(T x) { 616 switch (x == null ? nada : x.tok) { 617 case on: 618 case context: 619 return true; 620 case off: 621 return false; 622 case integer: 623 return x.intValue != 0; 624 case decimal: 625 case string: 626 case varray: 627 return fValue(x) != 0; 628 case bitset: 629 case barray: 630 return iValue(x) != 0; 631 case point3f: 632 case point4f: 633 case matrix3f: 634 case matrix4f: 635 return Math.abs(fValue(x)) > 0.0001f; 636 case hash: 637 return !((SV) x).getMap().isEmpty(); 638 default: 639 return false; 640 } 641 } 642 iValue(T x)643 public static int iValue(T x) { 644 switch (x == null ? nada : x.tok) { 645 case on: 646 return 1; 647 case off: 648 return 0; 649 case integer: 650 return x.intValue; 651 case decimal: 652 case varray: 653 case string: 654 case point3f: 655 case point4f: 656 case matrix3f: 657 case matrix4f: 658 case quaternion: 659 return (int) fValue(x); 660 case bitset: 661 return bsSelectToken(x).cardinality(); 662 case barray: 663 return ((BArray) x.value).data.length; 664 default: 665 return 0; 666 } 667 } 668 fValue(T x)669 public static float fValue(T x) { 670 switch (x == null ? nada : x.tok) { 671 case on: 672 return 1; 673 case off: 674 return 0; 675 case integer: 676 return x.intValue; 677 case decimal: 678 return ((Float) x.value).floatValue(); 679 case varray: 680 int i = x.intValue; 681 if (i == Integer.MAX_VALUE) 682 return ((SV)x).getList().size(); 683 //$FALL-THROUGH$ 684 case string: 685 return toFloat(sValue(x)); 686 case bitset: 687 case barray: 688 return iValue(x); 689 case point3f: 690 return ((P3) x.value).length(); 691 case point4f: 692 return Measure.distanceToPlane((P4) x.value, pt0); 693 case matrix3f: 694 P3 pt = new P3(); 695 ((M3) x.value).rotate(pt); 696 return pt.length(); 697 case matrix4f: 698 P3 pt1 = new P3(); 699 ((M4) x.value).rotTrans(pt1); 700 return pt1.length(); 701 default: 702 return 0; 703 } 704 } 705 sValue(T x)706 public static String sValue(T x) { 707 if (x == null) 708 return ""; 709 int i; 710 SB sb; 711 switch (x.tok) { 712 case on: 713 return "true"; 714 case off: 715 return "false"; 716 case integer: 717 return "" + x.intValue; 718 case bitset: 719 BS bs = bsSelectToken(x); 720 return (x.value instanceof BondSet ? Escape.eBond(bs) : Escape.eBS(bs)); 721 case varray: 722 Lst<SV> sv = ((SV) x).getList(); 723 i = x.intValue; 724 if (i <= 0) 725 i = sv.size() - i; 726 if (i != Integer.MAX_VALUE) 727 return (i < 1 || i > sv.size() ? "" : sValue(sv.get(i - 1))); 728 //$FALL-THROUGH$ 729 case hash: 730 case context: 731 if (x.value instanceof String) 732 return (String) x.value; // just the command 733 sb = new SB(); 734 sValueArray(sb, (SV) x, "", "", false, true, true, Integer.MAX_VALUE, false); 735 return PT.rep(sb.toString(), "\n\0", " "); // circular ref 736 case string: 737 String s = (String) x.value; 738 i = x.intValue; 739 if (i <= 0) 740 i = s.length() - i; 741 if (i == Integer.MAX_VALUE) 742 return s; 743 if (i < 1 || i > s.length()) 744 return ""; 745 return "" + s.charAt(i - 1); 746 case point3f: 747 return Escape.eP((T3) x.value); 748 case point4f: 749 return Escape.eP4((P4) x.value); 750 case matrix3f: 751 case matrix4f: 752 return Escape.e(x.value); 753 default: 754 return x.value.toString(); 755 } 756 } 757 sValueArray(SB sb, SV vx, String path, String tabs, boolean isEscaped, boolean isRaw, boolean addValues, int maxLevels, boolean skipEmpty)758 private static void sValueArray(SB sb, SV vx, String path, String tabs, 759 boolean isEscaped, boolean isRaw, 760 boolean addValues, int maxLevels, 761 boolean skipEmpty) { 762 switch (vx.tok) { 763 case hash: 764 case context: 765 case varray: 766 String thiskey = ";" + vx.hashCode() + ";"; 767 if (path.indexOf(thiskey) >= 0) { 768 sb.append(isEscaped ? (vx.tok == varray ? "[ ]" : "{ }") 769 : (vx.tok == varray ? "" : "\0") + "\"<" 770 + (vx.myName == null ? "circular reference" : vx.myName) + ">\""); 771 break; 772 } 773 path += thiskey; 774 if (vx.tok == varray) { 775 if (!addValues) 776 return; 777 if (!isRaw) 778 sb.append(isEscaped ? "[ " : tabs + "[\n"); 779 Lst<SV> sx = vx.getList(); 780 for (int i = 0; i < sx.size(); i++) { 781 if (isEscaped && i > 0) 782 sb.append(","); 783 SV sv = sx.get(i); 784 sValueArray(sb, sv, path, tabs + " ", isEscaped, tabs.length() == 0 785 && !isEscaped && isRawType(sv.tok), addValues, maxLevels, 786 skipEmpty); 787 if (!isEscaped) 788 sb.append("\n"); 789 } 790 if (!isRaw) 791 sb.append(isEscaped ? " ]" : tabs + "]"); 792 } else if (--maxLevels >= 0) { 793 Map<String, SV> ht = (vx.tok == context ? ((ScriptContext) vx.value) 794 .getFullMap() : vx.getMap()); 795 sValueAddKeys(sb, path, ht, tabs, isEscaped, addValues, maxLevels, skipEmpty); 796 } 797 break; 798 default: 799 if (!addValues) 800 return; 801 if (!isRaw && !isEscaped) 802 sb.append(tabs); 803 sb.append(isEscaped ? vx.escape() : sValue(vx)); 804 } 805 } 806 807 @SuppressWarnings("cast") sValueAddKeys(SB sb, String path, Map<String, SV> ht, String tabs, boolean isEscaped, boolean addValues, int maxLevels, boolean skipEmpty)808 private static void sValueAddKeys(SB sb, String path, Map<String, SV> ht, String tabs, boolean isEscaped, boolean addValues, int maxLevels, boolean skipEmpty) { 809 if (maxLevels < 0) 810 return; 811 Set<String> keyset = ht.keySet(); 812 String[] keys = ht.keySet().toArray(new String[keyset.size()]); 813 Arrays.sort(keys); 814 if (isEscaped) { 815 sb.append("{ "); 816 String sep = ""; 817 for (int i = 0; i < keys.length; i++) { 818 String key = keys[i]; 819 SV val = ht.get(key); 820 if (skipEmpty && (val.tok == varray && val.getList().size() == 0 821 || val.tok == hash && val.getMap().isEmpty())) 822 continue; 823 if (addValues) 824 sb.append(sep).append(PT.esc(key)).append(":"); 825 else 826 sb.appendC(' ').append(key); 827 sValueArray(sb, val, path, tabs+" ", true, false, addValues, maxLevels, skipEmpty); 828 sep = ","; 829 } 830 sb.append(" }"); 831 if (!addValues) 832 sb.append("\n"); 833 834 return; 835 } 836 sb.append(tabs).append("{\n"); 837 tabs += " "; 838 for (int i = 0; i < keys.length; i++) { 839 sb.append(tabs); 840 String key = keys[i]; 841 sb.append(PT.esc(key)).append(" :"); 842 SB sb2 = new SB(); 843 if (!(ht.get(key) instanceof SV)) 844 ht.put(key, SV.getVariable(ht.get(key))); 845 SV v = ht.get(key); 846 isEscaped = isRawType(v.tok); 847 sValueArray(sb2, v, path, tabs, isEscaped, false, addValues, maxLevels, skipEmpty); 848 String value = sb2.toString(); 849 if (isEscaped && addValues) 850 sb.append(" "); 851 else 852 sb.append("\n"); 853 sb.append(value).append("\n"); 854 } 855 sb.append(tabs.substring(1)).append("}"); 856 } 857 isRawType(int tok)858 private static boolean isRawType(int tok) { 859 switch (tok) { 860 case string: 861 case decimal: 862 case integer: 863 case point3f: 864 case point4f: 865 case bitset: 866 case barray: 867 case on: 868 case off: 869 return true; 870 } 871 return false; 872 } 873 ptValue(SV x)874 public static P3 ptValue(SV x) { 875 switch (x.tok) { 876 case point3f: 877 return (P3) x.value; 878 case string: 879 Object o = Escape.uP((String) x.value); 880 if (o instanceof P3) 881 return (P3) o; 882 } 883 return null; 884 } 885 pt4Value(SV x)886 public static P4 pt4Value(SV x) { 887 switch (x.tok) { 888 case point4f: 889 return (P4) x.value; 890 case string: 891 Object o = Escape.uP((String) x.value); 892 if (!(o instanceof P4)) 893 break; 894 return (P4) o; 895 } 896 return null; 897 } 898 toFloat(String s)899 private static float toFloat(String s) { 900 return (s.equalsIgnoreCase("true") ? 1 901 : s.length() == 0 || s.equalsIgnoreCase("false") ? 0 902 : PT.parseFloatStrict(PT.trim(s," \t\n"))); 903 } 904 concatList(SV x1, SV x2, boolean asNew)905 public static SV concatList(SV x1, SV x2, boolean asNew) { 906 Lst<SV> v1 = x1.getList(); 907 Lst<SV> v2 = x2.getList(); 908 if (!asNew) { 909 if (v2 == null) 910 v1.addLast(newT(x2)); 911 else 912 for (int i = 0; i < v2.size(); i++) 913 v1.addLast(v2.get(i)); 914 return x1; 915 } 916 Lst<SV> vlist = new Lst<SV>(); 917 //(v1 == null ? 1 : v1.size()) + (v2 == null ? 1 : v2.size()) 918 if (v1 == null) 919 vlist.addLast(x1); 920 else 921 for (int i = 0; i < v1.size(); i++) 922 vlist.addLast(v1.get(i)); 923 if (v2 == null) 924 vlist.addLast(x2); 925 else 926 for (int i = 0; i < v2.size(); i++) 927 vlist.addLast(v2.get(i)); 928 return getVariableList(vlist); 929 } 930 bsSelectToken(T x)931 private static BS bsSelectToken(T x) { 932 return (BS) selectItemTok(x, Integer.MIN_VALUE).value; 933 } 934 bsSelectRange(T x, int n)935 static BS bsSelectRange(T x, int n) { 936 x = selectItemTok(x, Integer.MIN_VALUE); 937 x = selectItemTok(x, (n <= 0 ? n : 1)); 938 x = selectItemTok(x, (n <= 0 ? Integer.MAX_VALUE - 1 : n)); 939 return (BS) x.value; 940 } 941 selectItemVar(SV var)942 static SV selectItemVar(SV var) { 943 // pass bitsets created by the select() or for() inline functions 944 // and all arrays by reference 945 return (var.index != Integer.MAX_VALUE 946 || (var.tok == varray || var.tok == barray) 947 && var.intValue == Integer.MAX_VALUE ? var : (SV) selectItemTok(var, 948 Integer.MIN_VALUE)); 949 } 950 selectItemTok(T tokenIn, int i2)951 static T selectItemTok(T tokenIn, int i2) { 952 switch (tokenIn.tok) { 953 case matrix3f: 954 case matrix4f: 955 case bitset: 956 case varray: 957 case barray: 958 case string: 959 break; 960 default: 961 return ((tokenIn instanceof SV) && ((SV) tokenIn).myName != null 962 ? newI(0).setv((SV) tokenIn) 963 : tokenIn); 964 } 965 966 // negative number is a count from the end 967 968 BS bs = null; 969 String s = null; 970 971 int i1 = tokenIn.intValue; 972 boolean isOne = (i2 == Integer.MIN_VALUE); 973 if (i1 == Integer.MAX_VALUE) { 974 // no selections have been made yet -- 975 // we just create a new token with the 976 // same bitset and now indicate either 977 // the selected value or "ALL" (max_value) 978 return newSV(tokenIn.tok, (isOne ? i1 : i2), tokenIn.value); 979 } 980 int len = 0; 981 boolean isInputSelected = (tokenIn instanceof SV && ((SV) tokenIn).index != Integer.MAX_VALUE); 982 SV tokenOut = newSV(tokenIn.tok, Integer.MAX_VALUE, null); 983 984 switch (tokenIn.tok) { 985 case bitset: 986 if (tokenIn.value instanceof BondSet) { 987 bs = BondSet.newBS((BS) tokenIn.value, 988 ((BondSet) tokenIn.value).associatedAtoms); 989 len = bs.cardinality(); 990 } else { 991 bs = BSUtil.copy((BS) tokenIn.value); 992 len = (isInputSelected ? 1 : bs.cardinality()); 993 } 994 break; 995 case barray: 996 len = ((BArray) (((SV) tokenIn).value)).data.length; 997 break; 998 case varray: 999 len = ((SV) tokenIn).getList().size(); 1000 break; 1001 case string: 1002 s = (String) tokenIn.value; 1003 len = s.length(); 1004 break; 1005 case matrix3f: 1006 len = -3; 1007 break; 1008 case matrix4f: 1009 len = -4; 1010 break; 1011 } 1012 1013 if (len < 0) { 1014 // matrix mode [1][3] or [13] 1015 len = -len; 1016 if (i1 > 0 && Math.abs(i1) > len) { 1017 int col = i1 % 10; 1018 int row = (i1 - col) / 10; 1019 if (col > 0 && col <= len && row <= len) { 1020 if (tokenIn.tok == matrix3f) 1021 return newV(decimal, Float.valueOf(((M3) tokenIn.value).getElement( 1022 row - 1, col - 1))); 1023 return newV(decimal, 1024 Float.valueOf(((M4) tokenIn.value).getElement(row - 1, col - 1))); 1025 } 1026 return newV(string, ""); 1027 } 1028 if (Math.abs(i1) > len) 1029 return newV(string, ""); 1030 float[] data = new float[len]; 1031 if (len == 3) { 1032 if (i1 < 0) 1033 ((M3) tokenIn.value).getColumn(-1 - i1, data); 1034 else 1035 ((M3) tokenIn.value).getRow(i1 - 1, data); 1036 } else { 1037 if (i1 < 0) 1038 ((M4) tokenIn.value).getColumn(-1 - i1, data); 1039 else 1040 ((M4) tokenIn.value).getRow(i1 - 1, data); 1041 } 1042 if (isOne) 1043 return getVariableAF(data); 1044 if (i2 < 1 || i2 > len) 1045 return newV(string, ""); 1046 return newV(decimal, Float.valueOf(data[i2 - 1])); 1047 } 1048 1049 // "testing"[0] gives "g" 1050 // "testing"[-1] gives "n" 1051 // "testing"[3][0] gives "sting" 1052 // "testing"[-1][0] gives "ng" 1053 // "testing"[0][-2] gives just "g" as well 1054 // "testing"[-10] gives "" 1055 if (i1 <= 0) 1056 i1 = len + i1; 1057 if (!isOne) { 1058 if (i1 < 1) 1059 i1 = 1; 1060 if (i2 == 0) 1061 i2 = len; 1062 else if (i2 < 0) 1063 i2 = len + i2; 1064 if (i2 < i1) 1065 i2 = i1; 1066 } 1067 1068 switch (tokenIn.tok) { 1069 case bitset: 1070 tokenOut.value = bs; 1071 if (isInputSelected) { 1072 if (i1 > 1) 1073 bs.clearAll(); 1074 break; 1075 } 1076 if (isOne) { 1077 // i2 will be Integer.MIN_VALUE at this point 1078 // take care of easy ones the easy way 1079 if (i1 == len) { 1080 // {xxx}[0] 1081 i2 = bs.length() - 1; 1082 } else if (i1 == 1) { 1083 // {xxx}[1] 1084 i2 = bs.nextSetBit(0); 1085 } 1086 if (i2 >= -1) { 1087 bs.clearAll(); 1088 if (i2 >= 0) 1089 bs.set(i2); 1090 break; 1091 } 1092 i2 = i1; 1093 } 1094 int n = 0; 1095 for (int j = bs.nextSetBit(0); j >= 0; j = bs.nextSetBit(j + 1)) 1096 if (++n < i1 || n > i2) 1097 bs.clear(j); 1098 break; 1099 case string: 1100 tokenOut.value = (--i1 < 0 || i1 >= len ? "" : isOne ? s.substring(i1, 1101 i1 + 1) : s.substring(i1, Math.min(i2, len))); 1102 break; 1103 case varray: 1104 if (--i1 < 0 || i1 >= len) 1105 return newV(string, ""); 1106 if (isOne) 1107 return ((SV) tokenIn).getList().get(i1); 1108 Lst<SV> o2 = new Lst<SV>(); 1109 Lst<SV> o1 = ((SV) tokenIn).getList(); 1110 1111 int nn = Math.min(i2, len) - i1; 1112 for (int i = 0; i < nn; i++) 1113 o2.addLast(newT(o1.get(i + i1))); 1114 tokenOut.value = o2; 1115 break; 1116 case barray: 1117 if (--i1 < 0 || i1 >= len) 1118 return newV(string, ""); 1119 byte[] data = ((BArray) (((SV) tokenIn).value)).data; 1120 if (isOne) 1121 return newI(data[i1]); 1122 byte[] b = new byte[Math.min(i2, len) - i1]; 1123 for (int i = b.length; --i >= 0;) 1124 b[i] = data[i1 + i]; 1125 tokenOut.value = new BArray(b); 1126 break; 1127 } 1128 return tokenOut; 1129 } 1130 setSelectedValue(int pt1, int pt2, SV var)1131 void setSelectedValue(int pt1, int pt2, SV var) { 1132 if (pt1 == Integer.MAX_VALUE) 1133 return; 1134 int len; 1135 switch (tok) { 1136 case matrix3f: 1137 case matrix4f: 1138 len = (tok == matrix3f ? 3 : 4); 1139 if (pt2 != Integer.MAX_VALUE) { 1140 int col = pt2; 1141 int row = pt1; 1142 if (col > 0 && col <= len && row <= len) { 1143 if (tok == matrix3f) 1144 ((M3) value).setElement(row - 1, col - 1, fValue(var)); 1145 else 1146 ((M4) value).setElement(row - 1, col - 1, fValue(var)); 1147 return; 1148 } 1149 } 1150 if (pt1 != 0 && Math.abs(pt1) <= len 1151 && var.tok == varray) { 1152 Lst<SV> sv = var.getList(); 1153 if (sv.size() == len) { 1154 float[] data = new float[len]; 1155 for (int i = 0; i < len; i++) 1156 data[i] = fValue(sv.get(i)); 1157 if (pt1 > 0) { 1158 if (tok == matrix3f) 1159 ((M3) value).setRowA(pt1 - 1, data); 1160 else 1161 ((M4) value).setRowA(pt1 - 1, data); 1162 } else { 1163 if (tok == matrix3f) 1164 ((M3) value).setColumnA(-1 - pt1, data); 1165 else 1166 ((M4) value).setColumnA(-1 - pt1, data); 1167 } 1168 break; 1169 } 1170 } 1171 break; 1172 case string: 1173 String str = (String) value; 1174 int pt = str.length(); 1175 if (pt1 <= 0) 1176 pt1 = pt + pt1; 1177 if (--pt1 < 0) 1178 pt1 = 0; 1179 while (pt1 >= str.length()) 1180 str += " "; 1181 if (pt2 == Integer.MAX_VALUE){ 1182 pt2 = pt1; 1183 } else { 1184 if (--pt2 < 0) 1185 pt2 = pt + pt2; 1186 while (pt2 >= str.length()) 1187 str += " "; 1188 } 1189 if (pt2 >= pt1) 1190 value = str.substring(0, pt1) + sValue(var) 1191 + str.substring(++pt2); 1192 intValue = index = Integer.MAX_VALUE; 1193 break; 1194 case varray: 1195 @SuppressWarnings("unchecked") 1196 Lst<SV> v = (Lst<SV>) value; 1197 len = v.size(); 1198 if (pt1 <= 0) 1199 pt1 = len + pt1; 1200 if (--pt1 < 0) 1201 pt1 = 0; 1202 if (len <= pt1) 1203 for (int i = len; i <= pt1; i++) 1204 v.addLast(newV(string, "")); 1205 v.set(pt1, var); 1206 break; 1207 } 1208 } 1209 escape()1210 public String escape() { 1211 switch (tok) { 1212 case string: 1213 return PT.esc((String) value); 1214 case matrix3f: 1215 case matrix4f: 1216 return PT.toJSON(null, value); 1217 case varray: 1218 case hash: 1219 case context: 1220 SB sb = new SB(); 1221 sValueArray(sb, this, "", "", true, false, true, Integer.MAX_VALUE, false); 1222 return sb.toString(); 1223 default: 1224 return sValue(this); 1225 } 1226 } 1227 unescapePointOrBitsetAsVariable(Object o)1228 public static Object unescapePointOrBitsetAsVariable(Object o) { 1229 if (o == null) 1230 return o; 1231 Object v = null; 1232 String s = null; 1233 if (o instanceof SV) { 1234 SV sv = (SV) o; 1235 switch (sv.tok) { 1236 case point3f: 1237 case point4f: 1238 case matrix3f: 1239 case matrix4f: 1240 case bitset: 1241 v = sv.value; 1242 break; 1243 case string: 1244 s = (String) sv.value; 1245 break; 1246 default: 1247 s = sValue(sv); 1248 break; 1249 } 1250 } else if (o instanceof String) { 1251 s = (String) o; 1252 } 1253 if (s != null && s.length() == 0) 1254 return s; 1255 if (v == null) 1256 v = Escape.uABsM(s); 1257 if (v instanceof P3) 1258 return (newV(point3f, v)); 1259 if (v instanceof P4) 1260 return newV(point4f, v); 1261 if (v instanceof BS) { 1262 if (s != null && s.indexOf("[{") == 0) 1263 v = BondSet.newBS((BS) v, null); 1264 return newV(bitset, v); 1265 } 1266 if (v instanceof M34) 1267 return (newV(v instanceof M3 ? matrix3f : matrix4f, v)); 1268 return o; 1269 } 1270 getBoolean(boolean value)1271 public static SV getBoolean(boolean value) { 1272 return newT(value ? vT : vF); 1273 } 1274 sprintf(String strFormat, SV var)1275 public static Object sprintf(String strFormat, SV var) { 1276 if (var == null) 1277 return strFormat; 1278 boolean isArray = (var.tok == varray); 1279 int[] vd = (strFormat.indexOf("d") >= 0 || strFormat.indexOf("i") >= 0 ? new int[1] 1280 : null); 1281 float[] vf = (strFormat.indexOf("f") >= 0 ? new float[1] : null); 1282 double[] ve = (strFormat.indexOf("e") >= 0 ? new double[1] : null); 1283 boolean getS = (strFormat.indexOf("s") >= 0); 1284 boolean getP = (strFormat.indexOf("p") >= 0 && (isArray || var.tok == point3f)); 1285 boolean getQ = (strFormat.indexOf("q") >= 0 && (isArray || var.tok == point4f)); 1286 Object[] of = new Object[] { vd, vf, ve, null, null, null}; 1287 if (!isArray) 1288 return sprintf(strFormat, var, of, vd, vf, ve, getS, getP, getQ); 1289 Lst<SV> sv = var.getList(); 1290 String[] list2 = new String[sv.size()]; 1291 for (int i = 0; i < list2.length; i++) 1292 list2[i] = sprintf(strFormat, sv.get(i), of, vd, vf, ve, getS, getP, getQ); 1293 return list2; 1294 } 1295 sprintf(String strFormat, SV var, Object[] of, int[] vd, float[] vf, double[] ve, boolean getS, boolean getP, boolean getQ)1296 private static String sprintf(String strFormat, SV var, Object[] of, 1297 int[] vd, float[] vf, double[] ve, boolean getS, boolean getP, boolean getQ) { 1298 if (var.tok == hash) { 1299 int pt = strFormat.indexOf("["); 1300 if (pt >= 0) { 1301 int pt1; 1302 var = var.getMap().get(strFormat.substring(pt + 1, pt1 = strFormat.indexOf("]"))); 1303 strFormat = strFormat.substring(0, pt) + strFormat.substring(pt1 + 1); 1304 } 1305 } 1306 if (vd != null) 1307 vd[0] = iValue(var); 1308 if (vf != null) 1309 vf[0] = fValue(var); 1310 if (ve != null) 1311 ve[0] = fValue(var); 1312 if (getS) 1313 of[3] = sValue(var); 1314 if (getP) 1315 of[4]= var.value; 1316 if (getQ) 1317 of[5]= var.value; 1318 return PT.sprintf(strFormat, "IFDspq", of ); 1319 } 1320 1321 /** 1322 * 1323 * @param format 1324 * @return 0: JSON, 5: base64, 12: bytearray, 22: array 1325 */ getFormatType(String format)1326 public static int getFormatType(String format) { 1327 return (format.indexOf(";") >= 0 ? -1 : 1328 ";json;base64;bytearray;array;" 1329 // 0 5 12 22 1330 .indexOf(";" + format.toLowerCase() + ";")); 1331 } 1332 1333 /** 1334 * Accepts arguments from the format() function First argument is a format 1335 * string. 1336 * 1337 * @param args 1338 * @param pt 1339 * 0: to JSON, 5: to base64, 12: to bytearray, 22: to array 1340 * @return formatted string 1341 */ format(SV[] args, int pt)1342 public static Object format(SV[] args, int pt) { 1343 switch (args.length) { 1344 case 0: 1345 return ""; 1346 case 1: 1347 return sValue(args[0]); 1348 case 2: 1349 if (pt == Integer.MAX_VALUE) 1350 pt = getFormatType(args[0].asString()); 1351 switch (pt) { 1352 case 0: 1353 String name = args[1].myName; 1354 args[1].myName = null; 1355 Object o = args[1].toJSON(); 1356 args[1].myName = name; 1357 return o; 1358 case 5: 1359 case 12: 1360 case 22: 1361 byte[] bytes; 1362 switch (args[1].tok) { 1363 case barray: 1364 bytes = AU.arrayCopyByte(((BArray) args[1].value).data, -1); 1365 break; 1366 case varray: 1367 Lst<SV> l = args[1].getList(); 1368 if (pt == 22) { 1369 Lst<SV> l1 = new Lst<SV>(); 1370 for (int i = l.size(); --i >= 0;) 1371 l1.addLast(l.get(i)); 1372 return l1; 1373 } 1374 bytes = new byte[l.size()]; 1375 for (int i = bytes.length; --i >= 0;) 1376 bytes[i] = (byte) l.get(i).asInt(); 1377 break; 1378 default: 1379 String s = args[1].asString(); 1380 if (s.startsWith(";base64,")) { 1381 if (pt == 5) 1382 return s; 1383 bytes = Base64.decodeBase64(s); 1384 } else { 1385 bytes = s.getBytes(); 1386 } 1387 } 1388 return (pt == 22 ? getVariable(bytes) : pt == 12 ? new BArray(bytes) 1389 : ";base64," + javajs.util.Base64.getBase64(bytes).toString()); 1390 } 1391 } 1392 // use values to replace codes in format string 1393 String[] format = PT.split(PT.rep(sValue(args[0]), "%%", "\1"), "%"); 1394 if (format.length == 0) 1395 return ""; 1396 SB sb = new SB(); 1397 sb.append(format[0]); 1398 for (int i = 1; i < format.length; i++) { 1399 Object ret = sprintf(PT.formatCheck("%" + format[i]), 1400 (args[1].tok == hash ? args[1] : args[1].tok == varray ? args[1] 1401 .getList().get(i - 1) : i < args.length ? args[i] : null)); 1402 if (AU.isAS(ret)) { 1403 String[] list = (String[]) ret; 1404 for (int j = 0; j < list.length; j++) 1405 sb.append(list[j]).append("\n"); 1406 continue; 1407 } 1408 sb.append((String) ret); 1409 } 1410 return sb.toString(); 1411 } 1412 getBitSet(SV x, boolean allowNull)1413 public static BS getBitSet(SV x, boolean allowNull) { 1414 switch (x.tok) { 1415 case bitset: 1416 // selectItemTok is important here because this may come from setVariable() 1417 // in the case of a[1].xyz = ptX1 1418 return (BS) (x.index == Integer.MAX_VALUE ? (SV) selectItemTok(x, 1419 Integer.MIN_VALUE) : x).value; 1420 case varray: 1421 return unEscapeBitSetArray(x.getList(), allowNull); 1422 default: 1423 return (allowNull ? null : new BS()); 1424 } 1425 } 1426 1427 /** 1428 * Turn an array of strings in the form of "{n,n,n:n...} or an array of 1429 * integers into a bitset. 1430 * 1431 * @param x 1432 * @param allowNull 1433 * @return bitset (or null if fails and allowNull is false) 1434 */ unEscapeBitSetArray(Lst<SV> x, boolean allowNull)1435 static BS unEscapeBitSetArray(Lst<SV> x, boolean allowNull) { 1436 BS bs = new BS(); 1437 for (int i = 0; i < x.size(); i++) { 1438 SV v = x.get(i); 1439 if (v.tok == T.integer && v.intValue >= 0) { 1440 bs.set(v.intValue); 1441 } else if (v.tok == T.varray) { 1442 BS bs2 = unEscapeBitSetArray(v.getList(), true); 1443 if (bs2 == null) 1444 return (allowNull ? null : new BS()); 1445 bs.or(bs2); 1446 } else if (!unEscapeBitSet(v, bs)) { 1447 return (allowNull ? null : new BS()); 1448 } 1449 } 1450 return bs; 1451 } 1452 1453 1454 /** 1455 * For legacy reasons, "x" == "X" but see isLike() 1456 * 1457 * @param x1 1458 * @param x2 1459 * @return x1 == x2 1460 */ areEqual(SV x1, SV x2)1461 public static boolean areEqual(SV x1, SV x2) { 1462 if (x1 == null || x2 == null) 1463 return false; 1464 if (x1.tok == x2.tok) { 1465 switch (x1.tok) { 1466 case integer: 1467 if (x2.tok == integer) { 1468 return x1.intValue == x2.intValue; 1469 } 1470 break; 1471 case string: 1472 return ((String)x1.value).equalsIgnoreCase((String) x2.value); 1473 case bitset: 1474 case barray: 1475 case hash: 1476 case varray: 1477 case context: 1478 return x1.equals(x2); 1479 case point3f: 1480 return (((P3) x1.value).distance((P3) x2.value) < 0.000001); 1481 case point4f: 1482 return (((P4) x1.value).distance4((P4) x2.value) < 0.000001); 1483 case matrix3f: 1484 return ((M3) x1.value).equals(x2.value); 1485 case matrix4f: 1486 return ((M4) x1.value).equals(x2.value); 1487 } 1488 } 1489 return (Math.abs(fValue(x1) - fValue(x2)) < 0.000001); 1490 } 1491 1492 /** 1493 * a LIKE "x" a is a string and equals x 1494 * a LIKE "*x" a is a string and ends with x 1495 * a LIKE "x*" a is a string and starts with x 1496 * a LIKE "*x*" a is a string and contains x 1497 * 1498 * @param x1 1499 * @param x2 1500 * @return x1 LIKE x2 1501 */ isLike(SV x1, SV x2)1502 public static boolean isLike(SV x1, SV x2) { 1503 return (x1 != null && x2 != null 1504 && x1.tok == string && x2.tok == string 1505 && PT.isLike((String)x1.value, (String) x2.value)); 1506 } 1507 1508 protected class Sort implements Comparator<SV> { 1509 private int arrayPt; 1510 private String myKey; 1511 Sort(int arrayPt, String myKey)1512 protected Sort(int arrayPt, String myKey) { 1513 this.arrayPt = arrayPt; 1514 this.myKey = myKey; 1515 } 1516 1517 @Override compare(SV x, SV y)1518 public int compare(SV x, SV y) { 1519 if (x.tok != y.tok) { 1520 if (x.tok == decimal || x.tok == integer || y.tok == decimal 1521 || y.tok == integer) { 1522 float fx = fValue(x); 1523 float fy = fValue(y); 1524 return (fx < fy ? -1 : fx > fy ? 1 : 0); 1525 } 1526 if (x.tok == string || y.tok == string) 1527 return sValue(x).compareTo(sValue(y)); 1528 } 1529 switch (x.tok) { 1530 case integer: 1531 return (x.intValue < y.intValue ? -1 : x.intValue > y.intValue ? 1 : 0); 1532 case string: 1533 return sValue(x).compareTo(sValue(y)); 1534 case varray: 1535 Lst<SV> sx = x.getList(); 1536 Lst<SV> sy = y.getList(); 1537 if (sx.size() != sy.size()) 1538 return (sx.size() < sy.size() ? -1 : 1); 1539 int iPt = arrayPt; 1540 if (iPt < 0) 1541 iPt += sx.size(); 1542 if (iPt < 0 || iPt >= sx.size()) 1543 return 0; 1544 return compare(sx.get(iPt), sy.get(iPt)); 1545 case hash: 1546 if (myKey != null) { 1547 return compare(x.getMap().get(myKey), y.getMap().get(myKey)); 1548 } 1549 //$FALL-THROUGH$ 1550 default: 1551 float fx = fValue(x); 1552 float fy = fValue(y); 1553 return (fx < fy ? -1 : fx > fy ? 1 : 0); 1554 } 1555 } 1556 } 1557 1558 /** 1559 * 1560 * @param arrayPt 1561 * 1-based or Integer.MIN_VALUE to reverse 1562 * @return sorted or reversed array 1563 */ sortOrReverse(int arrayPt)1564 public SV sortOrReverse(int arrayPt) { 1565 Lst<SV> x = getList(); 1566 if (x != null && x.size() > 1) { 1567 if (arrayPt == Integer.MIN_VALUE) { 1568 // reverse 1569 int n = x.size(); 1570 for (int i = 0; i < n; i++) { 1571 SV v = x.get(i); 1572 x.set(i, x.get(--n)); 1573 x.set(n, v); 1574 } 1575 } else { 1576 Collections.sort(getList(), new Sort(--arrayPt, null)); 1577 } 1578 } 1579 return this; 1580 } 1581 1582 /** 1583 * 1584 * Script variables are pushed after cloning, because the name comes with them 1585 * when we do otherwise they are not mutable anyway. We do want to have actual 1586 * references to points, lists, and associative arrays 1587 * @param mapKey 1588 * @param value 1589 * null to pop 1590 * 1591 * @return array 1592 */ pushPop(SV mapKey, SV value)1593 public SV pushPop(SV mapKey, SV value) { 1594 if (mapKey == null) { 1595 Map<String, SV> m = getMap(); 1596 if (m == null) { 1597 Lst<SV> x = getList(); 1598 if (value == null || x == null) { 1599 // array.pop() 1600 return (x == null || x.size() == 0 ? newS("") : x.removeItemAt(x 1601 .size() - 1)); 1602 } 1603 // array.push(value) 1604 x.addLast(newI(0).setv(value)); 1605 } else { 1606 if (value == null) { 1607 // assocArray.pop() 1608 m.clear(); // new Jmol 14.18 1609 } else { 1610 Map<String, SV> m1 = value.getMap(); 1611 if (m1 != null) 1612 m.putAll(m1); // new Jmol 14.18 1613 // assocArray.push(value) 1614 } 1615 } 1616 } else { 1617 Map<String, SV> m = getMap(); 1618 if (value == null) { 1619 SV v = null; 1620 if (m == null) { 1621 // array.pop(i) 1622 Lst<SV> lst = getList(); 1623 int len = lst.size(); 1624 int i = iValue(mapKey) - 1; 1625 if (i < 0) 1626 i += len; 1627 if (i >= 0 && i < len) { 1628 v = lst.removeItemAt(i); 1629 } 1630 } else { 1631 // assocArray.pop(key) 1632 v = m.remove(mapKey.asString()); 1633 } 1634 return (v == null ? newS("") : v); 1635 } 1636 if (m != null) { 1637 //assocArray.push(key,value) 1638 m.put(mapKey.asString(), newI(0).setv(value)); 1639 } 1640 } 1641 return this; 1642 } 1643 1644 /** 1645 * Turn the string "({3:5})" into a bitset 1646 * @param x 1647 * 1648 * @param bs 1649 * @return a bitset or a string converted to one 1650 */ unEscapeBitSet(SV x, BS bs)1651 private static boolean unEscapeBitSet(SV x, BS bs) { 1652 switch(x.tok) { 1653 case string: 1654 BS bs1 = BS.unescape((String) x.value); 1655 if (bs1 == null) 1656 return false; 1657 bs.or(bs1); 1658 return true; 1659 case bitset: 1660 bs.or((BS) x.value); 1661 return true; 1662 } 1663 return false; 1664 } 1665 strListValue(T x)1666 public static String[] strListValue(T x) { 1667 if (x.tok != varray) 1668 return new String[] { sValue(x) }; 1669 Lst<SV> sv = ((SV) x).getList(); 1670 String[] list = new String[sv.size()]; 1671 for (int i = sv.size(); --i >= 0;) 1672 list[i] = sValue(sv.get(i)); 1673 return list; 1674 } 1675 getArrayDepth(T x)1676 public static int getArrayDepth(T x) { 1677 int n = 0; 1678 Lst<SV> sv; 1679 while (x.tok == varray && (sv = ((SV) x).getList()).size() > 0) { 1680 n++; 1681 x = sv.get(0); 1682 } 1683 return n; 1684 } fflistValue(T x, int nMin)1685 public static float[][] fflistValue(T x, int nMin) { 1686 if (x.tok != varray) { 1687 return new float[][] { new float[] {fValue(x)} }; 1688 } 1689 Lst<SV> sv = ((SV) x).getList(); 1690 int svlen = sv.size(); 1691 float[][] list; 1692 list = AU.newFloat2(svlen); 1693 if (nMin == 0) 1694 nMin = list.length; 1695 for (int i = list.length; --i >= 0;) 1696 list[i] = flistValue(i >= svlen ? null : sv.get(i), 0); 1697 return list; 1698 } 1699 flistValue(T x, int nMin)1700 public static float[] flistValue(T x, int nMin) { 1701 if (x == null || x.tok != varray) 1702 return new float[] { fValue(x) }; 1703 Lst<SV> sv = ((SV) x).getList(); 1704 float[] list; 1705 list = new float[Math.max(nMin, sv.size())]; 1706 if (nMin == 0) 1707 nMin = list.length; 1708 for (int i = Math.min(sv.size(), nMin); --i >= 0;) 1709 list[i] = fValue(sv.get(i)); 1710 return list; 1711 } 1712 toArray()1713 public SV toArray() { 1714 int dim; 1715 Lst<SV> o2; 1716 M3 m3 = null; 1717 M4 m4 = null; 1718 switch (tok) { 1719 case matrix3f: 1720 m3 = (M3) value; 1721 dim = 3; 1722 break; 1723 case matrix4f: 1724 m4 = (M4) value; 1725 dim = 4; 1726 break; 1727 case varray: 1728 return this; 1729 default: 1730 o2 = new Lst<SV>(); 1731 o2.addLast(this); 1732 return newV(varray, o2); 1733 } 1734 o2 = new Lst<SV>(); 1735 for (int i = 0; i < dim; i++) { 1736 float[] a = new float[dim]; 1737 if (m3 == null) 1738 m4.getRow(i, a); 1739 else 1740 m3.getRow(i, a); 1741 o2.addLast(getVariableAF(a)); 1742 } 1743 return newV(varray, o2); 1744 } 1745 1746 @SuppressWarnings("unchecked") mapValue(String key)1747 SV mapValue(String key) { 1748 switch (tok) { 1749 case hash: 1750 return ((Map<String, SV>) value).get(key); 1751 case context: 1752 ScriptContext sc = ((ScriptContext) value); 1753 return (key.equals("_path") ? newS(sc.contextPath) : sc.getVariable(key)); 1754 } 1755 return null; 1756 } 1757 1758 @SuppressWarnings("unchecked") getList()1759 public Lst<SV> getList() { 1760 return (tok == varray ? (Lst<SV>) value : null); 1761 } 1762 isScalar(SV x)1763 public static boolean isScalar(SV x) { 1764 switch (x.tok) { 1765 case varray: 1766 case matrix3f: 1767 case matrix4f: 1768 return false; 1769 case string: 1770 return (((String) x.value).indexOf("\n") < 0); 1771 default: 1772 return true; 1773 } 1774 } 1775 1776 @Override toJSON()1777 public String toJSON() { 1778 switch (tok) { 1779 case on: 1780 case off: 1781 case integer: 1782 case decimal: 1783 return sValue(this); 1784 case barray: 1785 return PT.byteArrayToJSON(((BArray) value).data); 1786 case context: 1787 return PT.toJSON(null, ((ScriptContext) value).getFullMap()); 1788 case varray: 1789 case hash: 1790 if (myName != null) { 1791 myName = null; 1792 return (tok == hash ? "{ }" : "[ ]"); 1793 } 1794 myName = "x"; 1795 String s = PT.toJSON(null, value); 1796 myName = null; 1797 return s; 1798 default: 1799 return PT.toJSON(null, value); 1800 } 1801 } 1802 mapGet(String key)1803 public SV mapGet(String key) { 1804 return getMap().get(key); 1805 } 1806 mapPut(String key, SV v)1807 public void mapPut(String key, SV v) { 1808 getMap().put(key, v); 1809 } 1810 1811 @SuppressWarnings("unchecked") getMap()1812 public Map<String, SV> getMap() { 1813 switch (tok) { 1814 case hash: 1815 return (Map<String, SV>) value; 1816 case context: 1817 return ((ScriptContext) value).vars; 1818 } 1819 return null; 1820 } 1821 getMapKeys(int nLevels, boolean skipEmpty)1822 public String getMapKeys(int nLevels, boolean skipEmpty) { 1823 if (tok != hash) 1824 return ""; 1825 SB sb = new SB(); 1826 sValueArray(sb, this, "", "", true, false, false, nLevels + 1, skipEmpty); 1827 return sb.toString(); 1828 } 1829 1830 @Override toString()1831 public String toString() { 1832 return toString2() + "[" + myName + " index =" + index + " intValue=" + intValue + "]"; 1833 } 1834 getKeys(boolean isAll)1835 public String[] getKeys(boolean isAll) { 1836 switch (tok) { 1837 case hash: 1838 case context: 1839 case varray: 1840 break; 1841 default: 1842 return null; 1843 } 1844 Lst<String> keys = new Lst<String>(); 1845 getKeyList(isAll, keys, ""); 1846 String[] skeys = keys.toArray(new String[keys.size()]); 1847 Arrays.sort(skeys); 1848 return skeys; 1849 } 1850 getKeyList(boolean isAll, Lst<String> keys, String prefix)1851 private void getKeyList(boolean isAll, Lst<String> keys, String prefix) { 1852 Map<String, SV> map = getMap(); 1853 if (map == null) { 1854 if (isAll) { 1855 Lst<SV> lst; 1856 int n; 1857 if ((lst = getList()) != null && (n = lst.size()) > 0) 1858 lst.get(n - 1).getKeyList(true, keys, prefix + "." + n + "."); 1859 } 1860 return; 1861 } 1862 for(Entry<String, SV> e: map.entrySet()) { 1863 String k = e.getKey(); 1864 if (isAll && (k.length() == 0 || !PT.isLetter(k.charAt(0)))) { 1865 if (prefix.endsWith(".")) 1866 prefix = prefix.substring(0, prefix.length() - 1); 1867 k = "[" + PT.esc(k) + "]"; 1868 } 1869 keys.addLast(prefix + k); 1870 if (isAll) 1871 e.getValue().getKeyList(true, keys, prefix + k + "."); 1872 } 1873 } 1874 1875 /** 1876 * Copies a hash or array deeply; invoked by Jmol script 1877 * 1878 * x = @a 1879 * 1880 * where a.type == "hash" or a.type == "varray" 1881 * 1882 * 1883 * @param v hash or array 1884 * @param isHash 1885 * @param isDeep TODO 1886 * @return deeply copied variable 1887 */ 1888 @SuppressWarnings("unchecked") deepCopy(Object v, boolean isHash, boolean isDeep)1889 public static Object deepCopy(Object v, boolean isHash, boolean isDeep) { 1890 if (isHash) { 1891 Map<String, SV> vold = (Map<String, SV>) v; 1892 Map<String, SV> vnew = new Hashtable<String, SV>(); 1893 for (Entry<String, SV> e : vold.entrySet()) { 1894 SV v1 = e.getValue(); 1895 vnew.put(e.getKey(), isDeep ? deepCopySV(v1) : v1); 1896 } 1897 return vnew; 1898 } 1899 Lst<SV> vold2 = (Lst<SV>) v; 1900 Lst<SV> vnew2 = new Lst<SV>(); 1901 for (int i = 0, n = vold2.size(); i < n; i++) { 1902 SV vm = vold2.get(i); 1903 vnew2.addLast(isDeep ? deepCopySV(vm) : vm); 1904 } 1905 return vnew2; 1906 } 1907 deepCopySV(SV vm)1908 private static SV deepCopySV(SV vm) { 1909 switch (vm.tok) { 1910 case hash: 1911 case varray: 1912 if ("\r".equals(vm.myName)) { 1913 vm.myName = null; 1914 vm = SV.newV(vm.tok, (vm.tok == hash ? new Hashtable<String, SV>() : new Lst<SV>())); 1915 } else { 1916 String name0 = vm.myName; 1917 vm.myName = "\r"; 1918 SV vm0 = vm; 1919 vm = newV(vm.tok, deepCopy(vm.value, vm.tok == hash, true)); 1920 vm0.myName = name0; 1921 } 1922 break; 1923 } 1924 return vm; 1925 } 1926 sortMapArray(String key)1927 public SV sortMapArray(String key) { 1928 Lst<SV> lst = getList(); 1929 if (lst != null) { 1930 Collections.sort(getList(), new Sort(0, key)); 1931 } 1932 return this; 1933 } 1934 1935 /** 1936 * Safely create a JSON key - object pair, allowing for already-named arrays 1937 * 1938 * @param key 1939 * @param property 1940 * @return JSON object 1941 */ safeJSON(String key, Object property)1942 public static Object safeJSON(String key, Object property) { 1943 return "{" 1944 + (property instanceof SV ? PT.esc(key) + " : " + format(new SV[] { null, (SV) property }, 1945 0) : PT.toJSON(key, property)) + "}"; 1946 } 1947 1948 } 1949