passwordfile/io/entry.cpp

455 lines
12 KiB
C++
Raw Normal View History

2015-09-06 20:33:27 +02:00
#include "./entry.h"
#include "./parsingexception.h"
2015-04-22 19:06:29 +02:00
#include <c++utilities/io/binaryreader.h>
#include <c++utilities/io/binarywriter.h>
#include <algorithm>
2017-05-01 03:25:30 +02:00
#include <sstream>
2015-04-22 19:06:29 +02:00
using namespace std;
using namespace IoUtilities;
using namespace ConversionUtilities;
namespace Io {
/*!
* \namespace Io
* \brief Contains all IO related classes.
*/
/*!
* \class Entry
* \brief Instances of the Entry class form a hierarchic data strucutre used to store
* account information.
*
* Entries can be serialized and deserialized using the parse() and make() methods.
*/
/*!
* \brief Constructs a new entry with the specified \a label and \a parent.
*/
2017-05-01 03:25:30 +02:00
Entry::Entry(const string &label, NodeEntry *parent)
: m_parent(nullptr)
, m_index(-1)
2015-04-22 19:06:29 +02:00
{
setParent(parent);
setLabel(label);
}
/*!
* \brief Constructs a copy of the another entry.
* \remarks The copy will be parentless and thus not be embedded in the hierarchy
* of \a other. Child entries will be copied as well.
*/
2017-05-01 03:25:30 +02:00
Entry::Entry(const Entry &other)
: m_label(other.m_label)
, m_parent(nullptr)
, m_index(-1)
{
}
2015-04-22 19:06:29 +02:00
/*!
* \brief Destroys the entry.
*/
Entry::~Entry()
{
setParent(nullptr);
}
/*!
* \brief Internally called to make the label unique.
* \sa setLabel()
*/
void Entry::makeLabelUnique()
{
2017-05-01 03:25:30 +02:00
if (m_parent) {
2015-04-22 19:06:29 +02:00
int index = 1;
string currentLabel(label());
2017-05-01 03:25:30 +02:00
checkLabel:
for (Entry *sibling : m_parent->children()) {
if (sibling != this && currentLabel == sibling->label()) {
2015-04-22 19:06:29 +02:00
stringstream newLabel(currentLabel);
newLabel.seekp(0, ios_base::end);
2017-05-01 03:25:30 +02:00
if (newLabel.tellp()) {
2015-04-22 19:06:29 +02:00
newLabel << ' ';
}
newLabel << ++index;
currentLabel = newLabel.str();
goto checkLabel;
}
}
m_label = currentLabel;
}
}
/*!
* \brief Sets the \a parent for the entry.
*
* If an \a index is specified the entry will be inserted as child at this position.
* If \a parent is nullptr, the entry will be parentless.
*/
void Entry::setParent(NodeEntry *parent, int index)
{
2017-05-01 03:25:30 +02:00
if (m_parent != parent || (m_index != index && index >= 0)) {
if (m_parent) {
2015-04-22 19:06:29 +02:00
m_parent->m_children.erase(m_parent->m_children.begin() + m_index);
2017-05-01 03:25:30 +02:00
for (auto i = m_parent->m_children.begin() + m_index; i < m_parent->m_children.end(); ++i) {
2015-04-22 19:06:29 +02:00
(*i)->m_index -= 1;
}
}
2017-05-01 03:25:30 +02:00
if (parent) {
if (index < 0 || static_cast<size_t>(index) >= parent->m_children.size()) {
2015-04-22 19:06:29 +02:00
m_index = parent->m_children.size();
parent->m_children.push_back(this);
} else {
2017-05-01 03:25:30 +02:00
for (auto i = parent->m_children.insert(parent->m_children.begin() + index, this) + 1; i != parent->m_children.end(); ++i) {
2015-04-22 19:06:29 +02:00
(*i)->m_index += 1;
}
m_index = index;
}
} else {
m_index = -1;
}
m_parent = parent;
makeLabelUnique();
}
}
/*!
* \brief Returns an indication whether the instance is an indirect child of the specified \a entry.
*/
bool Entry::isIndirectChildOf(NodeEntry *entry) const
{
2017-05-01 03:25:30 +02:00
if (parent()) {
if (parent() == entry) {
2015-04-22 19:06:29 +02:00
return true;
} else {
return parent()->isIndirectChildOf(entry);
}
} else {
return false;
}
}
/*!
* \brief Returns the path of the entry.
*/
std::list<string> Entry::path() const
{
list<string> res;
path(res);
return res;
}
/*!
* \brief Stores to path of the entry in the specified list of string.
*/
void Entry::path(std::list<string> &res) const
{
2017-05-01 03:25:30 +02:00
if (m_parent) {
2015-04-22 19:06:29 +02:00
m_parent->path(res);
}
res.push_back(label());
}
/*!
* \brief Parses an entry from the specified \a stream.
* \throws Throws ParsingException when a parsing exception occurs.
*/
Entry *Entry::parse(istream &stream)
{
byte version = stream.peek();
2017-05-01 03:25:30 +02:00
if (denotesNodeEntry(version)) {
2015-04-22 19:06:29 +02:00
return new NodeEntry(stream);
} else {
return new AccountEntry(stream);
}
}
/*!
* \fn Entry::type()
* \brief Returns the type of the entry.
*/
/*!
* \fn Entry::make()
* \brief Serializes the entry to the specified \a stream.
*/
/*!
* \fn Entry::clone()
* \brief Clones the entry.
* \remarks The copy will be parentless and thus not be embedded in the hierarchy
* of \a other. Child entries will be copied as well.
*/
/*!
* \class NodeEntry
* \brief The NodeEntry class acts as parent for other entries.
*/
/*!
* \brief Constructs a new node entry.
*/
2017-05-01 03:25:30 +02:00
NodeEntry::NodeEntry()
: Entry()
, m_expandedByDefault(true)
{
}
2015-04-22 19:06:29 +02:00
/*!
* \brief Constructs a new node entry with the specified \a label and \a parent.
*/
2017-05-01 03:25:30 +02:00
NodeEntry::NodeEntry(const string &label, NodeEntry *parent)
: Entry(label, parent)
, m_expandedByDefault(true)
{
}
2015-04-22 19:06:29 +02:00
/*!
* \brief Constructs a new node entry which is deserialized from the specified \a stream.
*/
2017-05-01 03:25:30 +02:00
NodeEntry::NodeEntry(istream &stream)
: m_expandedByDefault(true)
2015-04-22 19:06:29 +02:00
{
BinaryReader reader(&stream);
byte version = reader.readByte();
2017-05-01 03:25:30 +02:00
if (denotesNodeEntry(version)) {
if (version == 0x0 || version == 0x1) {
2015-04-22 19:06:29 +02:00
setLabel(reader.readLengthPrefixedString());
2017-05-01 03:25:30 +02:00
if (version == 0x1) { // version 0x1 has an extended header
2015-04-22 19:06:29 +02:00
uint16 extendedHeaderSize = reader.readUInt16BE();
2017-05-01 03:25:30 +02:00
if (extendedHeaderSize >= 1) {
2015-04-22 19:06:29 +02:00
byte flags = reader.readByte();
m_expandedByDefault = flags & 0x80;
extendedHeaderSize -= 1;
}
m_extendedData = reader.readString(extendedHeaderSize);
2015-04-22 19:06:29 +02:00
}
uint32 childCount = reader.readUInt32BE();
2017-05-01 03:25:30 +02:00
for (uint32 i = 0; i < childCount; ++i) {
2015-04-22 19:06:29 +02:00
Entry::parse(stream)->setParent(this);
}
} else {
throw ParsingException("Entry version not supported.");
}
} else {
throw ParsingException("Node entry expected.");
}
}
/*!
* \brief Constructs a copy of the another entry.
* \remarks The copy will be parentless and thus not be embedded in the hierarchy
* of \a other. Child entries will be copied as well.
*/
2017-05-01 03:25:30 +02:00
NodeEntry::NodeEntry(const NodeEntry &other)
: Entry(other)
2015-04-22 19:06:29 +02:00
{
2017-05-01 03:25:30 +02:00
for (Entry *otherChild : other.m_children) {
2015-04-22 19:06:29 +02:00
Entry *clonedChild = otherChild->clone();
clonedChild->m_parent = this;
clonedChild->m_index = m_children.size();
m_children.push_back(clonedChild);
}
}
/*!
* \brief Destroys the entry.
*/
NodeEntry::~NodeEntry()
{
2017-05-01 03:25:30 +02:00
for (Entry *child : m_children) {
2015-04-22 19:06:29 +02:00
child->m_parent = nullptr;
delete child;
}
}
/*!
* \brief Deletes children from the node entry.
* \param begin Specifies the index of the first children to delete.
* \param end Specifies the index after the last children to delete.
*/
void NodeEntry::deleteChildren(int begin, int end)
{
auto iterator = m_children.cbegin() + begin;
auto endIterator = m_children.begin() + end;
2017-05-01 03:25:30 +02:00
for (; iterator < endIterator; ++iterator) {
2015-04-22 19:06:29 +02:00
(*iterator)->m_parent = nullptr;
delete *iterator;
}
m_children.erase(m_children.begin() + begin, endIterator);
}
/*!
* \brief Replaces the child \a at the specified index with the specified \a newChild.
*/
void NodeEntry::replaceChild(size_t at, Entry *newChild)
{
2017-05-01 03:25:30 +02:00
if (at < m_children.size()) {
2015-04-22 19:06:29 +02:00
m_children.at(at)->m_parent = nullptr;
m_children[at] = newChild;
}
}
/*!
* \brief Returns an entry specified by the provided \a path.
* \param path Specifies the path of the entry to be returned.
* \param includeThis Specifies whether the current instance should be included.
* \param creationType Specifies a pointer which dereferenced value determines what kind of entry should be created
* if the entry specified by the provided \a path does not exist. The parent of the entry
* to be created must exist. Specify nullptr if no entries should be created (default).
* \returns Returns the entry if found (or created); otherwise nullptr is returned.
*/
Entry *NodeEntry::entryByPath(list<string> &path, bool includeThis, EntryType *creationType)
{
2017-05-01 03:25:30 +02:00
if (path.size()) {
if (includeThis) {
if (path.front() == label()) {
2015-04-22 19:06:29 +02:00
path.pop_front();
} else {
return nullptr;
}
}
2017-05-01 03:25:30 +02:00
if (path.size()) {
for (Entry *child : m_children) {
if (path.front() == child->label()) {
2015-04-22 19:06:29 +02:00
path.pop_front();
2017-05-01 03:25:30 +02:00
if (path.empty()) {
2015-04-22 19:06:29 +02:00
return child;
2017-05-01 03:25:30 +02:00
} else if (child->type() == EntryType::Node) {
2015-04-22 19:06:29 +02:00
return static_cast<NodeEntry *>(child)->entryByPath(path, false, creationType);
} else {
return nullptr; // can not resolve path since an account entry can not have children
}
}
}
2017-05-01 03:25:30 +02:00
if (creationType) {
if (path.size() == 1) {
switch (*creationType) {
2015-04-22 19:06:29 +02:00
case EntryType::Account:
return new AccountEntry(path.front(), this);
case EntryType::Node:
return new NodeEntry(path.front(), this);
}
} else {
return nullptr;
}
}
} else {
return this;
}
}
return nullptr;
}
void NodeEntry::make(ostream &stream) const
{
BinaryWriter writer(&stream);
writer.writeByte(isExpandedByDefault() && m_extendedData.empty() ? 0x0 : 0x1); // version
2015-04-22 19:06:29 +02:00
writer.writeLengthPrefixedString(label());
2017-05-01 03:25:30 +02:00
if (!isExpandedByDefault() || !m_extendedData.empty()) {
writer.writeUInt16BE(1 + m_extendedData.size()); // extended header is 1 byte long
byte flags = 0x00;
2017-05-01 03:25:30 +02:00
if (isExpandedByDefault()) {
flags |= 0x80;
}
writer.writeByte(flags);
writer.writeString(m_extendedData);
2015-04-22 19:06:29 +02:00
}
writer.writeUInt32BE(m_children.size());
2017-05-01 03:25:30 +02:00
for (const Entry *child : m_children) {
2015-04-22 19:06:29 +02:00
child->make(stream);
}
}
NodeEntry *NodeEntry::clone() const
{
return new NodeEntry(*this);
}
/*!
* \class AccountEntry
* \brief The exception that is thrown when a parsing error occurs.
*/
AccountEntry::AccountEntry()
2017-05-01 03:25:30 +02:00
{
}
2015-04-22 19:06:29 +02:00
/*!
* \brief Constructs a new account entry with the specified \a label and \a parent.
*/
2017-05-01 03:25:30 +02:00
AccountEntry::AccountEntry(const string &label, NodeEntry *parent)
: Entry(label, parent)
{
}
2015-04-22 19:06:29 +02:00
/*!
* \brief Constructs a new account entry which is deserialized from the specified \a stream.
*/
AccountEntry::AccountEntry(istream &stream)
{
BinaryReader reader(&stream);
byte version = reader.readByte();
2017-05-01 03:25:30 +02:00
if (!denotesNodeEntry(version)) {
2015-04-22 19:06:29 +02:00
version ^= 0x80; // set bit 0 to false
2017-05-01 03:25:30 +02:00
if (version == 0x0 || version == 0x1) {
2015-04-22 19:06:29 +02:00
setLabel(reader.readLengthPrefixedString());
2017-05-01 03:25:30 +02:00
if (version == 0x1) { // version 0x1 has an extended header
2015-04-22 19:06:29 +02:00
uint16 extendedHeaderSize = reader.readUInt16BE();
// currently there's nothing to read here
m_extendedData = reader.readString(extendedHeaderSize);
2015-04-22 19:06:29 +02:00
}
uint32 fieldCount = reader.readUInt32BE();
2017-05-01 03:25:30 +02:00
for (uint32 i = 0; i < fieldCount; ++i) {
2015-04-22 19:06:29 +02:00
m_fields.push_back(Field(this, stream));
}
} else {
throw ParsingException("Entry version not supported.");
}
} else {
throw ParsingException("Account entry expected.");
}
}
/*!
* \brief Constructs a copy of the another entry.
* \remarks The copy will be parentless and thus not be embedded in the hierarchy
* of \a other. Child entries will be copied as well.
*/
2017-05-01 03:25:30 +02:00
AccountEntry::AccountEntry(const AccountEntry &other)
: Entry(other)
2015-04-22 19:06:29 +02:00
{
m_fields = other.m_fields;
}
/*!
* \brief Destroys the entry.
*/
AccountEntry::~AccountEntry()
2017-05-01 03:25:30 +02:00
{
}
2015-04-22 19:06:29 +02:00
void AccountEntry::make(ostream &stream) const
{
BinaryWriter writer(&stream);
writer.writeByte(0x80 | (m_extendedData.empty() ? 0x0 : 0x1)); // version
2015-04-22 19:06:29 +02:00
writer.writeLengthPrefixedString(label());
2017-05-01 03:25:30 +02:00
if (!m_extendedData.empty()) {
writer.writeUInt16BE(m_extendedData.size());
writer.writeString(m_extendedData);
}
2015-04-22 19:06:29 +02:00
writer.writeUInt32BE(m_fields.size());
2017-05-01 03:25:30 +02:00
for (const Field &field : m_fields) {
2015-04-22 19:06:29 +02:00
field.make(stream);
}
}
AccountEntry *AccountEntry::clone() const
{
return new AccountEntry(*this);
}
}