1 /* 2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /** 27 * jrunscript JavaScript built-in functions and objects. 28 */ 29 30 /** 31 * Creates an object that delegates all method calls on 32 * it to the 'invoke' method on the given delegate object.<br> 33 * 34 * Example: 35 * <pre> 36 * <code> 37 * var x = { invoke: function(name, args) { //code...} 38 * var y = new JSInvoker(x); 39 * y.func(3, 3); // calls x.invoke('func', args); where args is array of arguments 40 * </code> 41 * </pre> 42 * @param obj object to be wrapped by JSInvoker 43 * @constructor 44 */ 45 function JSInvoker(obj) { 46 return new JSAdapter({ 47 __get__ : function(name) { 48 return function() { 49 return obj.invoke(name, arguments); 50 } 51 } 52 }); 53 } 54 55 /** 56 * This variable represents OS environment. Environment 57 * variables can be accessed as fields of this object. For 58 * example, env.PATH will return PATH value configured. 59 */ 60 var env = new JSAdapter({ 61 __get__ : function (name) { 62 return java.lang.System.getenv(name); 63 }, 64 __has__ : function (name) { 65 return java.lang.System.getenv().containsKey(name); 66 }, 67 __getIds__ : function() { 68 return java.lang.System.getenv().keySet().toArray(); 69 }, 70 __delete__ : function(name) { 71 println("can't delete env item"); 72 }, 73 __put__ : function (name, value) { 74 println("can't change env item"); 75 }, 76 toString: function() { 77 return java.lang.System.getenv().toString(); 78 } 79 }); 80 81 /** 82 * Creates a convenient script object to deal with java.util.Map instances. 83 * The result script object's field names are keys of the Map. For example, 84 * scriptObj.keyName can be used to access value associated with given key.<br> 85 * Example: 86 * <pre> 87 * <code> 88 * var x = java.lang.SystemProperties(); 89 * var y = jmap(x); 90 * println(y['java.class.path']); // prints java.class.path System property 91 * delete y['java.class.path']; // remove java.class.path System property 92 * </code> 93 * </pre> 94 * 95 * @param map java.util.Map instance that will be wrapped 96 * @constructor 97 */ 98 function jmap(map) { 99 return new JSAdapter({ 100 __get__ : function(name) { 101 if (map.containsKey(name)) { 102 return map.get(name); 103 } else { 104 return undefined; 105 } 106 }, 107 __has__ : function(name) { 108 return map.containsKey(name); 109 }, 110 111 __delete__ : function (name) { 112 return map.remove(name); 113 }, 114 __put__ : function(name, value) { 115 map.put(name, value); 116 }, 117 __getIds__ : function() { 118 return map.keySet().toArray(); 119 }, 120 toString: function() { 121 return map.toString(); 122 } 123 }); 124 } 125 126 /** 127 * Creates a convenient script object to deal with java.util.List instances. 128 * The result script object behaves like an array. For example, 129 * scriptObj[index] syntax can be used to access values in the List instance. 130 * 'length' field gives size of the List. <br> 131 * 132 * Example: 133 * <pre> 134 * <code> 135 * var x = new java.util.ArrayList(4); 136 * x.add('Java'); 137 * x.add('JavaScript'); 138 * x.add('SQL'); 139 * x.add('XML'); 140 * 141 * var y = jlist(x); 142 * println(y[2]); // prints third element of list 143 * println(y.length); // prints size of the list 144 * 145 * @param map java.util.List instance that will be wrapped 146 * @constructor 147 */ 148 function jlist(list) { 149 function isValid(index) { 150 return typeof(index) == 'number' && 151 index > -1 && index < list.size(); 152 } 153 return new JSAdapter({ 154 __get__ : function(name) { 155 if (isValid(name)) { 156 return list.get(name); 157 } else if (name == 'length') { 158 return list.size(); 159 } else { 160 return undefined; 161 } 162 }, 163 __has__ : function (name) { 164 return isValid(name) || name == 'length'; 165 }, 166 __delete__ : function(name) { 167 if (isValid(name)) { 168 list.remove(name); 169 } 170 }, 171 __put__ : function(name, value) { 172 if (isValid(name)) { 173 list.set(name, value); 174 } 175 }, 176 __getIds__: function() { 177 var res = new Array(list.size()); 178 for (var i = 0; i < res.length; i++) { 179 res[i] = i; 180 } 181 return res; 182 }, 183 toString: function() { 184 return list.toString(); 185 } 186 }); 187 } 188 189 /** 190 * This is java.lang.System properties wrapped by jmap. 191 * For eg. to access java.class.path property, you can use 192 * the syntax sysProps["java.class.path"] 193 */ 194 var sysProps = jmap(java.lang.System.getProperties()); 195 196 // stdout, stderr & stdin 197 var out = java.lang.System.out; 198 var err = java.lang.System.err; 199 // can't use 'in' because it is a JavaScript keyword :-( 200 var inp = java.lang.System["in"]; 201 202 var BufferedInputStream = java.io.BufferedInputStream; 203 var BufferedOutputStream = java.io.BufferedOutputStream; 204 var BufferedReader = java.io.BufferedReader; 205 var DataInputStream = java.io.DataInputStream; 206 var File = java.io.File; 207 var FileInputStream = java.io.FileInputStream; 208 var FileOutputStream = java.io.FileOutputStream; 209 var InputStream = java.io.InputStream; 210 var InputStreamReader = java.io.InputStreamReader; 211 var OutputStream = java.io.OutputStream; 212 var Reader = java.io.Reader; 213 var URL = java.net.URL; 214 215 /** 216 * Generic any object to input stream mapper 217 * @param str input file name, URL or InputStream 218 * @return InputStream object 219 * @private 220 */ 221 function inStream(str) { 222 if (typeof(str) == "string") { 223 // '-' means standard input 224 if (str == '-') { 225 return java.lang.System["in"]; 226 } 227 // try file first 228 var file = null; 229 try { 230 file = pathToFile(str); 231 } catch (e) { 232 } 233 if (file && file.exists()) { 234 return new FileInputStream(file); 235 } else { 236 try { 237 // treat the string as URL 238 return new URL(str).openStream(); 239 } catch (e) { 240 throw 'file or URL ' + str + ' not found'; 241 } 242 } 243 } else { 244 if (str instanceof InputStream) { 245 return str; 246 } else if (str instanceof URL) { 247 return str.openStream(); 248 } else if (str instanceof File) { 249 return new FileInputStream(str); 250 } 251 } 252 // everything failed, just give input stream 253 return java.lang.System["in"]; 254 } 255 256 /** 257 * Generic any object to output stream mapper 258 * 259 * @param out output file name or stream 260 * @return OutputStream object 261 * @private 262 */ 263 function outStream(out) { 264 if (typeof(out) == "string") { 265 if (out == '>') { 266 return java.lang.System.out; 267 } else { 268 // treat it as file 269 return new FileOutputStream(pathToFile(out)); 270 } 271 } else { 272 if (out instanceof OutputStream) { 273 return out; 274 } else if (out instanceof File) { 275 return new FileOutputStream(out); 276 } 277 } 278 279 // everything failed, just return System.out 280 return java.lang.System.out; 281 } 282 283 /** 284 * stream close takes care not to close stdin, out & err. 285 * @private 286 */ 287 function streamClose(stream) { 288 if (stream) { 289 if (stream != java.lang.System["in"] && 290 stream != java.lang.System.out && 291 stream != java.lang.System.err) { 292 try { 293 stream.close(); 294 } catch (e) { 295 println(e); 296 } 297 } 298 } 299 } 300 301 /** 302 * Loads and evaluates JavaScript code from a stream or file or URL<br> 303 * 304 * Examples: 305 * <pre> 306 * <code> 307 * load('test.js'); // load script file 'test.js' 308 * load('http://java.sun.com/foo.js'); // load from a URL 309 * </code> 310 * </pre> 311 * 312 * @param str input from which script is loaded and evaluated 313 */ 314 if (typeof(load) == 'undefined') { 315 var load = function(str) { 316 var stream = inStream(str); 317 var bstream = new BufferedInputStream(stream); 318 var reader = new BufferedReader(new InputStreamReader(bstream)); 319 var oldFilename = engine.get(engine.FILENAME); 320 engine.put(engine.FILENAME, str); 321 try { 322 engine.eval(reader); 323 } finally { 324 engine.put(engine.FILENAME, oldFilename); 325 streamClose(stream); 326 } 327 } 328 } 329 330 // file system utilities 331 332 /** 333 * Creates a Java byte[] of given length 334 * @param len size of the array to create 335 * @private 336 */ 337 function javaByteArray(len) { 338 return java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, len); 339 } 340 341 var curDir = new File('.'); 342 343 /** 344 * Print present working directory 345 */ 346 function pwd() { 347 println(curDir.getAbsolutePath()); 348 } 349 350 /** 351 * Changes present working directory to given directory 352 * @param target directory to change to. optional, defaults to user's HOME 353 */ 354 function cd(target) { 355 if (target == undefined) { 356 target = sysProps["user.home"]; 357 } 358 if (!(target instanceof File)) { 359 target = pathToFile(target); 360 } 361 if (target.exists() && target.isDirectory()) { 362 curDir = target; 363 } else { 364 println(target + " is not a directory"); 365 } 366 } 367 368 /** 369 * Converts path to java.io.File taking care of shell present working dir 370 * 371 * @param pathname file path to be converted 372 * @private 373 */ 374 function pathToFile(pathname) { 375 var tmp = pathname; 376 if (!(tmp instanceof File)) { 377 tmp = new File(tmp); 378 } 379 if (!tmp.isAbsolute()) { 380 return new File(curDir, pathname); 381 } else { 382 return tmp; 383 } 384 } 385 386 /** 387 * Copies a file or URL or stream to another file or stream 388 * 389 * @param from input file or URL or stream 390 * @param to output stream or file 391 */ 392 function cp(from, to) { 393 if (from == to) { 394 println("file " + from + " cannot be copied onto itself!"); 395 return; 396 } 397 var inp = inStream(from); 398 var out = outStream(to); 399 var binp = new BufferedInputStream(inp); 400 var bout = new BufferedOutputStream(out); 401 var buff = javaByteArray(1024); 402 var len; 403 while ((len = binp.read(buff)) > 0 ) 404 bout.write(buff, 0, len); 405 406 bout.flush(); 407 streamClose(inp); 408 streamClose(out); 409 } 410 411 /** 412 * Shows the content of a file or URL or any InputStream<br> 413 * Examples: 414 * <pre> 415 * <code> 416 * cat('test.txt'); // show test.txt file contents 417 * cat('http://java.net'); // show the contents from the URL http://java.net 418 * </code> 419 * </pre> 420 * @param obj input to show 421 * @param pattern optional. show only the lines matching the pattern 422 */ 423 function cat(obj, pattern) { 424 if (obj instanceof File && obj.isDirectory()) { 425 ls(obj); 426 return; 427 } 428 429 var inp = null; 430 if (!(obj instanceof Reader)) { 431 inp = inStream(obj); 432 obj = new BufferedReader(new InputStreamReader(inp)); 433 } 434 var line; 435 if (pattern) { 436 var count = 1; 437 while ((line=obj.readLine()) != null) { 438 if (line.match(pattern)) { 439 println(count + "\t: " + line); 440 } 441 count++; 442 } 443 } else { 444 while ((line=obj.readLine()) != null) { 445 println(line); 446 } 447 } 448 } 449 450 /** 451 * Returns directory part of a filename 452 * 453 * @param pathname input path name 454 * @return directory part of the given file name 455 */ 456 function dirname(pathname) { 457 var dirName = "."; 458 // Normalize '/' to local file separator before work. 459 var i = pathname.replace('/', File.separatorChar ).lastIndexOf( 460 File.separator ); 461 if ( i != -1 ) 462 dirName = pathname.substring(0, i); 463 return dirName; 464 } 465 466 /** 467 * Creates a new dir of given name 468 * 469 * @param dir name of the new directory 470 */ 471 function mkdir(dir) { 472 dir = pathToFile(dir); 473 println(dir.mkdir()? "created" : "can not create dir"); 474 } 475 476 /** 477 * Creates the directory named by given pathname, including 478 * any necessary but nonexistent parent directories. 479 * 480 * @param dir input path name 481 */ 482 function mkdirs(dir) { 483 dir = pathToFile(dir); 484 println(dir.mkdirs()? "created" : "can not create dirs"); 485 } 486 487 /** 488 * Removes a given file 489 * 490 * @param pathname name of the file 491 */ 492 function rm(pathname) { 493 var file = pathToFile(pathname); 494 if (!file.exists()) { 495 println("file not found: " + pathname); 496 return false; 497 } 498 // note that delete is a keyword in JavaScript! 499 println(file["delete"]()? "deleted" : "can not delete"); 500 } 501 502 /** 503 * Removes a given directory 504 * 505 * @param pathname name of the directory 506 */ 507 function rmdir(pathname) { 508 rm(pathname); 509 } 510 511 /** 512 * Synonym for 'rm' 513 */ 514 function del(pathname) { 515 rm(pathname); 516 } 517 518 /** 519 * Moves a file to another 520 * 521 * @param from original name of the file 522 * @param to new name for the file 523 */ 524 function mv(from, to) { 525 println(pathToFile(from).renameTo(pathToFile(to))? 526 "moved" : "can not move"); 527 } 528 529 /** 530 * Synonym for 'mv'. 531 */ 532 function ren(from, to) { 533 mv(from, to); 534 } 535 536 var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", 537 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; 538 539 /** 540 * Helper function called by ls 541 * @private 542 */ 543 function printFile(f) { 544 var sb = new java.lang.StringBuffer(); 545 sb.append(f.isDirectory()? "d" : "-"); 546 sb.append(f.canRead() ? "r": "-" ); 547 sb.append(f.canWrite() ? "w": "-" ); 548 sb.append(" "); 549 550 var d = new java.util.Date(f.lastModified()); 551 var c = new java.util.GregorianCalendar(); 552 c.setTime(d); 553 var day = c.get(java.util.Calendar.DAY_OF_MONTH); 554 sb.append(months[c.get(java.util.Calendar.MONTH)] 555 + " " + day ); 556 if (day < 10) { 557 sb.append(" "); 558 } 559 560 // to get fixed length 'length' field 561 var fieldlen = 8; 562 var len = new java.lang.StringBuffer(); 563 for(var j=0; j<fieldlen; j++) 564 len.append(" "); 565 len.insert(0, java.lang.Long.toString(f.length())); 566 len.setLength(fieldlen); 567 // move the spaces to the front 568 var si = len.toString().indexOf(" "); 569 if ( si != -1 ) { 570 var pad = len.toString().substring(si); 571 len.setLength(si); 572 len.insert(0, pad); 573 } 574 sb.append(len.toString()); 575 sb.append(" "); 576 sb.append(f.getName()); 577 if (f.isDirectory()) { 578 sb.append('/'); 579 } 580 println(sb.toString()); 581 } 582 583 /** 584 * Lists the files in a directory 585 * 586 * @param dir directory from which to list the files. optional, default to pwd 587 * @param filter pattern to filter the files listed. optional, default is '.'. 588 */ 589 function ls(dir, filter) { 590 if (dir) { 591 dir = pathToFile(dir); 592 } else { 593 dir = curDir; 594 } 595 if (dir.isDirectory()) { 596 var files = dir.listFiles(); 597 for (var i in files) { 598 var f = files[i]; 599 if (filter) { 600 if(!f.getName().match(filter)) { 601 continue; 602 } 603 } 604 printFile(f); 605 } 606 } else { 607 printFile(dir); 608 } 609 } 610 611 /** 612 * Synonym for 'ls'. 613 */ 614 function dir(d, filter) { 615 ls(d, filter); 616 } 617 618 /** 619 * Unix-like grep, but accepts JavaScript regex patterns 620 * 621 * @param pattern to search in files 622 * @param files one or more files 623 */ 624 function grep(pattern, files /*, one or more files */) { 625 if (arguments.length < 2) return; 626 for (var i = 1; i < arguments.length; i++) { 627 println(arguments[i] + ":"); 628 cat(arguments[i], pattern); 629 } 630 } 631 632 /** 633 * Find in files. Calls arbitrary callback function 634 * for each matching file.<br> 635 * 636 * Examples: 637 * <pre> 638 * <code> 639 * find('.') 640 * find('.', '.*\.class', rm); // remove all .class files 641 * find('.', '.*\.java'); // print fullpath of each .java file 642 * find('.', '.*\.java', cat); // print all .java files 643 * </code> 644 * </pre> 645 * 646 * @param dir directory to search files 647 * @param pattern to search in the files 648 * @param callback function to call for matching files 649 */ 650 function find(dir, pattern, callback) { 651 dir = pathToFile(dir); 652 if (!callback) callback = print; 653 var files = dir.listFiles(); 654 for (var f in files) { 655 var file = files[f]; 656 if (file.isDirectory()) { 657 find(file, pattern, callback); 658 } else { 659 if (pattern) { 660 if (file.getName().match(pattern)) { 661 callback(file); 662 } 663 } else { 664 callback(file); 665 } 666 } 667 } 668 } 669 670 // process utilities 671 672 /** 673 * Exec's a child process, waits for completion & returns exit code 674 * 675 * @param cmd command to execute in child process 676 */ 677 function exec(cmd) { 678 var process = java.lang.Runtime.getRuntime().exec(cmd); 679 var inp = new DataInputStream(process.getInputStream()); 680 var line = null; 681 while ((line = inp.readLine()) != null) { 682 println(line); 683 } 684 process.waitFor(); 685 $exit = process.exitValue(); 686 } 687 688 /** 689 * Exit the shell program. 690 * 691 * @param exitCode integer code returned to OS shell. 692 * optional, defaults to 0 693 */ 694 function exit(code) { 695 if (code) { 696 java.lang.System.exit(code + 0); 697 } else { 698 java.lang.System.exit(0); 699 } 700 } 701 702 /** 703 * synonym for exit 704 */ 705 function quit(code) { 706 exit(code); 707 } 708 709 // XML utilities 710 711 /** 712 * Converts input to DOM Document object 713 * 714 * @param inp file or reader. optional, without this param, 715 * this function returns a new DOM Document. 716 * @return returns a DOM Document object 717 */ 718 function XMLDocument(inp) { 719 var factory = javax.xml.parsers.DocumentBuilderFactory.newInstance(); 720 var builder = factory.newDocumentBuilder(); 721 if (inp) { 722 if (typeof(inp) == "string") { 723 return builder.parse(pathToFile(inp)); 724 } else { 725 return builder.parse(inp); 726 } 727 } else { 728 return builder.newDocument(); 729 } 730 } 731 732 /** 733 * Converts arbitrary stream, file, URL to XMLSource 734 * 735 * @param inp input stream or file or URL 736 * @return XMLSource object 737 */ 738 function XMLSource(inp) { 739 if (inp instanceof javax.xml.transform.Source) { 740 return inp; 741 } else if (inp instanceof Packages.org.w3c.dom.Document) { 742 return new javax.xml.transform.dom.DOMSource(inp); 743 } else { 744 inp = new BufferedInputStream(inStream(inp)); 745 return new javax.xml.transform.stream.StreamSource(inp); 746 } 747 } 748 749 /** 750 * Converts arbitrary stream, file to XMLResult 751 * 752 * @param inp output stream or file 753 * @return XMLResult object 754 */ 755 function XMLResult(out) { 756 if (out instanceof javax.xml.transform.Result) { 757 return out; 758 } else if (out instanceof Packages.org.w3c.dom.Document) { 759 return new javax.xml.transform.dom.DOMResult(out); 760 } else { 761 out = new BufferedOutputStream(outStream(out)); 762 return new javax.xml.transform.stream.StreamResult(out); 763 } 764 } 765 766 /** 767 * Perform XSLT transform 768 * 769 * @param inp Input XML to transform (URL, File or InputStream) 770 * @param style XSL Stylesheet to be used (URL, File or InputStream). optional. 771 * @param out Output XML (File or OutputStream 772 */ 773 function XSLTransform(inp, style, out) { 774 switch (arguments.length) { 775 case 2: 776 inp = arguments[0]; 777 out = arguments[1]; 778 break; 779 case 3: 780 inp = arguments[0]; 781 style = arguments[1]; 782 out = arguments[2]; 783 break; 784 default: 785 println("XSL tranform requires 2 or 3 arguments"); 786 return; 787 } 788 789 var factory = javax.xml.transform.TransformerFactory.newInstance(); 790 var transformer; 791 if (style) { 792 transformer = factory.newTransformer(XMLSource(style)); 793 } else { 794 transformer = factory.newTransformer(); 795 } 796 var source = XMLSource(inp); 797 var result = XMLResult(out); 798 transformer.transform(source, result); 799 if (source.getInputStream) { 800 streamClose(source.getInputStream()); 801 } 802 if (result.getOutputStream) { 803 streamClose(result.getOutputStream()); 804 } 805 } 806 807 // miscellaneous utilities 808 809 /** 810 * Prints which command is selected from PATH 811 * 812 * @param cmd name of the command searched from PATH 813 */ 814 function which(cmd) { 815 var st = new java.util.StringTokenizer(env.PATH, File.pathSeparator); 816 while (st.hasMoreTokens()) { 817 var file = new File(st.nextToken(), cmd); 818 if (file.exists()) { 819 println(file.getAbsolutePath()); 820 return; 821 } 822 } 823 } 824 825 /** 826 * Prints IP addresses of given domain name 827 * 828 * @param name domain name 829 */ 830 function ip(name) { 831 var addrs = InetAddress.getAllByName(name); 832 for (var i in addrs) { 833 println(addrs[i]); 834 } 835 } 836 837 /** 838 * Prints current date in current locale 839 */ 840 function date() { 841 println(new Date().toLocaleString()); 842 } 843 844 /** 845 * Echoes the given string arguments 846 */ 847 function echo(x) { 848 for (var i = 0; i < arguments.length; i++) { 849 println(arguments[i]); 850 } 851 } 852 853 /** 854 * Reads one or more lines from stdin after printing prompt 855 * 856 * @param prompt optional, default is '>' 857 * @param multiline to tell whether to read single line or multiple lines 858 */ 859 function read(prompt, multiline) { 860 if (!prompt) { 861 prompt = '>'; 862 } 863 var inp = java.lang.System["in"]; 864 var reader = new BufferedReader(new InputStreamReader(inp)); 865 if (multiline) { 866 var line = ''; 867 while (true) { 868 java.lang.System.err.print(prompt); 869 java.lang.System.err.flush(); 870 var tmp = reader.readLine(); 871 if (tmp == '' || tmp == null) break; 872 line += tmp + '\n'; 873 } 874 return line; 875 } else { 876 java.lang.System.err.print(prompt); 877 java.lang.System.err.flush(); 878 return reader.readLine(); 879 } 880 } 881 882 if (typeof(println) == 'undefined') { 883 var print = function(str, newline) { 884 if (typeof(str) == 'undefined') { 885 str = 'undefined'; 886 } else if (str == null) { 887 str = 'null'; 888 } 889 890 if (!(out instanceof java.io.PrintWriter)) { 891 out = new java.io.PrintWriter(out); 892 } 893 894 out.print(String(str)); 895 if (newline) { 896 out.print('\n'); 897 } 898 out.flush(); 899 } 900 901 var println = function(str) { 902 print(str, true); 903 }; 904 } 905 906 /** 907 * This is C-like printf 908 * 909 * @param format string to format the rest of the print items 910 * @param args variadic argument list 911 */ 912 function printf(format, args/*, more args*/) { 913 print(sprintf.apply(this, arguments)); 914 } 915 916 /** 917 * This is C-like sprintf 918 * 919 * @param format string to format the rest of the print items 920 * @param args variadic argument list 921 */ 922 function sprintf(format, args/*, more args*/) { 923 var len = arguments.length - 1; 924 var array = []; 925 926 if (len < 0) { 927 return ""; 928 } 929 930 for (var i = 0; i < len; i++) { 931 if (arguments[i+1] instanceof Date) { 932 array[i] = arguments[i+1].getTime(); 933 } else { 934 array[i] = arguments[i+1]; 935 } 936 } 937 938 array = Java.toJavaArray(array); 939 return Packages.jdk.nashorn.api.scripting.Formatter.format(format, array); 940 }