| #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; |
| } |
| |