Added DocStoreSubscriber

Added DocStoreMergeEvent
DocumentStoreCore now handles merge events
Fixes to SelectionSubscriber
This commit is contained in:
Ralph Wessel
2024-09-10 23:35:12 +01:00
parent 7d9f939b8b
commit 53dd72989a
9 changed files with 281 additions and 17 deletions
@@ -3,6 +3,7 @@
#include "Active/Utility/Memory.h"
#include "Active/Utility/String.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Event/Type/DocStoreMergeEvent.h"
#include "Speckle/Utility/Guid.h"
#include "Speckle/Utility/String.h"
@@ -14,6 +15,7 @@
using namespace active::setting;
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::event;
using namespace speckle::utility;
using enum DocumentStoreCore::Status;
@@ -55,16 +57,27 @@ namespace {
if (storeID.id)
return true; //We must have a store if the ID is populated
bool isStoreFound = false;
#ifdef ARCHICAD
API_Guid acID;
if (auto statusCode = convertArchicadError(ACAPI_AddOnObject_GetObjectGuidFromName(String{storeID.name}, &acID)); statusCode != nominal)
throw std::system_error(DocumentStoreCore::makeError(statusCode));
storeID.id = Guid{acID};
isStoreFound = true;
#endif
return isStoreFound;
} //isExistingStore
/*--------------------------------------------------------------------
Copy a GS handle to a Memory object
handle: The GS handle
memory: The Memory object to receive the data
--------------------------------------------------------------------*/
void copyHandleToMemory(const GSHandle& handle, active::utility::Memory& memory) {
auto storeSize = BMGetHandleSize(handle);
memory.resize(storeSize);
active::utility::Memory::copy(memory.data(), *handle, storeSize, storeSize);
} //copyHandleToMemory
#endif
///Category for DocumentStore processing errors
@@ -111,6 +124,30 @@ std::error_code DocumentStoreCore::makeError(DocumentStoreCore::Status code) {
} //DocumentStoreCore::makeError
/*--------------------------------------------------------------------
Handle a document merge operation
event: The merge event
return: True if the event should be closed
--------------------------------------------------------------------*/
bool DocumentStoreCore::handle(const DocStoreMergeEvent& event) {
#ifdef ARCHICAD
if (event.objects == nullptr)
return false;
for (const auto& object : *event.objects) {
if (*object.name != String{m_id.name})
continue;
active::utility::Memory toMerge;
copyHandleToMemory(object.data, toMerge);
mergeStore(toMerge);
writeStore();
}
#endif
return false;
} //DocumentStoreCore::handle
/*--------------------------------------------------------------------
Read the data stored in the document
@@ -128,9 +165,7 @@ active::utility::Memory DocumentStoreCore::readStore() const {
if (auto statusCode = convertArchicadError(ACAPI_AddOnObject_GetObjectContent(Guid{m_id.id}, &storeName, &storedData)); statusCode != nominal)
throw std::system_error(makeError(statusCode));
//Copy the stored data into the result
auto storeSize = BMGetHandleSize(storedData);
result.resize(storeSize);
active::utility::Memory::copy(result.data(), *storedData, storeSize, storeSize);
copyHandleToMemory(storedData, result);
BMKillHandle(&storedData);
#endif
return result;
@@ -5,6 +5,7 @@
#include "Active/Setting/SettingList.h"
#include "Active/Database/Storage/DBaseSchema.h"
#include "Active/Utility/NameID.h"
#include "Speckle/Event/Subscriber/DocStoreSubscriber.h"
namespace speckle::database {
@@ -13,7 +14,7 @@ namespace speckle::database {
Currently implement for Archicad Add-On Objects
*/
class DocumentStoreCore {
class DocumentStoreCore : public event::DocStoreSubscriber {
public:
// MARK: - Types
@@ -54,6 +55,15 @@ namespace speckle::database {
*/
const active::utility::NameID& getID() const { return m_id; }
// MARK: - Functions (mutating)
/*!
Handle a document merge operation
@param event The merge event
@return True if the event should be closed
*/
bool handle(const event::DocStoreMergeEvent& event) override;
protected:
/*!
Read the data stored in the document (should be lazy-loading, only at the point where data is actually requested)
@@ -73,9 +83,8 @@ namespace speckle::database {
/*!
Merge existing stored data with incoming stored data (from an external source)
@param toMerge The external stored data to merge
@return The merged data to be stored
*/
virtual active::utility::Memory mergeStore(const active::utility::Memory& toMerge) = 0;
virtual void mergeStore(const active::utility::Memory& toMerge) = 0;
/*!
Reset the stored data (some external change has invalidated previous data, e.g. the document was closed)
*/
@@ -120,7 +120,7 @@ namespace speckle::database {
@param toMerge The external stored data to merge
@return The merged data to be stored
*/
active::utility::Memory mergeStore(const active::utility::Memory& toMerge) override;
void mergeStore(const active::utility::Memory& toMerge) override;
/*!
Reset the stored data (some external change has invalidated previous data, e.g. the document was closed)
*/
@@ -266,7 +266,7 @@ namespace speckle::database {
--------------------------------------------------------------------*/
template<typename Obj, typename Transport, typename ObjID>
requires DocumentStorable<Obj, Transport>
active::utility::Memory DocumentStoreEngine<Obj, Transport, ObjID>::mergeStore(const active::utility::Memory& toMerge) {
void DocumentStoreEngine<Obj, Transport, ObjID>::mergeStore(const active::utility::Memory& toMerge) {
//Import the incoming records from the data to merge
Cache incoming;
Transport().receive(std::forward<active::serialise::Cargo&&>(incoming), active::serialise::Identity{}, toMerge);
@@ -275,7 +275,6 @@ namespace speckle::database {
if (!cache)
cache = std::make_unique<Cache>(); //We still want to export an empty cache object even if it contains no records
cache->merge(incoming);
return buildStore();
} //DocumentStoreEngine<Obj, Transport, ObjID>::mergeStore
}
@@ -0,0 +1,88 @@
#include "Speckle/Event/Subscriber/DocStoreSubscriber.h"
#include "Speckle/Environment/Addon.h"
#include "Speckle/Database/Identity/Link.h"
#include "Speckle/Event/Type/DocStoreMergeEvent.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#endif
using namespace active::environment;
using namespace active::event;
using namespace speckle::database;
using namespace speckle::environment;
using namespace speckle::event;
namespace {
#ifdef ARCHICAD
/*!
Callback for an Archicad document merge operation
@param sourceObjects The source document objects to merge
*/
GSErrCode __ACENV_CALL docMergeCallback(const GS::Array<API_AddonObject>& sourceObjects) {
if (addon() != nullptr)
addon()->publishExternal(DocStoreMergeEvent{sourceObjects});
return NoError;
}
#endif
}
//True if a doc merge subscriber has already started (only one is required - there are no variants)
bool speckle::event::DocStoreSubscriber::m_isStarted = false;
/*--------------------------------------------------------------------
Get the event subscription list
return: The subscription list (an empty list will put the subscriber into a suspended state)
--------------------------------------------------------------------*/
Subscriber::Subscription DocStoreSubscriber::subscription() const {
return { {DocStoreMergeEvent::ID} };
} //DocStoreSubscriber::subscription
/*--------------------------------------------------------------------
Receive a subscribed event
event: The incoming event
return: True if the event should be closed
--------------------------------------------------------------------*/
bool DocStoreSubscriber::receive(const Event& event) {
//Pass a menu event to the specified handler function
if (auto selectEvent = dynamic_cast<const DocStoreMergeEvent*>(&event); selectEvent != nullptr)
return handle(*selectEvent);
return false;
} //DocStoreSubscriber::receive
/*--------------------------------------------------------------------
Attach participant components to the app (as required)
return: True if the participant is able to function
--------------------------------------------------------------------*/
bool DocStoreSubscriber::attach() {
#ifdef ARCHICAD
ACAPI_AddOnObject_RegisterAddOnObjectHandler();
#endif
return true;
} //DocStoreSubscriber::attach
/*--------------------------------------------------------------------
Start the participant operation
return: True if the participant is able to continue
--------------------------------------------------------------------*/
bool DocStoreSubscriber::start() {
if (m_isStarted)
return true;
m_isStarted = true;
#ifdef ARCHICAD
return (ACAPI_AddOnObject_InstallAddOnObjectMergeHandler(docMergeCallback) == NoError);
#else
return false;
#endif
} //DocStoreSubscriber::start
@@ -0,0 +1,74 @@
#ifndef SPECKLE_EVENT_DOC_STORE_SUBSCRIBER
#define SPECKLE_EVENT_DOC_STORE_SUBSCRIBER
#include "Active/Event/Subscriber.h"
namespace speckle::event {
class DocStoreMergeEvent;
/*!
Base class for subscribers responding to document merge operations (notably those managing custom data in the document)
*/
class DocStoreSubscriber : public active::event::Subscriber {
public:
// MARK: - Constructors
/*!
Default constructor
*/
DocStoreSubscriber() = default;
/*!
Copy constructor
@param source The object to copy
*/
DocStoreSubscriber(const DocStoreSubscriber& source) = default;
/*!
Destructor
*/
virtual ~DocStoreSubscriber() {}
// MARK: - Functions (const)
/*!
Get the event subscription list
@return The subscription list (an empty list will put the subscriber into a suspended state)
*/
Subscription subscription() const override;
// MARK: - Functions (mutating)
/*!
Receive a subscribed event
@param event The incoming event
@return True if the event should be closed
*/
bool receive(const active::event::Event& event) override;
protected:
/*!
Attach participant components to the app (as required)
@return True if the participant is able to function
*/
bool attach() override;
/*!
Start the participant operation
@return True if the participant is able to continue
*/
bool start() override;
/*!
Handle a document merge operation
@param event The merge event
@return True if the event should be closed
*/
virtual bool handle(const DocStoreMergeEvent& event) = 0;
private:
///True if a doc merge subscriber has already started (only one is required - there are no variants)
static bool m_isStarted;
};
}
#endif //SPECKLE_EVENT_DOC_STORE_SUBSCRIBER
@@ -4,6 +4,10 @@
#include "Speckle/Database/Identity/Link.h"
#include "Speckle/Event/Type/SelectionEvent.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#endif
using namespace active::environment;
using namespace active::event;
using namespace speckle::database;
@@ -28,6 +32,9 @@ namespace {
}
//True if a selection change subscriber has already started (only one is required - there are no variants)
bool speckle::event::SelectionSubscriber::m_isStarted = false;
/*--------------------------------------------------------------------
Get the event subscription list
@@ -59,6 +66,9 @@ bool SelectionSubscriber::receive(const Event& event) {
return: True if the participant is able to continue
--------------------------------------------------------------------*/
bool SelectionSubscriber::start() {
if (m_isStarted)
return true;
m_isStarted = true;
#ifdef ARCHICAD
return (ACAPI_Notification_CatchSelectionChange(selectionCallback) == NoError);
#else
@@ -3,16 +3,12 @@
#include "Active/Event/Subscriber.h"
#ifdef ARCHICAD
#include <ACAPinc.h>
#endif
namespace speckle::event {
class SelectionEvent;
/*!
Base class for subscribers responding to selectionm changes
Base class for subscribers responding to selection changes
*/
class SelectionSubscriber : public active::event::Subscriber {
public:
@@ -65,7 +61,7 @@ namespace speckle::event {
private:
///True if a selection change subscriber has already started (only one is required - there are no variants)
int32_t m_isStarted = false;
static bool m_isStarted;
};
}
@@ -0,0 +1,43 @@
#ifndef SPECKLE_EVENT_DOC_STORE_MERGE_EVENT
#define SPECKLE_EVENT_DOC_STORE_MERGE_EVENT
#include "Active/Event/Event.h"
#include "Active/Utility/Guid.h"
#include "Active/Utility/String.h"
#include "Speckle/Database/Identity/Link.h"
namespace speckle::event {
/*!
Class representing a selection change event
*/
class DocStoreMergeEvent : public active::event::Event {
public:
static const inline active::utility::NameID ID{active::utility::String{"document store merge"},
active::utility::Guid{active::utility::String{"c92e2c51-d47c-44e3-a54f-5068dccaa35a"}}};
// MARK: - Constructors
/*!
Default constructor
*/
DocStoreMergeEvent() : Event{ID} {}
#ifdef ARCHICAD
/*!
Constructor
@param incoming Incoming document objects to merge
*/
DocStoreMergeEvent(const GS::Array<API_AddonObject>& incoming) : Event{ID} { objects = &incoming; }
#endif
#ifdef ARCHICAD
//Incoming document objects to merge
const GS::Array<API_AddonObject>* objects = nullptr;
#endif
};
}
#endif //SPECKLE_EVENT_DOC_STORE_MERGE_EVENT
@@ -40,6 +40,8 @@
21D0BDB32C8F8AB60077E104 /* DocumentStoreCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21D0BDAC2C8F8AB60077E104 /* DocumentStoreCore.cpp */; };
21D0BDB42C8F8AB60077E104 /* DocumentStoreCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 21D0BDAD2C8F8AB60077E104 /* DocumentStoreCore.h */; };
21D0BDB52C8F8AB60077E104 /* DocumentStoreEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 21D0BDAE2C8F8AB60077E104 /* DocumentStoreEngine.h */; };
21D0BDBD2C90F2830077E104 /* DocStoreSubscriber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21D0BDBB2C90F2830077E104 /* DocStoreSubscriber.cpp */; };
21D0BDBF2C90F36B0077E104 /* DocStoreMergeEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 21D0BDBE2C90F36B0077E104 /* DocStoreMergeEvent.h */; };
21F69F3B2C6B880C008B6A06 /* JSBaseTransport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F382C6B880B008B6A06 /* JSBaseTransport.cpp */; };
21F69F512C6CCC25008B6A06 /* BrowserBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F4A2C6CCC25008B6A06 /* BrowserBridge.cpp */; };
21F69F612C6D0286008B6A06 /* GetBindingsMethodNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21F69F602C6D0286008B6A06 /* GetBindingsMethodNames.cpp */; };
@@ -138,6 +140,9 @@
21D0BDAC2C8F8AB60077E104 /* DocumentStoreCore.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocumentStoreCore.cpp; sourceTree = "<group>"; };
21D0BDAD2C8F8AB60077E104 /* DocumentStoreCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocumentStoreCore.h; sourceTree = "<group>"; };
21D0BDAE2C8F8AB60077E104 /* DocumentStoreEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocumentStoreEngine.h; sourceTree = "<group>"; };
21D0BDBA2C90F2830077E104 /* DocStoreSubscriber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocStoreSubscriber.h; sourceTree = "<group>"; };
21D0BDBB2C90F2830077E104 /* DocStoreSubscriber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DocStoreSubscriber.cpp; sourceTree = "<group>"; };
21D0BDBE2C90F36B0077E104 /* DocStoreMergeEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocStoreMergeEvent.h; sourceTree = "<group>"; };
21F69F012C66C229008B6A06 /* Doxyfile */ = {isa = PBXFileReference; lastKnownFileType = text; name = Doxyfile; path = Documentation/Doxyfile; sourceTree = "<group>"; };
21F69F192C6A0FE2008B6A06 /* JSBinding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBinding.h; sourceTree = "<group>"; };
21F69F352C6AA9B3008B6A06 /* JSFunction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSFunction.h; sourceTree = "<group>"; };
@@ -254,6 +259,8 @@
2193517A2C624FC100E5A69C /* Subscriber */ = {
isa = PBXGroup;
children = (
21D0BDBB2C90F2830077E104 /* DocStoreSubscriber.cpp */,
21D0BDBA2C90F2830077E104 /* DocStoreSubscriber.h */,
219351782C624FC100E5A69C /* MenuSubscriber.cpp */,
219351792C624FC100E5A69C /* MenuSubscriber.h */,
219351992C6278D900E5A69C /* SelectionSubscriber.cpp */,
@@ -265,6 +272,7 @@
2193518A2C62655700E5A69C /* Type */ = {
isa = PBXGroup;
children = (
21D0BDBE2C90F36B0077E104 /* DocStoreMergeEvent.h */,
219351892C62655700E5A69C /* MenuEvent.h */,
2193519C2C627E3100E5A69C /* SelectionEvent.h */,
);
@@ -462,6 +470,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
21D0BDBF2C90F36B0077E104 /* DocStoreMergeEvent.h in Headers */,
21D0BD212C86F0280077E104 /* AccountDatabase.h in Headers */,
210CC86F2C7E879700610F58 /* ArgumentBase.h in Headers */,
210CC8A02C81E34400610F58 /* Platform.h in Headers */,
@@ -608,6 +617,7 @@
21F69F812C6FF3B0008B6A06 /* BridgeArgumentWrap.cpp in Sources */,
2193517B2C624FC100E5A69C /* MenuSubscriber.cpp in Sources */,
21F69F612C6D0286008B6A06 /* GetBindingsMethodNames.cpp in Sources */,
21D0BDBD2C90F2830077E104 /* DocStoreSubscriber.cpp in Sources */,
21D0BDB32C8F8AB60077E104 /* DocumentStoreCore.cpp in Sources */,
21F93AEC2B2F406E009A2C5B /* Addon.cpp in Sources */,
21D0BD4E2C8901A00077E104 /* ServerInfo.cpp in Sources */,