/* * Copyright (C) 2014 Aaron Seigo * * This library 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 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 library. If not, see . */ #include "dataset.h" #include #include #include #include namespace HAWD { static const QString s_annotationKey("__annotation__"); static const QString s_hashKey("__commithash__"); static const QString s_timestampKey("__timestamp"); static const int s_fieldWidth(20); Dataset::Row::Row(const Row &other) : m_key(other.m_key), m_columns(other.m_columns), m_data(other.m_data), m_annotation(other.m_annotation), m_commitHash(other.m_commitHash), m_dataset(other.m_dataset) { } Dataset::Row::Row(const Dataset &dataset, qint64 key) : m_key(key), m_columns(dataset.definition().columns()), m_dataset(&dataset) { // TODO: pre-populate m_data, or do that on buffer creation? for (const auto &colum : dataset.definition().columns()) { m_data.insert(colum.first, QVariant()); } } Dataset::Row &Dataset::Row::operator=(const Row &rhs) { m_key = rhs.m_key; m_columns = rhs.m_columns; m_data = rhs.m_data; m_dataset = rhs.m_dataset; m_annotation = rhs.m_annotation; m_commitHash = rhs.m_commitHash; return *this; } void Dataset::Row::setValue(const QString &col, const QVariant &value) { for (const auto &column : m_columns) { if (column.first == col) { if (value.canConvert(column.second.type())) { m_data[col] = value; } return; } } } QVariant Dataset::Row::value(const QString &col) const { return m_data.value(col); } void Dataset::Row::annotate(const QString ¬e) { m_annotation = note; } void Dataset::Row::setCommitHash(const QString &hash) { m_commitHash = hash; } qint64 Dataset::Row::key() const { if (m_key < 1) { const_cast(this)->m_key = QDateTime::currentMSecsSinceEpoch(); } return m_key; } void Dataset::Row::fromBinary(QByteArray data) { QVariant value; QString key; QDataStream stream(&data, QIODevice::ReadOnly); while (!stream.atEnd()) { stream >> key; if (stream.atEnd()) { break; } stream >> value; if (key == s_annotationKey) { m_annotation = value.toString(); } else if (key == s_hashKey) { m_commitHash = value.toString(); } else if (key == s_timestampKey) { m_timeStamp = value.toDateTime(); } else { setValue(key, value); } } } QByteArray Dataset::Row::toBinary() const { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); QHashIterator it(m_data); while (it.hasNext()) { it.next(); if (it.value().isValid()) { stream << it.key() << it.value(); } } if (!m_commitHash.isEmpty()) { stream << s_hashKey << QVariant(m_commitHash); } if (!m_timeStamp.isValid()) { stream << s_timestampKey << QVariant(m_timeStamp); } if (!m_annotation.isEmpty()) { stream << s_annotationKey << QVariant(m_annotation); } return data; } QString Dataset::tableHeaders(const QStringList &cols, int standardCols, const QString &seperator) const { if (!isValid()) { return QString(); } QStringList strings; if (standardCols & Row::Timestamp) { strings << QObject::tr("Timestamp").leftJustified(s_fieldWidth, ' '); } if (standardCols & Row::CommitHash) { strings << QObject::tr("Commit").leftJustified(s_fieldWidth, ' '); } for (const auto &column : m_definition.columns()) { QString header = column.first; if (cols.isEmpty() || cols.contains(header)) { if (!column.second.unit().isEmpty()) { header.append(" (").append(column.second.unit()).append(")"); } strings << header.leftJustified(s_fieldWidth, ' '); } } if (standardCols & Row::Annotation) { strings << QObject::tr("Annotation").leftJustified(s_fieldWidth, ' '); } return strings.join(seperator); } QString Dataset::Row::commitHash() const { return m_commitHash; } QDateTime Dataset::Row::timestamp() const { if (m_timeStamp.isValid()) { return m_timeStamp; } QDateTime dt; dt.setMSecsSinceEpoch(m_key); return dt; } void Dataset::Row::setTimestamp(const QDateTime &dt) { m_timeStamp = dt; } QString Dataset::Row::toString(const QStringList &cols, int standardCols, const QString &seperator) const { if (m_data.isEmpty()) { return QString(); } QStringList strings; if (standardCols & Timestamp) { strings << timestamp().toString("yyMMdd:hhmmss").leftJustified(s_fieldWidth, ' '); } if (standardCols & CommitHash) { strings << m_commitHash.leftJustified(s_fieldWidth, ' '); } for (const auto &column : m_columns) { const auto key = column.first; if (cols.isEmpty() || cols.contains(key)) { const auto value = m_data.value(key); if (value.canConvert()) { strings << QString("%1").arg(value.toDouble(), s_fieldWidth, 'f', 3); } else { strings << value.toString().leftJustified(s_fieldWidth, ' '); } } } if (standardCols & Annotation) { strings << m_annotation.leftJustified(s_fieldWidth, ' '); } return strings.join(seperator); } Dataset::Dataset(const QString &name, const State &state) : m_definition(state.datasetDefinition(name)), m_storage(state.resultsPath(), name, Sink::Storage::DataStore::ReadWrite), m_transaction(m_storage.createTransaction()), m_commitHash(state.commitHash()) { } Dataset::~Dataset() { m_transaction.commit(); } bool Dataset::isValid() const { return m_definition.isValid(); } const DatasetDefinition &Dataset::definition() const { return m_definition; } qint64 Dataset::insertRow(const Row &row) { if (row.m_dataset != this) { return 0; } qint64 key = row.key(); m_transaction.openDatabase().write(QByteArray::fromRawData((const char *)&key, sizeof(qint64)), row.toBinary()); return key; } void Dataset::removeRow(const Row &row) { //TODO } void Dataset::eachRow(const std::function &resultHandler) { if (!isValid()) { return; } Row row(*this); m_transaction.openDatabase().scan("", [&](const QByteArray &key, const QByteArray &value) -> bool { if (key.size() != sizeof(qint64)) { return true; } row.fromBinary(value); row.m_key = *(const qint64 *)key.data(); resultHandler(row); return true; }, Sink::Storage::DataStore::basicErrorHandler()); } Dataset::Row Dataset::row(qint64 key) { if (key < 1) { Row row(*this, Sink::Storage::DataStore::maxRevision(m_transaction)); row.setCommitHash(m_commitHash); return row; } Row row(*this, key); m_transaction.openDatabase().scan(QByteArray::fromRawData((const char *)&key, sizeof(qint64)), [&row](const QByteArray &key, const QByteArray &value) -> bool { row.fromBinary(value); return true; }, Sink::Storage::DataStore::basicErrorHandler() ); return row; } Dataset::Row Dataset::lastRow() { //TODO return Row(*this); } } // namespace HAWD