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