1 /*! 2 * 3 * JavaLikeScript 4 * 5 */ 6 /** 7 * jls Base framework. 8 * All the framework objects are created under the jls namespace. 9 * The default objects are extended using prototype like behaviors. 10 * 11 * @namespace 12 * @see jls.lang 13 */ 14 var jls = {}; 15 16 /** 17 * @namespace Provides base classes for the jls language. 18 * @see jls.lang.Exception 19 * @see jls.lang.Logger 20 * @see jls.lang.ClassLoader 21 */ 22 jls.lang = {}; 23 24 /* 25 * Begin prototype.js 26 */ 27 Array.prototype.clone = function() { 28 return [].concat(this); 29 }; 30 Array.prototype.toArray = Array.prototype.clone; 31 // The primary use of Array.from() is to obtain an actual Array object based on anything that could pass as an array. 32 // the predefined arguments reference within your functions 33 Array.from = function(iterable) { 34 if (!iterable) return []; 35 if (iterable.toArray) return iterable.toArray(); 36 var length = iterable.length || 0, results = new Array(length); 37 while (length--) results[length] = iterable[length]; 38 return results; 39 } 40 // extend Object 41 Object.extend = function(destination, source) { 42 for (var property in source) 43 destination[property] = source[property]; 44 return destination; 45 }; 46 Object.extend(Object, { 47 toJSON: function(object) { 48 var type = typeof object; 49 switch (type) { 50 case 'undefined': 51 case 'function': 52 case 'unknown': return; 53 case 'boolean': return object.toString(); 54 } 55 if (object === null) return 'null'; 56 if (object.toJSON) return object.toJSON(); 57 var results = []; 58 for (var property in object) { 59 var value = Object.toJSON(object[property]); 60 if (!Object.isUndefined(value)) 61 results.push(property.toJSON() + ': ' + value); 62 } 63 return '{' + results.join(', ') + '}'; 64 }, 65 keys: function(object) { 66 var keys = []; 67 for (var property in object) 68 keys.push(property); 69 return keys; 70 }, 71 values: function(object) { 72 var values = []; 73 for (var property in object) 74 values.push(object[property]); 75 return values; 76 }, 77 isArray: function(object) { 78 return object != null && typeof object == "object" && 79 'splice' in object && 'join' in object; 80 }, 81 isHash: function(object) { 82 return object instanceof Hash; 83 }, 84 isFunction: function(object) { 85 return typeof object == "function"; 86 }, 87 isString: function(object) { 88 return typeof object == "string"; 89 }, 90 isNumber: function(object) { 91 return typeof object == "number"; 92 }, 93 isUndefined: function(object) { 94 return typeof object == "undefined"; 95 }, 96 inheritConstants: function(destination, source) { 97 for (var property in source) { 98 if ((typeof source[property] == "string") || (typeof source[property] == "number")) { 99 destination[property] = source[property]; 100 } 101 } 102 return destination; 103 } 104 }); 105 // extend Function 106 Object.extend(Function.prototype, { 107 argumentNames: function() { 108 var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1] 109 .replace(/\s+/g, '').split(','); 110 return names.length == 1 && !names[0] ? [] : names; 111 }, 112 bind: function() { 113 if (arguments.length < 2 && (typeof arguments[0] == "undefined")) return this; 114 var __method = this, args = Array.from(arguments), object = args.shift(); 115 return function() { 116 return __method.apply(object, args.concat(Array.from(arguments))); 117 } 118 }, 119 curry: function() { 120 if (!arguments.length) return this; 121 var __method = this, args = Array.from(arguments); 122 return function() { 123 return __method.apply(this, args.concat(Array.from(arguments))); 124 } 125 }, 126 wrap: function(wrapper) { 127 var __method = this; 128 return function() { 129 return wrapper.apply(this, [__method.bind(this)].concat(Array.from(arguments))); 130 } 131 }, 132 methodize: function() { 133 if (this._methodized) return this._methodized; 134 var __method = this; 135 return this._methodized = function() { 136 return __method.apply(null, [this].concat(Array.from(arguments))); 137 }; 138 } 139 }); 140 Object.extend(String.prototype, { 141 camelize: function(prefix, suffix) { 142 var parts = this.split('-'); 143 if (prefix) parts.unshift(prefix); 144 if (suffix) parts.push(suffix); 145 var len = parts.length; 146 if (len == 1) return parts[0]; 147 148 var camelized = this.charAt(0) == '-' 149 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) 150 : parts[0]; 151 152 for (var i = 1; i < len; i++) 153 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); 154 155 return camelized; 156 }, 157 capitalize: function() { 158 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); 159 }, 160 startsWith: function(pattern) { 161 return this.indexOf(pattern) === 0; 162 }, 163 endsWith: function(pattern) { 164 var d = this.length - pattern.length; 165 return d >= 0 && this.lastIndexOf(pattern) === d; 166 }, 167 empty: function() { 168 return this == ''; 169 }, 170 strip: function() { 171 return this.replace(/^\s+/, '').replace(/\s+$/, ''); 172 }, 173 // TODO Remove this 174 times: function(count) { 175 return count < 1 ? '' : new Array(count + 1).join(this); 176 }, 177 toJSON: function() { 178 // TODO Fix quotes... 179 return '\'' + this.toString().replace(/([\\'])/g, '\\$1') + '\''; 180 } 181 }); 182 Object.extend(Number.prototype, { 183 toPaddedString: function(length, radix) { 184 var string = this.toString(radix || 10); 185 return '0'.times(length - string.length) + string; 186 }, 187 toJSON: function() { 188 return isFinite(this) ? this.toString() : 'null'; 189 } 190 }); 191 Object.extend(Array.prototype, { 192 indexOf: function(object, from) { 193 var len = this.length; 194 from = from || 0; 195 for (var i = from; i < len; i++) { 196 if (this[i] === object) { 197 return i; 198 } 199 } 200 return -1; 201 }, 202 remove: function(object) { 203 var index = 0; 204 if (typeof object == 'number') { 205 index = object; 206 } else { 207 index = this.indexOf(object); 208 if (index < 0) { 209 return null; 210 } 211 } 212 return this.splice(index, 1)[0]; 213 }, 214 toJSON: function() { 215 var results = []; 216 this.each(function(object) { 217 var value = Object.toJSON(object); 218 if (!Object.isUndefined(value)) results.push(value); 219 }); 220 return '[' + results.join(', ') + ']'; 221 } 222 }); 223 Object.extend(Date.prototype, { 224 // TODO Fix UTC 225 toJSON: function() { 226 return '"' + this.getUTCFullYear() + '-' + 227 (this.getUTCMonth() + 1).toPaddedString(2) + '-' + 228 this.getUTCDate().toPaddedString(2) + 'T' + 229 this.getUTCHours().toPaddedString(2) + ':' + 230 this.getUTCMinutes().toPaddedString(2) + ':' + 231 this.getUTCSeconds().toPaddedString(2) + 'Z"'; 232 } 233 }); 234 /** 235 * @class Class like creation with inheritance. Based on Alex Arnell's inheritance implementation. 236 */ 237 jls.lang.Class = {/** @lends jls.lang.Class */ 238 /** 239 * Creates a new class. 240 * 241 * @param {Object} declaration An object containing the class declaration. 242 * @returns {Object} The new class. 243 */ 244 create: function() { 245 var parent = null, properties = Array.from(arguments); 246 if (typeof properties[0] == "function") 247 parent = properties.shift(); 248 249 function klass() { 250 this.initialize.apply(this, arguments); 251 } 252 253 Object.extend(klass, jls.lang.Class.Methods); 254 klass.superclass = parent; 255 klass.subclasses = []; 256 257 if (parent) { 258 var subclass = function() { }; 259 subclass.prototype = parent.prototype; 260 klass.prototype = new subclass; 261 parent.subclasses.push(klass); 262 } 263 264 for (var i = 0; i < properties.length; i++) 265 klass.addMethods(properties[i]); 266 267 if (!klass.prototype.initialize) 268 klass.prototype.initialize = jls.lang.Class.emptyFunction; 269 270 klass.prototype.constructor = klass; 271 272 return klass; 273 }, 274 emptyFunction: function() { }, 275 notAvailableFunction: function() { 276 throw new jls.lang.Exception('Function not available'); 277 }, 278 abstractMethod: function() { 279 throw new jls.lang.Exception('Abstract method'); 280 }, 281 // instantiate 282 newInstance: function(object) { 283 if (!(object && object.classname)) { 284 throw new jls.lang.Exception('Invalid object, the classname property is required'); 285 } 286 var classname = object.classname; 287 jls.loader.require(classname); 288 /*var properties = Array.from(arguments); 289 var args = ''; 290 for (var i = 0; i < properties.length; i++) { 291 if (i > 0) args += ', '; 292 args += 'properties[' + i + ']'; 293 }*/ 294 var args = 'object'; 295 for (var i = 1; i < arguments.length; i++) { 296 args += ', arguments[' + i + ']'; 297 } 298 //jls.logger.info('newInstance() : eval(\'new ' + classname + '(' + args + ')\')'); 299 return eval('new ' + classname + '(' + args + ')'); 300 } 301 }; 302 /** 303 * @class Class helper. 304 */ 305 jls.lang.Class.Methods = /** @lends jls.lang.Class.Methods */ 306 { 307 addMethods: function(source) { 308 var ancestor = this.superclass && this.superclass.prototype; 309 var properties = Object.keys(source); 310 311 if (!Object.keys({ toString: true }).length) 312 properties.push("toString", "valueOf"); 313 314 for (var i = 0, length = properties.length; i < length; i++) { 315 var property = properties[i], value = source[property]; 316 if (ancestor && (typeof value == 'function') && 317 value.argumentNames()[0] == "$super") { 318 var method = value; 319 value = (function(m) { 320 return function() { return ancestor[m].apply(this, arguments) }; 321 })(property).wrap(method); 322 323 value.valueOf = method.valueOf.bind(method); 324 value.toString = method.toString.bind(method); 325 } 326 this.prototype[property] = value; 327 } 328 329 return this; 330 } 331 }; 332 /** 333 * @class Class bean helper. 334 */ 335 jls.lang.Class.Bean = /** @lends jls.lang.Class.Bean */ 336 { 337 set : function(key, value) { 338 var setter = key.camelize('set'); 339 if (Object.isFunction(this[setter])) { 340 this[setter](value); 341 } 342 return this; 343 }, 344 get : function(key) { 345 var getter = key.camelize('get'); 346 if (Object.isFunction(this[getter])) { 347 return this[getter](); 348 } 349 return null; 350 }, 351 setAll : function(values) { 352 for (var key in values) { 353 this.set(key, values[key]); 354 } 355 return this; 356 } 357 }; 358 /* End prototype.js */ 359 360 // Load base classes 361 _native.core.evalScript('jls/lang/Exception.js'); 362 _native.core.evalScript('jls/lang/Logger.js'); 363 364 /** 365 * Logger. 366 * 367 * @type jls.lang.Logger 368 * @memberOf jls 369 */ 370 jls.logger = new jls.lang.Logger(jls.lang.Logger.INFO); 371 372 if ('jls.logger.logLevel' in _native.core.properties) { 373 jls.logger.setLogLevel(_native.core.properties['jls.logger.logLevel']); 374 } 375 376 _native.core.evalScript('jls/lang/ClassLoader.js'); 377 378 /** 379 * ClassLoader. 380 * 381 * @type jls.lang.ClassLoader 382 * @memberOf jls 383 */ 384 jls.loader = new jls.lang.ClassLoader(); 385 386 // Register base classes 387 jls.loader.provide('jls.lang.Exception', true); 388 jls.loader.provide('jls.lang.Logger', true); 389 jls.loader.provide('jls.lang.ClassLoader', true); 390 391 /* 392 * Add default namespaces 393 */ 394 /** 395 * @namespace Provides graphical user interface. 396 * @see jls.gui.Element 397 */ 398 jls.gui = {}; 399 /** 400 * @namespace Provides for system input and output through data streams, serialization and the file system. 401 * @see jls.io.File 402 */ 403 jls.io = {}; 404 /** 405 * @namespace Provides character sets. 406 * @see jls.io.cs.Charset 407 */ 408 jls.io.cs = {}; 409 /** 410 * @namespace Provides JSUnit core classes. 411 * @see jls.jsunit.TestCase 412 */ 413 jls.jsunit = {}; 414 /** 415 * @namespace Provides the classes for implementing networking applications. 416 * @see jls.net.Socket 417 */ 418 jls.net = {}; 419 /** 420 * @namespace Provides HTTP classes. 421 * @see jls.net.http.ServerMgr 422 */ 423 jls.net.http = {}; 424 /** 425 * @namespace Provides utility classes for the jls language. 426 */ 427 jls.util = {}; 428 429 /* 430 * Provide all lang classes 431 */ 432 jls.loader.require('jls.lang.Buffer'); 433 jls.loader.require('jls.lang.Process'); 434 jls.loader.require('jls.lang.Runtime'); 435 jls.loader.require('jls.lang.System'); 436 437 jls.lang.System._initialize(); 438 439 jls.loader.require('jls.lang.Thread'); 440 jls.loader.require('jls.lang.Signal'); 441 jls.loader.require('jls.lang.Lock'); 442 jls.loader.require('jls.lang.Monitor'); 443 //jls.loader.require('jls.lang.ProcessBuilder'); 444 jls.loader.require('jls.lang.Struct'); 445 //jls.loader.require('jls.lang.AssertionError'); 446 447 if ('jls.disableDefaultExceptionHandler' in _native.core.properties) { 448 jls.logger.info('Default exception handler disabled'); 449 } else { 450 /* 451 * Override exception handler 452 */ 453 _native.core.exceptionHandler = function(e) { 454 jls.lang.Exception.wrap(e).printStackTrace(jls.lang.System.err); 455 } 456 } 457 458 if ('jls.disableDefaultSignalHandler' in _native.core.properties) { 459 jls.logger.info('Default signal handlers disabled'); 460 } else { 461 /* 462 * Register default signal handlers. 463 */ 464 jls.lang.Signal.handle(jls.lang.Signal.SIGINT, function() { 465 jls.lang.Runtime.exit(0); 466 }); 467 /* 468 * Don't know how to dump javascript thread stacktraces 469 */ 470 /*jls.lang.Signal.handle(jls.lang.Signal.SIGBREAK, function() { 471 //jls.lang.System.out.println('... Dump stack trace ...'); 472 //new jls.lang.Exception().printStackTrace(jls.lang.System.out); 473 var name = 'jlsdump.' + new Date().getTime() + '.log'; 474 jls.lang.System.out.println('... Dump head to ' + name + '...'); 475 _native.core.dumpHead(name); 476 });*/ 477 } 478 479 if ('jls.enableCommonJSModule' in _native.core.properties) { 480 jls.loader.require('jls.lang.ModuleLoader'); 481 require = jls.lang.ModuleLoader.getInstance().require.bind(jls.lang.ModuleLoader.getInstance()); 482 } 483 484 if (_native.core.arguments.length > 0) { 485 var args = Array.from(_native.core.arguments); 486 var arg = args.shift(); 487 488 if ((arg == null) || (arg == '-help')) { 489 _native.core.boot = function() { 490 var lines = [ 491 'Usage: jls [options] classname [args...]', 492 'where options include:', 493 '\t-bs <bootstrap script file name>', 494 '\t\tThe file name of the script to use for bootstrap.', 495 '\t-ep <extension path>', 496 '\t\tA directory to search for native library files,', 497 '\t\tscript ZIP files and directories.', 498 '\t-lp <library search path of directories>', 499 '\t\tA ; separated list of directories to search for native library files.', 500 '\t-sp <script search path of directories and ZIP files>', 501 '\t\tA ; separated list of directories and ZIP archives', 502 '\t\tto search for script files.', 503 '\t-D<name>=<value>', 504 '\t\tSet a system property.', 505 '\t-logLevel <level>', 506 '\t\tSet the native log level.', 507 '\t--', 508 '\t\tStop the jls options parsing.', 509 'where system properties include:', 510 '\t-Djls.logger.logLevel=<level>', 511 '\t\tSet the log level.', 512 '\t-Djls.disableDefaultExceptionHandler', 513 '\t\tDisable uncaught exception default handler.', 514 '\t-Djls.disableDefaultSignalHandler', 515 '\t\tDisable default signal handlers.', 516 '\t-Djls.keep', 517 '\t\tWaits Ctrl-C if an exception occurs at boot phase.', 518 '\t-Djls.interactive', 519 '\t\tEnables interactive boot mode.', 520 'See http://javalikescript.free.fr/ for more details.' 521 ]; 522 for (var i = 0; i < lines.length; i++) { 523 jls.lang.System.out.println(lines[i]); 524 } 525 } 526 } else if (! arg.endsWith('.js')) { 527 /* 528 * Override boot function 529 */ 530 _native.core.boot = function() { 531 jls.loader.require(arg); 532 var mainclass = eval(arg); 533 if (! ('main' in mainclass)) { 534 throw new jls.lang.Exception('The class ' + arg + ' does not have a main method.'); 535 } 536 mainclass['main'](args); 537 }; 538 } 539 } else { 540 if ('jls.interactive' in _native.core.properties) { 541 // TODO 542 } 543 } 544 545 if ('jls.keep' in _native.core.properties) { 546 var previousBoot = _native.core.boot; 547 _native.core.boot = function() { 548 try { 549 previousBoot.apply(this, arguments); 550 } catch (e) { 551 jls.lang.System.err.println('Error during boot, hit ctrl-c to end.'); 552 jls.lang.Exception.wrap(e).printStackTrace(jls.lang.System.err); 553 // Start an infinite non daemon thread 554 var thread = new jls.lang.Thread(); 555 thread.run = function() { 556 while (true) { 557 jls.lang.Thread.sleep(500); 558 } 559 }; 560 thread.start(); 561 } 562 }; 563 } 564