// XML Serialization/Restoration implementation for JavaX // Require: prototype.js // Require: types.js // Require: aserializer.js // Bugs: Grep TODO's and NOTE's in the code //#include "/js/aserializer.js" function XMLRestore(oBuffer) { var oRestorer = new AXMLRestorer(oBuffer); return oRestorer.Restore(); } function XMLSerialize(oObj) { var oSerializer = new AXMLSerializer(); oSerializer.Serialize(oObj); return oSerializer.Buffer(); } // --------------------------------------------- // Restorer // --------------------------------------------- var AXMLRestorer = Class.create({ initialize: function(oSource) { this.oXML_ = new AXML(); if (typeof(oSource) == "string") this.oXML_.Parse(oSource); else { // check if it is a DOM tree (not perfect, but working) if (oSource.xml) this.oXML_.SetXML(oSource); else throw new Error("AXMLRestorer: Invalid argument passed in constructor. Allowed types are string and DOM tree"); } this.aPath_ = new Array(); this.aPath_.push(this.oXML_.XML().documentElement.firstChild); }, BeginType: function(sName) { if (!this.aPath_.length) throw new Error("AXMLRestorer::BeginType: Stream is empty! "); var oBack = this.aPath_.last(); if (!oBack) throw new Error("AXMLRestorer::BeginType: Stream is empty! "); if (oBack.nodeName != sName) throw new Error("AXMLRestorer::BeginType: Name mismatch detected ("+oBack.nodeName+" != "+sName+")! "); this.aPath_.push(this.aPath_.last().firstChild); }, EndType: function(sName) { if (!this.aPath_.length) throw new Error("AXMLRestorer::EndType: Stream is empty! "); var oBack = this.aPath_.last(); if (!oBack || !oBack.nextSibling) { // all good } else { // this condition is added for *long* text nodes (failures were detected on long base64 blocks) if (oBack.nodeName != "#text") { throw new Error("AXMLRestorer::EndType: Unexpected type end ("+sName+")! "); } // else all good } this.aPath_.pop(); var oBack = this.aPath_.last(); if (!oBack || oBack.nodeName != sName) throw new Error("AXMLRestorer::EndType: Name mismatch detected ("+ ((!oBack)?"":oBack.nodeName)+" != "+sName+")! "); this.aPath_[this.aPath_.length-1] = oBack.nextSibling; }, GetType: function() { var oBack = this.aPath_.last(); if (!oBack) return ""; else return oBack.nodeName; }, RestoreInt: function() { this.BeginType("int"); var oBack = this.aPath_.last(); var sText = oBack.parentNode.text; if (!sText) throw new Error("AXMLRestorer::RestoreInt: Node text content is NULL"); this.EndType("int"); return new Int(parseInt(sText)); }, RestoreInt64: function() { this.BeginType("int64_t"); var oBack = this.aPath_.last(); var sText = oBack.parentNode.text; if (!sText) throw new Error("AXMLRestorer::RestoreInt64: Node text content is NULL"); this.EndType("int64_t"); return new Int64(sText); }, RestoreDouble: function() { this.BeginType("double"); var oBack = this.aPath_.last(); var sText = oBack.parentNode.text; if (!sText) throw new Error("AXMLRestorer::RestoreDouble: Node text content is NULL"); this.EndType("double"); return new Double(sText); }, RestoreString: function() { this.BeginType("string"); var oBack = this.aPath_.last(); // handle empty text node var sV = (oBack && oBack.parentNode.text)?oBack.parentNode.text:""; this.EndType("string"); return sV; }, RestoreBool: function() { this.BeginType("bool"); var oBack = this.aPath_.last(); var sText = oBack.parentNode.text; if (!sText) throw new Error("AXMLRestorer::RestoreBool: Node text content is NULL"); this.EndType("bool"); if (sText == "1") return true; if (sText == "0") return false; throw new Error("AXMLRestorer::RestoreBool: Invalid bool value. "); }, RestoreBinary: function() { this.BeginType("base64"); var oBack = this.aPath_.last(); // handle empty text node var sV = (oBack && oBack.parentNode.text)?oBack.parentNode.text:""; this.EndType("base64"); return Encoder.Base64Decode(sV); }, RestoreCompound: function() { var oObj = null; this.BeginType("Value"); var typeName = this.GetType(); if (typeName == "Structure") { oObj = new Object(); this.BeginType("Structure"); while (this.GetType() == "string") { var sKey = this.RestoreString(); //this.BeginType("Value"); var oVal = this.Restore(); oObj[sKey] = oVal; //this.EndType("Value"); } this.EndType("Structure"); } else if (typeName == "Array") { oObj = new Array(); this.BeginType("Array"); while (this.GetType() == "Value") { //this.BeginType("Value"); oObj.push(this.Restore()); //this.EndType("Value"); } this.EndType("Array"); } else if (!typeName) { oObj = null; // NULL-FIX ('undefined' was here) } else { oObj = this.Restore(); } this.EndType("Value"); return oObj; }, Restore: function() { return RestorerIF.restore(this); }, Buffer: function() { return this.oXML_.XML().xml; } }); // --------------------------------------------- // Serializer // --------------------------------------------- var AXMLSerializer = Class.create({ initialize: function() { this.oXML_ = new AXML("xmlstream"); this.xCurrent_ = this.oXML_.XML().documentElement; }, Buffer: function() { if (this.xCurrent_ != this.oXML_.XML().documentElement) { throw new Error("AXMLSerializer: Type serialization not finished!"); } return this.oXML_.Serialize(); }, BeginType: function(sName) { var xChild = this.oXML_.XML().createElement(sName); this.xCurrent_.appendChild(xChild); this.xCurrent_ = xChild; }, EndType: function(sName) { if (this.xCurrent_.nodeName != sName) { throw new Error("Begin-End names do not match ('" + this.xCurrent_.nodeName + "' != '" + sName + "'"); } this.xCurrent_ = this.xCurrent_.parentNode; }, SerializeInt: function(oObj) { var xChild = this.oXML_.XML().createElement("int"); // TODO: toString()? var xText = this.oXML_.XML().createTextNode(oObj.toString()); xChild.appendChild(xText); this.xCurrent_.appendChild(xChild); }, SerializeInt64: function(oObj) { var xChild = this.oXML_.XML().createElement("int64_t"); // TODO: toString()? var xText = this.oXML_.XML().createTextNode(oObj.toString()); xChild.appendChild(xText); this.xCurrent_.appendChild(xChild); }, SerializeDouble: function(oObj) { var xChild = this.oXML_.XML().createElement("double"); var xText = this.oXML_.XML().createTextNode(oObj.toString()); xChild.appendChild(xText); this.xCurrent_.appendChild(xChild); }, SerializeString: function(oObj) { var xChild = this.oXML_.XML().createElement("string"); var xText = this.oXML_.XML().createTextNode(oObj); xChild.appendChild(xText); this.xCurrent_.appendChild(xChild); }, SerializeBool: function(oObj) { var xChild = this.oXML_.XML().createElement("bool"); var sV; if (oObj === true) sV = "1"; else if (oObj === false) sV = "0"; else { throw new Error("ERROR: Invalid value for Boolean!"); } var xText = this.oXML_.XML().createTextNode(sV); xChild.appendChild(xText); this.xCurrent_.appendChild(xChild); }, SerializeBinary: function(oObj) { alert("Serialization of binary types (bas64-encoded) is not implemented!"); }, SerializeObject: function(oObj, bValueElement) { if (!bValueElement) this.BeginType("Value"); var nCount = 0; for (var sKey in oObj) { if (typeof(oObj[sKey]) == "function") continue; ++nCount; } if (nCount) { this.BeginType("Structure"); for (var sKey in oObj) { if (typeof(oObj[sKey]) == "function") continue; var oItem = oObj[sKey]; // put key this.Serialize(String(sKey)); // put value as Value this.BeginType("Value"); this.Serialize(oItem, true); this.EndType("Value"); } this.EndType("Structure"); } if (!bValueElement) this.EndType("Value"); }, SerializeArray: function(oObj, bValueElement) { if (!bValueElement)this.BeginType("Value"); this.BeginType("Array"); for (var i = 0; i < oObj.length; i++) { var oItem = oObj[i]; // put value as Value this.BeginType("Value"); this.Serialize(oItem, true); this.EndType("Value"); } this.EndType("Array"); if (!bValueElement)this.EndType("Value"); }, Serialize: function(oObj, bValueElement) { SerializerIF.serialize(this, oObj, bValueElement); } });