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