1#include "ValuesFile.h" 2 3#include "XMLHandler.h" 4 5#include <algorithm> 6#include <fcntl.h> 7#include <expat.h> 8#include <unistd.h> 9#include <errno.h> 10 11using namespace std; 12 13const char* const ANDROID_XMLNS = "http://schemas.android.com/apk/res/android"; 14const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2"; 15 16const char *const NS_MAP[] = { 17 "android", ANDROID_XMLNS, 18 "xliff", XLIFF_XMLNS, 19 NULL, NULL 20}; 21 22const XMLNamespaceMap ANDROID_NAMESPACES(NS_MAP); 23 24 25// ===================================================================================== 26class ArrayHandler : public XMLHandler 27{ 28public: 29 ArrayHandler(ValuesFile* vf, int version, const string& versionString, const string& id); 30 31 virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name, 32 const vector<XMLAttribute>& attrs, XMLHandler** next); 33 virtual int OnText(const SourcePos& pos, const string& text); 34 virtual int OnComment(const SourcePos& pos, const string& text); 35 36private: 37 ValuesFile* m_vf; 38 int m_version; 39 int m_index; 40 string m_versionString; 41 string m_id; 42 string m_comment; 43}; 44 45ArrayHandler::ArrayHandler(ValuesFile* vf, int version, const string& versionString, 46 const string& id) 47 :m_vf(vf), 48 m_version(version), 49 m_index(0), 50 m_versionString(versionString), 51 m_id(id) 52{ 53} 54 55int 56ArrayHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name, 57 const vector<XMLAttribute>& attrs, XMLHandler** next) 58{ 59 if (ns == "" && name == "item") { 60 XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT); 61 m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(), 62 m_id, m_index, node, m_version, m_versionString, 63 trim_string(m_comment))); 64 *next = new NodeHandler(node, XMLNode::EXACT); 65 m_index++; 66 m_comment = ""; 67 return 0; 68 } else { 69 pos.Error("invalid <%s> element inside <array>\n", name.c_str()); 70 return 1; 71 } 72} 73 74int 75ArrayHandler::OnText(const SourcePos& pos, const string& text) 76{ 77 return 0; 78} 79 80int 81ArrayHandler::OnComment(const SourcePos& pos, const string& text) 82{ 83 m_comment += text; 84 return 0; 85} 86 87// ===================================================================================== 88class ValuesHandler : public XMLHandler 89{ 90public: 91 ValuesHandler(ValuesFile* vf, int version, const string& versionString); 92 93 virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name, 94 const vector<XMLAttribute>& attrs, XMLHandler** next); 95 virtual int OnText(const SourcePos& pos, const string& text); 96 virtual int OnComment(const SourcePos& pos, const string& text); 97 98private: 99 ValuesFile* m_vf; 100 int m_version; 101 string m_versionString; 102 string m_comment; 103}; 104 105ValuesHandler::ValuesHandler(ValuesFile* vf, int version, const string& versionString) 106 :m_vf(vf), 107 m_version(version), 108 m_versionString(versionString) 109{ 110} 111 112int 113ValuesHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name, 114 const vector<XMLAttribute>& attrs, XMLHandler** next) 115{ 116 if (ns == "" && name == "string") { 117 string id = XMLAttribute::Find(attrs, "", "name", ""); 118 XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT); 119 m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(), 120 id, -1, node, m_version, m_versionString, 121 trim_string(m_comment))); 122 *next = new NodeHandler(node, XMLNode::EXACT); 123 } 124 else if (ns == "" && name == "array") { 125 string id = XMLAttribute::Find(attrs, "", "name", ""); 126 *next = new ArrayHandler(m_vf, m_version, m_versionString, id); 127 } 128 m_comment = ""; 129 return 0; 130} 131 132int 133ValuesHandler::OnText(const SourcePos& pos, const string& text) 134{ 135 return 0; 136} 137 138int 139ValuesHandler::OnComment(const SourcePos& pos, const string& text) 140{ 141 m_comment += text; 142 return 0; 143} 144 145// ===================================================================================== 146ValuesFile::ValuesFile(const Configuration& config) 147 :m_config(config), 148 m_strings(), 149 m_arrays() 150{ 151} 152 153ValuesFile::~ValuesFile() 154{ 155} 156 157ValuesFile* 158ValuesFile::ParseFile(const string& filename, const Configuration& config, 159 int version, const string& versionString) 160{ 161 ValuesFile* result = new ValuesFile(config); 162 163 TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString)); 164 XMLHandler::ParseFile(filename, &top); 165 166 return result; 167} 168 169ValuesFile* 170ValuesFile::ParseString(const string& filename, const string& text, const Configuration& config, 171 int version, const string& versionString) 172{ 173 ValuesFile* result = new ValuesFile(config); 174 175 TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString)); 176 XMLHandler::ParseString(filename, text, &top); 177 178 return result; 179} 180 181const Configuration& 182ValuesFile::GetConfiguration() const 183{ 184 return m_config; 185} 186 187void 188ValuesFile::AddString(const StringResource& str) 189{ 190 if (str.index < 0) { 191 m_strings.insert(str); 192 } else { 193 m_arrays[str.id].insert(str); 194 } 195} 196 197set<StringResource> 198ValuesFile::GetStrings() const 199{ 200 set<StringResource> result = m_strings; 201 202 for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin(); 203 it != m_arrays.end(); it++) { 204 result.insert(it->second.begin(), it->second.end()); 205 } 206 207 return result; 208} 209 210XMLNode* 211ValuesFile::ToXMLNode() const 212{ 213 XMLNode* root; 214 215 // <resources> 216 { 217 vector<XMLAttribute> attrs; 218 ANDROID_NAMESPACES.AddToAttributes(&attrs); 219 root = XMLNode::NewElement(GENERATED_POS, "", "resources", attrs, XMLNode::PRETTY); 220 } 221 222 // <array> 223 for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin(); 224 it != m_arrays.end(); it++) { 225 vector<XMLAttribute> arrayAttrs; 226 arrayAttrs.push_back(XMLAttribute("", "name", it->first)); 227 const set<StringResource>& items = it->second; 228 XMLNode* arrayNode = XMLNode::NewElement(items.begin()->pos, "", "array", arrayAttrs, 229 XMLNode::PRETTY); 230 root->EditChildren().push_back(arrayNode); 231 232 // <item> 233 for (set<StringResource>::const_iterator item = items.begin(); 234 item != items.end(); item++) { 235 XMLNode* itemNode = item->value->Clone(); 236 itemNode->SetName("", "item"); 237 itemNode->EditAttributes().clear(); 238 arrayNode->EditChildren().push_back(itemNode); 239 } 240 } 241 242 // <string> 243 for (set<StringResource>::const_iterator it=m_strings.begin(); it!=m_strings.end(); it++) { 244 const StringResource& str = *it; 245 vector<XMLAttribute> attrs; 246 XMLNode* strNode = str.value->Clone(); 247 strNode->SetName("", "string"); 248 strNode->EditAttributes().clear(); 249 strNode->EditAttributes().push_back(XMLAttribute("", "name", str.id)); 250 root->EditChildren().push_back(strNode); 251 } 252 253 return root; 254} 255 256string 257ValuesFile::ToString() const 258{ 259 XMLNode* xml = ToXMLNode(); 260 string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; 261 s += xml->ToString(ANDROID_NAMESPACES); 262 delete xml; 263 s += '\n'; 264 return s; 265} 266 267