node-webodf
Version:
WebODF - JavaScript Document Engine http://webodf.org/
457 lines (448 loc) • 15.4 kB
JavaScript
/**
* Copyright (C) 2012 KO GmbH <copyright@kogmbh.com>
*
* @licstart
* This file is part of WebODF.
*
* WebODF is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License (GNU AGPL)
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* WebODF is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with WebODF. If not, see <http://www.gnu.org/licenses/>.
* @licend
*
* @source: http://www.webodf.org/
* @source: https://github.com/kogmbh/WebODF/
*/
/*global runtime, xmldom*/
runtime.loadClass("xmldom.RelaxNGParser");
var nsmap = {
"http://purl.org/dc/elements/1.1/": "purl",
"http://www.w3.org/1998/Math/MathML": "mathml",
"http://www.w3.org/1999/xhtml": "xhtml",
"http://www.w3.org/1999/xlink": "xlink",
"http://www.w3.org/2002/xforms": "xforms",
"http://www.w3.org/2003/g/data-view#": "dv",
"http://www.w3.org/XML/1998/namespace": "xmlns",
"urn:oasis:names:tc:opendocument:xmlns:animation:1.0": "animation",
"urn:oasis:names:tc:opendocument:xmlns:chart:1.0": "chart",
"urn:oasis:names:tc:opendocument:xmlns:config:1.0": "config",
"urn:oasis:names:tc:opendocument:xmlns:database:1.0": "database",
"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0": "datastyle",
"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0": "dr3d",
"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0": "drawing",
"urn:oasis:names:tc:opendocument:xmlns:form:1.0": "form",
"urn:oasis:names:tc:opendocument:xmlns:meta:1.0": "meta",
"urn:oasis:names:tc:opendocument:xmlns:office:1.0": "office",
"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0": "presentation",
"urn:oasis:names:tc:opendocument:xmlns:script:1.0": "script",
"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0": "smilc",
"urn:oasis:names:tc:opendocument:xmlns:style:1.0": "style",
"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0": "svgc",
"urn:oasis:names:tc:opendocument:xmlns:table:1.0": "table",
"urn:oasis:names:tc:opendocument:xmlns:text:1.0": "text",
"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0": "xslfoc"
},
typemap = {
"string": "const QString&",
"NCName": "const QString&",
"date": "const QString&",
"time": "const QString&",
"dateTime": "const QString&",
"duration": "const QString&",
"anyURI": "const QString&",
"ID": "const QString&",
"IDREF": "const QString&",
"IDREFS": "const QString&",
"QName": "const QString&",
"token": "const QString&",
"language": "const QString&",
"positiveInteger": "quint32",
"nonNegativeInteger": "quint32",
"integer": "qint32",
"decimal": "double"
},
args = arguments,
relaxngurl = args[1],
parser = new xmldom.RelaxNGParser(relaxngurl);
function out(string) {
"use strict";
runtime.log(string);
}
function toCamelCase(s) {
"use strict";
var str = "", i, up = true;
for (i = 0; i < s.length; i += 1) {
if (up) {
str += s.substr(i, 1).toUpperCase();
} else {
str += s.substr(i, 1);
}
up = false;
while (/\W/.test(s.substr(i + 1, 1))) {
up = true;
i += 1;
}
}
return str;
}
function getName(e) {
"use strict";
return toCamelCase(nsmap[e.a.ns]) + toCamelCase(e.text);
}
function getNames(e, names) {
"use strict";
if (e.name === "name") {
names.push(e);
} else if (e.name === "choice") {
getNames(e.e[0], names);
getNames(e.e[1], names);
}
}
function parseAttributes(e, att) {
"use strict";
var i, name;
if (e.name === "choice" || e.name === "interleave"
|| e.name === "group") {
for (i = 0; i < e.e.length; i += 1) {
parseAttributes(e.e[i], att);
}
} else if (e.name === "value") {
att.values.push(e.text);
} else if (e.name === "data") {
att.types.push(e.a.type);
} else if (e.name === "list") {
name = null; // todo
} else if (e.name === "empty") {
att.empty = true;
} else {
runtime.log("OOPS " + e.name);
throw null;
}
}
function writeAttributeSetter(name, type, a) {
"use strict";
var i, s = "";
out(" /**");
if (a.optional) {
out(" * Set optional attribute " + a.nsname + ".");
} else {
out(" * Set required attribute " + a.nsname + ".");
}
if (a.values.length > 0) {
s = "Choose one of these values: '" + a.values[0] + "'";
for (i = 1; i < a.values.length; i += 1) {
s += ", '" + a.values[i] + "'";
}
out(" * " + s + ".");
}
out(" */");
out(" inline void write" + name + "(" + type + " value) {");
out(" xml->addAttribute(\"" + a.nsname + "\", value);");
out(" }");
}
function writeAttribute(name, a) {
"use strict";
if (!a.optional) {
return;
}
var i, type, done = {}, needfallback = true;
for (i = 0; i < a.types.length; i += 1) {
needfallback = false;
type = typemap[a.types[i]] || a.types[i];
if (!done.hasOwnProperty(type)) {
done[type] = 1;
writeAttributeSetter(name, type, a);
}
}
if (a.values.indexOf("true") !== -1 && a.values.indexOf("false") !== -1 &&
done.hasOwnProperty("bool")) {
needfallback = false;
writeAttributeSetter(name, "bool", a);
}
if (needfallback) {
writeAttributeSetter(name, "const QString&", a);
}
}
function writeOptionalAttributes(atts) {
"use strict";
var name;
for (name in atts) {
if (atts.hasOwnProperty(name)) {
writeAttribute(name, atts[name]);
}
}
}
function writeFixedRequiredAttributes(atts) {
"use strict";
var name, a;
for (name in atts) {
if (atts.hasOwnProperty(name)) {
a = atts[name];
if (!a.optional && a.types.length === 0 && a.values.length === 1) {
out(" xml->addAttribute(\"" + a.nsname + "\", \"" +
a.values[0] + "\");");
}
}
}
}
function getRequiredAttributeArguments(atts) {
"use strict";
var name, a, s = "", type;
for (name in atts) {
if (atts.hasOwnProperty(name)) {
a = atts[name];
if (!a.optional && (a.types.length > 0 || a.values.length !== 1)) {
type = typemap[a.types[0]] || a.types[0] || "const QString&";
if (s) {
s += ", ";
}
s += type + " " + name.toLowerCase();
}
}
}
return s;
}
function getRequiredAttributeCall(atts) {
"use strict";
var name, a, s = "";
for (name in atts) {
if (atts.hasOwnProperty(name)) {
a = atts[name];
if (!a.optional && (a.types.length > 0 || a.values.length !== 1)) {
if (s) {
s += ", ";
}
s += name.toLowerCase();
}
}
}
return s;
}
function writeRequiredAttributesSetters(atts) {
"use strict";
var name, a;
for (name in atts) {
if (atts.hasOwnProperty(name)) {
a = atts[name];
if (!a.optional && (a.types.length > 0 || a.values.length !== 1)) {
out(" xml->addAttribute(\"" + a.nsname + "\", " +
name.toLowerCase() + ");");
}
}
}
}
function writeMembers(e, atts, optional) {
"use strict";
var ne, nsname, i, name, names;
if (e.name === "element") {
name = null;
} else if (e.name === "attribute") {
names = [];
getNames(e.e[0], names);
for (i = 0; i < names.length; i += 1) {
ne = names[i];
name = getName(ne);
if (!atts.hasOwnProperty(name)) {
nsname = nsmap[ne.a.ns] + ":" + ne.text;
atts[name] = {
nsname: nsname,
values: [],
types: [],
optional: optional,
empty: false
};
}
parseAttributes(e.e[1], atts[name]);
}
} else if (e.name === "choice") {
for (i = 0; i < e.e.length; i += 1) {
writeMembers(e.e[i], atts, true);
}
} else if (e.name === "interleave" || e.name === "group") {
for (i = 0; i < e.e.length; i += 1) {
writeMembers(e.e[i], atts, optional);
}
} else if (e.name === "oneOrMore") {
writeMembers(e.e[0], atts, optional);
} else if (e.name === "value") {
name = null; // todo
} else if (e.name === "data") {
name = null; // todo
} else if (e.name === "text") {
out(" void addTextNode(const QString& str) { xml->addTextNode(str); }");
} else if (e.name === "empty") {
name = null; // todo
} else {
runtime.log("OOPS " + e.name);
throw null;
}
}
function defineClass(e, parents, children) {
"use strict";
var c, p, i,
ne = e.e[0],
nsname = nsmap[ne.a.ns] + ":" + ne.text,
name = ne.cppname, atts = {};
out("/**");
out(" * Serialize a <" + nsname + "> element.");
out(" */");
out("class " + name + "Writer {");
for (c in children) {
if (children.hasOwnProperty(c) && c !== name) {
out("friend class " + c + "Writer;");
}
}
out("public:");
writeMembers(e.e[1], atts, false);
writeOptionalAttributes(atts);
e.requiredAttributes = getRequiredAttributeArguments(atts);
e.requiredAttributeCall = getRequiredAttributeCall(atts);
out("private:");
out(" inline void start(" + e.requiredAttributes + ") {");
out(" xml->startElement(\"" + nsname + "\");");
if (e.requiredAttributes) {
e.requiredAttributes = ", " + e.requiredAttributes;
}
writeFixedRequiredAttributes(atts);
writeRequiredAttributesSetters(atts);
out(" }");
out("public:");
out(" KoXmlWriter* const xml;");
for (p in parents) {
if (parents.hasOwnProperty(p)) {
out(" inline explicit " + name + "Writer(const " + p +
"Writer& p" + e.requiredAttributes + ");");
}
}
out(" inline explicit " + name + "Writer(KoXmlWriter* xml_" +
e.requiredAttributes +
") :xml(xml_) { start(" + e.requiredAttributeCall + "); }");
out(" void end() { xml->endElement(); }");
out(" void operator=(const " + name + "Writer&) { }");
out("};");
}
function defineConstructors(e, parents) {
"use strict";
var p,
ne = e.e[0],
nsname = nsmap[ne.a.ns] + ":" + ne.text,
name = ne.cppname;
for (p in parents) {
if (parents.hasOwnProperty(p)) {
out(name + "Writer::" + name + "Writer(const " + p +
"Writer& p" + e.requiredAttributes +
") :xml(p.xml) { start(" + e.requiredAttributeCall + "); }");
}
}
}
function getChildren(e, children) {
"use strict";
var name, i, names;
if (e.name === "element") {
names = [];
getNames(e.e[0], names);
for (i = 0; i < names.length; i += 1) {
children[names[i].cppname] = 1;
}
} else if (e.name === "choice" || e.name === "interleave"
|| e.name === "group") {
for (i = 0; i < e.e.length; i += 1) {
getChildren(e.e[i], children);
}
} else if (e.name === "oneOrMore") {
getChildren(e.e[0], children);
} else if (e.name === "attribute" || e.name === "value" ||
e.name === "data" || e.name === "text" || e.name === "empty") {
name = null; // ignore
} else {
runtime.log("OOPS " + e.name);
throw null;
}
}
function childrenToParents(childrenmap) {
"use strict";
var p, children, c, parents = {};
for (p in childrenmap) {
if (childrenmap.hasOwnProperty(p)) {
children = childrenmap[p];
for (c in children) {
if (children.hasOwnProperty(c)) {
if (!parents.hasOwnProperty(c)) {
parents[c] = {};
}
parents[c][p] = 1;
}
}
}
}
return parents;
}
function toCPP(elements) {
"use strict";
out("#include <KoXmlWriter.h>");
// first get a mapping for all the parents
var children = {}, parents = {}, i, j, ce, ec, name, names, c,
elementMap = {}, sortedElementNames = [];
for (i = 0; i < elements.length; i += 1) {
ce = elements[i];
if (ce.name !== "element") {
runtime.log("Error in parsed data.");
return;
}
names = [];
getNames(ce.e[0], names);
for (j = 0; j < names.length; j += 1) {
name = getName(names[j]);
while (elementMap.hasOwnProperty(name)) {
name = name + "_";
}
names[j].cppname = name;
ec = {e: [names[j], ce.e[1]]};
elementMap[name] = ec;
sortedElementNames.push(name);
}
}
sortedElementNames.sort();
for (i = 0; i < sortedElementNames.length; i += 1) {
name = sortedElementNames[i];
c = {};
getChildren(elementMap[name].e[1], c);
children[name] = c;
}
parents = childrenToParents(children);
for (i = 0; i < sortedElementNames.length; i += 1) {
name = sortedElementNames[i];
out("class " + name + "Writer;");
}
for (i = 0; i < sortedElementNames.length; i += 1) {
name = sortedElementNames[i];
defineClass(elementMap[name], parents[name], children[name]);
}
for (i = 0; i < sortedElementNames.length; i += 1) {
name = sortedElementNames[i];
defineConstructors(elementMap[name], parents[name]);
}
}
// load and parse the Relax NG
runtime.loadXML(relaxngurl, function (err, dom) {
"use strict";
var parser = new xmldom.RelaxNGParser();
if (err) {
runtime.log(err);
} else {
err = parser.parseRelaxNGDOM(dom);
if (err) {
runtime.log(err);
} else {
toCPP(parser.elements);
}
}
});