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 toJSON: function() { 171 // TODO Fix quotes... 172 return '\'' + this.toString().replace(/([\\'])/g, '\\$1') + '\''; 173 } 174 }); 175 Object.extend(Number.prototype, { 176 toPaddedString: function(length, radix) { 177 var string = this.toString(radix || 10); 178 return '0'.times(length - string.length) + string; 179 }, 180 toJSON: function() { 181 return isFinite(this) ? this.toString() : 'null'; 182 } 183 }); 184 Object.extend(Array.prototype, { 185 indexOf: function(object, from) { 186 var len = this.length; 187 from = from || 0; 188 for (var i = from; i < len; i++) { 189 if (this[i] === object) { 190 return i; 191 } 192 } 193 return -1; 194 }, 195 remove: function(object) { 196 var index = 0; 197 if (typeof object == 'number') { 198 index = object; 199 } else { 200 index = this.indexOf(object); 201 if (index < 0) { 202 return null; 203 } 204 } 205 return this.splice(index, 1)[0]; 206 }, 207 toJSON: function() { 208 var results = []; 209 this.each(function(object) { 210 var value = Object.toJSON(object); 211 if (!Object.isUndefined(value)) results.push(value); 212 }); 213 return '[' + results.join(', ') + ']'; 214 } 215 }); 216 Object.extend(Date.prototype, { 217 toJSON: function() { 218 return '"' + this.getUTCFullYear() + '-' + 219 (this.getUTCMonth() + 1).toPaddedString(2) + '-' + 220 this.getUTCDate().toPaddedString(2) + 'T' + 221 this.getUTCHours().toPaddedString(2) + ':' + 222 this.getUTCMinutes().toPaddedString(2) + ':' + 223 this.getUTCSeconds().toPaddedString(2) + 'Z"'; 224 } 225 }); 226 /** 227 * @class Class like creation with inheritance. Based on Alex Arnell's inheritance implementation. 228 */ 229 jls.lang.Class = {/** @lends jls.lang.Class */ 230 /** 231 * Creates a new class. 232 * 233 * @param {Object} declaration An object containing the class declaration. 234 * @returns {Object} The new class. 235 */ 236 create: function() { 237 var parent = null, properties = Array.from(arguments); 238 if (typeof properties[0] == "function") 239 parent = properties.shift(); 240 241 function klass() { 242 this.initialize.apply(this, arguments); 243 } 244 245 Object.extend(klass, jls.lang.Class.Methods); 246 klass.superclass = parent; 247 klass.subclasses = []; 248 249 if (parent) { 250 var subclass = function() { }; 251 subclass.prototype = parent.prototype; 252 klass.prototype = new subclass; 253 parent.subclasses.push(klass); 254 } 255 256 for (var i = 0; i < properties.length; i++) 257 klass.addMethods(properties[i]); 258 259 if (!klass.prototype.initialize) 260 klass.prototype.initialize = jls.lang.Class.emptyFunction; 261 262 klass.prototype.constructor = klass; 263 264 return klass; 265 }, 266 emptyFunction: function() { }, 267 notAvailableFunction: function() { 268 throw new jls.lang.Exception('Function not available'); 269 }, 270 abstractMethod: function() { 271 throw new jls.lang.Exception('Abstract method'); 272 }, 273 // instantiate 274 newInstance: function(object) { 275 if (!(object && object.classname)) { 276 throw new jls.lang.Exception('Invalid object, the classname property is required'); 277 } 278 var classname = object.classname; 279 jls.loader.require(classname); 280 /*var properties = Array.from(arguments); 281 var args = ''; 282 for (var i = 0; i < properties.length; i++) { 283 if (i > 0) args += ', '; 284 args += 'properties[' + i + ']'; 285 }*/ 286 var args = 'object'; 287 for (var i = 1; i < arguments.length; i++) { 288 args += ', arguments[' + i + ']'; 289 } 290 //jls.logger.info('newInstance() : eval(\'new ' + classname + '(' + args + ')\')'); 291 return eval('new ' + classname + '(' + args + ')'); 292 } 293 }; 294 /** 295 * @class Class helper. 296 */ 297 jls.lang.Class.Methods = /** @lends jls.lang.Class.Methods */ 298 { 299 addMethods: function(source) { 300 var ancestor = this.superclass && this.superclass.prototype; 301 var properties = Object.keys(source); 302 303 if (!Object.keys({ toString: true }).length) 304 properties.push("toString", "valueOf"); 305 306 for (var i = 0, length = properties.length; i < length; i++) { 307 var property = properties[i], value = source[property]; 308 if (ancestor && (typeof value == 'function') && 309 value.argumentNames()[0] == "$super") { 310 var method = value; 311 value = (function(m) { 312 return function() { return ancestor[m].apply(this, arguments) }; 313 })(property).wrap(method); 314 315 value.valueOf = method.valueOf.bind(method); 316 value.toString = method.toString.bind(method); 317 } 318 this.prototype[property] = value; 319 } 320 321 return this; 322 } 323 }; 324 /** 325 * @class Class bean helper. 326 */ 327 jls.lang.Class.Bean = /** @lends jls.lang.Class.Bean */ 328 { 329 set : function(key, value) { 330 var setter = key.camelize('set'); 331 if (Object.isFunction(this[setter])) { 332 this[setter](value); 333 } 334 return this; 335 }, 336 get : function(key) { 337 var getter = key.camelize('get'); 338 if (Object.isFunction(this[getter])) { 339 return this[getter](); 340 } 341 return null; 342 }, 343 setAll : function(values) { 344 for (var key in values) { 345 this.set(key, values[key]); 346 } 347 return this; 348 } 349 }; 350 /* End prototype.js */ 351 352 // Load base classes 353 _native.core.evalScript('jls/lang/Exception.js'); 354 _native.core.evalScript('jls/lang/Logger.js'); 355 356 /** 357 * Logger. 358 * 359 * @type jls.lang.Logger 360 * @memberOf jls 361 */ 362 jls.logger = new jls.lang.Logger(jls.lang.Logger.INFO); 363 364 if ('jls.logger.logLevel' in _native.core.properties) { 365 jls.logger.setLogLevel(_native.core.properties['jls.logger.logLevel']); 366 } 367 368 _native.core.evalScript('jls/lang/ClassLoader.js'); 369 370 /** 371 * ClassLoader. 372 * 373 * @type jls.lang.ClassLoader 374 * @memberOf jls 375 */ 376 jls.loader = new jls.lang.ClassLoader(); 377 378 // Register base classes 379 jls.loader.provide('jls.lang.Exception', true); 380 jls.loader.provide('jls.lang.Logger', true); 381 jls.loader.provide('jls.lang.ClassLoader', true); 382 383 /* 384 * Add default namespaces 385 */ 386 /** 387 * @namespace Provides graphical user interface. 388 * @see jls.gui.Element 389 */ 390 jls.gui = {}; 391 /** 392 * @namespace Provides for system input and output through data streams, serialization and the file system. 393 * @see jls.io.File 394 */ 395 jls.io = {}; 396 /** 397 * @namespace Provides character sets. 398 * @see jls.io.cs.Charset 399 */ 400 jls.io.cs = {}; 401 /** 402 * @namespace Provides JSUnit core classes. 403 * @see jls.jsunit.TestCase 404 */ 405 jls.jsunit = {}; 406 /** 407 * @namespace Provides the classes for implementing networking applications. 408 * @see jls.net.Socket 409 */ 410 jls.net = {}; 411 /** 412 * @namespace Provides HTTP classes. 413 * @see jls.net.http.ServerMgr 414 */ 415 jls.net.http = {}; 416 /** 417 * @namespace Provides utility classes for the jls language. 418 */ 419 jls.util = {}; 420 421 /* 422 * Provide all lang classes 423 */ 424 jls.loader.require('jls.lang.Buffer'); 425 jls.loader.require('jls.lang.Process'); 426 jls.loader.require('jls.lang.Runtime'); 427 jls.loader.require('jls.lang.System'); 428 429 jls.lang.System._initialize(); 430 431 jls.loader.require('jls.lang.Thread'); 432 jls.loader.require('jls.lang.Signal'); 433 jls.loader.require('jls.lang.Lock'); 434 jls.loader.require('jls.lang.Monitor'); 435 //jls.loader.require('jls.lang.ProcessBuilder'); 436 jls.loader.require('jls.lang.Struct'); 437 //jls.loader.require('jls.lang.AssertionError'); 438 439 if ('jls.disableDefaultExceptionHandler' in _native.core.properties) { 440 jls.logger.info('Default exception handler disabled'); 441 } else { 442 /* 443 * Override exception handler 444 */ 445 _native.core.exceptionHandler = function(e) { 446 jls.lang.Exception.wrap(e).printStackTrace(jls.lang.System.err); 447 } 448 } 449 450 if ('jls.disableDefaultSignalHandler' in _native.core.properties) { 451 jls.logger.info('Default signal handlers disabled'); 452 } else { 453 /* 454 * Register default signal handlers. 455 */ 456 jls.lang.Signal.handle(jls.lang.Signal.SIGINT, function() { 457 jls.lang.Runtime.exit(0); 458 }); 459 /* 460 * Don't know how to dump javascript thread stacktraces 461 */ 462 /*jls.lang.Signal.handle(jls.lang.Signal.SIGBREAK, function() { 463 //jls.lang.System.out.println('... Dump stack trace ...'); 464 //new jls.lang.Exception().printStackTrace(jls.lang.System.out); 465 var name = 'jlsdump.' + new Date().getTime() + '.log'; 466 jls.lang.System.out.println('... Dump head to ' + name + '...'); 467 _native.core.dumpHead(name); 468 });*/ 469 } 470 471 if (_native.core.arguments.length > 0) { 472 var args = Array.from(_native.core.arguments); 473 var arg = args.shift(); 474 475 if ((arg == null) || (arg == '-help')) { 476 _native.core.boot = function() { 477 var lines = [ 478 'Usage: jls [options] classname [args...]', 479 'where options include:', 480 '\t-bs <bootstrap script file name>', 481 '\t\tThe file name of the script to use for bootstrap.', 482 '\t-ep <extension path>', 483 '\t\tA directory to search for native library files,', 484 '\t\tscript ZIP files and directories.', 485 '\t-lp <library search path of directories>', 486 '\t\tA ; separated list of directories to search for native library files.', 487 '\t-sp <script search path of directories and ZIP files>', 488 '\t\tA ; separated list of directories and ZIP archives', 489 '\t\tto search for script files.', 490 '\t-D<name>=<value>', 491 '\t\tSet a system property.', 492 '\t-logLevel <level>', 493 '\t\tSet the native log level.', 494 '\t--', 495 '\t\tStop the jls options parsing.', 496 'where system properties include:', 497 '\t-Djls.logger.logLevel=<level>', 498 '\t\tSet the log level.', 499 '\t-Djls.disableDefaultExceptionHandler', 500 '\t\tDisable uncaught exception default handler.', 501 '\t-Djls.disableDefaultSignalHandler', 502 '\t\tDisable default signal handlers.', 503 '\t-Djls.keep', 504 '\t\tWaits Ctrl-C if an exception occurs at boot phase.', 505 '\t-Djls.interactive', 506 '\t\tEnables interactive boot mode.', 507 'See http://javalikescript.free.fr/ for more details.' 508 ]; 509 for (var i = 0; i < lines.length; i++) { 510 jls.lang.System.out.println(lines[i]); 511 } 512 } 513 } else if (! arg.endsWith('.js')) { 514 /* 515 * Override boot function 516 */ 517 _native.core.boot = function() { 518 jls.loader.require(arg); 519 var mainclass = eval(arg); 520 if (! ('main' in mainclass)) { 521 throw new jls.lang.Exception('The class ' + arg + ' does not have a main method.'); 522 } 523 mainclass['main'](args); 524 }; 525 } 526 } else { 527 if ('jls.interactive' in _native.core.properties) { 528 // TODO 529 } 530 } 531 532 if ('jls.keep' in _native.core.properties) { 533 var previousBoot = _native.core.boot; 534 _native.core.boot = function() { 535 try { 536 previousBoot.apply(this, arguments); 537 } catch (e) { 538 jls.lang.System.err.println('Error during boot, hit ctrl-c to end.'); 539 jls.lang.Exception.wrap(e).printStackTrace(jls.lang.System.err); 540 // Start an infinite non daemon thread 541 var thread = new jls.lang.Thread(); 542 thread.run = function() { 543 while (true) { 544 jls.lang.Thread.sleep(500); 545 } 546 }; 547 thread.start(); 548 } 549 }; 550 } 551