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