1/*
2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6#include "MimeSnifferAddonManager.h"
7
8#include <new>
9
10#include <Autolock.h>
11#include <MimeType.h>
12
13#include "MimeSnifferAddon.h"
14
15using std::nothrow;
16
17
18// singleton instance
19MimeSnifferAddonManager* MimeSnifferAddonManager::sManager = NULL;
20
21
22// AddonReference
23struct MimeSnifferAddonManager::AddonReference {
24	AddonReference(BMimeSnifferAddon* addon)
25		: fAddon(addon),
26		  fReferenceCount(1)
27	{
28	}
29
30	~AddonReference()
31	{
32		delete fAddon;
33	}
34
35	BMimeSnifferAddon* Addon() const
36	{
37		return fAddon;
38	}
39
40	void GetReference()
41	{
42		atomic_add(&fReferenceCount, 1);
43	}
44
45	void PutReference()
46	{
47		if (atomic_add(&fReferenceCount, -1) == 1)
48			delete this;
49	}
50
51private:
52	BMimeSnifferAddon*	fAddon;
53	vint32				fReferenceCount;
54};
55
56
57// constructor
58MimeSnifferAddonManager::MimeSnifferAddonManager()
59	: fLock("mime sniffer manager"),
60	  fAddons(20),
61	  fMinimalBufferSize(0)
62{
63}
64
65// destructor
66MimeSnifferAddonManager::~MimeSnifferAddonManager()
67{
68}
69
70// Default
71MimeSnifferAddonManager*
72MimeSnifferAddonManager::Default()
73{
74	return sManager;
75}
76
77// CreateDefault
78status_t
79MimeSnifferAddonManager::CreateDefault()
80{
81	MimeSnifferAddonManager* manager = new(nothrow) MimeSnifferAddonManager;
82	if (!manager)
83		return B_NO_MEMORY;
84
85	sManager = manager;
86
87	return B_OK;
88}
89
90// DeleteDefault
91void
92MimeSnifferAddonManager::DeleteDefault()
93{
94	MimeSnifferAddonManager* manager = sManager;
95	sManager = NULL;
96
97	delete manager;
98}
99
100// AddMimeSnifferAddon
101status_t
102MimeSnifferAddonManager::AddMimeSnifferAddon(BMimeSnifferAddon* addon)
103{
104	if (!addon)
105		return B_BAD_VALUE;
106
107	BAutolock locker(fLock);
108	if (!locker.IsLocked())
109		return B_ERROR;
110
111	// create a reference for the addon
112	AddonReference* reference = new(nothrow) AddonReference(addon);
113	if (!reference)
114		return B_NO_MEMORY;
115
116	// add the reference
117	if (!fAddons.AddItem(reference)) {
118		delete reference;
119		return B_NO_MEMORY;
120	}
121
122	// update minimal buffer size
123	size_t minBufferSize = addon->MinimalBufferSize();
124	if (minBufferSize > fMinimalBufferSize)
125		fMinimalBufferSize = minBufferSize;
126
127	return B_OK;
128}
129
130// MinimalBufferSize
131size_t
132MimeSnifferAddonManager::MinimalBufferSize()
133{
134	return fMinimalBufferSize;
135}
136
137// GuessMimeType
138float
139MimeSnifferAddonManager::GuessMimeType(const char* fileName, BMimeType* type)
140{
141	// get addons
142	AddonReference** addons = NULL;
143	int32 count = 0;
144	status_t error = _GetAddons(addons, count);
145	if (error != B_OK)
146		return -1;
147
148	// iterate over the addons and find the most fitting type
149	float bestPriority = -1;
150	for (int32 i = 0; i < count; i++) {
151		BMimeType currentType;
152		float priority = addons[i]->Addon()->GuessMimeType(fileName,
153			&currentType);
154		if (priority > bestPriority) {
155			type->SetTo(currentType.Type());
156			bestPriority = priority;
157		}
158	}
159
160	// release addons
161	_PutAddons(addons, count);
162
163	return bestPriority;
164}
165
166// GuessMimeType
167float
168MimeSnifferAddonManager::GuessMimeType(BFile* file, const void* buffer,
169	int32 length, BMimeType* type)
170{
171	// get addons
172	AddonReference** addons = NULL;
173	int32 count = 0;
174	status_t error = _GetAddons(addons, count);
175	if (error != B_OK)
176		return -1;
177
178	// iterate over the addons and find the most fitting type
179	float bestPriority = -1;
180	for (int32 i = 0; i < count; i++) {
181		BMimeType currentType;
182		float priority = addons[i]->Addon()->GuessMimeType(file, buffer,
183			length, &currentType);
184		if (priority > bestPriority) {
185			type->SetTo(currentType.Type());
186			bestPriority = priority;
187		}
188	}
189
190	// release addons
191	_PutAddons(addons, count);
192
193	return bestPriority;
194}
195
196// _GetAddons
197status_t
198MimeSnifferAddonManager::_GetAddons(AddonReference**& references, int32& count)
199{
200	BAutolock locker(fLock);
201	if (!locker.IsLocked())
202		return B_ERROR;
203
204	count = fAddons.CountItems();
205	references = new(nothrow) AddonReference*[count];
206	if (!references)
207		return B_NO_MEMORY;
208
209	for (int32 i = 0; i < count; i++) {
210		references[i] = (AddonReference*)fAddons.ItemAt(i);
211		references[i]->GetReference();
212	}
213
214	return B_OK;
215}
216
217// _PutAddons
218void
219MimeSnifferAddonManager::_PutAddons(AddonReference** references, int32 count)
220{
221	for (int32 i = 0; i < count; i++)
222		references[i]->PutReference();
223
224	delete[] references;
225}
226