@doc alias="persist" hierarchy="GMLDOM">
A helper object that deals with conversion of Units and GMLObjects to and from XML format.
(c) SAP AG 2003-2006. All rights reserved.
Class ModelSerializer inherit core.gml:Object
virtual property model = ^core.gml:Model;
virtual property unusedUnits = ^core.gml:Unit[id];
constructor(model)
this.model = model;
end
method deserializeJSUnit(gml , id)
if(!gml || !id ) return null;
var unit = null;
try{
var _CLASS = CLASS; //For performance reaseons only.
// Remove the CDATA tag from the string
//var startT = NOW();
var startStr = gml.indexOf("\<\!\[CDATA\[");
var endStr = gml.lastIndexOf("\]\]\>");
gml = gml.substring(startStr+9 , endStr);
//WRITE( "UNIT phase 1 " + id +" took " + DSUB(NOW(), startT, 'z'));
//startT = NOW();
eval(gml);
eval('unit=$_'+id);
//WRITE( "UNIT phase 2 " + id +" took " + DSUB(NOW(), startT, 'z'));
}catch(e){
#LOG[4,"Error while evaluting Unit: " +id +", Error Description: "+ e.description ]
}
return unit;
end
<@doc scope="private">
Deserializes GML representation into the corresponding unit object.
The GML representation of the unit
The parsed unit object
method deserializeUnit(gml)
try {
// build aspect prefixes map
var model=this.model, unit=null, aspectsMap={}, derefList=[], onloadList=[];
for (var i=0, A=gml.attributes, B=$ENV.kitsManifest.aspects, len=A.length, flag=false; i 0) {
var m=aspectsMap[n.substring(0,j)];
if (!m) throw new Error(-1, 'Unknown aspect property "'+n+'"');
n = m + '$' + n.substring(j+1);
}
}
var v=A[i].text, p=P[n];
if (!p || p.isStatic || p.isVirtual) throw new Error(-1, '#TEXT[XMSG_INVALID_PROPERTY]' + ' "'+n+'" ' + 'for class'+ ' "'+N+'"');
// convert property values to their native data types
// pointers are not converted at this stage
// Added the Dynamic Expressions handling the '=' character - mhoter July05
if(v.charAt(0) == '=') {
// Treat it as a dynamic expressions (a string that begins with '=')
V[n] = UNESCAPEXML(v);
} else {
switch (p.type.charAt(0)) {
case 'b': V[n] = BOOL(v); break;
case 'n': V[n] = FLOAT(v); break;
case 's': V[n] = UNESCAPEXML(v); break;
case 'p': // fall-through
case 'v': // fall-through
case 'c': // fall-through
default:
if (!v) break;
V[n] = v;
derefList.push({name:n, value:v, def:p});
break;
}
}
}
// create new object and hook it to parent and unit, as needed
var obj = new C(V); //this is where an object is created from the class, and V is the collection of all read attributes
for (var i=derefIndex; i < derefList.length; i++) derefList[i].obj = obj;
if (!parent) {
unit = obj;
obj.model = model;
} else {
obj.unit = unit;
obj.parent = parent;
if(!parent.children) parent.children={};
//orig code
parent.children[obj.id] = obj;
parent.assignChild(obj, agg_name);
unit.all_elements[obj.id] = obj; //patch - for deref ??
}
if (typeof obj.onLoad == 'function') onloadList.push(obj);
// recursively parse the child nodes, if any
if (!node.hasChildNodes()) return;
var chlist = node.childNodes, ch;
while (ch = chlist.nextNode()) {
var prop = ch.baseName;
if(!P[prop]) {
var msg = '#TEXT[XMSG_INVALID_PROPERTY]' + ' "'+prop+'" ' + 'for class'+ ' "'+C.name+'"';
#LOG[4, msg];
throw new Error(-1, msg );
}
//each node is a "strong" property. verify that.
if (!P[prop].isStrong) {
var msg1='#TEXT[XMSG_ERROR_LOADING_MODEL]'.replace('{0}', prop);
#LOG[4, msg1];
continue;
}
if (ch.hasChildNodes()) {
//property has content
var chlist2 = ch.childNodes;
var ch2;
while (ch2 = chlist2.nextNode()) {
parseBranch(ch2, obj, prop);
}
}
else {
obj[prop] = null;
}
}
}
} catch (e) {
var unitId = gml && gml.firstChild && gml.firstChild.getAttribute('id') || null;
var msg='#TEXT[XMSG_MODEL_SERIALIZER]' + ' ' + e.description;
if(unitId) msg += ' in unit ' + unitId ;
#LOG[4, msg ];
return null;
}
end
/*<@doc scope="private">
Serializes a unit object into GML representation
The unit to serialize
Indicates whether to return the results as an XML object or as a string
The GML representation of the unit object
*/
method serializeUnitOld(unitobj, asXML)
var buf=[], NS={};
var model = this.model;
// print unit tag
unitobj.modified = DSTR(new Date(), 'DD/MM/YYYY');
unitobj.version = '1.0';
if (#SYS[SUPPORT_TRANSLATION] && ISA(unitobj, 'core.gml:Scenario'))
{
// Validating that the Text objects do not have a reference to non existing elements.
// This is a part of a greater unit validation process:
this.validateTextRef(unitobj);
}
printObj(unitobj);
// build kit namespaces map
var nslist=[], nsmap=$ENV.kitsManifest.prefixes;
var NSarr = [];
for( var k in NS ){
NSarr.push(k);
}
NSarr = NSarr.sort(collate);
function collate(a,b) {
return (ab ? 1 : 0));
}
var NSarrLen = NSarr.length;
for (var i=0 ; i < NSarrLen; i++) {
var name = NSarr[i];
if (!name) {
var msg = 'Invalid package prefix for package: ' + nsmap[name];
#LOG[4, msg];
throw new Error(-1, msg);
}
nslist.push('xmlns:'+name+'="'+nsmap[name]+'"');
}
/*
for (var k in NS) {
if (null == k) {
var msg = '#TEXT[XMSG_INVALID_PACKAGE_PREFIX]: ' + nsmap[k];
#LOG[4, msg];
throw new Error(-1, msg);
}
nslist.push('xmlns:'+k+'="'+nsmap[k]+'"');
}
*/
// print the outer GML tag
buf.unshift('');
buf.push('');
// return the results
if (asXML === false) return JOIN(buf);
var gml = PARSEXML(JOIN(buf));
var err = CHECKXML(gml);
if (err) {
var msg = '#TEXT[XMSG_ERROR_SERIALIZING_UNIT]' + ': '+unitobj.id +' '+ err ;
#LOG[4, msg];
throw new Error(-1,msg);
}
return gml;
// recursively print a composite GmlObject into a corresponding GML branch
function printObj(obj) {
var C=obj.Class, P=C.persistent, tag=[], tagName=C.prefix+':'+C.name;
NS[C.prefix] = true;
tag.push(tagName);
for (var k in P) {
var p=P[k], v=obj[k];
if (p.isStatic || p.isVirtual || p.isStrong) continue;
if (!obj.hasOwnProperty(k) || v === null || v === undefined) continue;
// If the current element value (v) type is 'string' and different than the expected value type of the
// persistent element(p) - by different I mean bool or number only, it means that the request
// is for persisting a dynamic expression as the value.
// Thus, treat the persistent type as a string (and so prepare it for XML): - mhoter July05
if(typeof v == 'string') {
if(('boolean' == p.type) || ('number' == p.type)) p.type = typeof v;
}
switch (p.type.charAt(0)) {
case 'p':
if ((typeof v == 'string')) {
if (!model.isFullId(v)) {
var msg1='#TEXT[XMSG_POINTER]';
var msg2='value';
var msg3='is a string that does not represent an element in object';
#LOG[4, msg1+' ' + k + '(' + msg2 + ' ' + v + ') ' + msg3 +' "'+obj+'"'];
continue;
}
break;
}
if (!ISA(v, 'core.gml:Element')) {
var msg1 = '#TEXT[XMSG_INVALID_POINTER]';
var msg2 = 'in object';
#LOG[4, msg1 +' "'+k+'" '+ msg2 +' "'+obj+'"'];
continue;
}
if(!v.unit) {
var msg = 'In element ' + v.id + ' type '+ (v.Class && v.Class.name) +' #TEXT[XMSG_UNIT_PROPERTY_NULL]' ;
#LOG[4, msg];
}
if (v.unit.id != unitobj.id) {
//pointer to element in other unit - serialize full ID
v = model.getFullId(v);
} else {
//pointer to element in same unit - serialize ID
v = v.id;
}
break;
case 'v':
var V=[];
for (var i=0, len=v.length; i 0) NS[k.substring(0,j)]=true, k=k.replace('$',':');
tag.push(k+'="'+v+'"');
}
buf.push('<'+JOIN(tag,' ')+'>');
//loop on all strong properties
var Props = P=C.properties;
for (var k in Props) {
var p=Props[k], v=obj[k];
if (!p.isStrong) continue;
if (!obj.hasOwnProperty(k) || v === null || v === undefined) continue;
var V=[]; //collect elements aggregated by this strong property
switch (p.type.charAt(0)) {
case 'p':
if (!ISA(v, 'core.gml:Element')) {
var msg1='#TEXT[XMSG_INVALID_STRONG_POINTER]';
var msg2='in object';
#LOG[4, msg1+' "'+k+'" '+msg2+' "'+obj+'"'];
continue;
}
V.push(v);
break;
case 'v':
for (var i=0, len=v.length; i 0) {
buf.push('<' + k + '>');
for (var i=0, len=V.length; i');
}
}
buf.push(''+tagName+'>');
}
end
// Validating that the Text objects do not have a reference to non existing elements.
// This is a part of a greater unit validation process (mickey hoter May06)
method validateTextRef(unit)
if(unit.translationTable)
{
// Go over the translation table and remove non valid Text items (have bad references):
for(var text in unit.translationTable.items)
{
var ref = unit.translationTable.items[text].objID;
if(ref == unit.id)
{
// This means a translation of property that belongs to the unit itself!
// For example a translatable property 'title' on a scenario.
continue;
}
var refElement = unit.getElement(ref,true);
if(refElement)
{
// This one is valid:
continue;
}
// Remove - contains non valid reference:
delete unit.translationTable.items[text];
}
}
end
<@doc scope="private">
Serializes a unit object into GML representation
The unit to serialize
Indicates whether to return the results as an XML object or as a string
The GML representation of the unit object
method serializeUnit(unitobj, asXML)
var self = this;
var buf=[], NS={};
var model = this.model;
// for perfomance reasons hold a pointer to the methods:
var isElementDeleted = model.isElementDeleted;
var escapeXml = ESCAPEXML ;
var errorsList = [];
// this.unusedUnits = model.organizer.getUnusedUnits();
// print unit tag
unitobj.modified = DSTR(new Date(), 'DD/MM/YYYY');
unitobj.version = '1.0';
serializeGSObj(unitobj);
//if (!ISEMPTY( errorsList)) {
// features = {msg:"Errors while validating Unit " + unitobj.id + " press OK for fixing press Cancel to save anyway." , buttons:'OK Cancel', moreinfo:'', showmore:true, errors:errorsList };
// var result = AMODAL('#URL[core.dev:common.ErrorsDlg.htm]' , features,true);
//
//}
// build kit namespaces map
var nslist=[], nsmap=$ENV.kitsManifest.prefixes;
var NSarr = [];
for( var k in NS ){
NSarr.push(k);
}
NSarr = NSarr.sort(collate);
function collate(a,b) {
return (ab ? 1 : 0));
}
//var NSarrLen = NSarr.length;
for (var i=0 ,len = NSarr.length; i < len; i++) {
var name = NSarr[i];
if (!name) {
var msg = 'Invalid package prefix for package: ' + nsmap[name];
#LOG[4, msg];
throw new Error(-1, msg);
}
nslist.push('xmlns:'+name+'="'+nsmap[name]+'"');
}
/*
for (var k in NS) {
if (null == k) {
var msg = 'Invalid package prefix for package: ' + nsmap[k];
#LOG[4, msg];
throw new Error(-1, msg);
}
nslist.push('xmlns:'+k+'="'+nsmap[k]+'"');
}
*/
// print the outer GML tag
buf.unshift('');
buf.push('');
// return the results
if (asXML === false) return JOIN(buf);
var gml = PARSEXML(JOIN(buf));
var err = CHECKXML(gml);
if (err) throw new Error(-1, err);
return gml;
// recursively serialize a gml object
function serializeGSObj(obj) {
var C=obj.Class, P=C.persistent;
var anchor=buf.length, attrs={}, tagName=C.prefix+':'+C.name;
// Only valided Elements
if( unitobj!== obj){
RULE('validateElement',unitobj, obj,errorsList);
if( isElementDeleted.call(model,obj)){
return;
}
}
buf.push('<'+tagName);
NS[C.prefix] = true;
// loop over all persistent properties and serialize each in turn
for (var k in P) {
var p=P[k], v=obj[k];
if (p.isStatic || p.isVirtual) continue;
if (!obj.hasOwnProperty(k) || v === null || v === undefined ) continue;
switch (p.type.charAt(0)) {
case 's': // string
if (v === p.value && !p.isOverride) continue;
attrs[k] = escapeXml(v);
break;
case 'b': // boolean
case 'n': // number
if (v === p.value && !p.isOverride) continue;
// In case the propery is dynamic experssion
if( typeof v == 'string' ){
attrs[k] = escapeXml(v);
}else {
attrs[k] = v+'';
}
break;
case 'o': // object
case 'a': // array
if (v === p.value && !p.isOverride) continue;
attrs[k] = escapeXml(v);
break;
case 'p': // pointer
// if pointer is null, Do nothing.
if(!v) break;
// A pointer to an element in another unit which has not been loaded yet.
if( typeof v == 'string' && model.isFullId(v) ){
attrs[k] = v;
break;
}
// The element is loaded ( in memory.)
if (!ISA(v, model.elementClassifier)) {
#LOG[4, 'Invalid pointer "'+k+'" in object "'+obj+'"'];
} else if (v.unit != unitobj) {
// Check the element is valid.
// If element has been deleted then don't serialize it.
if( !isElementDeleted.call(model,v)){
var unitId = v.unit.id || v.unit;
attrs[k] = unitId+"."+v.id ;
}
}else if (p.isStrong) {
buf.push('<'+k+'>');
serializeGSObj(v);
buf.push(''+k+'>');
}else {
// If element has been deleted then don't serialize it.
if( !isElementDeleted.call(model, v )){
attrs[k] = v.id;
}
}
break;
case 'v': // vector
var V=[];
for (var i=0, len=v.length; i');
for (var i=0, len=V.length; i');
} else {
attrs[k] = JOIN(V,' ');
}
break;
case 'c': // collection
var V=[];
for (var k2 in v) {
if (!ISA(v[k2], model.elementClassifier) || v[k2].unit != unitobj || isElementDeleted.call(model,v[k2]) ) {
#LOG[4, 'Invalid collection pointer "'+k+':'+k2+'" in object "'+obj+'"'];
} else {
V.push(p.isStrong ? v[k2] : v[k2].id);
}
}
if (V.length == 0) break;
if (p.isStrong) {
buf.push('<'+k+'>');
for (var i=0, len=V.length; i');
} else {
attrs[k] = JOIN(V,' ');
}
break;
}
}
for (var k in attrs) {
var j=k.indexOf('$'), n=k;
if (j > 0) NS[k.substring(0,j)]=true, n=k.replace('$',':');
buf[anchor] += ' '+n+'="'+attrs[k]+'"';
}
buf[anchor] += '>';
buf.push(''+tagName+'>');
}
end