/*
PIP - Platform Independent Primitives
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
*/
#include "ts_file.h"
#include "pifile.h"
#include "piiostream.h"
#include "pitranslator.h"
bool TSFile::Context::confirm(const PIString & msg, const PIString & file, int line) {
if (msg.isEmpty()) return false;
bool is_new = !messages.contains(msg);
auto & m(messages[msg]);
m.source = msg;
m.filename = file;
m.line = line >= 0 ? PIString::fromNumber(line) : PIString();
m.type.setFlag(Message::Missing, false);
if (is_new) m.type.setFlag(Message::Unfinished);
return is_new;
}
PIString TSFile::mask(const PIString & in) {
static const PIVector> map = {
{"&", "&" },
{"<", "<" },
{">", ">" },
{"'", "'"},
{"\"", """},
};
PIString ret = in;
for (const auto & i: map)
ret.replaceAll(i.first, i.second);
return ret;
}
PIString TSFile::unmask(const PIString & in) {
static const PIVector> map = {
{"<", "<" },
{">", ">" },
{"'", "'"},
{"\"", """},
{"&", "&" },
};
PIString ret = in;
for (const auto & i: map)
ret.replaceAll(i.second, i.first);
return ret;
}
TSFile::Content TSFile::read(const PIString & path) {
enum Phase {
pHeader,
pContext,
pContextName,
pMessage,
pMessageBody,
};
Content ret;
PIFile f(path, PIIODevice::ReadOnly);
if (!f.isOpened()) return ret;
PIIOTextStream ts(&f);
Context * cc = nullptr;
Message msg;
int phase = pHeader;
bool multi_source = false, multi_translation = false, multi_comment = false;
while (!ts.isEnd()) {
auto line = ts.readLine().trim();
if (multi_source) {
if (line.endsWith("")) {
line.cutRight(9);
multi_source = false;
}
msg.source += "\n" + unmask(line);
continue;
}
if (multi_translation) {
if (line.endsWith("")) {
line.cutRight(14);
multi_translation = false;
}
msg.translation += "\n" + unmask(line);
continue;
}
if (multi_comment) {
if (line.endsWith("")) {
line.cutRight(20);
multi_comment = false;
}
msg.comment += "\n" + unmask(line);
continue;
}
switch (phase) {
case pHeader:
if (line.startsWith("") phase = pContextName;
break;
case pContextName:
if (line.startsWith("")) {
line.cutLeft(6).cutRight(7);
cc = &(ret.contexts[line]);
phase = pMessage;
}
break;
case pMessage:
if (line == "") {
msg = {};
phase = pMessageBody;
}
break;
case pMessageBody:
if (line == "") {
if (cc) cc->messages[msg.source] = msg;
phase = pMessage;
} else if (line.startsWith("")) {
line.cutLeft(8);
if (line.endsWith(""))
line.cutRight(9);
else
multi_source = true;
msg.source = unmask(line);
} else if (line.startsWith("').cutLeft(8);
while (trs.isNotEmpty()) {
PIString t = trs.takeCWord();
trs.cutLeft(1);
PIString v = trs.takeRange('\"', '\"');
if (t == "filename") msg.filename = v;
if (t == "line") msg.line = v;
if (trs.size_s() <= 2) break;
}
} else if (line.startsWith("').cutLeft(11);
while (trs.isNotEmpty()) {
PIString t = trs.takeCWord();
trs.cutLeft(1);
PIString v = trs.takeRange('\"', '\"');
if (t == "type") {
if (v == "unfinished")
msg.type.setFlag(Message::Unfinished);
else if (v == "vanished")
msg.type.setFlag(Message::Missing);
}
}
if (line.endsWith(""))
line.cutRight(14);
else
multi_translation = true;
msg.translation = unmask(line);
} else if (line.startsWith("")) {
line.cutLeft(19);
if (line.endsWith(""))
line.cutRight(20);
else
multi_comment = true;
msg.comment = unmask(line);
}
break;
}
if (line == "") {
cc = nullptr;
phase = pContext;
}
}
return ret;
}
bool TSFile::write(const PIString & path, const TSFile::Content & content, bool no_obsolete) {
PIFile outf(path, PIIODevice::ReadWrite);
if (!outf.isOpened()) {
piCerr << "Can`t open file \"%1\"!"_tr("TSFile").arg(outf.path());
return false;
}
outf.clear();
PIIOTextStream ts(&outf);
auto writeMessage = [&ts](const Message & m) {
if (m.source.isEmpty()) return;
ts << " \n";
if (m.filename.isNotEmpty()) {
ts << " \n";
}
ts << " " << mask(m.source) << "\n";
if (m.comment.isNotEmpty()) {
ts << " " << mask(m.comment) << "\n";
}
ts << " " << mask(m.translation) << "\n";
ts << " \n";
};
ts << "\n";
ts << "\n";
ts << "\n";
auto cit = content.contexts.makeIterator();
while (cit.next()) {
if (cit.value().messages.isEmpty()) continue;
ts << "\n";
ts << " " << cit.key() << "\n";
auto mit = cit.value().messages.makeIterator();
while (mit.next()) {
if (mit.value().type[Message::Missing] && no_obsolete) continue;
writeMessage(mit.value());
}
ts << "\n";
};
ts << "\n";
return true;
}
void TSFile::Content::markAllMissing() {
auto cit = contexts.makeIterator();
while (cit.next()) {
auto mit = cit.value().messages.makeIterator();
while (mit.next()) {
mit.value().type.setFlag(Message::Missing);
}
}
}