#include "ValuesFile.h" #include "XMLHandler.h" #include <algorithm> #include <fcntl.h> #include <expat.h> #include <unistd.h> #include <errno.h> using namespace std; const char* const ANDROID_XMLNS = "http://schemas.android.com/apk/res/android"; const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2"; const char *const NS_MAP[] = { "android", ANDROID_XMLNS, "xliff", XLIFF_XMLNS, NULL, NULL }; const XMLNamespaceMap ANDROID_NAMESPACES(NS_MAP); // ===================================================================================== class ArrayHandler : public XMLHandler { public: ArrayHandler(ValuesFile* vf, int version, const string& versionString, const string& id); virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name, const vector<XMLAttribute>& attrs, XMLHandler** next); virtual int OnText(const SourcePos& pos, const string& text); virtual int OnComment(const SourcePos& pos, const string& text); private: ValuesFile* m_vf; int m_version; int m_index; string m_versionString; string m_id; string m_comment; }; ArrayHandler::ArrayHandler(ValuesFile* vf, int version, const string& versionString, const string& id) :m_vf(vf), m_version(version), m_index(0), m_versionString(versionString), m_id(id) { } int ArrayHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name, const vector<XMLAttribute>& attrs, XMLHandler** next) { if (ns == "" && name == "item") { XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT); m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(), m_id, m_index, node, m_version, m_versionString, trim_string(m_comment))); *next = new NodeHandler(node, XMLNode::EXACT); m_index++; m_comment = ""; return 0; } else { pos.Error("invalid <%s> element inside <array>\n", name.c_str()); return 1; } } int ArrayHandler::OnText(const SourcePos& pos, const string& text) { return 0; } int ArrayHandler::OnComment(const SourcePos& pos, const string& text) { m_comment += text; return 0; } // ===================================================================================== class ValuesHandler : public XMLHandler { public: ValuesHandler(ValuesFile* vf, int version, const string& versionString); virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name, const vector<XMLAttribute>& attrs, XMLHandler** next); virtual int OnText(const SourcePos& pos, const string& text); virtual int OnComment(const SourcePos& pos, const string& text); private: ValuesFile* m_vf; int m_version; string m_versionString; string m_comment; }; ValuesHandler::ValuesHandler(ValuesFile* vf, int version, const string& versionString) :m_vf(vf), m_version(version), m_versionString(versionString) { } int ValuesHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name, const vector<XMLAttribute>& attrs, XMLHandler** next) { if (ns == "" && name == "string") { string id = XMLAttribute::Find(attrs, "", "name", ""); XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT); m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(), id, -1, node, m_version, m_versionString, trim_string(m_comment))); *next = new NodeHandler(node, XMLNode::EXACT); } else if (ns == "" && name == "array") { string id = XMLAttribute::Find(attrs, "", "name", ""); *next = new ArrayHandler(m_vf, m_version, m_versionString, id); } m_comment = ""; return 0; } int ValuesHandler::OnText(const SourcePos& pos, const string& text) { return 0; } int ValuesHandler::OnComment(const SourcePos& pos, const string& text) { m_comment += text; return 0; } // ===================================================================================== ValuesFile::ValuesFile(const Configuration& config) :m_config(config), m_strings(), m_arrays() { } ValuesFile::~ValuesFile() { } ValuesFile* ValuesFile::ParseFile(const string& filename, const Configuration& config, int version, const string& versionString) { ValuesFile* result = new ValuesFile(config); TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString)); XMLHandler::ParseFile(filename, &top); return result; } ValuesFile* ValuesFile::ParseString(const string& filename, const string& text, const Configuration& config, int version, const string& versionString) { ValuesFile* result = new ValuesFile(config); TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString)); XMLHandler::ParseString(filename, text, &top); return result; } const Configuration& ValuesFile::GetConfiguration() const { return m_config; } void ValuesFile::AddString(const StringResource& str) { if (str.index < 0) { m_strings.insert(str); } else { m_arrays[str.id].insert(str); } } set<StringResource> ValuesFile::GetStrings() const { set<StringResource> result = m_strings; for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin(); it != m_arrays.end(); it++) { result.insert(it->second.begin(), it->second.end()); } return result; } XMLNode* ValuesFile::ToXMLNode() const { XMLNode* root; // <resources> { vector<XMLAttribute> attrs; ANDROID_NAMESPACES.AddToAttributes(&attrs); root = XMLNode::NewElement(GENERATED_POS, "", "resources", attrs, XMLNode::PRETTY); } // <array> for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin(); it != m_arrays.end(); it++) { vector<XMLAttribute> arrayAttrs; arrayAttrs.push_back(XMLAttribute("", "name", it->first)); const set<StringResource>& items = it->second; XMLNode* arrayNode = XMLNode::NewElement(items.begin()->pos, "", "array", arrayAttrs, XMLNode::PRETTY); root->EditChildren().push_back(arrayNode); // <item> for (set<StringResource>::const_iterator item = items.begin(); item != items.end(); item++) { XMLNode* itemNode = item->value->Clone(); itemNode->SetName("", "item"); itemNode->EditAttributes().clear(); arrayNode->EditChildren().push_back(itemNode); } } // <string> for (set<StringResource>::const_iterator it=m_strings.begin(); it!=m_strings.end(); it++) { const StringResource& str = *it; vector<XMLAttribute> attrs; XMLNode* strNode = str.value->Clone(); strNode->SetName("", "string"); strNode->EditAttributes().clear(); strNode->EditAttributes().push_back(XMLAttribute("", "name", str.id)); root->EditChildren().push_back(strNode); } return root; } string ValuesFile::ToString() const { XMLNode* xml = ToXMLNode(); string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; s += xml->ToString(ANDROID_NAMESPACES); delete xml; s += '\n'; return s; }