QtNovelUI/WordsIDE/parsebridge.cpp

427 lines
15 KiB
C++
Raw Normal View History

2023-08-13 10:02:21 +00:00
#include "parsebridge.h"
#include <QFileInfo>
#include <QList>
#include <libConfig.h>
using namespace bridge;
class Impl_NovelParseException : public Config::ParseException {
private:
QString title_s, reason_s;
public:
Impl_NovelParseException(const QString &t, const QString &r) {
this->title_s = t;
this->reason_s = r;
}
// ParseException interface
public:
virtual QString reason() const override { return reason_s; }
virtual QString title() const override { return reason_s; }
};
ParseBridge::ParseBridge() : chains_model(new QStandardItemModel), volumes_model(new QStandardItemModel), errors_model(new QStandardItemModel) {}
ParseBridge::~ParseBridge() {
delete chains_model;
delete volumes_model;
delete errors_model;
for (auto it : docs_store)
delete it;
docs_store.clear();
}
QString ParseBridge::novelName() const { return name_store; }
QStandardItemModel *ParseBridge::chainsPresentModel() const { return chains_model; }
2023-08-13 10:02:21 +00:00
QStandardItemModel *ParseBridge::volumesPresentModel() const { return volumes_model; }
QStandardItemModel *ParseBridge::errorsPresentModel() const { return errors_model; }
2023-08-13 10:02:21 +00:00
void bridge::ParseBridge::load(const QString &data_path) {
// clear cache
this->doc_ins.clear();
chains_model->clear();
volumes_model->clear();
errors_model->clear();
for (auto it : docs_store)
delete it;
docs_store.clear();
// 重新载入小说编译结果
QFileInfo target_xml(data_path);
if (!target_xml.exists())
throw new Impl_NovelParseException("小说解析过程错", "指定路径数据文件不存在:" + data_path);
QFile data_in(target_xml.canonicalFilePath());
if (!data_in.open(QIODevice::ReadOnly | QIODevice::Text))
throw new Impl_NovelParseException("小说解析过程错", "指定数据文件无法打开:" + target_xml.canonicalFilePath());
QString err_msg;
int col, row;
if (!this->doc_ins.setContent(&data_in, &err_msg, &row, &col)) {
throw new Impl_NovelParseException("小说解析过程我", QString("数据文件格式错:<row:%1, col:%2>%3").arg(row).arg(col).arg(err_msg));
}
// 载入语法节点
auto root = doc_ins.documentElement();
name_store = root.attribute("name", "空白小说名称");
auto childnodes = root.childNodes();
for (auto idx = 0; idx < childnodes.size(); ++idx) {
auto child = childnodes.at(idx);
if (child.isElement()) {
auto elm_inst = child.toElement();
if (elm_inst.tagName() == "doc") {
load_document(elm_inst, this->docs_store);
}
}
}
}
void ParseBridge::load_document(const QDomElement &pdoc, std::vector<DocumentInst *> node_sets) {
auto doc_name = pdoc.attribute("name", "未命名文档名");
auto file_path = pdoc.attribute("file");
auto doc_ins = new DocumentInst(doc_name, file_path);
node_sets.push_back(doc_ins);
// 载入解析错误
auto errors = pdoc.elementsByTagName("error");
for (auto idx = 0; idx < errors.size(); ++idx) {
auto e_elm = errors.at(idx).toElement();
load_errors(errorsPresentModel(), e_elm, doc_ins);
}
auto children = pdoc.childNodes();
for (auto idx = 0; idx < children.size(); ++idx) {
auto node = children.at(idx);
if (node.isElement()) {
load_element(node.toElement(), doc_ins);
}
}
}
void ParseBridge::load_element(const QDomElement &telm, DocumentInst *pinst, QStandardItem *pitem) {
if (telm.tagName() == "story")
load_story(chainsPresentModel(), telm, pinst);
else if (telm.tagName() == "text")
load_desc(telm, static_cast<ContentNode *>(pitem));
else if (telm.tagName() == "fragment")
load_fragment(telm, pitem);
else if (telm.tagName() == "frag-refer")
load_frag_refer(telm, pitem);
else if (telm.tagName() == "volume")
load_volumes(volumesPresentModel(), telm, pinst);
else if (telm.tagName() == "article")
load_article(telm, pitem);
else
throw new Impl_NovelParseException("小说解析结果载入错", "位置tagName" + telm.tagName());
}
void ParseBridge::load_errors(QStandardItemModel *anchor, const QDomElement &error_elm, DocumentInst *pinst) {
auto pos_value = error_elm.attribute("pos");
auto message = error_elm.attribute("message");
QRegExp exp0("(\\d+)");
auto index = exp0.indexIn(pos_value);
auto row = exp0.cap(1).toInt();
exp0.indexIn(pos_value, index + exp0.cap(1).length());
auto col = exp0.cap(1).toInt();
auto perror = new ParseException(pinst);
perror->load(message, row, col);
QList<QStandardItem *> inst_row;
inst_row << perror;
inst_row << new SupplyItem<ParseException, 1>(perror);
inst_row << new SupplyItem<ParseException, 2>(perror);
inst_row << new SupplyItem<ParseException, 3>(perror);
anchor->appendRow(inst_row);
}
#define PEAK_POSITION(e) \
auto start_pos = (e).attribute("start-pos"); \
auto end_pos = (e).attribute("end-pos"); \
QRegExp exp("(\\d+)"); \
auto xidx = exp.indexIn(start_pos); \
auto start_row = exp.cap(1).toInt(); \
exp.indexIn(start_pos, xidx + exp.cap(1).length()); \
auto start_col = exp.cap(1).toInt(); \
xidx = exp.indexIn(end_pos); \
auto end_row = exp.cap(1).toInt(); \
exp.indexIn(start_pos, xidx + exp.cap(1).length()); \
auto end_col = exp.cap(1).toInt();
void ParseBridge::load_story(QStandardItemModel *model, const QDomElement &chain_elm, DocumentInst *pinst) {
auto name = chain_elm.attribute("name");
auto index = chain_elm.attribute("sort");
auto story = new StorychainInst(pinst, name);
model->appendRow(story);
story->indexSet(index.toInt());
PEAK_POSITION(chain_elm);
story->loadPos(start_row, start_col, end_row, end_col);
auto children = chain_elm.childNodes();
for (auto idx = 0; idx < children.size(); ++idx) {
auto node = children.at(idx);
if (node.isElement()) {
load_element(node.toElement(), nullptr, story);
}
}
}
void ParseBridge::load_desc(const QDomElement &desc_elm, ContentNode *pitem) {
auto content = desc_elm.attribute("content");
pitem->append(content);
}
void ParseBridge::load_fragment(const QDomElement &frag_elm, QStandardItem *pinst) {
auto name = frag_elm.attribute("name");
auto logic_index = frag_elm.attribute("logic-index");
PEAK_POSITION(frag_elm);
auto frag_inst = new StoryfragmentInst(name);
pinst->appendRow(frag_inst);
frag_inst->setLogicIndex(logic_index.toInt());
frag_inst->loadPos(start_row, start_col, end_row, end_col);
auto children = frag_elm.childNodes();
for (auto idx = 0; idx < children.size(); ++idx) {
auto node = children.at(idx);
if (node.isElement()) {
load_element(node.toElement(), nullptr, frag_inst);
}
}
}
void ParseBridge::load_frag_refer(const QDomElement &refer_elm, QStandardItem *pitem) {
auto story = refer_elm.attribute("story-ref");
auto frag = refer_elm.attribute("fragment-ref");
PEAK_POSITION(refer_elm);
auto item = locate_node(chainsPresentModel(), QStringList() << story << frag);
auto refer_inst = new StoryfragmentRefer(static_cast<StoryfragmentInst *>(item));
pitem->appendRow(refer_inst);
refer_inst->appointStory(story);
refer_inst->appointFragment(frag);
refer_inst->loadPos(start_row, start_col, end_row, end_col);
auto children = refer_elm.childNodes();
for (auto idx = 0; idx < children.size(); ++idx) {
auto node = children.at(idx);
if (node.isElement()) {
load_element(node.toElement(), nullptr, refer_inst);
}
}
}
void ParseBridge::load_volumes(QStandardItemModel *model, const QDomElement &volume_elm, DocumentInst *pinst) {
auto name = volume_elm.attribute("content");
PEAK_POSITION(volume_elm);
auto volume = new StoryvolumeInst(pinst, name);
model->appendRow(volume);
volume->loadPos(start_row, start_col, end_row, end_col);
auto children = volume_elm.childNodes();
for (auto idx = 0; idx < children.size(); ++idx) {
auto node = children.at(idx);
if (node.isElement()) {
load_element(node.toElement(), nullptr, volume);
}
}
}
void ParseBridge::load_article(const QDomElement &article_elm, QStandardItem *pitem) {
auto name = article_elm.attribute("content");
PEAK_POSITION(article_elm);
auto article = new StoryarticleInst(name);
pitem->appendRow(article);
article->loadPos(start_row, start_col, end_row, end_col);
auto children = article_elm.childNodes();
for (auto idx = 0; idx < children.size(); ++idx) {
auto node = children.at(idx);
if (node.isElement()) {
load_element(node.toElement(), nullptr, article);
}
}
}
QStandardItem *ParseBridge::locate_node(QStandardItemModel *model, const QStringList path) {
QStandardItem *current_item = nullptr;
for (auto &n : path) {
if (!current_item) {
for (auto idx = 0; idx < model->rowCount(); ++idx) {
if (model->item(idx)->text() == n)
current_item = model->item(idx);
}
} else {
2023-08-13 10:02:21 +00:00
for (auto idx = 0; idx < current_item->rowCount(); ++idx) {
if (current_item->child(idx)->text() == n) {
current_item = current_item->child(idx);
}
}
}
}
return current_item;
}
ParseException::ParseException(DocumentInst *pinst) : key_store(std::make_tuple(pinst, "尚未载入异常信息", 0, 0)) { setText(std::get<1>(key_store)); }
void ParseException::load(const QString &message, int row, int col) {
this->key_store = std::make_tuple(std::get<0>(key_store), message, row, col);
setText(std::get<1>(key_store));
}
const DocumentInst *ParseException::doc() const { return std::get<0>(key_store); }
QString ParseException::message() const { return std::get<1>(key_store); }
int ParseException::row() const { return std::get<2>(key_store); }
int ParseException::col() const { return std::get<3>(key_store); }
QString ParseException::operator[](int index) {
switch (index) {
case 0:
return message();
case 1:
return QString("%1").arg(row());
case 2:
return QString("%1").arg(col());
case 3:
return QString("%1").arg(doc()->name());
default:
return "超出索引范围";
}
}
DocumentInst::DocumentInst(const QString &name, const QString &path) : n(name), p(path) {}
QString DocumentInst::name() const { return n; }
QString DocumentInst::path() const { return p; }
StorychainInst::StorychainInst(DocumentInst *pinst, const QString &name) : ContentNode(name), pinst_store(pinst) {}
void StorychainInst::indexSet(int sort) { this->sort_store = sort; }
int StorychainInst::sortIndex() const { return sort_store; }
ContentNode::ContentNode(const QString &name) {
setText(name);
name_store = name;
}
std::tuple<int, int> SyntaxNode::startPos() const { return this->start; }
std::tuple<int, int> SyntaxNode::endPos() const { return this->end; }
void SyntaxNode::loadPos(int start_r, int start_c, int end_r, int end_c) {
this->start = std::make_tuple(start_r, start_c);
this->end = std::make_tuple(end_r, end_c);
}
void ContentNode::append(const QString &section) { this->desc_sections += section + "\n"; }
QString ContentNode::desc() const { return desc_sections; }
void ContentNode::clear() { desc_sections = ""; }
DocumentInst *StorychainInst::doc() const { return pinst_store; }
QString StorychainInst::operator[](int index) {
switch (index) {
case 0:
return text();
case 1:
return QString("%1").arg(sortIndex());
case 2:
return desc();
default:
return "超出索引范围";
}
}
StoryfragmentInst::StoryfragmentInst(const QString &name) : ContentNode(name) {}
int StoryfragmentInst::logicIndex() const { return sort_store; }
void StoryfragmentInst::setLogicIndex(int index) { this->sort_store = index; }
QString StoryfragmentInst::operator[](int index) {
switch (index) {
case 0:
return text();
case 1:
return QString("%1").arg(logicIndex());
case 2:
return desc();
default:
return "超出有效索引范围";
}
}
StoryfragmentRefer::StoryfragmentRefer(StoryfragmentInst *inst) : ContentNode(""), inst(inst) {
setText(QString("@%1/%2").arg(inst->parent()->text(), inst->text()));
}
2023-08-13 10:02:21 +00:00
QString StoryfragmentRefer::storyRef() const { return this->story; }
void StoryfragmentRefer::appointStory(const QString &ref) { this->story = ref; }
QString StoryfragmentRefer::fragmentRef() const { return fragment; }
void StoryfragmentRefer::appointFragment(const QString &ref) { fragment = ref; }
QString StoryfragmentRefer::operator[](int index) {
switch (index) {
case 0:
return storyRef();
case 1:
return fragmentRef();
case 2:
return desc();
default:
return "超出有效索引范围";
}
}
StoryvolumeInst::StoryvolumeInst(DocumentInst *inst, const QString &name) : ContentNode(name), doc_store(inst) {}
DocumentInst *StoryvolumeInst::doc() const { return doc_store; }
QString StoryvolumeInst::operator[](int index) {
switch (index) {
case 0:
return text();
case 1:
return desc();
default:
return "超出有效索引范围";
}
}
StoryarticleInst::StoryarticleInst(const QString &name) : ContentNode(name) {}
QString StoryarticleInst::operator[](int index) {
switch (index) {
case 0:
return text();
case 1:
return desc();
default:
return "超出有效索引范围";
}
}