* Copyright 2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alex Wilson, yourpalal2@gmail.com
*/
#include "ArchivingManagers.h"
#include <syslog.h>
#include <typeinfo>
#include <StackOrHeapArray.h>
namespace BPrivate {
namespace Archiving {
const char* kArchivableField = "_managed_archivable";
const char* kManagedField = "_managed_archive";
}
}
using namespace BPrivate::Archiving;
BArchiveManager*
BManagerBase::ArchiveManager(const BMessage* archive)
{
BManagerBase* manager = ManagerPointer(archive);
if (!manager)
return NULL;
if (manager->fType == ARCHIVE_MANAGER)
return static_cast<BArchiveManager*>(manager);
debugger("Overlapping managed unarchive/archive sessions.");
return NULL;
}
BUnarchiveManager*
BManagerBase::UnarchiveManager(const BMessage* archive)
{
BManagerBase* manager = ManagerPointer(archive);
if (!manager)
return NULL;
if (manager->fType == UNARCHIVE_MANAGER)
return static_cast<BUnarchiveManager*>(manager);
debugger("More calls to BUnarchiver::PrepareArchive()"
" than BUnarchivers created.");
return NULL;
}
struct BArchiveManager::ArchiveInfo {
ArchiveInfo()
:
token(-1),
archive(NULL)
{
}
~ArchiveInfo()
{
delete archive;
}
int32 token;
BMessage* archive;
};
BArchiveManager::BArchiveManager(const BArchiver* creator)
:
BManagerBase(creator->ArchiveMessage(), BManagerBase::ARCHIVE_MANAGER),
fTokenMap(),
fCreator(creator),
fError(B_OK)
{
}
BArchiveManager::~BArchiveManager()
{
fTopLevelArchive->AddBool(kManagedField, true);
}
status_t
BArchiveManager::GetTokenForArchivable(BArchivable* archivable, int32& _token)
{
if (!archivable) {
_token = NULL_TOKEN;
return B_OK;
}
TokenMap::iterator it = fTokenMap.find(archivable);
if (it == fTokenMap.end())
return B_ENTRY_NOT_FOUND;
_token = it->second.token;
return B_OK;
}
status_t
BArchiveManager::ArchiveObject(BArchivable* archivable,
bool deep, int32& _token)
{
if (!archivable) {
_token = NULL_TOKEN;
return B_OK;
}
ArchiveInfo& info = fTokenMap[archivable];
status_t err = B_OK;
if (!info.archive) {
info.archive = new BMessage();
info.token = fTokenMap.size() - 1;
MarkArchive(info.archive);
err = archivable->Archive(info.archive, deep);
}
if (err != B_OK) {
fTokenMap.erase(archivable);
_token = NULL_TOKEN;
} else
_token = info.token;
return err;
}
bool
BArchiveManager::IsArchived(BArchivable* archivable)
{
if (!archivable)
return true;
return fTokenMap.find(archivable) != fTokenMap.end();
}
status_t
BArchiveManager::ArchiverLeaving(const BArchiver* archiver, status_t err)
{
if (fError == B_OK)
fError = err;
if (archiver == fCreator && fError == B_OK) {
typedef std::pair<BMessage*, const BArchivable*> ArchivePair;
BStackOrHeapArray<ArchivePair, 64> pairs(fTokenMap.size());
for(TokenMap::iterator it = fTokenMap.begin(), end = fTokenMap.end();
it != end; it++) {
ArchiveInfo& info = it->second;
pairs[info.token].first = info.archive;
pairs[info.token].second = it->first;
if (info.archive == fTopLevelArchive)
info.archive = NULL;
}
int32 count = fTokenMap.size();
for (int32 i = 0; i < count; i++) {
const ArchivePair& pair = pairs[i];
fError = pair.second->AllArchived(pair.first);
if (fError == B_OK && i > 0) {
fError = fTopLevelArchive->AddMessage(kArchivableField,
pair.first);
}
if (fError != B_OK) {
syslog(LOG_ERR, "AllArchived failed for object of type %s.",
typeid(*pairs[i].second).name());
break;
}
}
}
status_t result = fError;
if (archiver == fCreator)
delete this;
return result;
}
void
BArchiveManager::RegisterArchivable(const BArchivable* archivable)
{
if (fTokenMap.size() == 0) {
ArchiveInfo& info = fTokenMap[archivable];
info.archive = fTopLevelArchive;
info.token = 0;
}
}
struct BUnarchiveManager::ArchiveInfo {
ArchiveInfo()
:
archivable(NULL),
archive(),
adopted(false)
{
}
bool
operator<(const ArchiveInfo& other)
{
return archivable < other.archivable;
}
BArchivable* archivable;
BMessage archive;
bool adopted;
};
BUnarchiveManager::BUnarchiveManager(BMessage* archive)
:
BManagerBase(archive, BManagerBase::UNARCHIVE_MANAGER),
fObjects(NULL),
fObjectCount(0),
fTokenInProgress(0),
fRefCount(0),
fError(B_OK)
{
archive->GetInfo(kArchivableField, NULL, &fObjectCount);
fObjectCount++;
fObjects = new ArchiveInfo[fObjectCount];
for (int32 i = 0; i < fObjectCount - 1; i++) {
BMessage* into = &fObjects[i + 1].archive;
status_t err = archive->FindMessage(kArchivableField, i, into);
MarkArchive(into);
if (err != B_OK)
syslog(LOG_ERR, "Failed to find managed archivable");
}
}
BUnarchiveManager::~BUnarchiveManager()
{
delete[] fObjects;
}
status_t
BUnarchiveManager::GetArchivableForToken(int32 token,
BUnarchiver::ownership_policy owning, BArchivable*& _archivable)
{
if (token >= fObjectCount)
return B_BAD_VALUE;
if (token < 0) {
_archivable = NULL;
return B_OK;
}
status_t err = B_OK;
ArchiveInfo& info = fObjects[token];
if (!info.archivable) {
if (fRefCount > 0) {
fTokenInProgress = token;
if(!instantiate_object(&info.archive))
err = B_ERROR;
} else {
syslog(LOG_ERR, "Object requested from AllUnarchived()"
" was not previously instantiated");
err = B_ERROR;
}
}
if (owning == BUnarchiver::B_ASSUME_OWNERSHIP)
info.adopted = true;
_archivable = info.archivable;
return err;
}
bool
BUnarchiveManager::IsInstantiated(int32 token)
{
if (token < 0 || token >= fObjectCount)
return false;
return fObjects[token].archivable;
}
void
BUnarchiveManager::RegisterArchivable(BArchivable* archivable)
{
if (!archivable)
debugger("Cannot register NULL pointer");
fObjects[fTokenInProgress].archivable = archivable;
archivable->fArchivingToken = fTokenInProgress;
}
status_t
BUnarchiveManager::UnarchiverLeaving(const BUnarchiver* unarchiver,
status_t err)
{
if (--fRefCount >= 0 && fError == B_OK)
fError = err;
if (fRefCount != 0)
return fError;
if (fError == B_OK) {
BArchivable* archivable = fObjects[0].archivable;
if (archivable) {
fError = archivable->AllUnarchived(fTopLevelArchive);
archivable->fArchivingToken = NULL_TOKEN;
}
for (int32 i = 1; i < fObjectCount && fError == B_OK; i++) {
archivable = fObjects[i].archivable;
if (archivable) {
fError = archivable->AllUnarchived(&fObjects[i].archive);
archivable->fArchivingToken = NULL_TOKEN;
}
}
if (fError != B_OK) {
syslog(LOG_ERR, "Error in AllUnarchived"
" method of object of type %s", typeid(*archivable).name());
}
}
if (fError != B_OK) {
syslog(LOG_ERR, "An error occured during unarchival, cleaning up.");
for (int32 i = 1; i < fObjectCount; i++) {
if (!fObjects[i].adopted)
delete fObjects[i].archivable;
}
}
status_t result = fError;
delete this;
return result;
}
void
BUnarchiveManager::RelinquishOwnership(BArchivable* archivable)
{
int32 token = NULL_TOKEN;
if (archivable)
token = archivable->fArchivingToken;
if (token < 0 || token >= fObjectCount
|| fObjects[token].archivable != archivable)
return;
fObjects[token].adopted = false;
}
void
BUnarchiveManager::AssumeOwnership(BArchivable* archivable)
{
int32 token = NULL_TOKEN;
if (archivable)
token = archivable->fArchivingToken;
if (token < 0 || token >= fObjectCount
|| fObjects[token].archivable != archivable)
return;
fObjects[token].adopted = true;
}
void
BUnarchiveManager::Acquire()
{
if (fRefCount >= 0)
fRefCount++;
}