// // Jala Project [http://opensvn.csie.org/traccgi/jala] // // Copyright 2004 ORF Online und Teletext GmbH // // Licensed under the Apache License, Version 2.0 (the ``License''); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an ``AS IS'' BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // $Revision$ // $LastChangedBy$ // $LastChangedDate$ // $HeadURL$ // /** * @fileoverview Fields and methods of the jala.XmlWriter class. */ // Define the global namespace for Jala modules if (!global.jala) { global.jala = {}; } /** * Construct a new XML writer. * @class This class defines a generic interface to write * arbitrary and validating XML source code. This is done * by first applying data objects onto template objects, * both in a specified format. Then, the resulting object * tree is transformed into XML. Moreover, template objects * can be extended with other template objects to provide * full flexibility in inheriting subclasses. * @param {String} header An optional XML header. * @returns A new XML writer. * @constructor */ jala.XmlWriter = function(header) { var self = this; var XMLHEADER = header || ''; var LOCALE = java.util.Locale.ENGLISH; var write = function(str) { return res.write(str); }; var writeln = function(str) { res.write(str); res.write("\n"); return; }; var getString = function(data, format) { if (data == null) return; switch (data.constructor) { case String: return encodeXml(data); case Number: case Date: if (format && data.format) return encodeXml(data.format(format, LOCALE)); else if (data.toUTCString) return encodeXml(data.toUTCString()); else return encodeXml(data.toString()); break; case Object: return null; } return encodeXml(data.toString()); }; /** @ignore */ var XmlElement = function(data) { if (!data) throw Error("Insufficient arguments to create XmlElement"); var children = {}; var properties = [ "name", "attributes", "type", "required", "format", "readonly" ]; if (data.value) { if (data.value.constructor == Object) { this.value = [new XmlElement(data.value)]; } else if (data.value.constructor == Array) { this.value = []; for (var i in data.value) { this.value[i] = new XmlElement(data.value[i]); } } else throw Error("Cannot handle unknown type of template value"); } for (var i in properties) { var key = properties[i]; this[key] = data[key] || null; } if (this.attributes) { this.attributes = self.clone(this.attributes); if (this.attributes.constructor == Object) this.attributes = [this.attributes]; } else { this.attributes = []; } return this; }; /** @ignore */ XmlElement.toString = function() { return "[XmlElement constructor]"; }; /** @ignore */ XmlElement.prototype.setValue = function(element) { if (element.constructor != this.constructor) throw Error("Invalid type for XmlElement addition"); if (!this.value) this.value = []; else { var pos = this.contains(element); if (pos > -1) this.value.splice(pos, 1); } this.addValue(element); return this; }; /** @ignore */ XmlElement.prototype.addValue = function(element) { if (element.constructor != this.constructor) throw Error("Invalid type for XmlElement addition"); if (!this.value) this.value = []; this.value.push(element); return this; }; /** @ignore */ XmlElement.prototype.contains = function(element) { if (!this.value || !element) return -1; for (var i in this.value) { if (this.value[i].name == element.name) return i; } return -1; }; /** @ignore */ XmlElement.prototype.populate = function(data) { if (this.attributes) { var value; for (var i in this.attributes) { var attr = this.attributes[i]; if (!attr.name) throw Error("Cannot populate unnamed attribute entry"); if (data && data.attributes) value = data.attributes[attr.name]; if (data && (data.value || data.attributes) && !value && attr.required) { throw Error('Missing required ' + (attr.type || Object).name + ' attribute "' + attr.name + '" in element <' + this.name + '> (' + value + ")"); } if (value && attr.type && attr.type != value.constructor) { throw Error('Type mismatch in attribute "' + this.name + ":" + attr.name + '"'); } if (value) { app.debug("populating attribute " + attr.name + " with " + value.constructor.name + ": " + value.toSource()); } if (!attr.readonly) { attr.value = getString(value, attr.format) || attr.value ; } } } if (data && data.value) // && data.value.constructor == Object) data = data.value; if (this.value && data) { for (var i in this.value) { var element = this.value[i]; element.populate(data[element.name]); } } else { if (!data && this.required) throw Error('Missing required element "' + this.name + '"'); if (data && this.type && this.type != data.constructor) { throw Error('Type mismatch in element "' + this.name + '"'); } if (data) { app.debug("populating element <" + this.name + "> with " + (this.type || Object).name + ": " + data.toSource()); } if (!this.readonly) this.value = getString(data, this.format) || this.value; } return; }; /** @ignore */ XmlElement.prototype.write = function(path) { if (!path) path = ""; if (!this.value && !this.attributes) return; var attrBuffer = new java.lang.StringBuffer(); if (this.attributes) { for (var a in this.attributes) { var attr = this.attributes[a]; if (attr.value) { attrBuffer.append(" " + attr.name + '="'); attrBuffer.append(attr.value); attrBuffer.append('"'); } } } var attrSize = attrBuffer.length(); if (!this.value && attrSize < 1) return; write("<" + this.name); if (attrSize > 0) { display = true; write(attrBuffer.toString()); } if (this.value) { write(">"); if (this.value && this.value.constructor == Array) { for (var i in this.value) this.value[i].write(path+"/"+this.name); } else write(this.value); write("\n"); } else write("/>\n"); return; }; /** @ignore */ XmlElement.prototype.toString = function() { return "[XmlElement: " + this.toSource() + "]"; }; /** * Get a newly created XML element. * @param {Object} data The XML data as object tree. * @returns The resulting XML element. * @type jala.XmlWriter.XmlElement */ this.createElement = function(data) { return new XmlElement(data); }; /** * Get the root XML element of this writer. * @returns The root XML element. * @type jala.XmlWriter.XmlElement */ this.getRoot = function() { return new XmlElement({}); }; /** * Extend a template object. * @param {Object} template The template object. * @param {Object} ext The extension object. * @returns The XML writer. * @type jala.XmlWriter */ this.extend = function(template, ext) { if (ext.constructor == Object) ext = [ext]; if (ext.constructor == Array) { for (var i in ext) template.value.push(ext[i]); } return this; }; /** * Add a namespace to this writer. * @param {String} name The name of the namespace. * @param {String} url The URL string of the namespace. * @returns The XML root element. * @type jala.XmlWriter.XmlElement */ this.addNamespace = function(name, url) { var ref = this.getRoot(); ref.attributes.push({ name: "xmlns:" + name, value: url }); return ref; }; /** * Write the XML to the response buffer. */ this.write = function() { res.contentType = "text/xml"; writeln(XMLHEADER); this.getRoot().write(); return; }; /** * Get the XML output as string. * @returns The XML output. * @type String */ this.toString = function() { res.push(); this.write(); return res.pop(); }; /** * Clone this XML writer. * @param {Object} The clone templare. * @returns The cloned XML writer. * @type jala.XmlWriter */ this.clone = function(obj) { if (!obj || typeof obj != "object") return obj; var copy = new obj.constructor; for (var i in obj) { if (obj[i].constructor == Object || obj[i].constructor == Array) copy[i]= this.clone(obj[i]); else copy[i] = obj[i]; } return copy; }; return this; };