xref: /haiku/src/kits/support/ArchivingManagers.cpp (revision 29e8fa5922c9f9a5eb09a2fcc92e7fb321d4cc59)
1 /*
2  * Copyright 2010 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Alex Wilson, yourpalal2@gmail.com
7  */
8 
9 
10 #include "ArchivingManagers.h"
11 
12 #include <syslog.h>
13 #include <typeinfo>
14 
15 #include <StackOrHeapArray.h>
16 
17 
18 namespace BPrivate {
19 namespace Archiving {
20 	const char* kArchivableField = "_managed_archivable";
21 	const char* kManagedField = "_managed_archive";
22 }
23 }
24 
25 
26 using namespace BPrivate::Archiving;
27 
28 
29 BArchiveManager*
ArchiveManager(const BMessage * archive)30 BManagerBase::ArchiveManager(const BMessage* archive)
31 {
32 	BManagerBase* manager = ManagerPointer(archive);
33 	if (!manager)
34 		return NULL;
35 
36 	if (manager->fType == ARCHIVE_MANAGER)
37 		return static_cast<BArchiveManager*>(manager);
38 
39 	debugger("Overlapping managed unarchive/archive sessions.");
40 	return NULL;
41 }
42 
43 
44 BUnarchiveManager*
UnarchiveManager(const BMessage * archive)45 BManagerBase::UnarchiveManager(const BMessage* archive)
46 {
47 	BManagerBase* manager = ManagerPointer(archive);
48 	if (!manager)
49 		return NULL;
50 
51 	if (manager->fType == UNARCHIVE_MANAGER)
52 		return static_cast<BUnarchiveManager*>(manager);
53 
54 	debugger("More calls to BUnarchiver::PrepareArchive()"
55 		" than BUnarchivers created.");
56 
57 	return NULL;
58 }
59 
60 
61 // #pragma mark -
62 
63 
64 struct BArchiveManager::ArchiveInfo {
ArchiveInfoBArchiveManager::ArchiveInfo65 	ArchiveInfo()
66 		:
67 		token(-1),
68 		archive(NULL)
69 	{
70 	}
71 
72 
~ArchiveInfoBArchiveManager::ArchiveInfo73 	~ArchiveInfo()
74 	{
75 		delete archive;
76 	}
77 
78 
79 	int32		token;
80 	BMessage*	archive;
81 };
82 
83 
BArchiveManager(const BArchiver * creator)84 BArchiveManager::BArchiveManager(const BArchiver* creator)
85 	:
86 	BManagerBase(creator->ArchiveMessage(), BManagerBase::ARCHIVE_MANAGER),
87 	fTokenMap(),
88 	fCreator(creator),
89 	fError(B_OK)
90 {
91 }
92 
93 
~BArchiveManager()94 BArchiveManager::~BArchiveManager()
95 {
96 	fTopLevelArchive->AddBool(kManagedField, true);
97 }
98 
99 
100 status_t
GetTokenForArchivable(BArchivable * archivable,int32 & _token)101 BArchiveManager::GetTokenForArchivable(BArchivable* archivable, int32& _token)
102 {
103 	if (!archivable) {
104 		_token = NULL_TOKEN;
105 		return B_OK;
106 	}
107 
108 	TokenMap::iterator it = fTokenMap.find(archivable);
109 
110 	if (it == fTokenMap.end())
111 		return B_ENTRY_NOT_FOUND;
112 
113 	_token = it->second.token;
114 	return B_OK;
115 }
116 
117 
118 status_t
ArchiveObject(BArchivable * archivable,bool deep,int32 & _token)119 BArchiveManager::ArchiveObject(BArchivable* archivable,
120 	bool deep, int32& _token)
121 {
122 	if (!archivable) {
123 		_token = NULL_TOKEN;
124 		return B_OK;
125 	}
126 
127 	ArchiveInfo& info = fTokenMap[archivable];
128 
129 	status_t err = B_OK;
130 
131 	if (!info.archive) {
132 		info.archive = new BMessage();
133 		info.token = fTokenMap.size() - 1;
134 
135 		MarkArchive(info.archive);
136 		err = archivable->Archive(info.archive, deep);
137 	}
138 
139 	if (err != B_OK) {
140 		fTokenMap.erase(archivable);
141 			// info.archive gets deleted here
142 		_token = NULL_TOKEN;
143 	} else
144 		_token = info.token;
145 
146 	return err;
147 }
148 
149 
150 bool
IsArchived(BArchivable * archivable)151 BArchiveManager::IsArchived(BArchivable* archivable)
152 {
153 	if (!archivable)
154 		return true;
155 
156 	return fTokenMap.find(archivable) != fTokenMap.end();
157 }
158 
159 
160 status_t
ArchiverLeaving(const BArchiver * archiver,status_t err)161 BArchiveManager::ArchiverLeaving(const BArchiver* archiver, status_t err)
162 {
163 	if (fError == B_OK)
164 		fError = err;
165 
166 	if (archiver == fCreator && fError == B_OK) {
167 		// first, we must sort the objects into the order they were archived in
168 		typedef std::pair<BMessage*, const BArchivable*> ArchivePair;
169 		BStackOrHeapArray<ArchivePair, 64> pairs(fTokenMap.size());
170 
171 		for(TokenMap::iterator it = fTokenMap.begin(), end = fTokenMap.end();
172 				it != end; it++) {
173 			ArchiveInfo& info = it->second;
174 			pairs[info.token].first = info.archive;
175 			pairs[info.token].second = it->first;
176 
177 			// make sure fTopLevelArchive isn't deleted
178 			if (info.archive == fTopLevelArchive)
179 				info.archive = NULL;
180 		}
181 
182 		int32 count = fTokenMap.size();
183 		for (int32 i = 0; i < count; i++) {
184 			const ArchivePair& pair = pairs[i];
185 			fError = pair.second->AllArchived(pair.first);
186 
187 			if (fError == B_OK && i > 0) {
188 				fError = fTopLevelArchive->AddMessage(kArchivableField,
189 					pair.first);
190 			}
191 
192 			if (fError != B_OK) {
193 				syslog(LOG_ERR, "AllArchived failed for object of type %s.",
194 					typeid(*pairs[i].second).name());
195 				break;
196 			}
197 		}
198 	}
199 
200 	status_t result = fError;
201 	if (archiver == fCreator)
202 		delete this;
203 
204 	return result;
205 }
206 
207 
208 void
RegisterArchivable(const BArchivable * archivable)209 BArchiveManager::RegisterArchivable(const BArchivable* archivable)
210 {
211 	if (fTokenMap.size() == 0) {
212 		ArchiveInfo& info = fTokenMap[archivable];
213 		info.archive = fTopLevelArchive;
214 		info.token = 0;
215 	}
216 }
217 
218 
219 // #pragma mark -
220 
221 
222 struct BUnarchiveManager::ArchiveInfo {
ArchiveInfoBUnarchiveManager::ArchiveInfo223 	ArchiveInfo()
224 		:
225 		archivable(NULL),
226 		archive(),
227 		adopted(false)
228 	{
229 	}
230 
231 	bool
operator <BUnarchiveManager::ArchiveInfo232 	operator<(const ArchiveInfo& other)
233 	{
234 		return archivable < other.archivable;
235 	}
236 
237 	BArchivable*	archivable;
238 	BMessage		archive;
239 	bool			adopted;
240 };
241 
242 
243 // #pragma mark -
244 
245 
BUnarchiveManager(BMessage * archive)246 BUnarchiveManager::BUnarchiveManager(BMessage* archive)
247 	:
248 	BManagerBase(archive, BManagerBase::UNARCHIVE_MANAGER),
249 	fObjects(NULL),
250 	fObjectCount(0),
251 	fTokenInProgress(0),
252 	fRefCount(0),
253 	fError(B_OK)
254 {
255 	archive->GetInfo(kArchivableField, NULL, &fObjectCount);
256 	fObjectCount++;
257 		// root object needs a spot too
258 	fObjects = new ArchiveInfo[fObjectCount];
259 
260 	// fObjects[0] is a placeholder for the object that started
261 	// this unarchiving session.
262 	for (int32 i = 0; i < fObjectCount - 1; i++) {
263 		BMessage* into = &fObjects[i + 1].archive;
264 		status_t err = archive->FindMessage(kArchivableField, i, into);
265 		MarkArchive(into);
266 
267 		if (err != B_OK)
268 			syslog(LOG_ERR, "Failed to find managed archivable");
269 	}
270 }
271 
272 
~BUnarchiveManager()273 BUnarchiveManager::~BUnarchiveManager()
274 {
275 	delete[] fObjects;
276 }
277 
278 
279 status_t
GetArchivableForToken(int32 token,BUnarchiver::ownership_policy owning,BArchivable * & _archivable)280 BUnarchiveManager::GetArchivableForToken(int32 token,
281 	BUnarchiver::ownership_policy owning, BArchivable*& _archivable)
282 {
283 	if (token >= fObjectCount)
284 		return B_BAD_VALUE;
285 
286 	if (token < 0) {
287 		_archivable = NULL;
288 		return B_OK;
289 	}
290 
291 	status_t err = B_OK;
292 	ArchiveInfo& info = fObjects[token];
293 	if (!info.archivable) {
294 		if (fRefCount > 0) {
295 			fTokenInProgress = token;
296 			if(!instantiate_object(&info.archive))
297 				err = B_ERROR;
298 		} else {
299 			syslog(LOG_ERR, "Object requested from AllUnarchived()"
300 				" was not previously instantiated");
301 			err = B_ERROR;
302 		}
303 	}
304 
305 	if (owning == BUnarchiver::B_ASSUME_OWNERSHIP)
306 		info.adopted = true;
307 
308 	_archivable = info.archivable;
309 	return err;
310 }
311 
312 
313 bool
IsInstantiated(int32 token)314 BUnarchiveManager::IsInstantiated(int32 token)
315 {
316 	if (token < 0 || token >= fObjectCount)
317 		return false;
318 	return fObjects[token].archivable;
319 }
320 
321 
322 void
RegisterArchivable(BArchivable * archivable)323 BUnarchiveManager::RegisterArchivable(BArchivable* archivable)
324 {
325 	if (!archivable)
326 		debugger("Cannot register NULL pointer");
327 
328 	fObjects[fTokenInProgress].archivable = archivable;
329 	archivable->fArchivingToken = fTokenInProgress;
330 }
331 
332 
333 status_t
UnarchiverLeaving(const BUnarchiver * unarchiver,status_t err)334 BUnarchiveManager::UnarchiverLeaving(const BUnarchiver* unarchiver,
335 	status_t err)
336 {
337 	if (--fRefCount >= 0 && fError == B_OK)
338 		fError = err;
339 
340 	if (fRefCount != 0)
341 		return fError;
342 
343 	if (fError == B_OK) {
344 		BArchivable* archivable = fObjects[0].archivable;
345 		if (archivable) {
346 			fError = archivable->AllUnarchived(fTopLevelArchive);
347 			archivable->fArchivingToken = NULL_TOKEN;
348 		}
349 
350 		for (int32 i = 1; i < fObjectCount && fError == B_OK; i++) {
351 			archivable = fObjects[i].archivable;
352 			if (archivable) {
353 				fError = archivable->AllUnarchived(&fObjects[i].archive);
354 				archivable->fArchivingToken = NULL_TOKEN;
355 			}
356 		}
357 		if (fError != B_OK) {
358 			syslog(LOG_ERR, "Error in AllUnarchived"
359 				" method of object of type %s", typeid(*archivable).name());
360 		}
361 	}
362 
363 	if (fError != B_OK) {
364 		syslog(LOG_ERR, "An error occured during unarchival, cleaning up.");
365 		for (int32 i = 1; i < fObjectCount; i++) {
366 			if (!fObjects[i].adopted)
367 				delete fObjects[i].archivable;
368 		}
369 	}
370 
371 	status_t result = fError;
372 	delete this;
373 	return result;
374 }
375 
376 
377 void
RelinquishOwnership(BArchivable * archivable)378 BUnarchiveManager::RelinquishOwnership(BArchivable* archivable)
379 {
380 	int32 token = NULL_TOKEN;
381 	if (archivable)
382 		token = archivable->fArchivingToken;
383 
384 	if (token < 0 || token >= fObjectCount
385 		|| fObjects[token].archivable != archivable)
386 		return;
387 
388 	fObjects[token].adopted = false;
389 }
390 
391 
392 void
AssumeOwnership(BArchivable * archivable)393 BUnarchiveManager::AssumeOwnership(BArchivable* archivable)
394 {
395 	int32 token = NULL_TOKEN;
396 	if (archivable)
397 		token = archivable->fArchivingToken;
398 
399 	if (token < 0 || token >= fObjectCount
400 		|| fObjects[token].archivable != archivable)
401 		return;
402 
403 	fObjects[token].adopted = true;
404 }
405 
406 
407 void
Acquire()408 BUnarchiveManager::Acquire()
409 {
410 	if (fRefCount >= 0)
411 		fRefCount++;
412 }
413