/** Classing{js} : brings the world of classical oop to javascript Version : 1.1.0 Developed By : Mostafa Samir Code Licensed Under the MIT License : ------------------------------------- Copyright (c) 2014 by Mostafa Samir Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. **/ var classing = {}; //Library's Namespace var _global = (typeof window != 'undefined' && this === window) ? window : global; /** * _instantiateOnce : a constructor function that is can be used only once to create the xSelf constant **/ classing._instantiateOnce = (function() { var flag = false; return function() { var _stamp = 1010; if(flag) { throw classing.xError("000" , "not allowed"); } else { flag = true; Object.defineProperty(this , 'timestamp' , { get : function() { return _stamp; }, set : function _setter(value) { var caller = _setter.caller; if(caller === classing.Class || caller === classing.Interface || caller === classing.xStamp) { _stamp = value } } }); } } })(); /** *Library's Constant keywords : xTyped , xNonTyped , xSelf **/ Object.defineProperty(classing , 'xTyped' , {value:true , writable:false}); Object.defineProperty(classing , 'xNonTyped' , {value:false , writable:false}); Object.defineProperty(classing , 'xSelf' , {value : new classing._instantiateOnce() , writable:false}); Object.defineProperty(classing , 'base', {value : null, writable:true}); //global shortcuts for the library's constants Object.defineProperty(_global, 'xTyped' ,{get : function() {return classing.xTyped}, set:function(v){}}); Object.defineProperty(_global, 'xNonTyped' ,{get : function() {return classing.xNonTyped}, set:function(v){}}); Object.defineProperty(_global, 'xSelf' ,{get : function() {return classing.xSelf}, set:function(v){}}); /** *base : a global variable used to reference the Base class in inhertance **/ Object.defineProperty(_global, 'base', {get:function(){return classing.base}, set:function(v){}}); /** xError : a custom error constructor to distinguish the library's errors from native errors @param {String} code : code of the error @param {String} msg : message of the error @return {Error} : The Error Object to throw **/ classing.xError = function(code , msg) { var err = new Error(); err.name = "xError"; err.code = parseInt(code); err.message = "[code=" + err.code + "]:" + msg; return err; } /** Object::instanceOf : a custom method that checks if the calling object is an instance of someclass or an interface @param {Function/Object} ancestor : the Class or Interface to be checked against @return {Boolean} : true if the object is instance of the class/intrface specified, false otherwise **/ Object.defineProperty(Object.prototype , 'instanceOf' ,{ value : function(ancestor) { var isInstace = false; if(typeof ancestor !== "object") { isInstace = this instanceof ancestor; } if(!isInstace) { if(ancestor.timestamp) { var constructor = this.constructor; while(constructor !== "_root_") { if(constructor._metadata._implements["" + ancestor.timestamp]) { isInstace = true; break; } else if(constructor._metadata._extends === ancestor) { isInstace = true; break; } else { constructor = constructor._metadata._extends; //go up the tree } } } } return isInstace; }, enumerable:false }); /** xStamp : attaches a timestamp to a constructor function which is considered an intermidate data to identify instances of this constructor @param {function} Constructor : the constructro function **/ classing.xStamp = function(Constructor) { if(typeof Constructor === "function") { Constructor.timestamp = classing.xSelf.timestamp; classing.xSelf.timestamp++; } } /** _xTypes : a special , limited access type to feed arguments type lists while overloading functions @param {numbr} count : the number of arguments @param {string} list : the list of types **/ classing._xTypes = function(count , list) { if(classing._xTypes.caller === classing.types) { this.count = count; this.list = list; } else { throw classing.xError("000" , "Not Allowed"); } } /** types : a creator function for the _xTypes objects @params {functions} : the constructor functions of the types (either native or custom) @return {Object : _xTypes} : the _xTypes Object Created **/ classing.types = function() { var count = arguments.length; var list = ""; for(var i = 0 ; i < count ; i++) { if(arguments[i] === classing.xSelf) { list += classing.xSelf.timestamp; continue; } if(typeof arguments[i] !== "function") { throw classing.xError("103" , "Invalid Type"); } if(arguments[i].timestamp) { list += arguments[i].timestamp; } else { var _str = arguments[i].toString(); var _typeName = _str.match(/function\s*(\w+)/)[1]; list += _typeName; } if(i !== arguments.length - 1) { list += ","; } } var xList = new classing._xTypes(count , list); return xList; } //global shortcut for types function Object.defineProperty(_global, 'types' , {get: function(){return classing.types}, set:function(v){}}); Function.create = (function() { /** _isEmpty : a Static function that checks if the fuctions used in the overloaded declerations are empty Used in marking abstract functions and interface's functions @param {boolean} typedFlag : true if typed overloaded , false otherwise @param {Array} internalMap : the map of functions inside the overloaded function @return {boolean} : true if all functions are empty , false otherwise **/ function _isEmpty(typedFlag , internalMap) { if(typedFlag) { for(i in internalMap) { for(j in internalMap[i]) { if(!classing.Abstract.isNotImplemented(internalMap[i][j])) { return false; } } } } else { for(i in internalMap) { if(!classing.Abstract.isNotImplemented(internalMap[i])) { return false; } } } return true; } /** _type : returns the type (or the stamp) of an object for argument types matching in overloaded functions @param {Object} : the object to identify its type @return {String} : the type (or the stamp) of the object **/ function _type(obj) { var type = typeof obj; if(type === "object") { if(obj.constructor.timestamp) { return obj.constructor.timestamp.toString(); } else { var _constructor = Object.getPrototypeOf(obj).constructor.toString(); var _name = _constructor.match(/function\s*(\w*)/)[1]; if(_name !== "") { return _name; } else { return "Object"; } } } type = type[0].toUpperCase() + type.substring(1); return type; } return function(typed , def , flag) { /** *The 'Function.create' *creates an overloaded function pattern *@param {Boolean} typed : sets the overloading mode to typed (if true) or non-typed (if false) *@param {Array} def : array of overloading instances *@param {Object} flag : flag for invalid extra arguments *@return {function} : returns the overloaded function pattern **/ if(typed === undefined || !def || flag) { throw classing.xError("100" , "invalid arguments"); } else if(typeof typed !== "boolean" || !(def instanceof Array )) { throw classing.xError("100" , "invalid arguments"); } else { var len = def.length ; var _metadata = { isTyped : false, } var _pattern; if(typed) { _metadata.isTyped = true; _metadata.types = []; /** checking the format of the array : - must be non-empty and of even size - even elements are string lists of valid types - odd elements are function having a number of arguments equal to number of types in the string list **/ var _compressed = []; if(len === 0 || len % 2 !== 0) { throw classing.xError("101" , "inavalid array format"); } else { for(var i = 0 ; i < len ; i = i + 2) { var _list , argsCount; if(!(def[i] instanceof classing._xTypes) || typeof def[i + 1] !== "function") { throw classing.xError("101" , "inavalid array format"); } else { if(def[i + 1].length !== def[i].count) { throw classing.xError("102" , "arguments number mismatch"); } def[i + 1].types = def[i].list; _compressed.push(def[i + 1]); } } //sorting the array of functions _compressed.sort(function(a,b) { return b.length - a.length; }); //creating a (typeList , argumentsCount)-instance map var _max = _compressed[0].length; var _map = new Array(_max + 1); for(var i = 0 ; i <= _max ; i++) { //intializing the map _map[i] = {}; } //filling the map for(var i = 0 ; i < len / 2 ; i++) { var current = _compressed[i]; _metadata.types.push(current.types); if(_map[current.length][current.types] !== undefined) { throw classing.xError("106" , "duplicate arguments list found"); } _map[current.length][current.types] = current ; } _pattern = function() { var max = _max; var map = _map; var args = []; var count = arguments.length; var counter = 0 ; while(counter < count) { args.push(arguments[counter]); counter++; } var argsTypes = ""; for(var i = 0 ; i < count ; i++) { argsTypes += _type(args[i]); if(i !== count - 1) { argsTypes += ","; } } if(count > max) { throw classing.xError("105", "no overloaded instance of the function matches the argument list"); } var target = map[count][argsTypes]; if(!target) { throw classing.xError("105", "no overloaded instance of the function matches the argument list"); } else { return target.apply(this , args); } } _pattern._meta_ = _metadata; Object.defineProperty(_pattern , 'isEmpty' , {get:function() {return _isEmpty(true , _map);} , set:function(val){}}); } } else { _metadata.counts = new Array(); /** checking the format of the array must be non-empty and of elements of only functions **/ if(len === 0) { throw classing.xError("101","inavlid array format"); } else { for(var i = 0 ; i < len ; i++) { if(typeof def[i] !== "function") { throw classing.xError("101","inavlid array format"); } } //sorting the array of functions def.sort(function(a,b) { return b.length - a.length; }); //Creating an argument length - instance map var _max = def[0].length; //maximum number of arguements in provided instances var _map = []; for(var i = 0 ; i < len ; i++) { var mapIndx = def[i].length; if(_map[mapIndx] !== undefined) { throw classing.xError("106" , "duplicate arguments list found"); } _map[mapIndx] = def[i]; _metadata.counts.push(mapIndx); } _pattern = function() { var max = _max; var map = _map; var args = []; var count = arguments.length; var counter = 0 ; while(counter < count) { args.push(arguments[counter]); counter++; } if(count > max) { throw classing.xError("105" , "no overloaded instance of the function matches the argument list"); return; } var target = map[count]; if(!target) { throw classing.xError("105" , "no overloaded instance of the function matches the argument list"); return; } else { return target.apply(this,args); } } _pattern._meta_ = _metadata; Object.defineProperty(_pattern , 'isEmpty' , {get:function() {return _isEmpty(false , _map);} , set:function(val){}}); } } return _pattern ; } } })(); /** Interface : creates an interface (a structure that contains only public abstract methods to be implemented by a class or more) @param {defintion} : the definition of the interface @return {Object} : the object representing the interface **/ classing.Interface = function(defintion) { var abstracts = {}; var reservedTimestamp = classing.xSelf.timestamp; classing.xSelf.timestamp++; for(key in defintion) { var currentComponent = defintion[key]; if(typeof currentComponent !== "function") { throw classing.xError("401" , "Interfaces contain only methods"); //interfaces contain only functions } else { if(currentComponent.isEmpty !== undefined) { //the function is defined using Function.create if(!currentComponent.isEmpty) { throw classing.xError("402","an interface method cannot have any implementation"); //every function must be empty } else { currentComponent._meta_.isOverloaded = true; abstracts[key] = { accessLevel : "public", description : currentComponent._meta_ } } } else { //the function is defined in the ordinary way if(!classing.Abstract.isNotImplemented(currentComponent)) { throw classing.xError("402","an interface method cannot have any implementation"); } else { abstracts[key] = { accessLevel : "public", description : { isOverloaded : false, argsCount : currentComponent.length } } } } } } var InterfaceObject = new Object(); Object.defineProperty(InterfaceObject , 'components' , {value : abstracts , writable:false}); Object.defineProperty(InterfaceObject , 'isInterface' , {value : true , writable:false}); Object.defineProperty(InterfaceObject , 'timestamp' , {value: reservedTimestamp, writable:false}); return InterfaceObject; } /** *xAreCompatiable : checks if an implementation of an abstract method matches the description of the previosly defined abstract method @param {Object} abstractRecord : the record that holds the description of the abstract method @param {function} implementation : the candidate method to implement the abstract method @param {string} accesssLevel : the access level in which the implementation resides @return {boolean} : true if the implementation is compatiable , false otherwise **/ classing.xAreCompatiable = function(abstractRecord , implementation , accessLevel) { if(abstractRecord.accessLevel === accessLevel) { if(abstractRecord.description.isOverloaded) { if(implementation._meta_) { if(abstractRecord.description.isTyped === implementation._meta_.isTyped) { if(abstractRecord.description.isTyped) { var max = abstractRecord.description.types.length; var counter = 0 var implementationTypes = implementation._meta_.types; var len = implementationTypes.length; for(var i = 0 ; i < len ; i++) { var indxInAbstractRecord = -1; for(var j = 0 ; j < max ; j++){ if(abstractRecord.description.types[j] === implementationTypes[i]){ indxInAbstractRecord = j; break; } } if(indxInAbstractRecord === -1) { return false; } else { counter++; } } if(counter === max) { return true; } } else { var max = abstractRecord.description.counts.length; var counter = 0; var implementationCounts = implementation._meta_.counts; for(var i = 0 ; i < implementationCounts.length ; i++) { var indxInAbstractRecord = -1; for(var j = 0 ; j < max ; j++){ if(abstractRecord.description.counts[j] === implementationCounts[i]) { indxInAbstractRecord = j; break; } } if(indxInAbstractRecord === -1) { return false; } else { counter++; } } if(counter === max) { return true; } } } } } else { if(abstractRecord.description.argsCount === implementation.length) { return true; } } } return false; } classing.Class = (function() { //Static Helper function /** *theEYE : a function that can carry out unallowed operations @param {number} option : a number that specifys the job to be done by theEYE 0 => extracts the _extendingUnit 1 => return an instance of an abstract class 2 => extracts the _hiddenUnit @param {Object/Function} obj : the object whose _hiddenUnit is to be viewed / the abstrcat class constructor function of which an instance is to be created @param {Array} args : the argument list to be passed in the creation of the instance of the abstract class @return {Object} : the _hiddenUnit of the object / the instance of the abstract class **/ function theEYE(option , obj , args) { if(option === 0) { if(obj.hasOwnProperty('_hu_')) { return obj._hu_; } else { return obj; } } else if(option === 2) { if(obj.hasOwnProperty('_eu_')) { return obj._eu_; } else { return obj; } } else { var instance = new Object(); obj.apply(instance , args); return instance; } } /** *xDefineIn : a function that defines proprties on objects. @param {Object} obj : the object to define proprties on @param {String} type : the type of property to be defined , there are 8 types : {method , property , protected-static , private-static , public-static, inherited , link , _eu_} @param {String} key : the name of the property to be defined @param {Object} extra : an extra argument that differ from type to type as follows 'method , property' => extra is the defintion object 'protected-static' => extra is the following object { own : the _privliagedDictionary of the class, childs : the _privliagedChilds of the class, loc : the defintion object } 'private-static' => extra is the following object { own : the _privliagedDictionary of the class loc : the defintion object } 'public-static' => {loc : the defintion object} 'inheriterd' => extra is undefined 'link' => extra is the _hiddenUnit object '_eu_' => extra is the _hiddenUnit object **/ function xDefineIn(obj , type , key , extra) { if(type === "method") { obj[key] = function() { /** Begin Resolving arguments **/ var args = []; var len = arguments.length; var counter = 0 ; while(counter < len) { args.push(arguments[counter]); if(args[counter].constructor.timestamp === obj.constructor.timestamp) { args[counter] = theEYE(0 , args[counter]); } counter++; } /** End Resolving Arguments **/ classing.base = obj._super; return extra[key].apply(obj , args); classing.base = null; } } else if(type === "property") { Object.defineProperty(obj , key , { get : function() { classing.base = obj._super; return extra[key].get.apply(obj , []); classing.base = null; }, set: function(newVal) { /** Resolving the 'newVal' argument **/ if(newVal.constructor.timestamp === obj.constructor.timestamp) { newVal = theEYE(0 , newVal); } classing.base = obj._super; return extra[key].set.apply(obj , [newVal]); classing.base = null; }, enumerable: true }); } else if(type === "protected-static") { if(typeof extra.loc[key] === "object" && extra.loc[key].___xisProperty) { Object.defineProperty(obj , key , { get: function _getter() { var callingFunction = _getter.caller; if((extra.own[callingFunction.privliagedMark] || extra.childs[callingFunction.privliagedMark]) || (callingFunction.caller._meta_ && (extra.own[callingFunction.caller.privliagedMark] || extra.childs[callingFunction.caller.privliagedMark]))) { return extra.loc[key].referTo[key]; } else { throw classing.xError("213" , "inaccessable member"); } }, set : function _setter(newVal) { var callingFunction = _setter.caller; if((extra.own[callingFunction.privliagedMark] || extra.childs[callingFunction.privliagedMark]) || (callingFunction.caller._meta_ && (extra.own[callingFunction.caller.privliagedMark] || extra.childs[callingFunction.caller.privliagedMark]))){ extra.loc[key].referTo[key] = newVal; } else { throw classing.xError("213","inaccessable member"); } }, enumerable: true }); } else { Object.defineProperty(obj , key , { get: function _getter() { var callingFunction = _getter.caller; if((extra.own[callingFunction.privliagedMark] || extra.childs[callingFunction.privliagedMark]) || (callingFunction.caller._meta_ && (extra.own[callingFunction.caller.privliagedMark] || extra.childs[callingFunction.caller.privliagedMark]))) { return extra.loc[key].value; } else { throw classing.xError("213" , "inaccessable member"); } }, set : function _setter(newVal) { var callingFunction = _setter.caller; if((extra.own[callingFunction.privliagedMark] || extra.childs[callingFunction.privliagedMark]) || (callingFunction.caller._meta_ && (extra.own[callingFunction.caller.privliagedMark] || extra.childs[callingFunction.caller.privliagedMark]))){ extra.loc[key].value = newVal; } else { throw classing.xError("213","inaccessable member"); } }, enumerable: true }); } } else if(type === "private-static") { if(typeof extra.loc[key] === "object" && extra.loc[key].___xisProperty) { Object.defineProperty(obj , key , { get: function _getter() { var callingFunction = _getter.caller; if(extra.own[callingFunction.privliagedMark] || (callingFunction.caller._meta_ && extra.own[callingFunction.caller.privliagedMark] !== -1)) { return extra.loc[key].referTo[key]; } else { throw classing.xError("213","inaccessable member"); } }, set : function _setter(newVal) { var callingFunction = _setter.caller; if(extra.own[callingFunction.privliagedMark] || (callingFunction.caller._meta_ && extra.own[callingFunction.caller.privliagedMark] !== -1)) { extra.loc[key].referTo[key] = newVal; } else { throw classing.xError("213","inaccessable member"); } }, enumerable: true }); } else { Object.defineProperty(obj , key , { get: function _getter() { var callingFunction = _getter.caller; if(extra.own[callingFunction.privliagedMark] || (callingFunction.caller._meta_ && extra.own[callingFunction.caller.privliagedMark] !== -1)) { return extra.loc[key].value; } else { throw classing.xError("213","inaccessable member"); } }, set : function _setter(newVal) { var callingFunction = _setter.caller; if(extra.own[callingFunction.privliagedMark] || (callingFunction.caller._meta_ && extra.own[callingFunction.caller.privliagedMark] !== -1)) { extra.loc[key].value = newVal; } else { throw classing.xError("213","inaccessable member"); } }, enumerable: true }); } } else if(type === 'public-static') { if(typeof extra.loc[key] === "object" && extra.loc[key].___xisProperty) { Object.defineProperty(obj , key , { get : function() { return extra.loc[key].referTo[key]; }, set : function(newVal) { extra.loc[key].referTo[key] = newVal; }, enumerable: true }); } else { obj[key] = extra.loc[key].value; } } else if(type === "inherited") { Object.defineProperty(obj , key , { get : function() { return obj._super[key]; }, set: function(n) { obj._super[key] = n; }, enumerable: true }); } else if(type === "link") { Object.defineProperty(obj , key , { get: function() { return extra[key]; }, set: function(n) { extra[key] = n; }, enumerable: true }); } else if(type === "_eu_") { Object.defineProperty(obj , key , { get : function() {return extra[key];}, set : function(val) {extra[key] = val;}, enumerable: true }); } } var xEmptyParent = { _metadata :{ methods:{}, proprties:{}, attributes:{}, abstracts:{} } } return function (definition) { var _reservedTimestamp = classing.xSelf.timestamp; classing.xSelf.timestamp++; /** * Level 0 Validation : Validating the definition. * Validation Rule : - definition must be an object - Only three access modifiers are available : public , protected , private **/ if(typeof definition !== "object") { throw classing.xError("201", "Invalid Definition"); } //Creating the Class return (function() { var _methodMarkerCounter = 0; //saving the class options in the closure of the class var classProprties = { isAbstract : classing.Class.options.isAbstract, isFinal : classing.Class.options.isFinal, parent : classing.Class.options.parent === null ? xEmptyParent : classing.Class.options.parent, interfaces : classing.Class.options.interfaces, timestamp : _reservedTimestamp, constructorAccessLevel : "public" }; //resetting class options classing.Class.options.parent = null; classing.Class.options.interfaces = null; classing.Class.options.isExtending = false; classing.Class.options.isImplementing = false; classing.Class.options.isAbstract = false; classing.Class.options.isFinal = false; var _privliagedDictionary = {}; var _privliagedChilds = {}; var _staticComponents = {}; var _ownAbstrcats = {}; var _ancestorsAbstracts = {}; var _metadata_ = { methods : {}, proprties : {}, attributes : {}, abstracts:null, constructorAccessLevel: "public", _implements:{}, _extends:classProprties.parent === xEmptyParent ? "_root_" : classProprties.parent, } var _staticProprties = {}; var _ownComponentsNames = {}; var constructor = function(){}; //Default Constructor //Importing interfaces methods if(classProprties.interfaces !== null) { var len = classProprties.interfaces.length; for(var i = 0 ; i < len ; i++) { var currentInterface = classProprties.interfaces[i]; _metadata_._implements["" + currentInterface.timestamp] = true; for(record in currentInterface.components) { _ancestorsAbstracts[record] = currentInterface.components[record]; } } } classProprties.interfaces = undefined; //Dispose classProprties.interfaces //Combining parent's abstracts (if any) within the '_ancestorsAbstracts' variable if(classProprties.parent._metadata.abstracts) { for(record in classProprties.parent._metadata.abstracts) { _ancestorsAbstracts[record] = classProprties.parent._metadata.abstracts[record]; } } for(modifier in definition) { if(typeof definition[modifier] !== 'object') { throw classing.xError("201" , "Invalid definition"); } if(modifier !== "public" && modifier !== "private" && modifier !== "protected") { throw classing.xError("202" , modifier + ": undefined access modifier"); } var currentModifier = definition[modifier]; for(key in currentModifier) { //checking if this component's name already used if(_ownComponentsNames[key]) { throw classing.xError("207","class cannot have two components with the same name"); } /* Begin Methods Case */ if(typeof currentModifier[key] === 'function') { //Checking if the method is a constructor if(key === "Construct") { if(currentModifier[key].isAbstract) { throw classing.xError("214" , "a constructor cannot be abstract"); } classProprties.constructorAccessLevel = modifier; _metadata_.constructorAccessLevel = modifier; constructor = currentModifier[key]; currentModifier[key].privliagedMark = _reservedTimestamp + ":" + _methodMarkerCounter; _privliagedDictionary[_reservedTimestamp + ":" + _methodMarkerCounter] = true; _methodMarkerCounter++; continue; } //Checking if the method is overriding an inherited method else if(classProprties.parent._metadata.methods[key]) { if(currentModifier[key].isAbstract) { throw classing.xError("215" , "an abstract method cannot override a concrete one"); } if(classProprties.parent._metadata.methods[key].accessLevel !== modifier) { throw classing.xError("216" , "overriding a method in a different access level is not allowed"); } if(classProprties.parent._metadata.methods[key].isFinal) { throw classing.xError("204","Cannot override a final method"); } } //Checking if the method is implementing an abstract method else if(_ancestorsAbstracts[key]) { if(classing.xAreCompatiable(_ancestorsAbstracts[key] , currentModifier[key] , modifier)) { _ancestorsAbstracts[key] = undefined; //marking the abstrcat record as undefined is equivelant to saying that this abstrcat method is implemented } else { throw classing.xError("205","on function " + key + ": mismatch between implementation and abstract decleration"); } } //Checking if the method itself is abstract if(currentModifier[key].isAbstract) { if(modifier === "private") { throw classing.xError("203" , "marking a private method as abstract is not allowed"); } _ownAbstrcats[key] = { accessLevel: modifier, description: currentModifier[key]._meta_ } currentModifier[key] = undefined; } //if the method is not abstrcat else { //Marking the method as privliaged to access the private/protected statics currentModifier[key].privliagedMark = _reservedTimestamp + ":" + _methodMarkerCounter; _privliagedDictionary[_reservedTimestamp + ":" + _methodMarkerCounter] = true; _methodMarkerCounter++; _ownComponentsNames[key] = true; if(modifier !== "private") { _metadata_.methods[key] = { isFinal : currentModifier[key].isFinal, accessLevel : modifier } } } } /* End Methods Case */ else if(typeof currentModifier[key] === "object") { //Checking if the component is static if(currentModifier[key] !== null && currentModifier[key].isStatic) { //Checking if it's a static method if(typeof currentModifier[key].value === "function") { //Marking the method as privilaged currentModifier[key].value.privliagedMark = _reservedTimestamp + ":" + _methodMarkerCounter; _privliagedDictionary[_reservedTimestamp + ":" + _methodMarkerCounter] = true; _methodMarkerCounter++; } //Checking if it's a static property else if(typeof currentModifier[key].value === "object") { var staticMax = 2; for(prop in currentModifier[key].value) { if((prop === "get" || prop == "set") && (typeof currentModifier[key].value[prop] === "function")) { staticMax--; } else { break; } } if(staticMax === 0) { //Marking the setter and getter as privliaged currentModifier[key].value.get.privliagedMark = _reservedTimestamp + ":" + _methodMarkerCounter; _privliagedDictionary[_reservedTimestamp + ":" + _methodMarkerCounter] = true; _methodMarkerCounter++; currentModifier[key].value.set.privliagedMark = _reservedTimestamp + ":" + _methodMarkerCounter; _privliagedDictionary[_reservedTimestamp + ":" + _methodMarkerCounter] = true; _methodMarkerCounter++; Object.defineProperty(_staticProprties , key , { get : currentModifier[key].value.get, set : currentModifier[key].value.set }); currentModifier[key] = { ___xisProperty : true, isStatic : true, referTo : _staticProprties } } } _staticComponents[key] = {accessLevel : modifier} _ownComponentsNames[key] = true; } // else { if(currentModifier[key] !== null) { //Checking if its a property var max = 2; for(prop in currentModifier[key]) { if((prop === "get" || prop == "set") && (typeof currentModifier[key][prop] === "function")) { max--; } else { break; } } if(max === 0) { //Marking the setter and getter as privliaged currentModifier[key].get.privliagedMark = _reservedTimestamp + ":" + _methodMarkerCounter; _privliagedDictionary[_reservedTimestamp + ":" + _methodMarkerCounter] = true; _methodMarkerCounter++; currentModifier[key].set.privliagedMark = _reservedTimestamp + ":" + _methodMarkerCounter; _privliagedDictionary[_reservedTimestamp + ":" + _methodMarkerCounter] = true; _methodMarkerCounter++; currentModifier[key].isProperty = true; _ownComponentsNames[key] = true; if(modifier !== "private") { _metadata_.proprties[key] = { accessLevel : modifier } } } else { throw classing.xError("206","setting a non-static attribute to an object is not allowed"); } } else { _ownComponentsNames[key] = true; if(modifier !== "private") { _metadata_.proprties[key] = { accessLevel : modifier } } } } } else { _ownComponentsNames[key] = true; if(modifier !== "private") { _metadata_.proprties[key] = { accessLevel : modifier } } } } } /** * Level 1 Validation : Validating the relation between the class and abstract methods. * Validation Rule : - an abstract class must contain at least one abstract method (own or inherited) - an abstract method (own or inherited) must be contained in an abstract class **/ for(record in _ancestorsAbstracts) { if(_ancestorsAbstracts[record] !== undefined) { _ownAbstrcats[record] = _ancestorsAbstracts[record]; } } _metadata_.abstracts = _ownAbstrcats; var len = Object.keys(_ownAbstrcats).length; if(len == 0 && classProprties.isAbstract) { throw classing.xError("208","an abstrcat class must contain at least one abstract method"); } if(len > 0 && !classProprties.isAbstract) { throw classing.xError("209","an abstract method must be contained in an abstract class"); } _ancestorsAbstracts = undefined; //Dispose _ancestorsAbstracts _ownComponentsNames = undefined; //Dispose _ownComponentsNames var _constructor = function() { var $this = this; /** Begin Resolving arguments **/ var args = []; var len = arguments.length; var counter = 0 ; while(counter < len) { args.push(arguments[counter]); if(args[counter].constructor.timestamp === classProprties.timestamp) { args[counter] = theEYE(0 , args[counter]); } counter++; } /** End Resolving Arguments **/ classing.base = function() { if(classProprties.parent !== xEmptyParent) { if(classProprties.parent.isAbstract) { $this._super = theEYE(1 , classProprties.parent , arguments); } else { $this._super = {}; classProprties.parent.apply($this._super , arguments); } } else { $this._super = new Object(); } } classing.base.isBase = true; constructor.apply($this , args); //checking if the constructor called the parent's constructor if($this._super === null) { if(classProprties.parent === xEmptyParent) { $this._super = new Object(); } else { try { if(classProprties.parent.isAbstract || classProprties.parent._metadata.constructorAccessLevel === "protected") { $this._super = theEYE(1 , classProprties.parent , []); } else { $this._super = new classProprties.parent(); } } catch(ex) { if(ex.code !== 212) { throw classing.xError("210","parent class doesn't contain a default constructor"); } throw ex; } } } $this._super = theEYE(2 , $this._super); //extracting the _extendingUnit of the super object //linking inherited components in _hiddenUnit to components in _super for(key in $this._super) { if(!$this[key]) { xDefineIn($this , 'inherited' , key); } } } var _classPattern = function() { var instantiator = _classPattern.caller; var pMark = !instantiator.isBase ? (instantiator.privliagedMark ? instantiator.privliagedMark : "") : instantiator.caller.privliagedMark; if(classProprties.constructorAccessLevel !== "public" && instantiator !== theEYE && !_privliagedDictionary[pMark] && !(_privliagedChilds[pMark] && classProprties.constructorAccessLevel === "protected")) { throw classing.xError("212" , "inaccessable constructor"); } if(classProprties.isAbstract && _classPattern.caller !== theEYE) { throw classing.xError("211","Cannot instantiate an abstract class"); } else { var _hiddenUnit = {}; var _extendingUnit = {}; _extendingUnit["publics"] = {}; var _this = this; _hiddenUnit["_super"] = null; _hiddenUnit.constructor = _classPattern; for(modifier in definition) { var currentModifier = definition[modifier]; for(key in currentModifier) { var mightNeedLink = false; if(currentModifier[key] !== null) { if(currentModifier[key] !== undefined && !currentModifier[key].isStatic && key != "Construct") { mightNeedLink = true; if(typeof currentModifier[key] === "function") { xDefineIn(_hiddenUnit , 'method' , key , definition[modifier]); } else if(currentModifier[key].isProperty) { xDefineIn(_hiddenUnit , 'property' , key , definition[modifier]); } else { _hiddenUnit[key] = definition[modifier][key]; } } if(modifier === "public" || modifier === "protected") { xDefineIn(_extendingUnit , '_eu_' , key , _hiddenUnit); if(modifier === "public") { _extendingUnit.publics[key] = true; } } } else { mightNeedLink = true; _hiddenUnit[key] = null; if(modifier === "public" || modifier === "protected") { xDefineIn(_extendingUnit , '_eu_' , key , _hiddenUnit); if(modifier === "public") { _extendingUnit.publics[key] = true; } } } //linking public components in user-visible object to the corresponding in _hiddenUnit if(modifier === "public" && mightNeedLink) { xDefineIn(_this , 'link' , key , _hiddenUnit); } } } _constructor.apply(_hiddenUnit , arguments); //linking inherited public components if(_hiddenUnit._super.publics) { for(key in _hiddenUnit._super.publics) { if(!_this[key]) { xDefineIn(_this , 'link' , key , _hiddenUnit); } } } // adding a refernece to the _hiddenUnit as a property of the object only accessable by theEYE Object.defineProperty(_this , '_hu_' , { get : function _getter() { if(_getter.caller === theEYE) { return _hiddenUnit; } }, set : function(n){} }); // adding a refernece to the _extendingUnit as a property of the object only accessable by theEYE Object.defineProperty(_this , '_eu_' , { get : function _getter() { if(_getter.caller === theEYE) { return _extendingUnit; } }, set : function(n){} }); } } //Attatching static components for(key in _staticComponents) { var accotiatedAccessLevel = _staticComponents[key].accessLevel; switch(accotiatedAccessLevel) { case "public": xDefineIn(_classPattern , 'public-static' , key , { loc : definition[accotiatedAccessLevel] });break; case "protected": xDefineIn(_classPattern , 'protected-static' , key , { own : _privliagedDictionary, childs : _privliagedChilds, loc : definition[accotiatedAccessLevel] });break; case "private" : xDefineIn(_classPattern , 'private-static' , key , { own : _privliagedDictionary, loc : definition[accotiatedAccessLevel] });break; } } _staticComponents = undefined; //Dispose _staticComponents _classPattern.__extendProtectedStaticAccess = function(childsPrivlaged) { if(_classPattern.__extendProtectedStaticAccess.caller.caller === classing.Class) { for(key in childsPrivlaged) { _privliagedChilds[key] = true; } } } if(classProprties.parent !== xEmptyParent) { classProprties.parent.__extendProtectedStaticAccess(_privliagedDictionary); } //marking the class to distinguish it form other non-classy structures Object.defineProperty(_classPattern , 'xClass' , { value : true, writable:false, }); //attaching isAbstract and isFinal flags Object.defineProperty(_classPattern , 'isAbstract' , { get : function() {return classProprties.isAbstract}, set : function(n) {} }); Object.defineProperty(_classPattern , 'isFinal' , { get : function() {return classProprties.isFinal}, set : function(n) {} }); //stamping the class for type recognition Object.defineProperty(_classPattern , 'timestamp' , {value : _reservedTimestamp , writable:false}); //Attaching metadata _classPattern._metadata = _metadata_; return _classPattern; })(); } })(); /** options is an object that holds the configurations of the class to be created. - parent {Object} : holds a reference to the parent object (if any). - interfaces {Array} : holds the interfaces that the class implements. - isExtending {Boolean} : holds true if the class extends a parent class , false otherwise. - isImplementing {Boolean} : holds true if the class extends any interface , false otherwise. - isAbstract {Boolean} : holds true if the class is declared abstract , false otherwise. - isFinal {Boolean} : holds true if the class is declared final , false otherwise. **/ classing.Class.options = { parent:null, interfaces : null, isExtending:false, isImplementing:false, isAbstract:false, isFinal:false }; /** Extends marks the class as extening a parent class an stores the reference that parent @param {function} parentRef : the reference to the parent class @return {function} : the Class function (to continue the definition of the class) **/ classing.Class.Extends = function(parentRef) { if(!classing.Class.options.isImplementing && !parentRef !== undefined && parentRef.xClass) { if(!parentRef.isFinal) { classing.Class.options.parent = parentRef; classing.Class.options.isExtending = true; return classing.Class; } else { throw classing.xError("301","Cannot extend a final class"); } } else { if(classing.Class.options.isImplementing) { throw classing.xError("302" , "Invalid Decleration"); } throw classing.xError("307","attempting to extend a non Classing{js} Class or undefined"); } } /** Implements marks the class as implementing one or more interfaces and stores refernces to these interfaces. @params {functions} : the list of interfaces the class is implementing @return {function} : the Class function (to continue the definition of the class) **/ classing.Class.Implements = function() { var args = []; var len = arguments.length; if(len !== 0) { var counter = 0; while(counter < len) { args.push(arguments[counter]); if(args[counter] === undefined || !args[counter].isInterface) { throw classing.xError("306","attempting to implement a non-interface or undefined"); } counter++; } classing.Class.options.isImplementing = true; classing.Class.options.interfaces = args; return classing.Class; } else { throw classing.xError("302","Invalid Decleration"); } } classing.Static = function(variable) { var staticWrapper = { value:variable, isStatic:true }; return staticWrapper; } // a global shortcut for Static function Object.defineProperty(_global, 'Static' ,{get : function() {return classing.Static}, set:function(v){}}); /** Final is a Class modifier that marks a function/class unextendable *Final(method) : Takes a single method and marks it final @param {function} method : an function to be marked final @return {function} : the marked function **/ classing.Final = function(method) { if(method.isAbstract) { throw classing.xError("303","abstract methods cannot be final"); } method.isFinal = true; return method; } // a global shortcut for Final function Object.defineProperty(_global, 'Final' ,{ value : function(method) { return classing.Final(method); }}); /** Final.Class : marks the class to be created as Final @return {function} : Class function. **/ Object.defineProperty(classing.Final , 'Class' , { get : function() { classing.Class.options.isFinal = true; return classing.Class; }, set : function(newVal) {} }); /** Abstract is a modifier that marks a function/class as abstract *Abstract(method) : Takes a single method and marks it abstract @param {function} method : an empty function to be marked abstract @return {function} : the marked function *Abstract(typed,overloads) : marks an overloaded function as abstract @param {boolean} typed : a flag to specify typed or nontyped overloading @param {array} overloads : array of overloading instances @return {function} : the marked overloaded function **/ classing.Abstract = function(method) { if(method.isFinal) { throw classing.xError("304","final methods cannot be abstract"); } if(method._meta_) { if(!method.isEmpty) { throw classing.xError("305","An abstract method cannot have implementation"); } method.isAbstract = true; return method; } else if(!classing.Abstract.isNotImplemented(method)) { throw classing.xError("305","An abstract method cannot have implementation"); } method.isAbstract = true; method._meta_= {argsCount : method.length}; return method; } // a global shortcut for Abstract function Object.defineProperty(_global, 'Abstract' ,{ value : function(method) { return classing.Abstract(method); }}); /** Abstract.isNotImplemented : checks the method to make sure that its empty @param {function} method : the function to be checked @return {boolean} : true if it's empty , false otherwise. **/ classing.Abstract.isNotImplemented = function(method) { var notImplementedPattern = new RegExp(/function\s*\w*\([A-Za-z0-9,$_ ]*\)\s*\{\s*\}/); var methodStr = method.toString(); return notImplementedPattern.test(methodStr); } /** Abstract.Class : marks the class to be created as abstract @return {function} : Class function. **/ Object.defineProperty(classing.Abstract , 'Class' , { get : function() { classing.Class.options.isAbstract = true; return classing.Class; }, set : function(newVal) {} });