199 lines
5.3 KiB
C++
199 lines
5.3 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
|
|
**
|
|
** This file is part of the Edyuk project <http://edyuk.org>
|
|
**
|
|
** This file may be used under the terms of the GNU General Public License
|
|
** version 3 as published by the Free Software Foundation and appearing in the
|
|
** file GPL.txt included in the packaging of this file.
|
|
**
|
|
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qcodeserializer.h"
|
|
|
|
/*!
|
|
\file qcodeserializer.cpp
|
|
\brief Implementation of the QCodeSerializer class.
|
|
*/
|
|
|
|
#include "qcodemodel.h"
|
|
#include "qcodenode.h"
|
|
#include "qcodenodepool.h"
|
|
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QIODevice>
|
|
#include <QRegularExpression>
|
|
#include <QTextStream>
|
|
|
|
/*
|
|
File format :
|
|
* header : '{' MIME type (text/x-tag) ',' version ',' source '}'
|
|
* each line represents on node as a at ('@') separated list of roles
|
|
* each space at the begining of the line represents a parenting level
|
|
|
|
*/
|
|
|
|
#define Q_VERSION(maj, min, patch) ((maj << 8) | (min << 8) | patch)
|
|
|
|
static int _version = Q_VERSION(2, 0, 0);
|
|
static int _version_min = Q_VERSION(2, 0, 0), _version_max = Q_VERSION(2, 0, 0);
|
|
static QString header_pattern("{text/x-tag,%1,%2}");
|
|
static QRegularExpression
|
|
header_matcher("^\\{text/x-tag,([0-9a-fA-F]+),([^}]+)\\}\n$");
|
|
|
|
/*!
|
|
\class QCodeSerializer
|
|
\brief Serializer/deserializer for code trees
|
|
*/
|
|
|
|
QCodeSerializer::QCodeSerializer() : m_model(0), m_pool(0) {}
|
|
|
|
QCodeSerializer::~QCodeSerializer() {}
|
|
|
|
QCodeModel *QCodeSerializer::targetModel() const { return m_model; }
|
|
|
|
void QCodeSerializer::setTargetModel(QCodeModel *m) { m_model = m; }
|
|
|
|
QCodeNodePool *QCodeSerializer::nodePool() const { return m_pool; }
|
|
|
|
void QCodeSerializer::setNodePool(QCodeNodePool *p) { m_pool = p; }
|
|
|
|
void QCodeSerializer::serialize(QCodeNode *n, QTextStream &out, int indent) {
|
|
if (!n)
|
|
return;
|
|
|
|
if (indent > 0)
|
|
out << QByteArray(indent, ' ');
|
|
else
|
|
out << header_pattern.arg(QString::number(_version))
|
|
.arg(QString::fromLocal8Bit(n->role(QCodeNode::Context)))
|
|
<< Qt::endl;
|
|
|
|
out << n->roles << "\n";
|
|
|
|
foreach (QCodeNode *c, n->children)
|
|
serialize(c, out, indent + 1);
|
|
}
|
|
|
|
void QCodeSerializer::deserialize(const QString &file, bool *ok,
|
|
QString *source, const QDateTime &t) {
|
|
QFile f(file);
|
|
|
|
if (f.open(QFile::Text | QFile::ReadOnly))
|
|
deserialize(&f, ok, source, t);
|
|
}
|
|
|
|
void QCodeSerializer::deserialize(QIODevice *device, bool *ok, QString *source,
|
|
const QDateTime &t) {
|
|
if (!device)
|
|
return;
|
|
|
|
QByteArray line;
|
|
int indent = 0, spaces;
|
|
QCodeNode *n, *previous = 0, *parent = 0;
|
|
|
|
line = device->readLine();
|
|
auto match = header_matcher.match(line);
|
|
if (!match.hasMatch()) {
|
|
qWarning("Header mismatch : %s", line.constData());
|
|
|
|
if (ok)
|
|
*ok = false;
|
|
|
|
return;
|
|
}
|
|
|
|
int version = match.captured(1).toInt(0, 0);
|
|
|
|
if ((version < _version_min) || (version > _version_max)) {
|
|
qWarning("Version mismatch : %i !E [%i, %i]", version, _version_min,
|
|
_version_max);
|
|
|
|
if (ok)
|
|
*ok = false;
|
|
|
|
return;
|
|
}
|
|
|
|
if (source)
|
|
*source = match.captured(2);
|
|
|
|
if (t.isValid() && ok) {
|
|
// check time stamps if someone pay attention to success/failure
|
|
QFileInfo info(*source);
|
|
|
|
if (info.lastModified() > t) {
|
|
// source modified after tag file... must reload...
|
|
|
|
*ok = false;
|
|
}
|
|
}
|
|
|
|
while (!device->atEnd()) {
|
|
spaces = 0;
|
|
char c;
|
|
bool ok = device->getChar(&c);
|
|
|
|
while (ok && isspace(c) && (c != '\n')) {
|
|
++spaces;
|
|
ok = device->getChar(&c);
|
|
}
|
|
|
|
if (!ok || c == '\n')
|
|
continue;
|
|
|
|
if (spaces > indent) {
|
|
// down one level
|
|
parent = previous;
|
|
|
|
// if ( parent )
|
|
// qDebug("down a level : parent = %s",
|
|
// parent->role(QCodeNode::Name).constData());
|
|
} else if (spaces < indent) {
|
|
|
|
// up one (or more) level
|
|
for (int i = 0; (i < (indent - spaces)) && parent && parent->parent;
|
|
++i)
|
|
parent = parent ? parent->parent : 0;
|
|
|
|
// if ( parent )
|
|
// qDebug("up one level : parent = %s",
|
|
// parent->role(QCodeNode::Name).constData());
|
|
} else {
|
|
// qDebug("adding child %s", line.constData());
|
|
}
|
|
|
|
n = m_pool ? m_pool->alloc() : new QCodeNode;
|
|
|
|
while (ok && c != '\n') {
|
|
n->roles += c;
|
|
ok = device->getChar(&c);
|
|
}
|
|
|
|
// n->roles = device->readLine();
|
|
// n->roles.prepend(c);
|
|
|
|
n->roles.squeeze();
|
|
|
|
if (parent)
|
|
n->attach(parent);
|
|
|
|
indent = spaces;
|
|
previous = n;
|
|
}
|
|
|
|
while (parent && parent->parent)
|
|
parent = parent->parent;
|
|
|
|
if (parent && m_model)
|
|
m_model->appendTopLevelNode(parent);
|
|
|
|
if (ok)
|
|
*ok = true;
|
|
}
|