Jack Palevich bdb087c930 Add includes to enable host C++ tools to compile with GCC 4.4
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.)
2009-06-24 19:01:27 -07:00

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;
}