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 11 using namespace std; 12 13 const char* const ANDROID_XMLNS = "http://schemas.android.com/apk/res/android"; 14 const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2"; 15 16 const char *const NS_MAP[] = { 17 "android", ANDROID_XMLNS, 18 "xliff", XLIFF_XMLNS, 19 NULL, NULL 20 }; 21 22 const XMLNamespaceMap ANDROID_NAMESPACES(NS_MAP); 23 24 25 // ===================================================================================== 26 class ArrayHandler : public XMLHandler 27 { 28 public: 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 36 private: 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 45 ArrayHandler::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 55 int 56 ArrayHandler::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 74 int 75 ArrayHandler::OnText(const SourcePos& pos, const string& text) 76 { 77 return 0; 78 } 79 80 int 81 ArrayHandler::OnComment(const SourcePos& pos, const string& text) 82 { 83 m_comment += text; 84 return 0; 85 } 86 87 // ===================================================================================== 88 class ValuesHandler : public XMLHandler 89 { 90 public: 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 98 private: 99 ValuesFile* m_vf; 100 int m_version; 101 string m_versionString; 102 string m_comment; 103 }; 104 105 ValuesHandler::ValuesHandler(ValuesFile* vf, int version, const string& versionString) 106 :m_vf(vf), 107 m_version(version), 108 m_versionString(versionString) 109 { 110 } 111 112 int 113 ValuesHandler::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 132 int 133 ValuesHandler::OnText(const SourcePos& pos, const string& text) 134 { 135 return 0; 136 } 137 138 int 139 ValuesHandler::OnComment(const SourcePos& pos, const string& text) 140 { 141 m_comment += text; 142 return 0; 143 } 144 145 // ===================================================================================== 146 ValuesFile::ValuesFile(const Configuration& config) 147 :m_config(config), 148 m_strings(), 149 m_arrays() 150 { 151 } 152 153 ValuesFile::~ValuesFile() 154 { 155 } 156 157 ValuesFile* 158 ValuesFile::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 169 ValuesFile* 170 ValuesFile::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 181 const Configuration& 182 ValuesFile::GetConfiguration() const 183 { 184 return m_config; 185 } 186 187 void 188 ValuesFile::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 197 set<StringResource> 198 ValuesFile::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 210 XMLNode* 211 ValuesFile::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 256 string 257 ValuesFile::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