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