1/*
2 * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
5
6#include <TypeConstants.h>
7
8#include "DebugSupport.h"
9#include "Entry.h"
10#include "EntryListener.h"
11#include "IndexImpl.h"
12#include "LastModifiedIndex.h"
13#include "Node.h"
14#include "NodeListener.h"
15#include "Volume.h"
16
17// LastModifiedIndexPrimaryKey
18class LastModifiedIndexPrimaryKey {
19public:
20	LastModifiedIndexPrimaryKey(Node *node, time_t modified)
21		: node(node), modified(modified) {}
22	LastModifiedIndexPrimaryKey(Node *node)
23		: node(node), modified(node->GetMTime()) {}
24	LastModifiedIndexPrimaryKey(time_t modified)
25		: node(NULL), modified(modified) {}
26
27	Node	*node;
28	time_t	modified;
29};
30
31// LastModifiedIndexGetPrimaryKey
32class LastModifiedIndexGetPrimaryKey {
33public:
34	inline LastModifiedIndexPrimaryKey operator()(Node *a)
35	{
36		return LastModifiedIndexPrimaryKey(a);
37	}
38
39	inline LastModifiedIndexPrimaryKey operator()(Node *a) const
40	{
41		return LastModifiedIndexPrimaryKey(a);
42	}
43};
44
45// LastModifiedIndexPrimaryKeyCompare
46class LastModifiedIndexPrimaryKeyCompare
47{
48public:
49	inline int operator()(const LastModifiedIndexPrimaryKey &a,
50						  const LastModifiedIndexPrimaryKey &b) const
51	{
52		if (a.node != NULL && a.node == b.node)
53			return 0;
54		if (a.modified < b.modified)
55			return -1;
56		if (a.modified > b.modified)
57			return 1;
58		return 0;
59	}
60};
61
62
63// NodeTree
64typedef TwoKeyAVLTree<Node*, LastModifiedIndexPrimaryKey,
65					  LastModifiedIndexPrimaryKeyCompare,
66					  LastModifiedIndexGetPrimaryKey>
67		_NodeTree;
68class LastModifiedIndex::NodeTree : public _NodeTree {};
69
70
71// IteratorList
72class LastModifiedIndex::IteratorList : public DoublyLinkedList<Iterator> {};
73
74
75// Iterator
76class LastModifiedIndex::Iterator
77	: public NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator>,
78	  public DoublyLinkedListLinkImpl<Iterator>, public EntryListener,
79	  public NodeListener {
80public:
81	Iterator();
82	virtual ~Iterator();
83
84	virtual Entry *GetCurrent();
85	virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
86
87	virtual status_t Suspend();
88	virtual status_t Resume();
89
90	bool SetTo(LastModifiedIndex *index, time_t modified,
91			   bool ignoreValue = false);
92	void Unset();
93
94	virtual void EntryRemoved(Entry *entry);
95	virtual void NodeRemoved(Node *node);
96
97private:
98	typedef NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator> BaseClass;
99
100private:
101	LastModifiedIndex						*fIndex;
102};
103
104
105// LastModifiedIndex
106
107// constructor
108LastModifiedIndex::LastModifiedIndex(Volume *volume)
109	: Index(volume, "last_modified", B_INT32_TYPE, true, sizeof(time_t)),
110	  fNodes(new(nothrow) NodeTree),
111	  fIterators(new(nothrow) IteratorList)
112{
113	if (fInitStatus == B_OK && (!fNodes || !fIterators))
114		fInitStatus = B_NO_MEMORY;
115	if (fInitStatus == B_OK) {
116		fInitStatus = fVolume->AddNodeListener(this,
117			NULL, NODE_LISTEN_ANY_NODE | NODE_LISTEN_ALL);
118	}
119}
120
121// destructor
122LastModifiedIndex::~LastModifiedIndex()
123{
124	if (fVolume)
125		fVolume->RemoveNodeListener(this, NULL);
126	if (fIterators) {
127		// unset the iterators
128		for (Iterator *iterator = fIterators->First();
129			 iterator;
130			 iterator = fIterators->GetNext(iterator)) {
131			iterator->SetTo(NULL, 0);
132		}
133		delete fIterators;
134	}
135	if (fNodes)
136		delete fNodes;
137}
138
139// CountEntries
140int32
141LastModifiedIndex::CountEntries() const
142{
143	return fNodes->CountItems();
144}
145
146// Changed
147status_t
148LastModifiedIndex::Changed(Node *node, time_t oldModified)
149{
150	status_t error = B_BAD_VALUE;
151	if (node) {
152		NodeTree::Iterator it;
153		Node **foundNode = fNodes->Find(LastModifiedIndexPrimaryKey(node,
154			oldModified), node, &it);
155		if (foundNode && *foundNode == node) {
156			// update the iterators
157			for (Iterator *iterator = fIterators->First();
158				 iterator;
159				 iterator = fIterators->GetNext(iterator)) {
160				if (iterator->GetCurrentNode() == node)
161					iterator->NodeRemoved(node);
162			}
163			// remove and re-insert the node
164			it.Remove();
165			error = fNodes->Insert(node);
166
167			// udpate live queries
168			time_t newModified = node->GetMTime();
169			fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(),
170				(const uint8*)&oldModified, sizeof(oldModified),
171				(const uint8*)&newModified, sizeof(newModified));
172		}
173	}
174	return error;
175}
176
177// NodeAdded
178void
179LastModifiedIndex::NodeAdded(Node *node)
180{
181	if (node)
182		fNodes->Insert(node);
183}
184
185// NodeRemoved
186void
187LastModifiedIndex::NodeRemoved(Node *node)
188{
189	if (node)
190		fNodes->Remove(node, node);
191}
192
193// InternalGetIterator
194AbstractIndexEntryIterator *
195LastModifiedIndex::InternalGetIterator()
196{
197	Iterator *iterator = new(nothrow) Iterator;
198	if (iterator) {
199		if (!iterator->SetTo(this, 0, true)) {
200			delete iterator;
201			iterator = NULL;
202		}
203	}
204	return iterator;
205}
206
207// InternalFind
208AbstractIndexEntryIterator *
209LastModifiedIndex::InternalFind(const uint8 *key, size_t length)
210{
211	if (!key || length != sizeof(time_t))
212		return NULL;
213	Iterator *iterator = new(nothrow) Iterator;
214	if (iterator) {
215		if (!iterator->SetTo(this, *(const time_t*)key)) {
216			delete iterator;
217			iterator = NULL;
218		}
219	}
220	return iterator;
221}
222
223// _AddIterator
224void
225LastModifiedIndex::_AddIterator(Iterator *iterator)
226{
227	fIterators->Insert(iterator);
228}
229
230// _RemoveIterator
231void
232LastModifiedIndex::_RemoveIterator(Iterator *iterator)
233{
234	fIterators->Remove(iterator);
235}
236
237
238// Iterator
239
240// constructor
241LastModifiedIndex::Iterator::Iterator()
242	: BaseClass(),
243	  fIndex(NULL)
244{
245}
246
247// destructor
248LastModifiedIndex::Iterator::~Iterator()
249{
250	SetTo(NULL, 0);
251}
252
253// GetCurrent
254Entry *
255LastModifiedIndex::Iterator::GetCurrent()
256{
257	return BaseClass::GetCurrent();
258}
259
260// GetCurrent
261Entry *
262LastModifiedIndex::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
263{
264	Entry *entry = GetCurrent();
265	if (entry) {
266		*(time_t*)buffer = entry->GetNode()->GetMTime();
267		*keyLength = sizeof(time_t);
268	}
269	return entry;
270}
271
272// Suspend
273status_t
274LastModifiedIndex::Iterator::Suspend()
275{
276	status_t error = BaseClass::Suspend();
277	if (error == B_OK) {
278		if (fNode) {
279			error = fIndex->GetVolume()->AddNodeListener(this, fNode,
280														 NODE_LISTEN_REMOVED);
281			if (error == B_OK && fEntry) {
282				error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
283					ENTRY_LISTEN_REMOVED);
284				if (error != B_OK)
285					fIndex->GetVolume()->RemoveNodeListener(this, fNode);
286			}
287			if (error != B_OK)
288				BaseClass::Resume();
289		}
290	}
291	return error;
292}
293
294// Resume
295status_t
296LastModifiedIndex::Iterator::Resume()
297{
298	status_t error = BaseClass::Resume();
299	if (error == B_OK) {
300		if (fEntry)
301			error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
302		if (fNode) {
303			if (error == B_OK)
304				error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
305			else
306				fIndex->GetVolume()->RemoveNodeListener(this, fNode);
307		}
308	}
309	return error;
310}
311
312// SetTo
313bool
314LastModifiedIndex::Iterator::SetTo(LastModifiedIndex *index, time_t modified,
315								   bool ignoreValue)
316{
317	Resume();
318	Unset();
319	// set the new values
320	fIndex = index;
321	if (fIndex)
322		fIndex->_AddIterator(this);
323	fInitialized = fIndex;
324	// get the node's first entry
325	if (fIndex) {
326		// get the first node
327		bool found = true;
328		if (ignoreValue)
329			fIndex->fNodes->GetIterator(&fIterator);
330		else
331			found = fIndex->fNodes->FindFirst(modified, &fIterator);
332		// get the first entry
333		if (found) {
334			if (Node **nodeP = fIterator.GetCurrent()) {
335				fNode = *nodeP;
336				fEntry = fNode->GetFirstReferrer();
337				if (!fEntry)
338					BaseClass::GetNext();
339				if (!ignoreValue && fNode && fNode->GetMTime() != modified)
340					Unset();
341			}
342		}
343	}
344	return fEntry;
345}
346
347// Unset
348void
349LastModifiedIndex::Iterator::Unset()
350{
351	if (fIndex) {
352		fIndex->_RemoveIterator(this);
353		fIndex = NULL;
354	}
355	BaseClass::Unset();
356}
357
358// EntryRemoved
359void
360LastModifiedIndex::Iterator::EntryRemoved(Entry */*entry*/)
361{
362	Resume();
363	fIsNext = BaseClass::GetNext();
364	Suspend();
365}
366
367// NodeRemoved
368void
369LastModifiedIndex::Iterator::NodeRemoved(Node */*node*/)
370{
371	Resume();
372	fEntry = NULL;
373	fIsNext = BaseClass::GetNext();
374	Suspend();
375}
376
377