1/*
2 * Copyright 2011-2013, Ingo Weinhold, ingo_weinhold@gmx.de
3 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8
9#include <StringList.h>
10
11#include <algorithm>
12
13#include <StringPrivate.h>
14#include <TypeConstants.h>
15
16
17static int
18compare_private_data(const void* a, const void* b)
19{
20	return BString::Private::StringFromData(*(char**)a).Compare(
21		BString::Private::StringFromData(*(char**)b));
22}
23
24
25static int
26compare_private_data_ignore_case(const void* a, const void* b)
27{
28	return BString::Private::StringFromData(*(char**)a).ICompare(
29		BString::Private::StringFromData(*(char**)b));
30}
31
32
33// #pragma mark - BStringList
34
35
36BStringList::BStringList(int32 count)
37	:
38	fStrings(count)
39{
40}
41
42
43BStringList::BStringList(const BStringList& other)
44	:
45	fStrings(other.fStrings)
46{
47	_IncrementRefCounts();
48}
49
50
51BStringList::~BStringList()
52{
53	_DecrementRefCounts();
54}
55
56
57bool
58BStringList::Add(const BString& _string, int32 index)
59{
60	BString string(_string);
61		// makes sure the string is shareable
62	if (string.Length() != _string.Length())
63		return false;
64
65	char* privateData = BString::Private(string).Data();
66	if (!fStrings.AddItem(privateData, index))
67		return false;
68
69	BString::Private::IncrementDataRefCount(privateData);
70	return true;
71}
72
73
74bool
75BStringList::Add(const BString& _string)
76{
77	BString string(_string);
78		// makes sure the string is shareable
79	if (string.Length() != _string.Length())
80		return false;
81
82	char* privateData = BString::Private(string).Data();
83	if (!fStrings.AddItem(privateData))
84		return false;
85
86	BString::Private::IncrementDataRefCount(privateData);
87	return true;
88}
89
90
91bool
92BStringList::Add(const BStringList& list, int32 index)
93{
94	if (!fStrings.AddList(&list.fStrings, index))
95		return false;
96
97	list._IncrementRefCounts();
98	return true;
99}
100
101
102bool
103BStringList::Add(const BStringList& list)
104{
105	if (!fStrings.AddList(&list.fStrings))
106		return false;
107
108	list._IncrementRefCounts();
109	return true;
110}
111
112
113bool
114BStringList::Remove(const BString& string, bool ignoreCase)
115{
116	bool result = false;
117	int32 count = fStrings.CountItems();
118
119	if (ignoreCase) {
120		int32 length = string.Length();
121
122		for (int32 i = count - 1; i >= 0; i--) {
123			BString element(StringAt(i));
124			if (length == element.Length() && string.ICompare(element) == 0) {
125				Remove(i);
126				result = true;
127			}
128		}
129	} else {
130		for (int32 i = count - 1; i >= 0; i--) {
131			if (string == StringAt(i)) {
132				Remove(i);
133				result = true;
134			}
135		}
136	}
137
138	return result;
139}
140
141
142bool
143BStringList::Remove(const BStringList& list, bool ignoreCase)
144{
145	bool removedAnything = false;
146	int32 stringCount = list.CountStrings();
147	for (int32 i = 0; i < stringCount; i++)
148		removedAnything |= Remove(list.StringAt(i), ignoreCase);
149
150	return removedAnything;
151}
152
153
154BString
155BStringList::Remove(int32 index)
156{
157	if (index < 0 || index >= fStrings.CountItems())
158		return BString();
159
160	char* privateData = (char*)fStrings.RemoveItem(index);
161	BString string(BString::Private::StringFromData(privateData));
162	BString::Private::DecrementDataRefCount(privateData);
163	return string;
164}
165
166
167bool
168BStringList::Remove(int32 index, int32 count)
169{
170	int32 stringCount = fStrings.CountItems();
171	if (index < 0 || index > stringCount)
172		return false;
173
174	int32 end = index + std::min(stringCount - index, count);
175	for (int32 i = index; i < end; i++)
176		BString::Private::DecrementDataRefCount((char*)fStrings.ItemAt(i));
177
178	fStrings.RemoveItems(index, end - index);
179	return true;
180}
181
182
183bool
184BStringList::Replace(int32 index, const BString& string)
185{
186	if (index < 0 || index >= fStrings.CountItems())
187		return false;
188
189	BString::Private::DecrementDataRefCount((char*)fStrings.ItemAt(index));
190
191	char* privateData = BString::Private(string).Data();
192	BString::Private::IncrementDataRefCount(privateData);
193	fStrings.ReplaceItem(index, privateData);
194
195	return true;
196}
197
198
199void
200BStringList::MakeEmpty()
201{
202	_DecrementRefCounts();
203	fStrings.MakeEmpty();
204}
205
206
207void
208BStringList::Sort(bool ignoreCase)
209{
210	fStrings.SortItems(ignoreCase
211		? compare_private_data_ignore_case : compare_private_data);
212}
213
214
215bool
216BStringList::Swap(int32 indexA, int32 indexB)
217{
218	return fStrings.SwapItems(indexA, indexB);
219}
220
221
222bool
223BStringList::Move(int32 fromIndex, int32 toIndex)
224{
225	return fStrings.MoveItem(fromIndex, toIndex);
226}
227
228
229BString
230BStringList::StringAt(int32 index) const
231{
232	return BString::Private::StringFromData((char*)fStrings.ItemAt(index));
233}
234
235
236BString
237BStringList::First() const
238{
239	return BString::Private::StringFromData((char*)fStrings.FirstItem());
240}
241
242
243BString
244BStringList::Last() const
245{
246	return BString::Private::StringFromData((char*)fStrings.LastItem());
247}
248
249
250int32
251BStringList::IndexOf(const BString& string, bool ignoreCase) const
252{
253	int32 count = fStrings.CountItems();
254
255	if (ignoreCase) {
256		int32 length = string.Length();
257
258		for (int32 i = 0; i < count; i++) {
259			BString element(StringAt(i));
260			if (length == element.Length() && string.ICompare(element) == 0)
261				return i;
262		}
263	} else {
264		for (int32 i = 0; i < count; i++) {
265			if (string == StringAt(i))
266				return i;
267		}
268	}
269
270	return -1;
271}
272
273
274int32
275BStringList::CountStrings() const
276{
277	return fStrings.CountItems();
278}
279
280
281bool
282BStringList::IsEmpty() const
283{
284	return fStrings.IsEmpty();
285}
286
287
288BString
289BStringList::Join(const char* separator, int32 length) const
290{
291	return _Join(separator,
292		length >= 0 ? strnlen(separator, length) : strlen(separator));
293}
294
295
296void
297BStringList::DoForEach(bool (*func)(const BString& string))
298{
299	bool terminate = false;
300	int32 count = fStrings.CountItems();
301	for (int32 i = 0; i < count && !terminate; i++)
302		terminate = func(StringAt(i));
303}
304
305
306void
307BStringList::DoForEach(bool (*func)(const BString& string, void* arg2),
308	void* arg2)
309{
310	bool terminate = false;
311	int32 count = fStrings.CountItems();
312	for (int32 i = 0; i < count && !terminate; i++)
313		terminate = func(StringAt(i), arg2);
314}
315
316
317BStringList&
318BStringList::operator=(const BStringList& other)
319{
320	if (this != &other) {
321		_DecrementRefCounts();
322		fStrings = other.fStrings;
323		_IncrementRefCounts();
324	}
325
326	return *this;
327}
328
329
330bool
331BStringList::operator==(const BStringList& other) const
332{
333	if (this == &other)
334		return true;
335
336	int32 count = fStrings.CountItems();
337	if (count != other.fStrings.CountItems())
338		return false;
339
340	for (int32 i = 0; i < count; i++) {
341		if (StringAt(i) != other.StringAt(i))
342			return false;
343	}
344
345	return true;
346}
347
348
349bool
350BStringList::IsFixedSize() const
351{
352	return false;
353}
354
355
356type_code
357BStringList::TypeCode() const
358{
359	return B_STRING_LIST_TYPE;
360}
361
362
363
364bool
365BStringList::AllowsTypeCode(type_code code) const
366{
367	return code == B_STRING_LIST_TYPE;
368}
369
370
371ssize_t
372BStringList::FlattenedSize() const
373{
374	ssize_t size = 0;
375	int32 count = CountStrings();
376	for (int32 i = 0; i < count; i++)
377		size += StringAt(i).Length() + 1;
378
379	return size;
380}
381
382
383status_t
384BStringList::Flatten(void* buf, ssize_t size) const
385{
386	const char* buffer = (const char*)buf;
387
388	if (size < FlattenedSize())
389		return B_NO_MEMORY;
390
391	int32 count = CountStrings();
392	for (int32 i = 0; i < count; i++) {
393		BString item = StringAt(i);
394		ssize_t storeSize = item.Length() + 1;
395		memcpy((void*)buffer, (const void*)item.String(), storeSize);
396		buffer += storeSize;
397	}
398
399	return B_OK;
400}
401
402
403status_t
404BStringList::Unflatten(type_code code, const void* buffer, ssize_t size)
405{
406	if (code != B_STRING_LIST_TYPE)
407		return B_ERROR;
408	const char* bufferStart = (const char*)buffer;
409
410	MakeEmpty();
411
412	off_t offset = 0;
413	while (offset < size) {
414		const char* string = bufferStart + offset;
415		size_t restSize = size - offset;
416		size_t read = strnlen(string, restSize);
417		if (read == restSize)
418			return B_BAD_VALUE;
419
420		if (!Add(string))
421			return B_NO_MEMORY;
422		offset += read + 1;
423	}
424
425	return B_OK;
426}
427
428
429void
430BStringList::_IncrementRefCounts() const
431{
432	int32 count = fStrings.CountItems();
433	for (int32 i = 0; i < count; i++) {
434		BString::Private::IncrementDataRefCount((char*)fStrings.ItemAt(i));
435	}
436}
437
438
439void
440BStringList::_DecrementRefCounts() const
441{
442	int32 count = fStrings.CountItems();
443	for (int32 i = 0; i < count; i++)
444		BString::Private::DecrementDataRefCount((char*)fStrings.ItemAt(i));
445}
446
447
448BString
449BStringList::_Join(const char* separator, int32 length) const
450{
451	// handle simple cases (0 or 1 element)
452	int32 count = CountStrings();
453	if (count == 0)
454		return BString();
455	if (count == 1)
456		return StringAt(0);
457
458	// determine the total length
459	int32 totalLength = length * (count - 1);
460	for (int32 i = 0; i < count; i++)
461		totalLength += StringAt(i).Length();
462
463	// compose the result string
464	BString result;
465	char* buffer = result.LockBuffer(totalLength);
466	if (buffer == NULL)
467		return result;
468
469	for (int32 i = 0; i < count; i++) {
470		if (i > 0 && length > 0) {
471			memcpy(buffer, separator, length);
472			buffer += length;
473		}
474
475		BString string = StringAt(i);
476		memcpy(buffer, string.String(), string.Length());
477		buffer += string.Length();
478	}
479
480	return result.UnlockBuffer(totalLength);
481}
482