QtNovelUI/WordsIDE/parsebridge.cpp

427 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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; }
QStandardItemModel *ParseBridge::volumesPresentModel() const { return volumes_model; }
QStandardItemModel *ParseBridge::errorsPresentModel() const { return errors_model; }
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 {
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()));
}
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 "超出有效索引范围";
}
}