helma/modules/jala/code/XmlWriter.js

365 lines
10 KiB
JavaScript

//
// 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 ||
'<?xml version="1.0" encoding="iso-8859-15"?>';
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 &lt;' + this.name + '&gt; (' + 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 &lt;" + this.name + "&gt; 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("</" + this.name + ">\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;
};