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