267 lines
7.5 KiB
C++
267 lines
7.5 KiB
C++
#include "ValuesFile.h"
|
|
|
|
#include "XMLHandler.h"
|
|
|
|
#include <algorithm>
|
|
#include <fcntl.h>
|
|
#include <expat.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
using namespace std;
|
|
|
|
const char* const ANDROID_XMLNS = "http://schemas.android.com/apk/res/android";
|
|
const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
|
|
|
|
const char *const NS_MAP[] = {
|
|
"android", ANDROID_XMLNS,
|
|
"xliff", XLIFF_XMLNS,
|
|
NULL, NULL
|
|
};
|
|
|
|
const XMLNamespaceMap ANDROID_NAMESPACES(NS_MAP);
|
|
|
|
|
|
// =====================================================================================
|
|
class ArrayHandler : public XMLHandler
|
|
{
|
|
public:
|
|
ArrayHandler(ValuesFile* vf, int version, const string& versionString, const string& id);
|
|
|
|
virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
|
|
const vector<XMLAttribute>& attrs, XMLHandler** next);
|
|
virtual int OnText(const SourcePos& pos, const string& text);
|
|
virtual int OnComment(const SourcePos& pos, const string& text);
|
|
|
|
private:
|
|
ValuesFile* m_vf;
|
|
int m_version;
|
|
int m_index;
|
|
string m_versionString;
|
|
string m_id;
|
|
string m_comment;
|
|
};
|
|
|
|
ArrayHandler::ArrayHandler(ValuesFile* vf, int version, const string& versionString,
|
|
const string& id)
|
|
:m_vf(vf),
|
|
m_version(version),
|
|
m_index(0),
|
|
m_versionString(versionString),
|
|
m_id(id)
|
|
{
|
|
}
|
|
|
|
int
|
|
ArrayHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
|
|
const vector<XMLAttribute>& attrs, XMLHandler** next)
|
|
{
|
|
if (ns == "" && name == "item") {
|
|
XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT);
|
|
m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(),
|
|
m_id, m_index, node, m_version, m_versionString,
|
|
trim_string(m_comment)));
|
|
*next = new NodeHandler(node, XMLNode::EXACT);
|
|
m_index++;
|
|
m_comment = "";
|
|
return 0;
|
|
} else {
|
|
pos.Error("invalid <%s> element inside <array>\n", name.c_str());
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int
|
|
ArrayHandler::OnText(const SourcePos& pos, const string& text)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ArrayHandler::OnComment(const SourcePos& pos, const string& text)
|
|
{
|
|
m_comment += text;
|
|
return 0;
|
|
}
|
|
|
|
// =====================================================================================
|
|
class ValuesHandler : public XMLHandler
|
|
{
|
|
public:
|
|
ValuesHandler(ValuesFile* vf, int version, const string& versionString);
|
|
|
|
virtual int OnStartElement(const SourcePos& pos, const string& ns, const string& name,
|
|
const vector<XMLAttribute>& attrs, XMLHandler** next);
|
|
virtual int OnText(const SourcePos& pos, const string& text);
|
|
virtual int OnComment(const SourcePos& pos, const string& text);
|
|
|
|
private:
|
|
ValuesFile* m_vf;
|
|
int m_version;
|
|
string m_versionString;
|
|
string m_comment;
|
|
};
|
|
|
|
ValuesHandler::ValuesHandler(ValuesFile* vf, int version, const string& versionString)
|
|
:m_vf(vf),
|
|
m_version(version),
|
|
m_versionString(versionString)
|
|
{
|
|
}
|
|
|
|
int
|
|
ValuesHandler::OnStartElement(const SourcePos& pos, const string& ns, const string& name,
|
|
const vector<XMLAttribute>& attrs, XMLHandler** next)
|
|
{
|
|
if (ns == "" && name == "string") {
|
|
string id = XMLAttribute::Find(attrs, "", "name", "");
|
|
XMLNode* node = XMLNode::NewElement(pos, ns, name, attrs, XMLNode::EXACT);
|
|
m_vf->AddString(StringResource(pos, pos.file, m_vf->GetConfiguration(),
|
|
id, -1, node, m_version, m_versionString,
|
|
trim_string(m_comment)));
|
|
*next = new NodeHandler(node, XMLNode::EXACT);
|
|
}
|
|
else if (ns == "" && name == "array") {
|
|
string id = XMLAttribute::Find(attrs, "", "name", "");
|
|
*next = new ArrayHandler(m_vf, m_version, m_versionString, id);
|
|
}
|
|
m_comment = "";
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ValuesHandler::OnText(const SourcePos& pos, const string& text)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ValuesHandler::OnComment(const SourcePos& pos, const string& text)
|
|
{
|
|
m_comment += text;
|
|
return 0;
|
|
}
|
|
|
|
// =====================================================================================
|
|
ValuesFile::ValuesFile(const Configuration& config)
|
|
:m_config(config),
|
|
m_strings(),
|
|
m_arrays()
|
|
{
|
|
}
|
|
|
|
ValuesFile::~ValuesFile()
|
|
{
|
|
}
|
|
|
|
ValuesFile*
|
|
ValuesFile::ParseFile(const string& filename, const Configuration& config,
|
|
int version, const string& versionString)
|
|
{
|
|
ValuesFile* result = new ValuesFile(config);
|
|
|
|
TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString));
|
|
XMLHandler::ParseFile(filename, &top);
|
|
|
|
return result;
|
|
}
|
|
|
|
ValuesFile*
|
|
ValuesFile::ParseString(const string& filename, const string& text, const Configuration& config,
|
|
int version, const string& versionString)
|
|
{
|
|
ValuesFile* result = new ValuesFile(config);
|
|
|
|
TopElementHandler top("", "resources", new ValuesHandler(result, version, versionString));
|
|
XMLHandler::ParseString(filename, text, &top);
|
|
|
|
return result;
|
|
}
|
|
|
|
const Configuration&
|
|
ValuesFile::GetConfiguration() const
|
|
{
|
|
return m_config;
|
|
}
|
|
|
|
void
|
|
ValuesFile::AddString(const StringResource& str)
|
|
{
|
|
if (str.index < 0) {
|
|
m_strings.insert(str);
|
|
} else {
|
|
m_arrays[str.id].insert(str);
|
|
}
|
|
}
|
|
|
|
set<StringResource>
|
|
ValuesFile::GetStrings() const
|
|
{
|
|
set<StringResource> result = m_strings;
|
|
|
|
for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin();
|
|
it != m_arrays.end(); it++) {
|
|
result.insert(it->second.begin(), it->second.end());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
XMLNode*
|
|
ValuesFile::ToXMLNode() const
|
|
{
|
|
XMLNode* root;
|
|
|
|
// <resources>
|
|
{
|
|
vector<XMLAttribute> attrs;
|
|
ANDROID_NAMESPACES.AddToAttributes(&attrs);
|
|
root = XMLNode::NewElement(GENERATED_POS, "", "resources", attrs, XMLNode::PRETTY);
|
|
}
|
|
|
|
// <array>
|
|
for (map<string,set<StringResource> >::const_iterator it = m_arrays.begin();
|
|
it != m_arrays.end(); it++) {
|
|
vector<XMLAttribute> arrayAttrs;
|
|
arrayAttrs.push_back(XMLAttribute("", "name", it->first));
|
|
const set<StringResource>& items = it->second;
|
|
XMLNode* arrayNode = XMLNode::NewElement(items.begin()->pos, "", "array", arrayAttrs,
|
|
XMLNode::PRETTY);
|
|
root->EditChildren().push_back(arrayNode);
|
|
|
|
// <item>
|
|
for (set<StringResource>::const_iterator item = items.begin();
|
|
item != items.end(); item++) {
|
|
XMLNode* itemNode = item->value->Clone();
|
|
itemNode->SetName("", "item");
|
|
itemNode->EditAttributes().clear();
|
|
arrayNode->EditChildren().push_back(itemNode);
|
|
}
|
|
}
|
|
|
|
// <string>
|
|
for (set<StringResource>::const_iterator it=m_strings.begin(); it!=m_strings.end(); it++) {
|
|
const StringResource& str = *it;
|
|
vector<XMLAttribute> attrs;
|
|
XMLNode* strNode = str.value->Clone();
|
|
strNode->SetName("", "string");
|
|
strNode->EditAttributes().clear();
|
|
strNode->EditAttributes().push_back(XMLAttribute("", "name", str.id));
|
|
root->EditChildren().push_back(strNode);
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
string
|
|
ValuesFile::ToString() const
|
|
{
|
|
XMLNode* xml = ToXMLNode();
|
|
string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
|
s += xml->ToString(ANDROID_NAMESPACES);
|
|
delete xml;
|
|
s += '\n';
|
|
return s;
|
|
}
|
|
|