Otherwise printf is undeclared. These files worked with earlier versions of gcc because either cstdio or stdio.h was being included by some other header file. But this was not guaranteed behavior, so with GCC 4.4 there are errors. The fix is backwards compatible with earlier versions of GCC. This change includes either <cstdio> or <stdio.h> whichever looks more appropriate given the other headers included by the given source file. Note, I'm using "GCC" to mean Gnu Compile Collection, as this problem is specific to C++ source files. (Presumably a C++-specific header file changed to no longer include cstdio.)
611 lines
21 KiB
C++
611 lines
21 KiB
C++
#include "XLIFFFile.h"
|
|
|
|
#include <algorithm>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <cstdio>
|
|
|
|
const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
|
|
|
|
const char *const NS_MAP[] = {
|
|
"", XLIFF_XMLNS,
|
|
"xml", XMLNS_XMLNS,
|
|
NULL, NULL
|
|
};
|
|
|
|
const XMLNamespaceMap XLIFF_NAMESPACES(NS_MAP);
|
|
|
|
int
|
|
XLIFFFile::File::Compare(const XLIFFFile::File& that) const
|
|
{
|
|
if (filename != that.filename) {
|
|
return filename < that.filename ? -1 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// =====================================================================================
|
|
XLIFFFile::XLIFFFile()
|
|
{
|
|
}
|
|
|
|
XLIFFFile::~XLIFFFile()
|
|
{
|
|
}
|
|
|
|
static XMLNode*
|
|
get_unique_node(const XMLNode* parent, const string& ns, const string& name, bool required)
|
|
{
|
|
size_t count = parent->CountElementsByName(ns, name);
|
|
if (count == 1) {
|
|
return parent->GetElementByNameAt(ns, name, 0);
|
|
} else {
|
|
if (required) {
|
|
SourcePos pos = count == 0
|
|
? parent->Position()
|
|
: parent->GetElementByNameAt(XLIFF_XMLNS, name, 1)->Position();
|
|
pos.Error("<%s> elements must contain exactly one <%s> element",
|
|
parent->Name().c_str(), name.c_str());
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
XLIFFFile*
|
|
XLIFFFile::Parse(const string& filename)
|
|
{
|
|
XLIFFFile* result = new XLIFFFile();
|
|
|
|
XMLNode* root = NodeHandler::ParseFile(filename, XMLNode::PRETTY);
|
|
if (root == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// <file>
|
|
vector<XMLNode*> files = root->GetElementsByName(XLIFF_XMLNS, "file");
|
|
for (size_t i=0; i<files.size(); i++) {
|
|
XMLNode* file = files[i];
|
|
|
|
string datatype = file->GetAttribute("", "datatype", "");
|
|
string originalFile = file->GetAttribute("", "original", "");
|
|
|
|
Configuration sourceConfig;
|
|
sourceConfig.locale = file->GetAttribute("", "source-language", "");
|
|
result->m_sourceConfig = sourceConfig;
|
|
|
|
Configuration targetConfig;
|
|
targetConfig.locale = file->GetAttribute("", "target-language", "");
|
|
result->m_targetConfig = targetConfig;
|
|
|
|
result->m_currentVersion = file->GetAttribute("", "build-num", "");
|
|
result->m_oldVersion = "old";
|
|
|
|
// <body>
|
|
XMLNode* body = get_unique_node(file, XLIFF_XMLNS, "body", true);
|
|
if (body == NULL) continue;
|
|
|
|
// <trans-unit>
|
|
vector<XMLNode*> transUnits = body->GetElementsByName(XLIFF_XMLNS, "trans-unit");
|
|
for (size_t j=0; j<transUnits.size(); j++) {
|
|
XMLNode* transUnit = transUnits[j];
|
|
|
|
string rawID = transUnit->GetAttribute("", "id", "");
|
|
if (rawID == "") {
|
|
transUnit->Position().Error("<trans-unit> tag requires an id");
|
|
continue;
|
|
}
|
|
string id;
|
|
int index;
|
|
|
|
if (!StringResource::ParseTypedID(rawID, &id, &index)) {
|
|
transUnit->Position().Error("<trans-unit> has invalid id '%s'\n", rawID.c_str());
|
|
continue;
|
|
}
|
|
|
|
// <source>
|
|
XMLNode* source = get_unique_node(transUnit, XLIFF_XMLNS, "source", false);
|
|
if (source != NULL) {
|
|
XMLNode* node = source->Clone();
|
|
node->SetPrettyRecursive(XMLNode::EXACT);
|
|
result->AddStringResource(StringResource(source->Position(), originalFile,
|
|
sourceConfig, id, index, node, CURRENT_VERSION,
|
|
result->m_currentVersion));
|
|
}
|
|
|
|
// <target>
|
|
XMLNode* target = get_unique_node(transUnit, XLIFF_XMLNS, "target", false);
|
|
if (target != NULL) {
|
|
XMLNode* node = target->Clone();
|
|
node->SetPrettyRecursive(XMLNode::EXACT);
|
|
result->AddStringResource(StringResource(target->Position(), originalFile,
|
|
targetConfig, id, index, node, CURRENT_VERSION,
|
|
result->m_currentVersion));
|
|
}
|
|
|
|
// <alt-trans>
|
|
XMLNode* altTrans = get_unique_node(transUnit, XLIFF_XMLNS, "alt-trans", false);
|
|
if (altTrans != NULL) {
|
|
// <source>
|
|
XMLNode* altSource = get_unique_node(altTrans, XLIFF_XMLNS, "source", false);
|
|
if (altSource != NULL) {
|
|
XMLNode* node = altSource->Clone();
|
|
node->SetPrettyRecursive(XMLNode::EXACT);
|
|
result->AddStringResource(StringResource(altSource->Position(),
|
|
originalFile, sourceConfig, id, index, node, OLD_VERSION,
|
|
result->m_oldVersion));
|
|
}
|
|
|
|
// <target>
|
|
XMLNode* altTarget = get_unique_node(altTrans, XLIFF_XMLNS, "target", false);
|
|
if (altTarget != NULL) {
|
|
XMLNode* node = altTarget->Clone();
|
|
node->SetPrettyRecursive(XMLNode::EXACT);
|
|
result->AddStringResource(StringResource(altTarget->Position(),
|
|
originalFile, targetConfig, id, index, node, OLD_VERSION,
|
|
result->m_oldVersion));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete root;
|
|
return result;
|
|
}
|
|
|
|
XLIFFFile*
|
|
XLIFFFile::Create(const Configuration& sourceConfig, const Configuration& targetConfig,
|
|
const string& currentVersion)
|
|
{
|
|
XLIFFFile* result = new XLIFFFile();
|
|
result->m_sourceConfig = sourceConfig;
|
|
result->m_targetConfig = targetConfig;
|
|
result->m_currentVersion = currentVersion;
|
|
return result;
|
|
}
|
|
|
|
set<string>
|
|
XLIFFFile::Files() const
|
|
{
|
|
set<string> result;
|
|
for (vector<File>::const_iterator f = m_files.begin(); f != m_files.end(); f++) {
|
|
result.insert(f->filename);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void
|
|
XLIFFFile::AddStringResource(const StringResource& str)
|
|
{
|
|
string id = str.TypedID();
|
|
|
|
File* f = NULL;
|
|
const size_t I = m_files.size();
|
|
for (size_t i=0; i<I; i++) {
|
|
if (m_files[i].filename == str.file) {
|
|
f = &m_files[i];
|
|
break;
|
|
}
|
|
}
|
|
if (f == NULL) {
|
|
File file;
|
|
file.filename = str.file;
|
|
m_files.push_back(file);
|
|
f = &m_files[I];
|
|
}
|
|
|
|
const size_t J = f->transUnits.size();
|
|
TransUnit* g = NULL;
|
|
for (size_t j=0; j<J; j++) {
|
|
if (f->transUnits[j].id == id) {
|
|
g = &f->transUnits[j];
|
|
}
|
|
}
|
|
if (g == NULL) {
|
|
TransUnit group;
|
|
group.id = id;
|
|
f->transUnits.push_back(group);
|
|
g = &f->transUnits[J];
|
|
}
|
|
|
|
StringResource* res = find_string_res(*g, str);
|
|
if (res == NULL) {
|
|
return ;
|
|
}
|
|
if (res->id != "") {
|
|
str.pos.Error("Duplicate string resource: %s", res->id.c_str());
|
|
res->pos.Error("Previous definition here");
|
|
return ;
|
|
}
|
|
*res = str;
|
|
|
|
m_strings.insert(str);
|
|
}
|
|
|
|
void
|
|
XLIFFFile::Filter(bool (*func)(const string&,const TransUnit&,void*), void* cookie)
|
|
{
|
|
const size_t I = m_files.size();
|
|
for (size_t ix=0, i=I-1; ix<I; ix++, i--) {
|
|
File& file = m_files[i];
|
|
|
|
const size_t J = file.transUnits.size();
|
|
for (size_t jx=0, j=J-1; jx<J; jx++, j--) {
|
|
TransUnit& tu = file.transUnits[j];
|
|
|
|
bool keep = func(file.filename, tu, cookie);
|
|
if (!keep) {
|
|
if (tu.source.id != "") {
|
|
m_strings.erase(tu.source);
|
|
}
|
|
if (tu.target.id != "") {
|
|
m_strings.erase(tu.target);
|
|
}
|
|
if (tu.altSource.id != "") {
|
|
m_strings.erase(tu.altSource);
|
|
}
|
|
if (tu.altTarget.id != "") {
|
|
m_strings.erase(tu.altTarget);
|
|
}
|
|
file.transUnits.erase(file.transUnits.begin()+j);
|
|
}
|
|
}
|
|
if (file.transUnits.size() == 0) {
|
|
m_files.erase(m_files.begin()+i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
XLIFFFile::Map(void (*func)(const string&,TransUnit*,void*), void* cookie)
|
|
{
|
|
const size_t I = m_files.size();
|
|
for (size_t i=0; i<I; i++) {
|
|
File& file = m_files[i];
|
|
|
|
const size_t J = file.transUnits.size();
|
|
for (size_t j=0; j<J; j++) {
|
|
func(file.filename, &(file.transUnits[j]), cookie);
|
|
}
|
|
}
|
|
}
|
|
|
|
TransUnit*
|
|
XLIFFFile::EditTransUnit(const string& filename, const string& id)
|
|
{
|
|
const size_t I = m_files.size();
|
|
for (size_t ix=0, i=I-1; ix<I; ix++, i--) {
|
|
File& file = m_files[i];
|
|
if (file.filename == filename) {
|
|
const size_t J = file.transUnits.size();
|
|
for (size_t jx=0, j=J-1; jx<J; jx++, j--) {
|
|
TransUnit& tu = file.transUnits[j];
|
|
if (tu.id == id) {
|
|
return &tu;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
StringResource*
|
|
XLIFFFile::find_string_res(TransUnit& g, const StringResource& str)
|
|
{
|
|
int index;
|
|
if (str.version == CURRENT_VERSION) {
|
|
index = 0;
|
|
}
|
|
else if (str.version == OLD_VERSION) {
|
|
index = 2;
|
|
}
|
|
else {
|
|
str.pos.Error("Internal Error %s:%d\n", __FILE__, __LINE__);
|
|
return NULL;
|
|
}
|
|
if (str.config == m_sourceConfig) {
|
|
// index += 0;
|
|
}
|
|
else if (str.config == m_targetConfig) {
|
|
index += 1;
|
|
}
|
|
else {
|
|
str.pos.Error("unknown config for string %s: %s", str.id.c_str(),
|
|
str.config.ToString().c_str());
|
|
return NULL;
|
|
}
|
|
switch (index) {
|
|
case 0:
|
|
return &g.source;
|
|
case 1:
|
|
return &g.target;
|
|
case 2:
|
|
return &g.altSource;
|
|
case 3:
|
|
return &g.altTarget;
|
|
}
|
|
str.pos.Error("Internal Error %s:%d\n", __FILE__, __LINE__);
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
convert_html_to_xliff(const XMLNode* original, const string& name, XMLNode* addTo, int* phID)
|
|
{
|
|
int err = 0;
|
|
if (original->Type() == XMLNode::TEXT) {
|
|
addTo->EditChildren().push_back(original->Clone());
|
|
return 0;
|
|
} else {
|
|
string ctype;
|
|
if (original->Namespace() == "") {
|
|
if (original->Name() == "b") {
|
|
ctype = "bold";
|
|
}
|
|
else if (original->Name() == "i") {
|
|
ctype = "italic";
|
|
}
|
|
else if (original->Name() == "u") {
|
|
ctype = "underline";
|
|
}
|
|
}
|
|
if (ctype != "") {
|
|
vector<XMLAttribute> attrs;
|
|
attrs.push_back(XMLAttribute(XLIFF_XMLNS, "ctype", ctype));
|
|
XMLNode* copy = XMLNode::NewElement(original->Position(), XLIFF_XMLNS, "g",
|
|
attrs, XMLNode::EXACT);
|
|
|
|
const vector<XMLNode*>& children = original->Children();
|
|
size_t I = children.size();
|
|
for (size_t i=0; i<I; i++) {
|
|
err |= convert_html_to_xliff(children[i], name, copy, phID);
|
|
}
|
|
return err;
|
|
}
|
|
else {
|
|
if (original->Namespace() == XLIFF_XMLNS) {
|
|
addTo->EditChildren().push_back(original->Clone());
|
|
return 0;
|
|
} else {
|
|
if (original->Namespace() == "") {
|
|
// flatten out the tag into ph tags -- but only if there is no namespace
|
|
// that's still unsupported because propagating the xmlns attribute is hard.
|
|
vector<XMLAttribute> attrs;
|
|
char idStr[30];
|
|
(*phID)++;
|
|
sprintf(idStr, "id-%d", *phID);
|
|
attrs.push_back(XMLAttribute(XLIFF_XMLNS, "id", idStr));
|
|
|
|
if (original->Children().size() == 0) {
|
|
XMLNode* ph = XMLNode::NewElement(original->Position(), XLIFF_XMLNS,
|
|
"ph", attrs, XMLNode::EXACT);
|
|
ph->EditChildren().push_back(
|
|
XMLNode::NewText(original->Position(),
|
|
original->ToString(XLIFF_NAMESPACES),
|
|
XMLNode::EXACT));
|
|
addTo->EditChildren().push_back(ph);
|
|
} else {
|
|
XMLNode* begin = XMLNode::NewElement(original->Position(), XLIFF_XMLNS,
|
|
"bpt", attrs, XMLNode::EXACT);
|
|
begin->EditChildren().push_back(
|
|
XMLNode::NewText(original->Position(),
|
|
original->OpenTagToString(XLIFF_NAMESPACES, XMLNode::EXACT),
|
|
XMLNode::EXACT));
|
|
XMLNode* end = XMLNode::NewElement(original->Position(), XLIFF_XMLNS,
|
|
"ept", attrs, XMLNode::EXACT);
|
|
string endText = "</";
|
|
endText += original->Name();
|
|
endText += ">";
|
|
end->EditChildren().push_back(XMLNode::NewText(original->Position(),
|
|
endText, XMLNode::EXACT));
|
|
|
|
addTo->EditChildren().push_back(begin);
|
|
|
|
const vector<XMLNode*>& children = original->Children();
|
|
size_t I = children.size();
|
|
for (size_t i=0; i<I; i++) {
|
|
err |= convert_html_to_xliff(children[i], name, addTo, phID);
|
|
}
|
|
|
|
addTo->EditChildren().push_back(end);
|
|
}
|
|
return err;
|
|
} else {
|
|
original->Position().Error("invalid <%s> element in <%s> tag\n",
|
|
original->Name().c_str(), name.c_str());
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
XMLNode*
|
|
create_string_node(const StringResource& str, const string& name)
|
|
{
|
|
vector<XMLAttribute> attrs;
|
|
attrs.push_back(XMLAttribute(XMLNS_XMLNS, "space", "preserve"));
|
|
XMLNode* node = XMLNode::NewElement(str.pos, XLIFF_XMLNS, name, attrs, XMLNode::EXACT);
|
|
|
|
const vector<XMLNode*>& children = str.value->Children();
|
|
size_t I = children.size();
|
|
int err = 0;
|
|
for (size_t i=0; i<I; i++) {
|
|
int phID = 0;
|
|
err |= convert_html_to_xliff(children[i], name, node, &phID);
|
|
}
|
|
|
|
if (err != 0) {
|
|
delete node;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
static bool
|
|
compare_id(const TransUnit& lhs, const TransUnit& rhs)
|
|
{
|
|
string lid, rid;
|
|
int lindex, rindex;
|
|
StringResource::ParseTypedID(lhs.id, &lid, &lindex);
|
|
StringResource::ParseTypedID(rhs.id, &rid, &rindex);
|
|
if (lid < rid) return true;
|
|
if (lid == rid && lindex < rindex) return true;
|
|
return false;
|
|
}
|
|
|
|
XMLNode*
|
|
XLIFFFile::ToXMLNode() const
|
|
{
|
|
XMLNode* root;
|
|
size_t N;
|
|
|
|
// <xliff>
|
|
{
|
|
vector<XMLAttribute> attrs;
|
|
XLIFF_NAMESPACES.AddToAttributes(&attrs);
|
|
attrs.push_back(XMLAttribute(XLIFF_XMLNS, "version", "1.2"));
|
|
root = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "xliff", attrs, XMLNode::PRETTY);
|
|
}
|
|
|
|
vector<TransUnit> groups;
|
|
|
|
// <file>
|
|
vector<File> files = m_files;
|
|
sort(files.begin(), files.end());
|
|
const size_t I = files.size();
|
|
for (size_t i=0; i<I; i++) {
|
|
const File& file = files[i];
|
|
|
|
vector<XMLAttribute> fileAttrs;
|
|
fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "datatype", "x-android-res"));
|
|
fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "original", file.filename));
|
|
|
|
struct timeval tv;
|
|
struct timezone tz;
|
|
gettimeofday(&tv, &tz);
|
|
fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "date", trim_string(ctime(&tv.tv_sec))));
|
|
|
|
fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "source-language", m_sourceConfig.locale));
|
|
fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "target-language", m_targetConfig.locale));
|
|
fileAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "build-num", m_currentVersion));
|
|
|
|
XMLNode* fileNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "file", fileAttrs,
|
|
XMLNode::PRETTY);
|
|
root->EditChildren().push_back(fileNode);
|
|
|
|
// <body>
|
|
XMLNode* bodyNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "body",
|
|
vector<XMLAttribute>(), XMLNode::PRETTY);
|
|
fileNode->EditChildren().push_back(bodyNode);
|
|
|
|
// <trans-unit>
|
|
vector<TransUnit> transUnits = file.transUnits;
|
|
sort(transUnits.begin(), transUnits.end(), compare_id);
|
|
const size_t J = transUnits.size();
|
|
for (size_t j=0; j<J; j++) {
|
|
const TransUnit& transUnit = transUnits[j];
|
|
|
|
vector<XMLAttribute> tuAttrs;
|
|
|
|
// strings start with string:
|
|
tuAttrs.push_back(XMLAttribute(XLIFF_XMLNS, "id", transUnit.id));
|
|
XMLNode* transUnitNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "trans-unit",
|
|
tuAttrs, XMLNode::PRETTY);
|
|
bodyNode->EditChildren().push_back(transUnitNode);
|
|
|
|
// <extradata>
|
|
if (transUnit.source.comment != "") {
|
|
vector<XMLAttribute> extradataAttrs;
|
|
XMLNode* extraNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "extradata",
|
|
extradataAttrs, XMLNode::EXACT);
|
|
transUnitNode->EditChildren().push_back(extraNode);
|
|
extraNode->EditChildren().push_back(
|
|
XMLNode::NewText(GENERATED_POS, transUnit.source.comment,
|
|
XMLNode::PRETTY));
|
|
}
|
|
|
|
// <source>
|
|
if (transUnit.source.id != "") {
|
|
transUnitNode->EditChildren().push_back(
|
|
create_string_node(transUnit.source, "source"));
|
|
}
|
|
|
|
// <target>
|
|
if (transUnit.target.id != "") {
|
|
transUnitNode->EditChildren().push_back(
|
|
create_string_node(transUnit.target, "target"));
|
|
}
|
|
|
|
// <alt-trans>
|
|
if (transUnit.altSource.id != "" || transUnit.altTarget.id != ""
|
|
|| transUnit.rejectComment != "") {
|
|
vector<XMLAttribute> altTransAttrs;
|
|
XMLNode* altTransNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS, "alt-trans",
|
|
altTransAttrs, XMLNode::PRETTY);
|
|
transUnitNode->EditChildren().push_back(altTransNode);
|
|
|
|
// <extradata>
|
|
if (transUnit.rejectComment != "") {
|
|
vector<XMLAttribute> extradataAttrs;
|
|
XMLNode* extraNode = XMLNode::NewElement(GENERATED_POS, XLIFF_XMLNS,
|
|
"extradata", extradataAttrs,
|
|
XMLNode::EXACT);
|
|
altTransNode->EditChildren().push_back(extraNode);
|
|
extraNode->EditChildren().push_back(
|
|
XMLNode::NewText(GENERATED_POS, transUnit.rejectComment,
|
|
XMLNode::PRETTY));
|
|
}
|
|
|
|
// <source>
|
|
if (transUnit.altSource.id != "") {
|
|
altTransNode->EditChildren().push_back(
|
|
create_string_node(transUnit.altSource, "source"));
|
|
}
|
|
|
|
// <target>
|
|
if (transUnit.altTarget.id != "") {
|
|
altTransNode->EditChildren().push_back(
|
|
create_string_node(transUnit.altTarget, "target"));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
|
|
string
|
|
XLIFFFile::ToString() const
|
|
{
|
|
XMLNode* xml = ToXMLNode();
|
|
string s = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
|
s += xml->ToString(XLIFF_NAMESPACES);
|
|
delete xml;
|
|
s += '\n';
|
|
return s;
|
|
}
|
|
|
|
Stats
|
|
XLIFFFile::GetStats(const string& config) const
|
|
{
|
|
Stats stat;
|
|
stat.config = config;
|
|
stat.files = m_files.size();
|
|
stat.toBeTranslated = 0;
|
|
stat.noComments = 0;
|
|
|
|
for (vector<File>::const_iterator file=m_files.begin(); file!=m_files.end(); file++) {
|
|
stat.toBeTranslated += file->transUnits.size();
|
|
|
|
for (vector<TransUnit>::const_iterator tu=file->transUnits.begin();
|
|
tu!=file->transUnits.end(); tu++) {
|
|
if (tu->source.comment == "") {
|
|
stat.noComments++;
|
|
}
|
|
}
|
|
}
|
|
|
|
stat.totalStrings = stat.toBeTranslated;
|
|
|
|
return stat;
|
|
}
|