auto import from //depot/cupcake/@135843
diff --git a/tools/localize/XMLHandler.cpp b/tools/localize/XMLHandler.cpp
new file mode 100644
index 0000000..3fab211
--- /dev/null
+++ b/tools/localize/XMLHandler.cpp
@@ -0,0 +1,793 @@
+#include "XMLHandler.h"
+
+#include <algorithm>
+#include <expat.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define NS_SEPARATOR 1
+#define MORE_INDENT " "
+
+static string
+xml_text_escape(const string& s)
+{
+ string result;
+ const size_t N = s.length();
+ for (size_t i=0; i<N; i++) {
+ char c = s[i];
+ switch (c) {
+ case '<':
+ result += "<";
+ break;
+ case '>':
+ result += ">";
+ break;
+ case '&':
+ result += "&";
+ break;
+ default:
+ result += c;
+ break;
+ }
+ }
+ return result;
+}
+
+static string
+xml_attr_escape(const string& s)
+{
+ string result;
+ const size_t N = s.length();
+ for (size_t i=0; i<N; i++) {
+ char c = s[i];
+ switch (c) {
+ case '\"':
+ result += """;
+ break;
+ default:
+ result += c;
+ break;
+ }
+ }
+ return result;
+}
+
+XMLNamespaceMap::XMLNamespaceMap()
+{
+}
+
+XMLNamespaceMap::XMLNamespaceMap(char const*const* nspaces)
+
+{
+ while (*nspaces) {
+ m_map[nspaces[1]] = nspaces[0];
+ nspaces += 2;
+ }
+}
+
+string
+XMLNamespaceMap::Get(const string& ns) const
+{
+ if (ns == "xml") {
+ return ns;
+ }
+ map<string,string>::const_iterator it = m_map.find(ns);
+ if (it == m_map.end()) {
+ return "";
+ } else {
+ return it->second;
+ }
+}
+
+string
+XMLNamespaceMap::GetPrefix(const string& ns) const
+{
+ if (ns == "") {
+ return "";
+ }
+ map<string,string>::const_iterator it = m_map.find(ns);
+ if (it != m_map.end()) {
+ if (it->second == "") {
+ return "";
+ } else {
+ return it->second + ":";
+ }
+ } else {
+ return ":"; // invalid
+ }
+}
+
+void
+XMLNamespaceMap::AddToAttributes(vector<XMLAttribute>* attrs) const
+{
+ map<string,string>::const_iterator it;
+ for (it=m_map.begin(); it!=m_map.end(); it++) {
+ if (it->second == "xml") {
+ continue;
+ }
+ XMLAttribute attr;
+ if (it->second == "") {
+ attr.name = "xmlns";
+ } else {
+ attr.name = "xmlns:";
+ attr.name += it->second;
+ }
+ attr.value = it->first;
+ attrs->push_back(attr);
+ }
+}
+
+XMLAttribute::XMLAttribute()
+{
+}
+
+XMLAttribute::XMLAttribute(const XMLAttribute& that)
+ :ns(that.ns),
+ name(that.name),
+ value(that.value)
+{
+}
+
+XMLAttribute::XMLAttribute(string n, string na, string v)
+ :ns(n),
+ name(na),
+ value(v)
+{
+}
+
+XMLAttribute::~XMLAttribute()
+{
+}
+
+int
+XMLAttribute::Compare(const XMLAttribute& that) const
+{
+ if (ns != that.ns) {
+ return ns < that.ns ? -1 : 1;
+ }
+ if (name != that.name) {
+ return name < that.name ? -1 : 1;
+ }
+ return 0;
+}
+
+string
+XMLAttribute::Find(const vector<XMLAttribute>& list, const string& ns, const string& name,
+ const string& def)
+{
+ const size_t N = list.size();
+ for (size_t i=0; i<N; i++) {
+ const XMLAttribute& attr = list[i];
+ if (attr.ns == ns && attr.name == name) {
+ return attr.value;
+ }
+ }
+ return def;
+}
+
+struct xml_handler_data {
+ vector<XMLHandler*> stack;
+ XML_Parser parser;
+ vector<vector<XMLAttribute>*> attributes;
+ string filename;
+};
+
+XMLNode::XMLNode()
+{
+}
+
+XMLNode::~XMLNode()
+{
+// for_each(m_children.begin(), m_children.end(), delete_object<XMLNode>);
+}
+
+XMLNode*
+XMLNode::Clone() const
+{
+ switch (m_type) {
+ case ELEMENT: {
+ XMLNode* e = XMLNode::NewElement(m_pos, m_ns, m_name, m_attrs, m_pretty);
+ const size_t N = m_children.size();
+ for (size_t i=0; i<N; i++) {
+ e->m_children.push_back(m_children[i]->Clone());
+ }
+ return e;
+ }
+ case TEXT: {
+ return XMLNode::NewText(m_pos, m_text, m_pretty);
+ }
+ default:
+ return NULL;
+ }
+}
+
+XMLNode*
+XMLNode::NewElement(const SourcePos& pos, const string& ns, const string& name,
+ const vector<XMLAttribute>& attrs, int pretty)
+{
+ XMLNode* node = new XMLNode();
+ node->m_type = ELEMENT;
+ node->m_pretty = pretty;
+ node->m_pos = pos;
+ node->m_ns = ns;
+ node->m_name = name;
+ node->m_attrs = attrs;
+ return node;
+}
+
+XMLNode*
+XMLNode::NewText(const SourcePos& pos, const string& text, int pretty)
+{
+ XMLNode* node = new XMLNode();
+ node->m_type = TEXT;
+ node->m_pretty = pretty;
+ node->m_pos = pos;
+ node->m_text = text;
+ return node;
+}
+
+void
+XMLNode::SetPrettyRecursive(int value)
+{
+ m_pretty = value;
+ const size_t N = m_children.size();
+ for (size_t i=0; i<N; i++) {
+ m_children[i]->SetPrettyRecursive(value);
+ }
+}
+
+string
+XMLNode::ContentsToString(const XMLNamespaceMap& nspaces) const
+{
+ return contents_to_string(nspaces, "");
+}
+
+string
+XMLNode::ToString(const XMLNamespaceMap& nspaces) const
+{
+ return to_string(nspaces, "");
+}
+
+string
+XMLNode::OpenTagToString(const XMLNamespaceMap& nspaces, int pretty) const
+{
+ return open_tag_to_string(nspaces, "", pretty);
+}
+
+string
+XMLNode::contents_to_string(const XMLNamespaceMap& nspaces, const string& indent) const
+{
+ string result;
+ const size_t N = m_children.size();
+ for (size_t i=0; i<N; i++) {
+ const XMLNode* child = m_children[i];
+ switch (child->Type()) {
+ case ELEMENT:
+ if (m_pretty == PRETTY) {
+ result += '\n';
+ result += indent;
+ }
+ case TEXT:
+ result += child->to_string(nspaces, indent);
+ break;
+ }
+ }
+ return result;
+}
+
+string
+trim_string(const string& str)
+{
+ const char* p = str.c_str();
+ while (*p && isspace(*p)) {
+ p++;
+ }
+ const char* q = str.c_str() + str.length() - 1;
+ while (q > p && isspace(*q)) {
+ q--;
+ }
+ q++;
+ return string(p, q-p);
+}
+
+string
+XMLNode::open_tag_to_string(const XMLNamespaceMap& nspaces, const string& indent, int pretty) const
+{
+ if (m_type != ELEMENT) {
+ return "";
+ }
+ string result = "<";
+ result += nspaces.GetPrefix(m_ns);
+ result += m_name;
+
+ vector<XMLAttribute> attrs = m_attrs;
+
+ sort(attrs.begin(), attrs.end());
+
+ const size_t N = attrs.size();
+ for (size_t i=0; i<N; i++) {
+ const XMLAttribute& attr = attrs[i];
+ if (i == 0 || m_pretty == EXACT || pretty == EXACT) {
+ result += ' ';
+ }
+ else {
+ result += "\n";
+ result += indent;
+ result += MORE_INDENT;
+ result += MORE_INDENT;
+ }
+ result += nspaces.GetPrefix(attr.ns);
+ result += attr.name;
+ result += "=\"";
+ result += xml_attr_escape(attr.value);
+ result += '\"';
+ }
+
+ if (m_children.size() > 0) {
+ result += '>';
+ } else {
+ result += " />";
+ }
+ return result;
+}
+
+string
+XMLNode::to_string(const XMLNamespaceMap& nspaces, const string& indent) const
+{
+ switch (m_type)
+ {
+ case TEXT: {
+ if (m_pretty == EXACT) {
+ return xml_text_escape(m_text);
+ } else {
+ return xml_text_escape(trim_string(m_text));
+ }
+ }
+ case ELEMENT: {
+ string result = open_tag_to_string(nspaces, indent, PRETTY);
+
+ if (m_children.size() > 0) {
+ result += contents_to_string(nspaces, indent + MORE_INDENT);
+
+ if (m_pretty == PRETTY && m_children.size() > 0) {
+ result += '\n';
+ result += indent;
+ }
+
+ result += "</";
+ result += nspaces.GetPrefix(m_ns);
+ result += m_name;
+ result += '>';
+ }
+ return result;
+ }
+ default:
+ return "";
+ }
+}
+
+string
+XMLNode::CollapseTextContents() const
+{
+ if (m_type == TEXT) {
+ return m_text;
+ }
+ else if (m_type == ELEMENT) {
+ string result;
+
+ const size_t N=m_children.size();
+ for (size_t i=0; i<N; i++) {
+ result += m_children[i]->CollapseTextContents();
+ }
+
+ return result;
+ }
+ else {
+ return "";
+ }
+}
+
+vector<XMLNode*>
+XMLNode::GetElementsByName(const string& ns, const string& name) const
+{
+ vector<XMLNode*> result;
+ const size_t N=m_children.size();
+ for (size_t i=0; i<N; i++) {
+ XMLNode* child = m_children[i];
+ if (child->m_type == ELEMENT && child->m_ns == ns && child->m_name == name) {
+ result.push_back(child);
+ }
+ }
+ return result;
+}
+
+XMLNode*
+XMLNode::GetElementByNameAt(const string& ns, const string& name, size_t index) const
+{
+ vector<XMLNode*> result;
+ const size_t N=m_children.size();
+ for (size_t i=0; i<N; i++) {
+ XMLNode* child = m_children[i];
+ if (child->m_type == ELEMENT && child->m_ns == ns && child->m_name == name) {
+ if (index == 0) {
+ return child;
+ } else {
+ index--;
+ }
+ }
+ }
+ return NULL;
+}
+
+size_t
+XMLNode::CountElementsByName(const string& ns, const string& name) const
+{
+ size_t result = 0;
+ const size_t N=m_children.size();
+ for (size_t i=0; i<N; i++) {
+ XMLNode* child = m_children[i];
+ if (child->m_type == ELEMENT && child->m_ns == ns && child->m_name == name) {
+ result++;
+ }
+ }
+ return result;
+}
+
+string
+XMLNode::GetAttribute(const string& ns, const string& name, const string& def) const
+{
+ return XMLAttribute::Find(m_attrs, ns, name, def);
+}
+
+static void
+parse_namespace(const char* data, string* ns, string* name)
+{
+ const char* p = strchr(data, NS_SEPARATOR);
+ if (p != NULL) {
+ ns->assign(data, p-data);
+ name->assign(p+1);
+ } else {
+ ns->assign("");
+ name->assign(data);
+ }
+}
+
+static void
+convert_attrs(const char** in, vector<XMLAttribute>* out)
+{
+ while (*in) {
+ XMLAttribute attr;
+ parse_namespace(in[0], &attr.ns, &attr.name);
+ attr.value = in[1];
+ out->push_back(attr);
+ in += 2;
+ }
+}
+
+static bool
+list_contains(const vector<XMLHandler*>& stack, XMLHandler* handler)
+{
+ const size_t N = stack.size();
+ for (size_t i=0; i<N; i++) {
+ if (stack[i] == handler) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void XMLCALL
+start_element_handler(void *userData, const char *name, const char **attrs)
+{
+ xml_handler_data* data = (xml_handler_data*)userData;
+
+ XMLHandler* handler = data->stack[data->stack.size()-1];
+
+ SourcePos pos(data->filename, (int)XML_GetCurrentLineNumber(data->parser));
+ string nsString;
+ string nameString;
+ XMLHandler* next = handler;
+ vector<XMLAttribute> attributes;
+
+ parse_namespace(name, &nsString, &nameString);
+ convert_attrs(attrs, &attributes);
+
+ handler->OnStartElement(pos, nsString, nameString, attributes, &next);
+
+ if (next == NULL) {
+ next = handler;
+ }
+
+ if (next != handler) {
+ next->elementPos = pos;
+ next->elementNamespace = nsString;
+ next->elementName = nameString;
+ next->elementAttributes = attributes;
+ }
+
+ data->stack.push_back(next);
+}
+
+static void XMLCALL
+end_element_handler(void *userData, const char *name)
+{
+ xml_handler_data* data = (xml_handler_data*)userData;
+
+ XMLHandler* handler = data->stack[data->stack.size()-1];
+ data->stack.pop_back();
+
+ SourcePos pos(data->filename, (int)XML_GetCurrentLineNumber(data->parser));
+
+ if (!list_contains(data->stack, handler)) {
+ handler->OnDone(pos);
+ if (data->stack.size() > 1) {
+ // not top one
+ delete handler;
+ }
+ }
+
+ handler = data->stack[data->stack.size()-1];
+
+ string nsString;
+ string nameString;
+
+ parse_namespace(name, &nsString, &nameString);
+
+ handler->OnEndElement(pos, nsString, nameString);
+}
+
+static void XMLCALL
+text_handler(void *userData, const XML_Char *s, int len)
+{
+ xml_handler_data* data = (xml_handler_data*)userData;
+ XMLHandler* handler = data->stack[data->stack.size()-1];
+ SourcePos pos(data->filename, (int)XML_GetCurrentLineNumber(data->parser));
+ handler->OnText(pos, string(s, len));
+}
+
+static void XMLCALL
+comment_handler(void *userData, const char *comment)
+{
+ xml_handler_data* data = (xml_handler_data*)userData;
+ XMLHandler* handler = data->stack[data->stack.size()-1];
+ SourcePos pos(data->filename, (int)XML_GetCurrentLineNumber(data->parser));
+ handler->OnComment(pos, string(comment));
+}
+
+bool
+XMLHandler::ParseFile(const string& filename, XMLHandler* handler)
+{
+ char buf[16384];
+ int fd = open(filename.c_str(), O_RDONLY);
+ if (fd < 0) {
+ SourcePos(filename, -1).Error("Unable to open file for read: %s", strerror(errno));
+ return false;
+ }
+
+ XML_Parser parser = XML_ParserCreateNS(NULL, NS_SEPARATOR);
+ xml_handler_data state;
+ state.stack.push_back(handler);
+ state.parser = parser;
+ state.filename = filename;
+
+ XML_SetUserData(parser, &state);
+ XML_SetElementHandler(parser, start_element_handler, end_element_handler);
+ XML_SetCharacterDataHandler(parser, text_handler);
+ XML_SetCommentHandler(parser, comment_handler);
+
+ ssize_t len;
+ bool done;
+ do {
+ len = read(fd, buf, sizeof(buf));
+ done = len < (ssize_t)sizeof(buf);
+ if (len < 0) {
+ SourcePos(filename, -1).Error("Error reading file: %s\n", strerror(errno));
+ close(fd);
+ return false;
+ }
+ if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
+ SourcePos(filename, (int)XML_GetCurrentLineNumber(parser)).Error(
+ "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser)));
+ close(fd);
+ return false;
+ }
+ } while (!done);
+
+ XML_ParserFree(parser);
+
+ close(fd);
+
+ return true;
+}
+
+bool
+XMLHandler::ParseString(const string& filename, const string& text, XMLHandler* handler)
+{
+ XML_Parser parser = XML_ParserCreateNS(NULL, NS_SEPARATOR);
+ xml_handler_data state;
+ state.stack.push_back(handler);
+ state.parser = parser;
+ state.filename = filename;
+
+ XML_SetUserData(parser, &state);
+ XML_SetElementHandler(parser, start_element_handler, end_element_handler);
+ XML_SetCharacterDataHandler(parser, text_handler);
+ XML_SetCommentHandler(parser, comment_handler);
+
+ if (XML_Parse(parser, text.c_str(), text.size(), true) == XML_STATUS_ERROR) {
+ SourcePos(filename, (int)XML_GetCurrentLineNumber(parser)).Error(
+ "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser)));
+ return false;
+ }
+
+ XML_ParserFree(parser);
+
+ return true;
+}
+
+XMLHandler::XMLHandler()
+{
+}
+
+XMLHandler::~XMLHandler()
+{
+}
+
+int
+XMLHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+ const vector<XMLAttribute>& attrs, XMLHandler** next)
+{
+ return 0;
+}
+
+int
+XMLHandler::OnEndElement(const SourcePos& pos, const string& ns, const string& name)
+{
+ return 0;
+}
+
+int
+XMLHandler::OnText(const SourcePos& pos, const string& text)
+{
+ return 0;
+}
+
+int
+XMLHandler::OnComment(const SourcePos& pos, const string& text)
+{
+ return 0;
+}
+
+int
+XMLHandler::OnDone(const SourcePos& pos)
+{
+ return 0;
+}
+
+TopElementHandler::TopElementHandler(const string& ns, const string& name, XMLHandler* next)
+ :m_ns(ns),
+ m_name(name),
+ m_next(next)
+{
+}
+
+int
+TopElementHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+ const vector<XMLAttribute>& attrs, XMLHandler** next)
+{
+ *next = m_next;
+ return 0;
+}
+
+int
+TopElementHandler::OnEndElement(const SourcePos& pos, const string& ns, const string& name)
+{
+ return 0;
+}
+
+int
+TopElementHandler::OnText(const SourcePos& pos, const string& text)
+{
+ return 0;
+}
+
+int
+TopElementHandler::OnDone(const SourcePos& pos)
+{
+ return 0;
+}
+
+
+NodeHandler::NodeHandler(XMLNode* root, int pretty)
+ :m_root(root),
+ m_pretty(pretty)
+{
+ if (root != NULL) {
+ m_nodes.push_back(root);
+ }
+}
+
+NodeHandler::~NodeHandler()
+{
+}
+
+int
+NodeHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
+ const vector<XMLAttribute>& attrs, XMLHandler** next)
+{
+ int pretty;
+ if (XMLAttribute::Find(attrs, XMLNS_XMLNS, "space", "") == "preserve") {
+ pretty = XMLNode::EXACT;
+ } else {
+ if (m_root == NULL) {
+ pretty = m_pretty;
+ } else {
+ pretty = m_nodes[m_nodes.size()-1]->Pretty();
+ }
+ }
+ XMLNode* n = XMLNode::NewElement(pos, ns, name, attrs, pretty);
+ if (m_root == NULL) {
+ m_root = n;
+ } else {
+ m_nodes[m_nodes.size()-1]->EditChildren().push_back(n);
+ }
+ m_nodes.push_back(n);
+ return 0;
+}
+
+int
+NodeHandler::OnEndElement(const SourcePos& pos, const string& ns, const string& name)
+{
+ m_nodes.pop_back();
+ return 0;
+}
+
+int
+NodeHandler::OnText(const SourcePos& pos, const string& text)
+{
+ if (m_root == NULL) {
+ return 1;
+ }
+ XMLNode* n = XMLNode::NewText(pos, text, m_nodes[m_nodes.size()-1]->Pretty());
+ m_nodes[m_nodes.size()-1]->EditChildren().push_back(n);
+ return 0;
+}
+
+int
+NodeHandler::OnComment(const SourcePos& pos, const string& text)
+{
+ return 0;
+}
+
+int
+NodeHandler::OnDone(const SourcePos& pos)
+{
+ return 0;
+}
+
+XMLNode*
+NodeHandler::ParseFile(const string& filename, int pretty)
+{
+ NodeHandler handler(NULL, pretty);
+ if (!XMLHandler::ParseFile(filename, &handler)) {
+ fprintf(stderr, "error parsing file: %s\n", filename.c_str());
+ return NULL;
+ }
+ return handler.Root();
+}
+
+XMLNode*
+NodeHandler::ParseString(const string& filename, const string& text, int pretty)
+{
+ NodeHandler handler(NULL, pretty);
+ if (!XMLHandler::ParseString(filename, text, &handler)) {
+ fprintf(stderr, "error parsing file: %s\n", filename.c_str());
+ return NULL;
+ }
+ return handler.Root();
+}
+
+