/* * Copyright (C) 2014 Christian Mollekopf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "facade.h" #include #include #include "common/resourceaccess.h" #include "common/commands.h" #include "common/resultset.h" #include "common/domain/event.h" #include "dummycalendar_generated.h" #include "event_generated.h" #include "entity_generated.h" #include "metadata_generated.h" #include "domainadaptor.h" #include #include #include using namespace DummyCalendar; using namespace flatbuffers; DummyResourceFacade::DummyResourceFacade() : Akonadi2::GenericFacade("org.kde.dummy", QSharedPointer::create()) { } DummyResourceFacade::~DummyResourceFacade() { } static void scan(const QSharedPointer &storage, const QByteArray &key, std::function callback) { storage->scan(key, [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { //Skip internals if (Akonadi2::Storage::isInternalKey(keyValue, keySize)) { return true; } //Extract buffers Akonadi2::EntityBuffer buffer(dataValue, dataSize); const auto resourceBuffer = Akonadi2::EntityBuffer::readBuffer(buffer.entity().resource()); const auto localBuffer = Akonadi2::EntityBuffer::readBuffer(buffer.entity().local()); const auto metadataBuffer = Akonadi2::EntityBuffer::readBuffer(buffer.entity().metadata()); if ((!resourceBuffer && !localBuffer) || !metadataBuffer) { qWarning() << "invalid buffer " << QByteArray::fromRawData(static_cast(keyValue), keySize); return true; } return callback(QByteArray::fromRawData(static_cast(keyValue), keySize), buffer.entity(), resourceBuffer, localBuffer, metadataBuffer); }, [](const Akonadi2::Storage::Error &error) { qWarning() << "Error during query: " << error.message; }); } void DummyResourceFacade::readValue(const QSharedPointer &storage, const QByteArray &key, const std::function &resultCallback) { scan(storage, key, [=](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { qint64 revision = metadataBuffer ? metadataBuffer->revision() : -1; //This only works for a 1:1 mapping of resource to domain types. //Not i.e. for tags that are stored as flags in each entity of an imap store. //additional properties that don't have a 1:1 mapping (such as separately stored tags), //could be added to the adaptor auto event = QSharedPointer::create("org.kde.dummy", key, revision, mDomainTypeAdaptorFactory->createAdaptor(entity)); resultCallback(event); return true; }); } static ResultSet getResultSet(const Akonadi2::Query &query, const QSharedPointer &storage) { QSet appliedFilters; ResultSet resultSet = Akonadi2::ApplicationDomain::TypeImplementation::queryIndexes(query, "org.kde.dummy", appliedFilters); const auto remainingFilters = query.propertyFilter.keys().toSet() - appliedFilters; if (resultSet.isEmpty()) { QVector keys; scan(storage, QByteArray(), [=, &keys](const QByteArray &key, const Akonadi2::Entity &entity, DummyEvent const *buffer, Akonadi2::ApplicationDomain::Buffer::Event const *local, Akonadi2::Metadata const *metadataBuffer) { keys << key; return true; }); resultSet = ResultSet(keys); } return resultSet; } KAsync::Job DummyResourceFacade::load(const Akonadi2::Query &query, const QSharedPointer > &resultProvider, qint64 oldRevision, qint64 newRevision) { return KAsync::start([=]() { auto storage = QSharedPointer::create(Akonadi2::Store::storageLocation(), "org.kde.dummy"); storage->setDefaultErrorHandler([](const Akonadi2::Storage::Error &error) { Warning() << "Error during query: " << error.store << error.message; }); storage->startTransaction(Akonadi2::Storage::ReadOnly); //TODO start transaction on indexes as well const qint64 revision = storage->maxRevision(); auto resultSet = getResultSet(query, storage); // TODO only emit changes and don't replace everything resultProvider->clear(); auto resultCallback = std::bind(&Akonadi2::ResultProvider::add, resultProvider, std::placeholders::_1); while (resultSet.next()) { readValue(storage, resultSet.id(), [resultCallback](const Akonadi2::ApplicationDomain::Event::Ptr &event) { //We create an in-memory copy because the result provider will store the value, and the result we get back is only valid during the callback resultCallback(Akonadi2::ApplicationDomain::ApplicationDomainType::getInMemoryRepresentation(event)); }); } storage->abortTransaction(); return revision; }); }