@script name="ClassFactory" hide="y">
A collection of macros for creating and managing namespaces, classes, prototypes, and aspect declarations
This file must be loaded exactly once, into the top-level %STUDIO% framework
(c) SAP AG 2003-2006. All rights reserved.
// GLOBALS
var $DOM = {}; // The global GML DOM
$DOM.namespaces = {}; // The global namespaces collection
$DOM.allClasses = {}; // The global classes collection
$DOM.allEvents = {}; // The global events collection
$DOM.allRoles = {}; // The global aspect roles collection
$DOM.allAssociations = {}; // The global associations collection
$DOM.currNS = null; // The namespace currently in scope
$DOM.currClass = null; // The class currently in scope
$DOM.currFile = null; // The source file urn currently in scope
$DOM.prefixes = {}; // The global namespaces prefixes collection
$DOM._partialClassOrder = {}; // The classes partial ordering relation
$DOM._metadataCounter = 0; // Determines the priority of the 'metadata for' statements
$DOM.perfCheck = #[PERF_MSR]; //true;
var _reinstalling = false;
$DOM.sysAliases = { // The builtin system aliases
dev: 'core.dev',
env: 'core.env',
gkb: 'core.gkb',
gml: 'core.gml',
gml2: 'core.gml2',
lib: 'core.lib',
lyt: 'core.lyt',
svg: 'core.svg'
}
$DOM.sysEvents = { // The builtin system events
oninit: '',
onexit: '',
onshow: '',
onhide: '',
onresize: '',
onoptionsmenu: '',
onchannelconnect: '$ENV',
oncontextswitch: '$ENV',
onSyncState: '$ENV',
onkitchange: '$ENV',
onmodelclose: '$ENV',
onmodeldirty: '$ENV',
onmodelevent: '$ENV',
onmodelorganize: '$ENV',
onmodelopen: '$ENV',
onmodelsave: '$ENV',
onsettingschange: '$CTL',
onSyncState: '$ENV',
onstudioexit: '$CTL',
onLogEvent: '$ENV'
}
////////////////////////////////////////////////////////////////////////
// PRIMARY DECLARATIONS
<@func name="declareClass">
Starts a class declaration
The class name
The direct superclass name, if any
$DOM.declareClass = function(className, superClassName, isAbstract) {
try {
var newClass = $DOM.startDeclaration('Class', className, superClassName, isAbstract);
var superClass = newClass.superclass;
if (superClass) {
for (var k in superClass.dependents) newClass.dependents[k] = superClass.dependents[k];
}
if (!superClassName) {
$DOM._addNativeMethod(newClass, 'isa', $DOM._isaInstance, {public:true});
$DOM._addNativeMethod(newClass, 'getProperty', $DOM._getInstanceProp, {public:true});
$DOM._addNativeMethod(newClass, 'setProperty', $DOM._setInstanceProp, {public:true});
}
$DOM._addNativeMethod(newClass, 'toString', $DOM._toString, {public:true});
return newClass;
} catch (e) {
$DOM.raiseDOMError(e.description);
return null;
}
}
<@func name="declarePrototype">
Starts a prototype declaration
The prototype name
The direct super-prototype name, if any
$DOM.declarePrototype = function(prototypeName, superPrototypeName, isAbstract) {
try {
return $DOM.startDeclaration('Prototype', prototypeName, superPrototypeName, isAbstract);
} catch (e) {
$DOM.raiseDOMError(e.description);
return null;
}
}
<@func name="declareAspect">
Starts an aspect declaration
The aspect name
The direct super-aspect name, if any
The aspect role
$DOM.declareAspect = function(aspectName, superAspectName, aspectRole, isAbstract) {
try {
var roleClass = $DOM._getClassifier(aspectRole), superAspect=roleClass;
if (!roleClass || !roleClass.dollar) throw new Error(-1, 'Unknown aspect role "'+aspectRole+'"');
if (superAspectName) {
var superAspect = $DOM._getClassifier(superAspectName);
if (!superAspect) throw new Error(-1, 'Unknown super aspect "'+superAspectName+'"');
if (!(roleClass.fullname in superAspect.precedents)) throw new Error(-1, 'Super aspect "'+superAspect.name+'" must belong to "'+roleClass.name+'" Aspect role');
}
var aspectClass = $DOM.startDeclaration('Aspect', aspectName, superAspect.fullname, isAbstract);
aspectClass.dollar = roleClass.dollar;
aspectClass.rolename = roleClass.rolename;
return aspectClass;
} catch (e) {
$DOM.raiseDOMError(e.description);
return null;
}
}
<@func name="declareAspectRole">
Declares a new aspect role
The aspect role's qualified name
The aspect role's dollar qualifier
$DOM.declareAspectRole = function(aspectRole, dollar) {
try {
var roleClass = $DOM.startDeclaration('Aspect', aspectRole);
var roleName = roleClass.fullname;
if (roleName in $DOM.allRoles) throw new Error(-1, 'Aspect role "'+roleName+'" is already defined.');
try {
var mft=$ENV.channel1.getKitsManifest();
dollar = mft.aspects[roleName].prefix; // use the correct dollar qualifier for the currently connected server
} catch(e) { throw new Error(-1, 'Unrecognized aspect role "'+roleName+'"'); }
roleClass.dollar = dollar;
roleClass.rolename = roleName;
roleClass.isAbstract = true;
$DOM.allRoles[dollar] = roleClass;
$DOM.allRoles[roleClass.rolename] = roleClass;
$DOM.endDeclaration();
return roleClass;
} catch (e) {
$DOM.raiseDOMError(e.description);
return null;
}
}
<@func name="attachPrototype">
Attaches a prototype to an class/aspect
The name of the prototype to add
The name of the class/aspect that implements the prototype; if omitted, the class/aspect currently in scope is assumed
$DOM.attachPrototype = function(prototypeName, implementorName) {
try {
// validate prototype and class
var protoclass = $DOM._getClassifier(prototypeName);
var implementor = $DOM._getClassifier(implementorName);
if (!protoclass) throw new Error(-1, 'Prototype "'+prototypeName+'" is undefined');
if (!implementor) throw new Error(-1, 'Implementor "'+implementorName+'" is undefined');
if (!protoclass.isPrototype || !(implementor.isClass || implementor.isAspect)) throw new Error(-1, implementorName+' cannot implement '+prototypeName);
if (protoclass.fullname in implementor.precedents) return;
// attach prototype implementation
implementor.ownRelations[protoclass.fullname] = protoclass;
protoclass.ownRelations[implementor.fullname] = implementor;
for (var k in protoclass.precedents) implementor.precedents[k] = protoclass.precedents[k];
protoclass.dependents[implementor.fullname] = implementor;
$DOM._mergeMembers(implementor, protoclass);
} catch (e) { $DOM.raiseDOMError(e.description); }
}
<@func name="attachAspect">
Attaches an aspect to a class
The name of the aspect to attach
The name of the class to attach
A table of property values to override on the attached object
$DOM.attachAspect = function(aspectName, className, values) {
// This method is used for attaching aspects to classes based on GmlScript attach statements.
// The aspect can later be tailored dynamically using the tailorAspect method in AspectFactory.
try {
// validate aspect and class
var aspect = $DOM._getClassifier(aspectName);
var baseclass = $DOM._getClassifier(className);
if (!aspect) throw new Error(-1, 'Aspect "'+aspectName+'" is undefined');
if (!baseclass) throw new Error(-1, 'Base class "'+className+'" is undefined');
if (!aspect.isAspect || !baseclass.isClass) throw new Error(-1, className+' cannot be attached to '+aspectName);
var originalAspect=baseclass.dependents[aspect.rolename] || null;
var V={};
if (values) {
for (var k in values) V[aspect.dollar+'$'+k] = values[k];
}
// recursively attach aspect to class and all its sub-classes, if any are defined at this point
baseclass.ownRelations[aspect.fullname] = aspect;
aspect.ownRelations[baseclass.fullname] = baseclass;
attachClass(baseclass);
function attachClass(cstor) {
var oldAspect = cstor.dependents[aspect.rolename] || null;
if (oldAspect && oldAspect !== originalAspect) return;
cstor.dependents[aspect.rolename] = aspect;
aspect.dependents[cstor.fullname] = cstor;
// add non-virtual members (virtual members are owned by the aspect instances)
var P = aspect.properties;
var Q = (cstor.superclass ? cstor.superclass.properties : null);
var M = aspect.prototype;
for (var k in M) {
var p=P[k], m=M[k], q=(Q ? Q[k] : '');
if (q && q.isProperty && !q.isVirtual) {
cstor.properties[k] = q;
cstor.prototype[k] = ((k in V) ? V[k] : cstor.superclass.prototype[k]);
} else if (p && p.isProperty && !p.isVirtual) {
cstor.properties[k] = p;
cstor.prototype[k] = ((k in V) ? V[k] : aspect.prototype[k]);
} else if (m && m.isMethod && !m.isVirtual) {
cstor.prototype[k] = m;
}
}
for (var k in cstor.subclasses) {
attachClass(cstor.subclasses[k]);
}
}
} catch (e) { $DOM.raiseDOMError(e.description); }
}
////////////////////////////////////////////////////////////////////////
// MEMBER DECLARATIONS
<@func name="addConstructor">
Adds a constructor to the current declaration unit
The constructor function
$DOM.addConstructor = function(cstor) {
try {
if (!$DOM.currClass) throw new Error(-1, 'Constructor definition out of scope');
var modifiers = {override:true};
if ($DOM.currClass.isAspect) modifiers.virtual = true;
$DOM.addMethod('Constructor', cstor, modifiers);
} catch (e) { $DOM.raiseDOMError(e.description); }
}
<@func name="addDestructor">
Adds a destructor to the current declaration unit
The destructor function
$DOM.addDestructor = function(dstor) {
try {
if (!$DOM.currClass) throw new Error(-1, 'Destructor definition out of scope');
var modifiers = {override:true};
if ($DOM.currClass.isAspect) modifiers.virtual = true;
$DOM.addMethod('Destructor', dstor, modifiers);
} catch (e) { $DOM.raiseDOMError(e.description); }
}
<@func name="addMethod">
Adds a method to the currrent declaration unit
The method name
The method function
The property's modifiers {override, native, sealed, static, and virtual}
$DOM.perfCheckFn = function(func){
return function (p1,p2, p3, p4 , p5, p6, p7,p8, p9){
var dis = $ENV.perfMng.disabled;
dis || $ENV.perfMng.begin('Checking method:' + func.Class.fullname+ '.' +func.name);
var res = func.call(this ,p1,p2, p3, p4 , p5, p6, p7,p8, p9);
dis || $ENV.perfMng.end();
return res;
}
}
$DOM.addMethodPerfCheck = function(name, func, modifiers) {
try {
if (name.search(/\b(constructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid method name');
var M=$DOM._splitName(name);
if (!M) throw new Error(-1, '"'+name+'" is not a valid method name');
var newFn = $DOM.perfCheckFn(func);
newFn.member = func.member = 'method';
newFn.name = func.name = (M.Class.isAspect ? M.Class.dollar+'$' : '') + M.Name;
newFn.Class = func.Class = M.Class;
newFn.isMethod = func.isMethod = true;
if(!modifiers) modifiers = {};
// Methods by default are public
if (!modifiers.public && !modifiers.private && !modifiers.protected)
modifiers.public = true;
for (var k in modifiers) newFn['is'+PREF(k)+k.substring(1)] = func['is'+PREF(k)+k.substring(1)] = true;
newFn.member = func.member = (modifiers.public ? 'public ' : '') + (modifiers.private ? 'private ' : '') +
(modifiers.protected ? 'protected ' : '') + (modifiers.static ? 'static ' : '') +
(modifiers.virtual ? 'virtual ' : '') + (modifiers.abstract ? 'abstract ' : '') +
func.member;
if (func.isSealed) newFn.isOverride = func.isOverride = true;
newFn.metadata = func.metadata = {};
if( M.Class.fullname == 'core.env:PerformanceMng' || func.name == 'supercall' || func.name == 'protocall' ) {
func.Class.ownMethods[func.name] = func ;
$DOM._addMember(func);
}else{
func.Class.ownMethods[func.name] = newFn ;
$DOM._addMember(newFn);
}
} catch (e) { $DOM.raiseDOMError(e.description); }
}
$DOM.addMethodRegular = function(name, func, modifiers) {
try {
if (name.search(/\b(constructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid method name');
var M=$DOM._splitName(name);
if (!M) throw new Error(-1, '"'+name+'" is not a valid method name');
func.member = 'method';
func.name = (M.Class.isAspect ? M.Class.dollar+'$' : '') + M.Name;
func.Class = M.Class;
func.isMethod = true;
if(!modifiers) modifiers = {};
// Methods by default are public
if (!modifiers.public && !modifiers.private && !modifiers.protected)
modifiers.public = true;
for (var k in modifiers) func['is'+PREF(k)+k.substring(1)] = true;
func.member = (modifiers.public ? 'public ' : '') + (modifiers.private ? 'private ' : '') +
(modifiers.protected ? 'protected ' : '') + (modifiers.static ? 'static ' : '') +
(modifiers.virtual ? 'virtual ' : '') + (modifiers.abstract ? 'abstract ' : '') +
func.member;
if (func.isSealed) func.isOverride = true;
func.metadata = {};
func.Class.ownMethods[func.name] = func;
$DOM._addMember(func);
} catch (e) { $DOM.raiseDOMError(e.description); }
}
$DOM.addMethod = $DOM.perfCheck ? $DOM.addMethodPerfCheck : $DOM.addMethodRegular;
//////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
<@func name="addProperty">
Adds a property definition to the current declaration unit
The property's name
The property's data type (boolean|number|string|object|array|pointer|collection|vector).
The property's default value.
The property's modifiers {override, native, sealed, static, virtual, readonly}
The property's expression type
$DOM.addProperty = function(name, type, value, modifiers, exprType) {
try {
if (name.search(/\b(constructor|Constructor|Destructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid property name');
var N=$DOM._splitName(name);
if (!N) throw new Error(-1, '"'+name+'" is not a valid property name');
var prop={};
prop.member = 'property';
prop.name = (N.Class.isAspect ? N.Class.dollar+'$' : '') + N.Name;
prop.type = type;
if(('undefined' != exprType) && (null != exprType)) {
prop.exprType = exprType;
}
if ('p|v|c'.indexOf(type.charAt(0)) >= 0 ) {
if (N.Class.isAspect) {
if (!modifiers || !modifiers.virtual) {
throw new Error(-1, '"'+name+'": non-virtual aspect pointer|vector|collection properties are not supported');
}
}
prop.typecheck = value; //this is a class name that we should make type checks against
prop.value = null;
}
else {
prop.value = value;
}
/* NadavL - every property has a 'Class' attribute, to indicate the class where the property was originated.
This attribute should therefore point to the class that first defined this proeprty.
What do we do then, when a class overrides its predecessor's property?
This depends on the nature of the change - is it 'minor' or 'radical'?
If it's minor, the property should still point to the corresponding ancestor.
If it's radical, the property should point to the new class (the class we are currently creating).
The only 'radical' change we identified so far, is if the property's 'type' is modified.
There is one exception - meaning an override with no 'type' change, yet we still set the 'Class' to point to
the new class.
This exception is the 'Class' property - this is because the 'Class' property has a special semantic meaning.
Note: All this is valid for properties coming from the 'inherit' parent.
Properties coming from the 'implement' parents, always point to the new class.
*/
prop.Class = N.Class;
// By default, the property points to the new class
// Now, check if this is the one exception - property is not 'Class', it's overriden, and its type is the same as in the ancestor
if (name != 'Class' && modifiers && modifiers['override']) { // use name because prop.name may contain 'dollar'
var olddef = N.Class.properties[prop.name]; // olddef is this property's def in the 'inherit' parent (or undefined if overriding 'implement' parent)
if (olddef && olddef.type == prop.type)// is it a minor change? (meaning same 'type')
prop.Class = olddef.Class; // take ancestor's Class
}
prop.isProperty = true;
if(!modifiers) modifiers = {};
// If no scope specified for the property, if it is virtual, set scope to
// private, otherwise set to public
if (!modifiers.public && !modifiers.private && !modifiers.protected) {
if (modifiers.virtual)
modifiers.private = true;
else
modifiers.public = true;
}
for (var k in modifiers) prop['is'+PREF(k)+k.substring(1)] = true;
prop.member = (modifiers.public ? 'public ' : '') + (modifiers.private ? 'private ' : '') +
(modifiers.protected ? 'protected ' : '') + (modifiers.static ? 'static ' : '') +
(modifiers.virtual ? 'virtual ' : '') + (modifiers.readonly ? 'readonly ' : '') +
(modifiers.strong ? 'strong ' : '') + prop.member;
if (prop.isSealed) prop.isOverride = true;
prop.metadata = {};
// prop.Class.ownProperties[prop.name] = prop; // NadavL - prop.Class can now point to an ancestor class ==> use the following instead:
N.Class.ownProperties[prop.name] = prop;
$DOM._addMember(prop);
} catch (e) { $DOM.raiseDOMError(e.description); }
}
<@func name="addMetadata">
Adds a metadata definition to the current declaration unit
The metadata name
The metadata value
$DOM.addMetadata = function(name, value) {
try {
if (name.search(/\b(constructor|Constructor|Destructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid metadata name');
var N=$DOM._splitName(name);
if (!N) throw new Error(-1, '"'+name+'" is not a valid property name');
N.Class.metadata[N.Name] = value;
} catch (e) { $DOM.raiseDOMError(e.description); }
}
<@func name="addMetadataFor">
Adds a member metadata definition to the current declaration unit
The member name
The metadata value
The metadata modifiers {override}
$DOM.addMetadataFor = function(name, value, modifiers) {
try {
if (name.search(/\b(constructor|Constructor|Destructor|prototype|class|aspect)\b/) == 0) {
throw new Error(-1, '"'+name+'" is not a valid member name');
}
var N=$DOM._splitName(name);
if (!N) throw new Error(-1, '"'+name+'" is not a valid member name');
var classObj = N.Class;
var name = (N.Class.isAspect ? N.Class.dollar+'$' : '') + N.Name;
if ('override' in modifiers && !classObj.ownProperties[name]) {
// If the metadata was overriden, but the property was not, override the propetry
var prop = classObj.properties[name];
$DOM._addOverrideProperty(N.Name, prop);
}
var member = classObj.ownProperties[name]; // check if the member is a property
if (!member) {
member = classObj.prototype[name]; // check if the member is a method
if (!member) {
member = classObj.ownEvents[name]; // check if the member is an event
if (!member) throw new Error(-1, '"'+name+'" is not a valid member name');
}
}
if (ISARRAY(value)) {
for (var k=0; k
Adds an event to the currrent declaration unit
The event name
The event parameters list
$DOM.addEvent = function(name, params,modifiers) {
try {
if (name.search(/\b(constructor|prototype|class|aspect)\b/) == 0) throw new Error(-1, '"'+name+'" is not a valid event name');
var E=$DOM._splitName(name);
if (!E) throw new Error(-1, '"'+name+'" is not a valid event name');
if (!$ENV.isValidId(name)) {
return $ENV.raiseKitError('Invalid namespace specification of event name: "'+ name + '" in kit :' + E.NS.$name );
}
var evt=[], type=LOWER(name);
for (var i=0, len=params.length; i all attached aspects; Aspect -> all base classes; Prototype -> all implementors (classes/aspects}
thisClass.metadata = {};
thisClass.ownEvents = {};
thisClass.ownMethods = {};
thisClass.ownRelations = {};
thisClass.isValid = true;
thisClass.isa = $DOM._isaClass;
thisClass.getProperty = $DOM._getClassProp
Namespace[bareName] = thisClass;
$DOM.currClass = thisClass;
thisClass['is'+classType] = true;
for (var k in thisClass.prototype) {
delete thisClass.prototype[k];
}
// Setup the classifier inheritance chain
thisClass.precedents[className] = thisClass;
if (typeof superClass == 'function') {
if (!(thisClass.fullname in superClass.subclasses)) superClass.subcount++;
superClass.subclasses[thisClass.fullname] = thisClass;
for (var k in superClass.precedents) thisClass.precedents[k] = superClass.precedents[k];
$DOM._mergeMembers(thisClass, superClass);
}
// Complete the classifier and return
thisClass.prototype.constructor = thisClass;
$DOM.addProperty('Class', 'object', thisClass, {static:true, virtual:true, readonly:true, override:true});
return thisClass;
}
// INTERNAL: Ends a declaration unit
$DOM.endDeclaration = function() {
var classObj=$DOM.currClass;
if (classObj && classObj.inheritCstors) {
var A=[], C=classObj.inheritCstors;
for (var k in C) {
var insert = (C[k].isPrototype ? 'unshift' : 'push');
A[insert]('$DOM.allClasses["'+k+'"].prototype.Constructor.apply(this, arguments);');
}
var func = new Function(JOIN(A,'\n'));
func.member = 'method';
func.name = 'Constructor';
func.Class = classObj;
func.isMethod = true;
func.isOverride = true;
classObj.prototype.Constructor = func;
delete classObj.inheritCstors;
}
//check abstract. loop on all methods, if any is abstract - mark class as abstract
for (var name in classObj.prototype) {
var def = classObj.prototype[name];
if (!def) continue;
if (def.isMethod && def.isAbstract) {
classObj.isAbstract = true;
//$DOM.raiseDOMError("Class " + classObj.fullname + ' is abstract due to unimplemented method ' + name );
break;
}
}
//end this declaration unit
$DOM.currClass = null;
}
// INTERNAL: appends a new member (method or property) to a declaration unit
$DOM._addMember = function(newdef) {
// var classObj = newdef.Class; // NadavL - newdef can now point to an ancestor class ==> use the following instead:
var classObj = $DOM.currClass;
var newname = newdef.name;
var olddef = classObj.properties[newname] || classObj.prototype[newname] || null;
if (olddef && olddef.isMethod && olddef.isAbstract) {
if (newdef.isAbstract)
{
var errmsg = 'Abstract method ' + newname + ' definition in class ' + olddef.Class.name + ' was redefined in class ' + classObj.name;
throw new Error(-1, errmsg);
}
if (!newdef.isOverride)
{
var errmsg = 'Abstract method ' + newname + ' inherited from class ' + olddef.Class.name +
" must be overriden using the 'override' keyword in class " + classObj.name;
throw new Error(-1, errmsg);
}
}
if (olddef && ((olddef.isMethod && !olddef.isAbstract) || olddef.isProperty || olddef.isEvent)) {
// check for duplicate members
if (olddef.Class === classObj && !olddef.isNative) {
throw new Error(-1, 'Encountered a duplicate definition for member "'+newname+'".');
}
// check for incompatible or missing members override
if (olddef.member != newdef.member || !newdef.isOverride) {
var errmsg = 'A '+olddef.member+' "'+newname+'" has already been inherited from "'+olddef.Class.name+'". ';
if (olddef.member != newdef.member) {
errmsg += 'This definition cannot be overridden into a '+newdef.member;
} else {
errmsg += 'You need to use the override keyword, or to supply a different name.'
}
throw new Error(-1, errmsg);
}
// check for sealed members override
if (olddef.isSealed) {
throw new Error(-1, 'Cannot override member "'+newname+'" since it has been sealed by "'+olddef.Class.name+'".');
}
}
if (newdef.isProperty){
classObj.properties[newname] = newdef;
classObj.prototype[newname] = newdef.value;
// Check the metadata of overidden properties: if the metadata statement was
// not overriden, inherit the metadata
if (newdef.isOverride && ISEMPTY(newdef.metadada) && olddef) {
if (!ISEMPTY(olddef.metadata) && !newdef.exprType) { // don't inherit the metadata if the property is an expression
newdef.metadata = CLONE(olddef.metadata, true)
}
}
} else {
classObj.prototype[newname] = newdef;
}
}
// INTERNAL: merges the members of a source declaration unit into a target declaration unit
$DOM._mergeMembers = function(targetObj, sourceObj) {
for (var name in sourceObj.prototype) {
var newdef = sourceObj.properties[name] || sourceObj.prototype[name] || null;
var olddef = targetObj.properties[name] || targetObj.prototype[name] || null;
var newcstor = newdef && newdef.Class || null;
var oldcstor = olddef && olddef.Class || null;
if (olddef) {
if (newdef && newdef.isAbstract) {
if (olddef.isAbstract) {
var errmsg = 'Abstract method ' + name + ' definition in class ' + newdef.Class.name + ' was redefined in class ' + olddef.Class.name;
throw new Error(-1, errmsg);
}
if (!olddef.isOverride) {
var errmsg = 'Abstract method ' + name + ' inherited from class ' + newdef.Class.name+
" must be overriden using the 'override' keyword in class " + olddef.Class.name;
throw new Error(-1, errmsg);
}
}
else {
if (oldcstor === targetObj) {
// check for incompatible or missing members override
if (olddef.member != newdef.member || !olddef.isOverride) {
var errmsg = 'A '+newdef.member+' "'+name+'" has already been inherited from '+LOWER(newcstor.type)+' "'+newcstor.name+'". ';
if (olddef.member != newdef.member) {
errmsg += 'This definition cannot be overridden into a '+olddef.member+'.';
} else {
errmsg += 'You need to use the override keyword, or to supply a different name.'
}
throw new Error(-1, errmsg);
}
// check for sealed members override
if (newdef.isSealed) {
throw new Error(-1, 'Cannot override member "'+name+'" since it has been sealed by "'+newcstor.name+'"');
}
} else {
// check for ambigous inheritance
if (name == 'Constructor') {
if (!targetObj.inheritCstors) targetObj.inheritCstors={};
targetObj.inheritCstors[newcstor.fullname] = newcstor;
targetObj.inheritCstors[oldcstor.fullname] = oldcstor;
} else if (name != 'toString') {
// IE8 includes 'toString' in each object's prototype, while IE6/7 - doesn't. The IF above elliminates throwing in IE8
throw new Error(-1, 'Member "'+name+'" is ambiguously inherited from '+LOWER(newcstor.type)+' "'+newcstor.name+'" and '+LOWER(oldcstor.type)+' "'+oldcstor.name+'". Override the member definition to resolve the ambiguity. ');
}
}
}
} else {
if (newdef.isProperty){
targetObj.properties[name] = newdef;
targetObj.prototype[name] = sourceObj.prototype[name] || newdef.value;
} else {
targetObj.prototype[name] = newdef;
}
}
}
for (var name in sourceObj.metadata) {
if (targetObj.metadata[name] == null) targetObj.metadata[name] = sourceObj.metadata[name];
}
}
// INTERNAL: Declares the start of a source file
$DOM.fileStart = function(package, fileName) {
try {
var ns = $DOM.namespaces;
var pkgName = (typeof package == 'object' ? package.name : package);
if (fileName && fileName.indexOf(':') < 0) fileName = pkgName+':'+fileName;
$DOM.currNS = ns[pkgName];
$DOM.currFile = fileName || null;
$DOM.currClass = null;
if ($DOM.currNS) return;
// this is the first time the namespace is encountered, so add it to the namespaces table
var pref='';
if (pkgName == 'core.env') {
// use the builtin prefix (the kits manifest is not available at this stage)
pref = 'env';
} else {
// use the kits manifest to obtain the correct namespace prefix for the current server.
try {
var mft=$ENV.channel1.getKitsManifest();
pref = mft.packages[pkgName].prefix;
} catch(e) { throw new Error(-1, 'Unrecognized package "'+pkgName+'"'); }
}
$DOM.currNS = {$name:pkgName, $ver:package.version, $pref:pref};
$DOM.namespaces[pkgName] = $DOM.currNS;
$DOM.prefixes[pref] = pkgName;
} catch (e) {
var err = e.description;
try { $ENV.raiseKitError(err); } catch(e) { alert(err); }
}
}
// INTERNAL: Declares the end of a source file
$DOM.fileEnd = function() {
$DOM.currNS = null;
$DOM.currFile = null;
$DOM.currClass = null;
}
////////////////////////////////////////////////////////////////////////
// UTILITIES
<@func name="getAspectRole">
Gets a specified aspect role
Gets the aspect role by name
The qualified aspect role name
Gets the aspect role by its properties prefix
The aspect dollar qualifier
The requested aspect role
$DOM.getAspectRole = function(roleName) {
return $DOM.allRoles[roleName] || null;
}
<@func name="getAspect$Name">
Gets the dollar-qualified member name for a specified aspect role
The qualified aspect role name
The bare member name
The prefixed member name
$DOM.getAspect$Name = function(roleName, propName) {
var R=$DOM.allRoles[roleName];
return (R ? R.dollar+'$' : '') + propName;
}
<@func name="toQName">
Converts a given name to a qualified classifier name
The classifier name to qualify
The qualified classifier name
$DOM.toQName = function(qname) {
if (qname in $DOM.allClasses) return qname;
qname = qname+'';
if (qname.indexOf(':') < 0) {
qname = 'core.gml:'+qname;
} else if (qname.indexOf('.') < 0) {
qname = 'core.'+qname;
}
return (qname in $DOM.allClasses) ? qname : '';
}
// INTERNAL: gets a specified classifier (class, prototype, or aspect)
$DOM._getClassifier = function(className) {
if (!className) return $DOM.currClass || null;
if (typeof className == 'object') {
if (className.isClass || className.isAspect) return className;
return className.Class;
}
if (className in $DOM.allClasses) return $DOM.allClasses[className];
var k=className.indexOf(':'), ns=className.substring(0,k), cls=className.substring(k+1);
var nsobj = ns && ($DOM.namespaces[ns] || $DOM.namespaces[$DOM.sysAliases[ns]]) || $DOM.currNS || $DOM.namespaces[$DOM.prefixes[ns]] || null;
if (cls.indexOf('.') >= 0) cls=cls.substring(0,cls.indexOf('.'));
return nsobj && nsobj[cls] || null;
}
// INTERNAL: parses a given member name and returns its composite parts
$DOM._splitName = function(name) {
if (!name) return null;
var k=name.indexOf(':'), ns=name.substring(0,k), mbr=name.substring(k+1);
var k=mbr.indexOf('.'), cls=mbr.substring(0,k), mbr=mbr.substring(k+1)||'';
var nsobj = ns && ($DOM.namespaces[ns] || $DOM.namespaces[$DOM.sysAliases[ns]]) || $DOM.currNS || $DOM.namespaces[$DOM.prefixes[ns]] || null;
if (!nsobj) return null;
var cstor = cls && nsobj[cls] || $DOM.currClass || null;
if (!cstor || !(cstor.name in nsobj)) return null;
return {NS:nsobj, Class:cstor, Name:mbr};
}
// INTERNAL: raises a DOM parsing error
$DOM.raiseDOMError = function(error) {
var file=$DOM.currFile || '';
if (file) file = ' [' + file.replace(/^.+?(\w+\.\w+)$/, '$1') + ']';
error = error.replace(/[\s\.]*$/,'.');
try { $ENV.raiseKitError(error+file); } catch(e) { alert(error+file); }
}
// INTERNAL: records a parsing error in the kit errors log
$DOM.raiseKitError = function(error, where) {
$ENV.raiseKitError(error, where);
}
// INTERNAL: registers a kit configuration function
$DOM.registerConfigFunc = function(namespace, sections, func, priority) {
$ENV.registerConfigFunc(func, sections, namespace, $ENV.getCurrentKit(), $DOM.currFile, priority);
}
////////////////////////////////////////////////////////////////////////
// NATIVE OBJECT METHODS
// INTERNAL: adds a native method
$DOM._addNativeMethod = function(classObj, name, func , modifiers) {
func.member = 'method';
if(modifiers && modifiers.public) func.member = 'public ' + func.member;
else func.member = 'private ' + func.member;
func.name = name;
func.isMethod = true;
func.isNative = true;
func.Class = classObj;
classObj.prototype[name] = func;
}
// INTERNAL: native class constructor
$DOM._cstorClass = function(values) {
var cstor=this.constructor, I=cstor.instantiators;
if (I) {
for (var k in I) {
var val = cstor.properties[k].value;
if( ISEMPTY(val) ){
this[k] = new I[k]();
}else{
this[k] = CLONE(val,true);
}
}
}
if (this.Constructor) {
this.Constructor.apply(this, arguments);
} else if (values) {
for (var k in values) this[k] = values[k];
}
}
// INTERNAL: native GML class constructor
$DOM._cstorGmlClass = function(values) {
var cstor=this.constructor, I=cstor.instantiators;
if (I) {
for (var k in I) {
var val = cstor.properties[k].value;
if( ISEMPTY(val) ){
this[k] = new I[k]();
}else{
this[k] = CLONE(val,true);
}
}
}
if (!values) return;
for (var k in values) this[k] = values[k];
// actual constructor will be invoked only at the completeDiagram stage
}
// INTERNAL: native aspect constructor
$DOM._cstorAspect = function() {
throw new Error(-1, this.constructor.name + ' cannot be instantiated');
}
// INTERNAL: native prototype constructor
$DOM._cstorPrototype = function() {
throw new Error(-1, this.constructor.name + ' cannot be instantiated');
}
// INTERNAL: native class property getter
$DOM._getClassProp = function(name) {
var P=this.properties[name];
return (P && P.isProperty && P.isStatic) ? P.value : null;
}
// INTERNAL: native instance property getter
$DOM._getInstanceProp = function(name) {
if (this.hasOwnProperty(name)) return this[name];
var P=this.Class.properties[name];
return (P && P.isProperty) ? P.value : null;
}
// INTERNAL: native instance property setter
$DOM._setInstanceProp = function(name, value) {
var C=this.Class, P=C.properties[name];
if (!P || P.isReadonly) return this;
if (P.isStatic) { C.prototype[name] = value; return this; }
if (value === undefined) delete this[name]; else this[name] = value;
return this;
}
// INTERNAL: native subclassof operator
$DOM._isaClass = function() {
var P = this.precedents;
if (arguments[0] in P) return true; // fast check the most common case
for (var i=0, len=arguments.length; i