//
// Copyright 2005 The Android Open Source Project
//
// Preferences file access.
//
// For compilers that support precompilation, include "wx/wx.h".
#include "wx/wxprec.h"
// Otherwise, include all standard headers
#ifndef WX_PRECOMP
//# include "wx/wx.h"
# include "wx/string.h"
#endif
#include "Preferences.h"
#include "utils.h"
#include "tinyxml.h"
static const char* kName = "name";
static const char* kValue = "value";
/*
* Load from a file.
*/
bool Preferences::Load(const char* fileName)
{
assert(fileName != NULL);
printf("SimPref: reading preferences file '%s'\n", fileName);
// throw out any existing stuff
delete mpDoc;
mpDoc = new TiXmlDocument;
if (mpDoc == NULL)
return false;
if (!mpDoc->LoadFile(fileName)) {
fprintf(stderr, "SimPref: ERROR: failed loading '%s'\n", fileName);
if (mpDoc->ErrorRow() != 0)
fprintf(stderr, " XML: %s (row=%d col=%d)\n",
mpDoc->ErrorDesc(), mpDoc->ErrorRow(), mpDoc->ErrorCol());
else
fprintf(stderr, " XML: %s\n", mpDoc->ErrorDesc());
goto fail;
}
TiXmlNode* pPrefs;
pPrefs = mpDoc->FirstChild("prefs");
if (pPrefs == NULL) {
fprintf(stderr, "SimPref: ERROR: could not find <prefs> in '%s'\n",
fileName);
goto fail;
}
// set defaults for anything we haven't set explicitly
SetDefaults();
return true;
fail:
delete mpDoc;
mpDoc = NULL;
return false;
}
/*
* Save to a file.
*/
bool Preferences::Save(const char* fileName)
{
assert(fileName != NULL);
if (mpDoc == NULL)
return false;
if (!mpDoc->SaveFile(fileName)) {
fprintf(stderr, "SimPref: ERROR: failed saving '%s': %s\n",
fileName, mpDoc->ErrorDesc());
return false;
}
mDirty = false;
return true;
}
/*
* Create an empty collection of preferences.
*/
bool Preferences::Create(void)
{
static const char* docBase =
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
"<!-- Android device simulator preferences -->\n"
"<!-- This file is updated by the simulator -->\n"
"<prefs>\n"
"</prefs>\n";
// throw out any existing stuff
delete mpDoc;
// alloc and initialize
mpDoc = new TiXmlDocument;
if (mpDoc == NULL)
return false;
if (!mpDoc->Parse(docBase)) {
fprintf(stderr, "SimPref: bad docBase: %s\n", mpDoc->ErrorDesc());
return false;
}
SetDefaults();
mDirty = true; // should already be, mbut make sure
return true;
}
/*
* Add default values to XML doc.
*
* This isn't strictly necessary, because the functions that are interested
* in the preferences can set appropriate defaults themselves when the
* "get" function returns "false". However, in some cases a preference
* can be interesting to more than one function, and you either have to
* cut & paste the default value or write a "get default for xxx" function.
*
* We want this to work even if they already have an older config file, so
* this only sets values that don't already exist.
*/
void Preferences::SetDefaults(void)
{
/* table of default values */
static const struct {
const char* type;
const char* name;
const char* value;
} kDefault[] = {
{ "pref", "auto-power-on", "true" },
{ "pref", "debug", "false" },
{ "pref", "valgrind", "false" },
{ "pref", "check-jni", "true" },
{ "pref", "enable-sound", "true" },
{ "pref", "enable-fake-camera", "true" },
{ "pref", "java-vm", "Dalvik" },
/* goobuntu dapper needed LD_ASSUME_KERNEL or gdb choked badly */
{ "pref", "ld-assume-kernel", "" /*2.4.19*/ },
{ "pref", "launch-command",
"xterm -geom 80x60+10+10 -sb -title Simulator -e" },
{ "pref", "launch-wrapper-args", "-wait" },
};
TiXmlNode* pPrefs;
assert(mpDoc != NULL);
pPrefs = mpDoc->FirstChild("prefs");
/*
* Look up the name. If it doesn't exist, add it.
*/
for (int i = 0; i < NELEM(kDefault); i++) {
TiXmlNode* pNode = _FindNode(kDefault[i].type, kDefault[i].name);
if (pNode == NULL) {
TiXmlElement elem(kDefault[i].type);
elem.SetAttribute(kName, kDefault[i].name);
elem.SetAttribute(kValue, kDefault[i].value);
pPrefs->InsertEndChild(elem);
printf("SimPref: added default <%s> '%s'='%s'\n",
kDefault[i].type, kDefault[i].name, kDefault[i].value);
} else {
printf("SimPref: found existing <%s> '%s'\n",
kDefault[i].type, kDefault[i].name);
}
}
}
static TiXmlNode* get_next_node(TiXmlNode* pNode)
{
if (!pNode->NoChildren())
{
pNode = pNode->FirstChild();
}
else if (pNode->NoChildren() &&
(pNode->NextSibling() == NULL))
{
pNode = pNode->Parent()->NextSibling();
}
else
{
pNode = pNode->NextSibling();
}
return pNode;
}
/*
* Returns the node with element type and name specified
*
* WARNING: this searches through the tree and returns the first matching
* node.
*/
TiXmlNode* Preferences::_FindNode(const char* type, const char* str) const
{
assert((type != NULL) && (str != NULL));
TiXmlNode* pRoot;
TiXmlNode* pNode;
pRoot = mpDoc->FirstChild("prefs");
assert(pRoot != NULL);
for (pNode = pRoot->FirstChild(); pNode != NULL;)
{
if (pNode->Type() != TiXmlNode::ELEMENT ||
strcasecmp(pNode->Value(), type) != 0)
{
pNode = get_next_node(pNode);
continue;
}
TiXmlElement* pElem = pNode->ToElement();
assert(pElem != NULL);
const char* name = pElem->Attribute(kName);
/* 1. If the name is blank, something is wrong with the config file
* 2. If the name matches the passed in string, we found the node
* 3. If the node has children, descend another level
* 4. If there are no children and no siblings of the node, go up a level
* 5. Otherwise, grab the next sibling
*/
if (name == NULL)
{
fprintf(stderr, "WARNING: found <%s> without name\n", type);
continue;
}
else if (strcasecmp(name, str) == 0)
{
return pNode;
}
else
{
pNode = get_next_node(pNode);
}
}
return NULL;
}
/*
* Locate the specified preference.
*/
TiXmlNode* Preferences::FindPref(const char* str) const
{
TiXmlNode* pNode = _FindNode("pref", str);
return pNode;
}
/*
* Like FindPref(), but returns a TiXmlElement.
*/
TiXmlElement* Preferences::FindPrefElement(const char* str) const
{
TiXmlNode* pNode;
pNode = FindPref(str);
if (pNode != NULL)
return pNode->ToElement();
return NULL;
}
/*
* Add a new preference entry with a blank entry for value. Returns a
* pointer to the new element.
*/
TiXmlElement* Preferences::AddPref(const char* str)
{
assert(FindPref(str) == NULL);
TiXmlNode* pPrefs;
pPrefs = mpDoc->FirstChild("prefs");
assert(pPrefs != NULL);
TiXmlElement elem("pref");
elem.SetAttribute(kName, str);
elem.SetAttribute(kValue, "");
pPrefs->InsertEndChild(elem);
TiXmlNode* pNewPref = FindPref(str);
return pNewPref->ToElement();
}
/*
* Remove a node from the tree
*/
bool Preferences::_RemoveNode(TiXmlNode* pNode)
{
if (pNode == NULL)
return false;
TiXmlNode* pParent = pNode->Parent();
if (pParent == NULL)
return false;
pParent->RemoveChild(pNode);
mDirty = true;
return true;
}
/*
* Remove a preference entry.
*/
bool Preferences::RemovePref(const char* delName)
{
return _RemoveNode(FindPref(delName));
}
/*
* Test for existence.
*/
bool Preferences::Exists(const char* name) const
{
TiXmlElement* pElem = FindPrefElement(name);
return (pElem != NULL);
}
/*
* Internal implemenations for getting values
*/
bool Preferences::_GetBool(TiXmlElement* pElem, bool* pVal) const
{
if (pElem != NULL)
{
const char* str = pElem->Attribute(kValue);
if (str != NULL)
{
if (strcasecmp(str, "true") == 0)
*pVal = true;
else if (strcasecmp(str, "false") == 0)
*pVal = false;
else
{
printf("SimPref: evaluating as bool name='%s' val='%s'\n",
pElem->Attribute(kName), str);
return false;
}
return true;
}
}
return false;
}
bool Preferences::_GetInt(TiXmlElement* pElem, int* pInt) const
{
int val;
if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) {
*pInt = val;
return true;
}
return false;
}
bool Preferences::_GetDouble(TiXmlElement* pElem, double* pDouble) const
{
double val;
if (pElem != NULL && pElem->Attribute(kValue, &val) != NULL) {
*pDouble = val;
return true;
}
return false;
}
bool Preferences::_GetString(TiXmlElement* pElem, wxString& str) const
{
const char* val;
if (pElem != NULL) {
val = pElem->Attribute(kValue);
if (val != NULL) {
str = wxString::FromAscii(val);
return true;
}
}
return false;
}
/*
* Get a value. Do not disturb "*pVal" unless we have something to return.
*/
bool Preferences::GetBool(const char* name, bool* pVal) const
{
return _GetBool(FindPrefElement(name), pVal);
}
bool Preferences::GetInt(const char* name, int* pInt) const
{
return _GetInt(FindPrefElement(name), pInt);
}
bool Preferences::GetDouble(const char* name, double* pDouble) const
{
return _GetDouble(FindPrefElement(name), pDouble);
}
bool Preferences::GetString(const char* name, char** pVal) const
{
wxString str = wxString::FromAscii(*pVal);
if (_GetString(FindPrefElement(name), str))
{
*pVal = android::strdupNew(str.ToAscii());
return true;
}
return false;
}
bool Preferences::GetString(const char* name, wxString& str) const
{
return _GetString(FindPrefElement(name), str);
}
/*
* Set a value. If the preference already exists, and the value hasn't
* changed, don't do anything. This avoids setting the "dirty" flag
* unnecessarily.
*/
void Preferences::SetBool(const char* name, bool val)
{
bool oldVal;
if (GetBool(name, &oldVal) && val == oldVal)
return;
SetString(name, val ? "true" : "false");
mDirty = true;
}
void Preferences::SetInt(const char* name, int val)
{
int oldVal;
if (GetInt(name, &oldVal) && val == oldVal)
return;
TiXmlElement* pElem = FindPrefElement(name);
if (pElem == NULL)
pElem = AddPref(name);
pElem->SetAttribute(kValue, val);
mDirty = true;
}
void Preferences::SetDouble(const char* name, double val)
{
double oldVal;
if (GetDouble(name, &oldVal) && val == oldVal)
return;
TiXmlElement* pElem = FindPrefElement(name);
if (pElem == NULL)
pElem = AddPref(name);
pElem->SetDoubleAttribute(kValue, val);
mDirty = true;
}
void Preferences::SetString(const char* name, const char* val)
{
wxString oldVal;
if (GetString(name, /*ref*/oldVal) && strcmp(oldVal.ToAscii(), val) == 0)
return;
TiXmlElement* pElem = FindPrefElement(name);
if (pElem == NULL)
pElem = AddPref(name);
pElem->SetAttribute(kValue, val);
mDirty = true;
}