• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ArrayHandler(ValuesFile * vf,int version,const string & versionString,const string & id)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
OnStartElement(const SourcePos & pos,const string & ns,const string & name,const vector<XMLAttribute> & attrs,XMLHandler ** next)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
OnText(const SourcePos & pos,const string & text)75 ArrayHandler::OnText(const SourcePos& pos, const string& text)
76 {
77     return 0;
78 }
79 
80 int
OnComment(const SourcePos & pos,const string & text)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 
ValuesHandler(ValuesFile * vf,int version,const string & versionString)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
OnStartElement(const SourcePos & pos,const string & ns,const string & name,const vector<XMLAttribute> & attrs,XMLHandler ** next)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
OnText(const SourcePos & pos,const string & text)133 ValuesHandler::OnText(const SourcePos& pos, const string& text)
134 {
135     return 0;
136 }
137 
138 int
OnComment(const SourcePos & pos,const string & text)139 ValuesHandler::OnComment(const SourcePos& pos, const string& text)
140 {
141     m_comment += text;
142     return 0;
143 }
144 
145 // =====================================================================================
ValuesFile(const Configuration & config)146 ValuesFile::ValuesFile(const Configuration& config)
147     :m_config(config),
148      m_strings(),
149      m_arrays()
150 {
151 }
152 
~ValuesFile()153 ValuesFile::~ValuesFile()
154 {
155 }
156 
157 ValuesFile*
ParseFile(const string & filename,const Configuration & config,int version,const string & versionString)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*
ParseString(const string & filename,const string & text,const Configuration & config,int version,const string & versionString)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&
GetConfiguration() const182 ValuesFile::GetConfiguration() const
183 {
184     return m_config;
185 }
186 
187 void
AddString(const StringResource & str)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>
GetStrings() const198 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*
ToXMLNode() const211 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
ToString() const257 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