1 //
2 // Copyright 2005 The Android Open Source Project
3 //
4 // Preferences file access.
5 //
6
7 // For compilers that support precompilation, include "wx/wx.h".
8 #include "wx/wxprec.h"
9 // Otherwise, include all standard headers
10 #ifndef WX_PRECOMP
11 //# include "wx/wx.h"
12 # include "wx/string.h"
13 #endif
14
15 #include "Preferences.h"
16
17 #include "utils.h"
18 #include "tinyxml.h"
19
20 static const char* kName = "name";
21 static const char* kValue = "value";
22
23
24 /*
25 * Load from a file.
26 */
Load(const char * fileName)27 bool Preferences::Load(const char* fileName)
28 {
29 assert(fileName != NULL);
30 printf("SimPref: reading preferences file '%s'\n", fileName);
31
32 // throw out any existing stuff
33 delete mpDoc;
34
35 mpDoc = new TiXmlDocument;
36 if (mpDoc == NULL)
37 return false;
38
39 if (!mpDoc->LoadFile(fileName)) {
40 fprintf(stderr, "SimPref: ERROR: failed loading '%s'\n", fileName);
41 if (mpDoc->ErrorRow() != 0)
42 fprintf(stderr, " XML: %s (row=%d col=%d)\n",
43 mpDoc->ErrorDesc(), mpDoc->ErrorRow(), mpDoc->ErrorCol());
44 else
45 fprintf(stderr, " XML: %s\n", mpDoc->ErrorDesc());
46 goto fail;
47 }
48
49 TiXmlNode* pPrefs;
50 pPrefs = mpDoc->FirstChild("prefs");
51 if (pPrefs == NULL) {
52 fprintf(stderr, "SimPref: ERROR: could not find <prefs> in '%s'\n",
53 fileName);
54 goto fail;
55 }
56
57 // set defaults for anything we haven't set explicitly
58 SetDefaults();
59
60 return true;
61
62 fail:
63 delete mpDoc;
64 mpDoc = NULL;
65 return false;
66 }
67
68 /*
69 * Save to a file.
70 */
Save(const char * fileName)71 bool Preferences::Save(const char* fileName)
72 {
73 assert(fileName != NULL);
74
75 if (mpDoc == NULL)
76 return false;
77
78 if (!mpDoc->SaveFile(fileName)) {
79 fprintf(stderr, "SimPref: ERROR: failed saving '%s': %s\n",
80 fileName, mpDoc->ErrorDesc());
81 return false;
82 }
83
84 mDirty = false;
85
86 return true;
87 }
88
89 /*
90 * Create an empty collection of preferences.
91 */
Create(void)92 bool Preferences::Create(void)
93 {
94 static const char* docBase =
95 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
96 "<!-- Android device simulator preferences -->\n"
97 "<!-- This file is updated by the simulator -->\n"
98 "<prefs>\n"
99 "</prefs>\n";
100
101 // throw out any existing stuff
102 delete mpDoc;
103
104 // alloc and initialize
105 mpDoc = new TiXmlDocument;
106 if (mpDoc == NULL)
107 return false;
108
109 if (!mpDoc->Parse(docBase)) {
110 fprintf(stderr, "SimPref: bad docBase: %s\n", mpDoc->ErrorDesc());
111 return false;
112 }
113
114 SetDefaults();
115 mDirty = true; // should already be, mbut make sure
116 return true;
117 }
118
119 /*
120 * Add default values to XML doc.
121 *
122 * This isn't strictly necessary, because the functions that are interested
123 * in the preferences can set appropriate defaults themselves when the
124 * "get" function returns "false". However, in some cases a preference
125 * can be interesting to more than one function, and you either have to
126 * cut & paste the default value or write a "get default for xxx" function.
127 *
128 * We want this to work even if they already have an older config file, so
129 * this only sets values that don't already exist.
130 */
SetDefaults(void)131 void Preferences::SetDefaults(void)
132 {
133 /* table of default values */
134 static const struct {
135 const char* type;
136 const char* name;
137 const char* value;
138 } kDefault[] = {
139 { "pref", "auto-power-on", "true" },
140 { "pref", "debug", "false" },
141 { "pref", "valgrind", "false" },
142 { "pref", "check-jni", "true" },
143 { "pref", "enable-sound", "true" },
144 { "pref", "enable-fake-camera", "true" },
145 { "pref", "java-vm", "Dalvik" },
146 /* goobuntu dapper needed LD_ASSUME_KERNEL or gdb choked badly */
147 { "pref", "ld-assume-kernel", "" /*2.4.19*/ },
148 { "pref", "launch-command",
149 "xterm -geom 80x60+10+10 -sb -title Simulator -e" },
150 { "pref", "launch-wrapper-args", "-wait" },
151 };
152 TiXmlNode* pPrefs;
153
154 assert(mpDoc != NULL);
155
156 pPrefs = mpDoc->FirstChild("prefs");
157
158 /*
159 * Look up the name. If it doesn't exist, add it.
160 */
161 for (int i = 0; i < NELEM(kDefault); i++) {
162 TiXmlNode* pNode = _FindNode(kDefault[i].type, kDefault[i].name);
163
164 if (pNode == NULL) {
165 TiXmlElement elem(kDefault[i].type);
166 elem.SetAttribute(kName, kDefault[i].name);
167 elem.SetAttribute(kValue, kDefault[i].value);
168 pPrefs->InsertEndChild(elem);
169
170 printf("SimPref: added default <%s> '%s'='%s'\n",
171 kDefault[i].type, kDefault[i].name, kDefault[i].value);
172 } else {
173 printf("SimPref: found existing <%s> '%s'\n",
174 kDefault[i].type, kDefault[i].name);
175 }
176 }
177 }
178
get_next_node(TiXmlNode * pNode)179 static TiXmlNode* get_next_node(TiXmlNode* pNode)
180 {
181 if (!pNode->NoChildren())
182 {
183 pNode = pNode->FirstChild();
184 }
185 else if (pNode->NoChildren() &&
186 (pNode->NextSibling() == NULL))
187 {
188 pNode = pNode->Parent()->NextSibling();
189 }
190 else
191 {
192 pNode = pNode->NextSibling();
193 }
194 return pNode;
195 }
196
197 /*
198 * Returns the node with element type and name specified
199 *
200 * WARNING: this searches through the tree and returns the first matching
201 * node.
202 */
_FindNode(const char * type,const char * str) const203 TiXmlNode* Preferences::_FindNode(const char* type, const char* str) const
204 {
205 assert((type != NULL) && (str != NULL));
206 TiXmlNode* pRoot;
207 TiXmlNode* pNode;
208
209 pRoot = mpDoc->FirstChild("prefs");
210 assert(pRoot != NULL);
211
212 for (pNode = pRoot->FirstChild(); pNode != NULL;)
213 {
214 if (pNode->Type() != TiXmlNode::ELEMENT ||
215 strcasecmp(pNode->Value(), type) != 0)
216 {
217 pNode = get_next_node(pNode);
218 continue;
219 }
220
221 TiXmlElement* pElem = pNode->ToElement();
222 assert(pElem != NULL);
223
224 const char* name = pElem->Attribute(kName);
225
226 /* 1. If the name is blank, something is wrong with the config file
227 * 2. If the name matches the passed in string, we found the node
228 * 3. If the node has children, descend another level
229 * 4. If there are no children and no siblings of the node, go up a level
230 * 5. Otherwise, grab the next sibling
231 */
232 if (name == NULL)
233 {
234 fprintf(stderr, "WARNING: found <%s> without name\n", type);
235 continue;
236 }
237 else if (strcasecmp(name, str) == 0)
238 {
239 return pNode;
240 }
241 else
242 {
243 pNode = get_next_node(pNode);
244 }
245 }
246
247 return NULL;
248 }
249
250 /*
251 * Locate the specified preference.
252 */
FindPref(const char * str) const253 TiXmlNode* Preferences::FindPref(const char* str) const
254 {
255 TiXmlNode* pNode = _FindNode("pref", str);
256 return pNode;
257 }
258
259 /*
260 * Like FindPref(), but returns a TiXmlElement.
261 */
FindPrefElement(const char * str) const262 TiXmlElement* Preferences::FindPrefElement(const char* str) const
263 {
264 TiXmlNode* pNode;
265
266 pNode = FindPref(str);
267 if (pNode != NULL)
268 return pNode->ToElement();
269 return NULL;
270 }
271
272 /*
273 * Add a new preference entry with a blank entry for value. Returns a
274 * pointer to the new element.
275 */
AddPref(const char * str)276 TiXmlElement* Preferences::AddPref(const char* str)
277 {
278 assert(FindPref(str) == NULL);
279
280 TiXmlNode* pPrefs;
281
282 pPrefs = mpDoc->FirstChild("prefs");
283 assert(pPrefs != NULL);
284
285 TiXmlElement elem("pref");
286 elem.SetAttribute(kName, str);
287 elem.SetAttribute(kValue, "");
288 pPrefs->InsertEndChild(elem);
289
290 TiXmlNode* pNewPref = FindPref(str);
291 return pNewPref->ToElement();
292 }
293
294 /*
295 * Remove a node from the tree
296 */
_RemoveNode(TiXmlNode * pNode)297 bool Preferences::_RemoveNode(TiXmlNode* pNode)
298 {
299 if (pNode == NULL)
300 return false;
301
302 TiXmlNode* pParent = pNode->Parent();
303 if (pParent == NULL)
304 return false;
305
306 pParent->RemoveChild(pNode);
307 mDirty = true;
308 return true;
309 }
310
311 /*
312 * Remove a preference entry.
313 */
RemovePref(const char * delName)314 bool Preferences::RemovePref(const char* delName)
315 {
316 return _RemoveNode(FindPref(delName));
317 }
318
319 /*
320 * Test for existence.
321 */
Exists(const char * name) const322 bool Preferences::Exists(const char* name) const
323 {
324 TiXmlElement* pElem = FindPrefElement(name);
325 return (pElem != NULL);
326 }
327
328 /*
329 * Internal implemenations for getting values
330 */
_GetBool(TiXmlElement * pElem,bool * pVal) const331 bool Preferences::_GetBool(TiXmlElement* pElem, bool* pVal) const
332 {
333 if (pElem != NULL)
334 {
335 const char* str = pElem->Attribute(kValue);
336 if (str != NULL)
337 {
338 if (strcasecmp(str, "true") == 0)
339 *pVal = true;
340 else if (strcasecmp(str, "false") == 0)
341 *pVal = false;
342 else
343 {
344 printf("SimPref: evaluating as bool name='%s' val='%s'\n",
345 pElem->Attribute(kName), str);
346 return false;
347 }
348 return true;
349 }
350 }
351 return false;
352 }
353
_GetInt(TiXmlElement * pElem,int * pInt) const354 bool Preferences::_GetInt(TiXmlElement* pElem, int* pInt) const
355 {
356 int val;
357 if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) {
358 *pInt = val;
359 return true;
360 }
361 return false;
362 }
363
_GetDouble(TiXmlElement * pElem,double * pDouble) const364 bool Preferences::_GetDouble(TiXmlElement* pElem, double* pDouble) const
365 {
366 double val;
367 if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) {
368 *pDouble = val;
369 return true;
370 }
371 return false;
372 }
373
_GetString(TiXmlElement * pElem,wxString & str) const374 bool Preferences::_GetString(TiXmlElement* pElem, wxString& str) const
375 {
376 const char* val;
377 if (pElem != NULL) {
378 val = pElem->Attribute(kValue);
379 if (val != NULL) {
380 str = wxString::FromAscii(val);
381 return true;
382 }
383 }
384 return false;
385 }
386
387 /*
388 * Get a value. Do not disturb "*pVal" unless we have something to return.
389 */
GetBool(const char * name,bool * pVal) const390 bool Preferences::GetBool(const char* name, bool* pVal) const
391 {
392 return _GetBool(FindPrefElement(name), pVal);
393 }
394
GetInt(const char * name,int * pInt) const395 bool Preferences::GetInt(const char* name, int* pInt) const
396 {
397 return _GetInt(FindPrefElement(name), pInt);
398 }
399
GetDouble(const char * name,double * pDouble) const400 bool Preferences::GetDouble(const char* name, double* pDouble) const
401 {
402 return _GetDouble(FindPrefElement(name), pDouble);
403 }
404
GetString(const char * name,char ** pVal) const405 bool Preferences::GetString(const char* name, char** pVal) const
406 {
407 wxString str = wxString::FromAscii(*pVal);
408 if (_GetString(FindPrefElement(name), str))
409 {
410 *pVal = android::strdupNew(str.ToAscii());
411 return true;
412 }
413 return false;
414 }
415
GetString(const char * name,wxString & str) const416 bool Preferences::GetString(const char* name, wxString& str) const
417 {
418 return _GetString(FindPrefElement(name), str);
419 }
420
421 /*
422 * Set a value. If the preference already exists, and the value hasn't
423 * changed, don't do anything. This avoids setting the "dirty" flag
424 * unnecessarily.
425 */
SetBool(const char * name,bool val)426 void Preferences::SetBool(const char* name, bool val)
427 {
428 bool oldVal;
429 if (GetBool(name, &oldVal) && val == oldVal)
430 return;
431
432 SetString(name, val ? "true" : "false");
433 mDirty = true;
434 }
435
SetInt(const char * name,int val)436 void Preferences::SetInt(const char* name, int val)
437 {
438 int oldVal;
439 if (GetInt(name, &oldVal) && val == oldVal)
440 return;
441
442 TiXmlElement* pElem = FindPrefElement(name);
443 if (pElem == NULL)
444 pElem = AddPref(name);
445 pElem->SetAttribute(kValue, val);
446 mDirty = true;
447 }
448
SetDouble(const char * name,double val)449 void Preferences::SetDouble(const char* name, double val)
450 {
451 double oldVal;
452 if (GetDouble(name, &oldVal) && val == oldVal)
453 return;
454
455 TiXmlElement* pElem = FindPrefElement(name);
456 if (pElem == NULL)
457 pElem = AddPref(name);
458 pElem->SetDoubleAttribute(kValue, val);
459 mDirty = true;
460 }
461
SetString(const char * name,const char * val)462 void Preferences::SetString(const char* name, const char* val)
463 {
464 wxString oldVal;
465 if (GetString(name, /*ref*/oldVal) && strcmp(oldVal.ToAscii(), val) == 0)
466 return;
467
468 TiXmlElement* pElem = FindPrefElement(name);
469 if (pElem == NULL)
470 pElem = AddPref(name);
471 pElem->SetAttribute(kValue, val);
472 mDirty = true;
473 }
474
475
476