Added classes for managing detached objects:

- DetachmentManager: Manages detached objects during (de)serialisation
- DetachedWrap: Wrapper for detached objects, generating references on demand from serialised data
- DetachedReference: Wrapper for representation of wrapped objects with a reference
- DetachedObjectStore: Interface for filing/retrieving detached objects in storage
This commit is contained in:
Ralph Wessel
2024-09-30 10:50:24 +01:00
parent 4c8a2237bf
commit deee1e80c5
8 changed files with 604 additions and 0 deletions
@@ -0,0 +1,90 @@
#include "Speckle/Serialise/Detached/DetachedReference.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Speckle/Serialise/Detached/DetachmentManager.h"
#include "Speckle/Utility/String.h"
using namespace active::serialise;
using namespace speckle::serialise;
using namespace speckle::utility;
#include <array>
namespace {
///Serialisation fields
enum FieldIndex {
refdID,
speckTypeID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"referencedId"},
Identity{"speckle_type"},
};
///Type signature for a reference to a detached object
String referenceType{"reference"};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool DetachedReference::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
inventory.merge(Inventory{
{
{ fieldID[refdID], refdID, element },
{ fieldID[speckTypeID], speckTypeID, element },
},
}.withType(&typeid(DetachedReference)));
return true;
} //DetachedReference::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique DetachedReference::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(DetachedReference))
return base::get().getCargo(item);
using namespace active::serialise;
switch (item.index) {
case refdID: {
//If we don't have an allocated string for receiving a reference (from 'setDefault') then we need to create one
if (!m_reference) {
//Ask a manager to send the detached data and provide a reference
auto detachmentManager = getManager<DetachmentManager>();
if (detachmentManager == nullptr)
return nullptr; //TODO: Discuss if this is a serious error - possibly throwing an exception is warranted
m_reference = detachmentManager->send(std::forward<Package&&>(base::get()), item.identity());
if (!m_reference)
throw; //TODO: Throw a more descriptive exception
}
std::make_unique<StringWrap>(*m_reference);
}
case speckTypeID:
return std::make_unique<StringWrap>(m_type);
default:
return nullptr; //Requested an unknown index
}
} //DetachedReference::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void DetachedReference::setDefault() {
m_type.clear();
m_reference = String{};
} //DetachedReference::setDefault
@@ -0,0 +1,77 @@
#ifndef SPECKLE_SERIALISE_DETACHED_REFERENCE
#define SPECKLE_SERIALISE_DETACHED_REFERENCE
#include "Active/Serialise/Package/Package.h"
#include "Speckle/Serialise/Detached/DetachedWrap.h"
#include "Speckle/Utility/String.h"
namespace speckle::serialise {
/*!
Wrapper for references to a detached (serialised) object
An object holding a child object that should be serialised as a detached object should wrap the child in this package for (de)serialisation
*/
class DetachedReference : public active::serialise::Package, std::reference_wrapper<active::serialise::Package> {
public:
// MARK: - Types
///Item reference base
using base = std::reference_wrapper<active::serialise::Package>;
// MARK: - Constructors
/*!
Constructor
@param source The source package to wrap
*/
DetachedReference(Package& source) : base{source} {}
/*!
Constructor
@param source The source package to wrap
*/
DetachedReference(const active::serialise::Package& source) : base(const_cast<active::serialise::Package&>(source)) {}
DetachedReference(const DetachedReference& source) = delete;
/*!
Destructor
*/
~DetachedReference() override = default;
// MARK: - Functions (const)
/*!
Fill an inventory with the cargo items
@param inventory The inventory to receive the cargo items
@return True if items have been added to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
// MARK: - Functions (mutating)
/*!
Set to the default package content
*/
void setDefault() override;
/*!
Validate the cargo data
@return True if the data has been validated
*/
bool validate() override;
private:
///The reference value, i.e. a hash of the object content
mutable std::optional<database::RecordID> m_reference;
///The reference type
utility::String m_type;
};
}
#endif //SPECKLE_SERIALISE_DETACHED_REFERENCE
@@ -0,0 +1,93 @@
#include "Speckle/Serialise/Detached/DetachedWrap.h"
#include "Active/Serialise/Item/Wrapper/ValueWrap.h"
#include "Active/Utility/BufferOut.h"
#include "Active/Utility/SHA256.h"
#include "Speckle/Serialise/Detached/DetachmentManager.h"
#include "Speckle/Utility/String.h"
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::serialise;
using namespace speckle::utility;
#include <array>
namespace {
///Serialisation fields
enum FieldIndex {
refdID,
};
///Serialisation field IDs
static std::array fieldID = {
Identity{"id"},
};
}
/*--------------------------------------------------------------------
Fill an inventory with the package items
inventory: The inventory to receive the package items
return: True if the package has added items to the inventory
--------------------------------------------------------------------*/
bool DetachedWrap::fillInventory(Inventory& inventory) const {
using enum Entry::Type;
if (!base::get().fillInventory(inventory))
return false;
inventory.merge(Inventory{
{
{ fieldID[refdID], refdID, element },
},
}.withType(&typeid(DetachedWrap)));
return true;
} //DetachedWrap::fillInventory
/*--------------------------------------------------------------------
Get the specified cargo
item: The inventory item to retrieve
return: The requested cargo (nullptr on failure)
--------------------------------------------------------------------*/
Cargo::Unique DetachedWrap::getCargo(const Inventory::Item& item) const {
if (item.ownerType != &typeid(DetachedWrap))
return base::get().getCargo(item);
using namespace active::serialise;
switch (item.index) {
case refdID: {
if (!m_reference) {
if (m_bufferOut == nullptr)
return nullptr; //TODO: Consider throwing an exception here
auto serialisedData = m_bufferOut->getOutput();
//Produce a hash from the serialised object. NB: We are currently using only the first 32 chars of the SHA256 hash
m_reference = (active::utility::SHA256() << serialisedData << String{"}"}).base64Hash().substr(0, 32);
}
return std::make_unique<ValueWrap<RecordID>>(*m_reference);
}
default:
return nullptr; //Requested an unknown index
}
} //DetachedWrap::getCargo
/*--------------------------------------------------------------------
Set to the default package content
--------------------------------------------------------------------*/
void DetachedWrap::setDefault() {
m_reference = String{};
} //DetachedWrap::setDefault
/*--------------------------------------------------------------------
Validate the cargo data
return: True if the data has been validated
--------------------------------------------------------------------*/
bool DetachedWrap::validate() {
return m_reference && base::get().validate();
} //DetachedWrap::validate
@@ -0,0 +1,89 @@
#ifndef SPECKLE_SERIALISE_DETACHED_WRAP
#define SPECKLE_SERIALISE_DETACHED_WRAP
#include "Active/Serialise/Package/Package.h"
#include "Speckle/Database/Identity/RecordID.h"
namespace active::utility {
class BufferOut;
}
namespace speckle::serialise {
/*!
Lightweight interface wrapper for a detached object
The wrapper generates a new reference ID for the object during serialisation, embedding it in the output
*/
class DetachedWrap : public active::serialise::Package, public std::reference_wrapper<active::serialise::Package> {
public:
// MARK: - Types
///Item reference base
using base = std::reference_wrapper<active::serialise::Package>;
// MARK: - Constructors
/*!
Constructor
@param source The source package to wrap
@param bufferOut The serialisation output buffer
*/
DetachedWrap(active::serialise::Package& source, active::utility::BufferOut& bufferOut) : base(source), m_bufferOut{&bufferOut} {}
/*!
Constructor
@param source The source package to wrap
@param bufferOut The serialisation output buffer
*/
DetachedWrap(const active::serialise::Package& source, active::utility::BufferOut& bufferOut) :
DetachedWrap{const_cast<active::serialise::Package&>(source), bufferOut} {}
/*!
Copy constructor
@param source The object to copy
*/
DetachedWrap(const DetachedWrap& source) : base(source), m_reference{source.m_reference} {}
// MARK: - Functions (const)
/*!
Get the detached object reference
@return The detached object reference (nullopt if the reference has not been determined yet - call after (de)serialisation)
*/
std::optional<database::RecordID> getReference() const { return m_reference; }
/*!
Fill an inventory with the cargo items
@param inventory The inventory to receive the cargo items
@return True if items have been added to the inventory
*/
bool fillInventory(active::serialise::Inventory& inventory) const override;
/*!
Get the specified cargo
@param item The inventory item to retrieve
@return The requested cargo (nullptr on failure)
*/
Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override;
// MARK: - Functions (mutating)
/*!
Set to the default package content
*/
void setDefault() override;
/*!
Validate the cargo data
@return True if the data has been validated
*/
bool validate() override;
private:
///The detached record reference
mutable std::optional<database::RecordID> m_reference;
///The serialisation output buffer
active::utility::BufferOut* m_bufferOut;
};
}
#endif //SPECKLE_SERIALISE_DETACHED_WRAP
@@ -0,0 +1,89 @@
#include "Speckle/Serialise/Detached/DetachmentManager.h"
#include "Active/Utility/BufferOut.h"
#include "Speckle/Serialise/Detached/DetachedWrap.h"
#include "Speckle/Serialise/Detached/Storage/DetachedObjectStore.h"
using namespace active::serialise;
using namespace speckle::database;
using namespace speckle::serialise;
using namespace speckle::utility;
/*--------------------------------------------------------------------
Default constructor
--------------------------------------------------------------------*/
DetachmentManager::DetachmentManager() {
} //DetachmentManager::DetachmentManager
/*--------------------------------------------------------------------
Destructor
--------------------------------------------------------------------*/
DetachmentManager::~DetachmentManager() {
} //DetachmentManager::~DetachmentManager
/*--------------------------------------------------------------------
Send cargo as a detached object
package: The package to become a detached object
identity: The package identity (name, optional namespace)
return: The ID of the sent detached object
--------------------------------------------------------------------*/
std::optional<RecordID> DetachmentManager::send(Package&& package, const Identity& identity) const {
auto transport = m_transport.lock();
if (!transport)
throw; //TODO: Throw meaningful exception type
//Prepare the detached object data
String serialisedData;
active::utility::BufferOut buffer{serialisedData};
//The wrapper will manage the object serialisation and generate a unique reference based on the output
DetachedWrap wrapper{package, buffer};
transport->send(std::forward<Cargo&&>(wrapper), identity, serialisedData);
auto detachedReference = wrapper.getReference();
//If a reference is obtained, file/cache the object as required
if (detachedReference) {
m_store->file(*detachedReference, serialisedData);
if (m_cache)
m_cache->file(*detachedReference, serialisedData);
}
return detachedReference;
} //DetachmentManager::send
/*--------------------------------------------------------------------
Receive cargo from a detached object
id: The ID of the detached object to receive
package: The package to receive the detached object data
identity: The package identity (name, optional namespace)
return: True if the detached object was found and received
--------------------------------------------------------------------*/
bool DetachmentManager::receive(const database::RecordID& id, Package&& package, const Identity& identity) const {
return false; //TODO: Implement when we start receiving in Archicad
} //DetachmentManager::receive
/*--------------------------------------------------------------------
Set the manager primary storage
store: the primary storage (this is the long-term storage, e.g. a remote/cloud database)
--------------------------------------------------------------------*/
void DetachmentManager::setStore(std::unique_ptr<DetachedObjectStore> store) {
m_store = std::move(store);
} //DetachmentManager::setStore
/*--------------------------------------------------------------------
Set the manager storage cache
cache: the storage cache (this is intended to provide faster access than the primary storage for repeated detached objects)
--------------------------------------------------------------------*/
void DetachmentManager::setCache(std::unique_ptr<DetachedObjectStore> cache) {
m_cache = std::move(cache);
} //DetachmentManager::setCache
@@ -0,0 +1,82 @@
#ifndef SPECKLE_SERIALISE_DETACHMENT_MANAGER
#define SPECKLE_SERIALISE_DETACHMENT_MANAGER
#include "Active/Serialise/Manager.h"
#include "Active/Serialise/Transport.h"
#include "Speckle/Database/Identity/RecordID.h"
namespace active::serialise {
class Package;
}
namespace speckle::serialise {
class DetachedObjectStore;
/*!
Manager for detached (serialised) objects
Pass this manager to the target transport for (de)serialisation supporting detached objects. Note that the manager is expected to know how
to send/receive detached objects, e.g. to a JS portal, remote server or local database (or some combo)
*/
class DetachmentManager : public virtual active::serialise::Manager {
public:
// MARK: - Types
///Base class
using base = active::serialise::Manager;
/*!
Default constructor
*/
DetachmentManager();
DetachmentManager(const DetachmentManager&) = delete;
/*!
Destructor
*/
~DetachmentManager();
// MARK: - Functions (const)
/*!
Send cargo as a detached object
@param package The package to become a detached object
@param identity The package identity (name, optional namespace)
@return The ID of the sent detached object
*/
std::optional<database::RecordID> send(active::serialise::Package&& package, const active::serialise::Identity& identity) const;
/*!
Receive cargo from a detached object
@param id The ID of the detached object to receive
@param package The package to receive the detached object data
@param identity The package identity (name, optional namespace)
@return True if the detached object was found and received
*/
bool receive(const database::RecordID& id, active::serialise::Package&& package, const active::serialise::Identity& identity) const;
// MARK: - Functions (mutating)
/*!
Set the manager primary storage
@param store the primary storage (this is the long-term storage, e.g. a remote/cloud database)
*/
void setStore(std::unique_ptr<DetachedObjectStore> store);
/*!
Set the manager storage cache
@param cache the storage cache (this is intended to provide faster access than the primary storage for repeated detached objects)
*/
void setCache(std::unique_ptr<DetachedObjectStore> cache);
private:
///The manager serialisation transport (NB - this is expected to be a shared resource, so the manager does not own it)
std::weak_ptr<active::serialise::Transport> m_transport;
///The primary store for sending/retrieving detached objects
std::unique_ptr<DetachedObjectStore> m_store;
///A cache for detached objects - can be used as a local store to save
std::unique_ptr<DetachedObjectStore> m_cache;
};
}
#endif //SPECKLE_SERIALISE_DETACHMENT_MANAGER
@@ -0,0 +1,46 @@
#ifndef SPECKLE_SERIALISE_DETACHED_OBJECT_STORE
#define SPECKLE_SERIALISE_DETACHED_OBJECT_STORE
#include "Speckle/Database/Identity/RecordID.h"
#include "Speckle/Utility/String.h"
namespace speckle::serialise {
/*!
Interface for objects that store/cache/send/receive detached objects
It is currently assumed that objects will be serialised and can be held in a string. This can be revisited in future if binary data is required.
*/
class DetachedObjectStore {
public:
// MARK: Constructors
/*!
Destructor
*/
virtual ~DetachedObjectStore() {}
// MARK: - Functions (const)
/*!
Retrieve a detached object from storage
@param reference The required object reference
@return The object data (nullopt if the object cannot be found in storage)
*/
virtual utility::String::Option retrieve(const database::RecordID& reference) = 0;
// MARK: - Functions (mutating)
/*!
File a detached object in storage
@param reference The object reference (typically a hash generated from the object data)
@param data The object data (currently expected to be serialised as JSON)
@return True if the object was filed (typically rejected if it duplicates an object already in the store)
*/
virtual bool file(const database::RecordID& reference, const utility::String& data) const = 0;
};
}
#endif //SPECKLE_SERIALISE_DETACHED_OBJECT_STORE
@@ -37,6 +37,10 @@
219351B12C62CC1A00E5A69C /* Guid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 219351AC2C62CC1A00E5A69C /* Guid.cpp */; };
219351B32C62CC1A00E5A69C /* String.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 219351AE2C62CC1A00E5A69C /* String.cpp */; };
2199881E2BD833830035E5EA /* libArchicad27.a in CopyFiles */ = {isa = PBXBuildFile; fileRef = 21379E082AE47A6400A1584C /* libArchicad27.a */; };
21AEF9BA2CA606B5000B8681 /* DetachedReference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9B92CA606B4000B8681 /* DetachedReference.cpp */; };
21AEF9BC2CA6DF84000B8681 /* DetachmentManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9BB2CA6DF84000B8681 /* DetachmentManager.cpp */; };
21AEF9BE2CA6FDA4000B8681 /* DetachedWrap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9BD2CA6FDA4000B8681 /* DetachedWrap.cpp */; };
21AEF9DD2CAAA4EA000B8681 /* DetachedObjectStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 21AEF9DB2CAAA4EA000B8681 /* DetachedObjectStore.h */; };
21B67D002C7CE15100FD64FC /* Exception.h in Headers */ = {isa = PBXBuildFile; fileRef = 21B67CFE2C7CE15100FD64FC /* Exception.h */; };
21B67D0D2C7E0E8D00FD64FC /* ErrorReport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21B67D092C7E0E8D00FD64FC /* ErrorReport.h */; };
21B67D0E2C7E0E8D00FD64FC /* ErrorReport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21B67D0C2C7E0E8D00FD64FC /* ErrorReport.cpp */; };
@@ -159,6 +163,13 @@
219351AE2C62CC1A00E5A69C /* String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = String.cpp; sourceTree = "<group>"; };
219351AF2C62CC1A00E5A69C /* String.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = String.h; sourceTree = "<group>"; };
219712682BE7E2D500D9EF7E /* Serialisation.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Serialisation.md; sourceTree = "<group>"; };
21AEF9B32CA5F7CF000B8681 /* DetachedWrap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachedWrap.h; sourceTree = "<group>"; };
21AEF9B52CA5FA02000B8681 /* DetachedReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachedReference.h; sourceTree = "<group>"; };
21AEF9B72CA5FCB6000B8681 /* DetachmentManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachmentManager.h; sourceTree = "<group>"; };
21AEF9B92CA606B4000B8681 /* DetachedReference.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DetachedReference.cpp; sourceTree = "<group>"; };
21AEF9BB2CA6DF84000B8681 /* DetachmentManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DetachmentManager.cpp; sourceTree = "<group>"; };
21AEF9BD2CA6FDA4000B8681 /* DetachedWrap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DetachedWrap.cpp; sourceTree = "<group>"; };
21AEF9DB2CAAA4EA000B8681 /* DetachedObjectStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachedObjectStore.h; sourceTree = "<group>"; };
21B67CFE2C7CE15100FD64FC /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; };
21B67D092C7E0E8D00FD64FC /* ErrorReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ErrorReport.h; sourceTree = "<group>"; };
21B67D0C2C7E0E8D00FD64FC /* ErrorReport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorReport.cpp; sourceTree = "<group>"; };
@@ -255,6 +266,7 @@
isa = PBXGroup;
children = (
2167E27C2C49121F000827D3 /* CMakeLists.txt */,
21AEF9C72CA818EA000B8681 /* Detached */,
21F69F3A2C6B880B008B6A06 /* JSBase */,
219712682BE7E2D500D9EF7E /* Serialisation.md */,
);
@@ -403,6 +415,28 @@
path = SpeckleLibDoctest;
sourceTree = "<group>";
};
21AEF9C72CA818EA000B8681 /* Detached */ = {
isa = PBXGroup;
children = (
21AEF9B92CA606B4000B8681 /* DetachedReference.cpp */,
21AEF9B52CA5FA02000B8681 /* DetachedReference.h */,
21AEF9BD2CA6FDA4000B8681 /* DetachedWrap.cpp */,
21AEF9B32CA5F7CF000B8681 /* DetachedWrap.h */,
21AEF9BB2CA6DF84000B8681 /* DetachmentManager.cpp */,
21AEF9B72CA5FCB6000B8681 /* DetachmentManager.h */,
21AEF9DE2CAAA78A000B8681 /* Storage */,
);
path = Detached;
sourceTree = "<group>";
};
21AEF9DE2CAAA78A000B8681 /* Storage */ = {
isa = PBXGroup;
children = (
21AEF9DB2CAAA4EA000B8681 /* DetachedObjectStore.h */,
);
path = Storage;
sourceTree = "<group>";
};
21D0BD1F2C86F0280077E104 /* Database */ = {
isa = PBXGroup;
children = (
@@ -596,6 +630,7 @@
21D0BD5A2C8910400077E104 /* UserInfo.h in Headers */,
21D0BD562C890B1C0077E104 /* ServerMigration.h in Headers */,
210CC88F2C81A98500610F58 /* Guid64.h in Headers */,
21AEF9DD2CAAA4EA000B8681 /* DetachedObjectStore.h in Headers */,
215F088D2CA195EC00CD343B /* ArchicadElementDBaseEngine.h in Headers */,
21B67D002C7CE15100FD64FC /* Exception.h in Headers */,
21D0BD2C2C86FC350077E104 /* Record.h in Headers */,
@@ -749,6 +784,7 @@
21B67D0E2C7E0E8D00FD64FC /* ErrorReport.cpp in Sources */,
21F69F7E2C6FD9FC008B6A06 /* GetCallResult.cpp in Sources */,
219245FE2CA2CC4300CF5703 /* BIMRecord.cpp in Sources */,
21AEF9BE2CA6FDA4000B8681 /* DetachedWrap.cpp in Sources */,
2193519B2C6278D900E5A69C /* SelectionSubscriber.cpp in Sources */,
21D0BD2B2C86FC350077E104 /* Record.cpp in Sources */,
219246042CA2CE2700CF5703 /* BIMLink.cpp in Sources */,
@@ -760,11 +796,13 @@
219351B32C62CC1A00E5A69C /* String.cpp in Sources */,
219351B12C62CC1A00E5A69C /* Guid.cpp in Sources */,
21F69F512C6CCC25008B6A06 /* BrowserBridge.cpp in Sources */,
21AEF9BC2CA6DF84000B8681 /* DetachmentManager.cpp in Sources */,
215F08552C99DA8D00CD343B /* Project.cpp in Sources */,
21F69F3B2C6B880C008B6A06 /* JSBaseTransport.cpp in Sources */,
210CC89F2C81E34400610F58 /* Platform.cpp in Sources */,
21D0BD202C86F0280077E104 /* AccountDatabase.cpp in Sources */,
21F69F962C71087A008B6A06 /* Account.cpp in Sources */,
21AEF9BA2CA606B5000B8681 /* DetachedReference.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};