xref: /haiku/src/apps/mail/QueryList.cpp (revision aa3083e086e5a929c061c72983e09d916c548a38)
1 /*
2  * Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "QueryList.h"
8 
9 #include <Autolock.h>
10 #include <Debug.h>
11 #include <NodeMonitor.h>
12 #include <VolumeRoster.h>
13 
14 
15 static BLooper* sQueryLooper = NULL;
16 static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
17 
18 
19 static void
20 initQueryLooper()
21 {
22 	sQueryLooper = new BLooper("query looper");
23 	sQueryLooper->Run();
24 }
25 
26 
27 // #pragma mark - QueryListener
28 
29 
30 QueryListener::~QueryListener()
31 {
32 }
33 
34 
35 // #pragma mark - QueryList
36 
37 
38 QueryList::QueryList()
39 	:
40 	fQuit(false),
41 	fListeners(5, true)
42 {
43 }
44 
45 
46 QueryList::~QueryList()
47 {
48 	fQuit = true;
49 
50 	ThreadVector::const_iterator threadIterator = fFetchThreads.begin();
51 	for (; threadIterator != fFetchThreads.end(); threadIterator++) {
52 		wait_for_thread(*threadIterator, NULL);
53 	}
54 
55 	QueryVector::iterator queryIterator = fQueries.begin();
56 	for (; queryIterator != fQueries.end(); queryIterator++) {
57 		delete *queryIterator;
58 	}
59 }
60 
61 
62 status_t
63 QueryList::Init(const char* predicate, BVolume* specificVolume)
64 {
65 	if (sQueryLooper == NULL)
66 		pthread_once(&sInitOnce, &initQueryLooper);
67 
68 	if (Looper() != NULL)
69 		debugger("Init() called twice!");
70 
71 	sQueryLooper->Lock();
72 	sQueryLooper->AddHandler(this);
73 	sQueryLooper->Unlock();
74 
75 	if (specificVolume == NULL) {
76 		BVolumeRoster roster;
77 		BVolume volume;
78 
79 		while (roster.GetNextVolume(&volume) == B_OK) {
80 			if (volume.KnowsQuery() && volume.KnowsAttr() && volume.KnowsMime())
81 				_AddVolume(volume, predicate);
82 		}
83 	} else
84 		_AddVolume(*specificVolume, predicate);
85 
86 	return B_OK;
87 }
88 
89 
90 void
91 QueryList::AddListener(QueryListener* listener)
92 {
93 	BAutolock locker(this);
94 
95 	// Add all entries that were already retrieved
96 	RefMap::const_iterator iterator = fRefs.begin();
97 	for (; iterator != fRefs.end(); iterator++) {
98 		listener->EntryCreated(*this, iterator->second, iterator->first.node);
99 	}
100 
101 	fListeners.AddItem(listener);
102 }
103 
104 
105 void
106 QueryList::RemoveListener(QueryListener* listener)
107 {
108 	BAutolock locker(this);
109 	fListeners.RemoveItem(listener, false);
110 }
111 
112 
113 void
114 QueryList::MessageReceived(BMessage* message)
115 {
116 	switch (message->what) {
117 		case B_QUERY_UPDATE:
118 		{
119 			int32 opcode = message->GetInt32("opcode", -1);
120 			int64 directory = message->GetInt64("directory", -1);
121 			int32 device = message->GetInt32("device", -1);
122 			int64 node = message->GetInt64("node", -1);
123 
124 			if (opcode == B_ENTRY_CREATED) {
125 				const char* name = message->GetString("name");
126 				if (name != NULL) {
127 					entry_ref ref(device, directory, name);
128 					_AddEntry(ref, node);
129 				}
130 			} else if (opcode == B_ENTRY_REMOVED) {
131 				node_ref nodeRef(device, node);
132 				_RemoveEntry(nodeRef);
133 			}
134 			break;
135 		}
136 
137 		default:
138 			BHandler::MessageReceived(message);
139 			break;
140 	}
141 }
142 
143 
144 void
145 QueryList::_AddEntry(const entry_ref& ref, ino_t node)
146 {
147 	BAutolock locker(this);
148 
149 	// TODO: catch bad_alloc
150 	fRefs.insert(std::make_pair(node_ref(ref.device, node), ref));
151 
152 	_NotifyEntryCreated(ref, node);
153 }
154 
155 
156 void
157 QueryList::_RemoveEntry(const node_ref& nodeRef)
158 {
159 	BAutolock locker(this);
160 	RefMap::iterator found = fRefs.find(nodeRef);
161 	if (found != fRefs.end())
162 		_NotifyEntryRemoved(nodeRef);
163 }
164 
165 
166 void
167 QueryList::_NotifyEntryCreated(const entry_ref& ref, ino_t node)
168 {
169 	ASSERT(IsLocked());
170 
171 	int32 count = fListeners.CountItems();
172 	for (int32 index = 0; index < count; index++) {
173 		fListeners.ItemAt(index)->EntryCreated(*this, ref, node);
174 	}
175 }
176 
177 
178 void
179 QueryList::_NotifyEntryRemoved(const node_ref& nodeRef)
180 {
181 	ASSERT(IsLocked());
182 
183 	int32 count = fListeners.CountItems();
184 	for (int32 index = 0; index < count; index++) {
185 		fListeners.ItemAt(index)->EntryRemoved(*this, nodeRef);
186 	}
187 }
188 
189 
190 void
191 QueryList::_AddVolume(BVolume& volume, const char* predicate)
192 {
193 	BQuery* query = new BQuery();
194 	if (query->SetVolume(&volume) != B_OK
195 		|| query->SetPredicate(predicate) != B_OK
196 		|| query->SetTarget(this) != B_OK) {
197 		delete query;
198 	}
199 
200 	// TODO: catch bad_alloc
201 	fQueries.push_back(query);
202 	Lock();
203 	fQueryQueue.push_back(query);
204 	Unlock();
205 
206 	thread_id thread = spawn_thread(_FetchQuery, "query fetcher",
207 		B_NORMAL_PRIORITY, this);
208 	if (thread >= B_OK) {
209 		resume_thread(thread);
210 
211 		fFetchThreads.push_back(thread);
212 	}
213 }
214 
215 
216 /*static*/ status_t
217 QueryList::_FetchQuery(void* self)
218 {
219 	return static_cast<QueryList*>(self)->_FetchQuery();
220 }
221 
222 
223 status_t
224 QueryList::_FetchQuery()
225 {
226 	RefMap map;
227 
228 	BAutolock locker(this);
229 	BQuery* query = fQueryQueue.back();
230 	fQueryQueue.pop_back();
231 	locker.Unlock();
232 
233 	query->Fetch();
234 
235 	entry_ref ref;
236 	while (!fQuit && query->GetNextRef(&ref) == B_OK) {
237 		BEntry entry(&ref);
238 		node_ref nodeRef;
239 		if (entry.GetNodeRef(&nodeRef) == B_OK)
240 			map.insert(std::make_pair(nodeRef, ref));
241 	}
242 	if (fQuit)
243 		return B_INTERRUPTED;
244 
245 	locker.Lock();
246 
247 	RefMap::const_iterator iterator = map.begin();
248 	for (; iterator != map.end(); iterator++) {
249 		_AddEntry(iterator->second, iterator->first.node);
250 	}
251 
252 	return B_OK;
253 }
254