source: branches/profile/CORE/arb_strarray.cxx

Last change on this file was 12848, checked in by westram, 10 years ago
  • reintegrates 'fix' into 'trunk'
    • removes calls to aw_string_selection and aw_string_selection2awar (implementing another step for #179)
  • adds:
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.9 KB
Line 
1// ============================================================ //
2//                                                              //
3//   File      : arb_strarray.cxx                               //
4//   Purpose   : handle arrays of strings                       //
5//                                                              //
6//   Coded by Ralf Westram (coder@reallysoft.de) in July 2011   //
7//   Institute of Microbiology (Technical University Munich)    //
8//   http://www.arb-home.de/                                    //
9//                                                              //
10// ============================================================ //
11
12#include "arb_strarray.h"
13
14#include <arb_str.h>
15#include <arb_string.h>
16#include <arb_strbuf.h>
17#include <arb_sort.h>
18
19void CharPtrArray::sort(CharPtrArray_compare_fun compare, void *client_data) {
20    /*! sort the array
21     * @see sort_and_uniq for parameters
22     */
23    GB_sort((void**)str, 0, size(), compare, client_data);
24}
25
26void CharPtrArray::uniq(CharPtrArray_compare_fun compare, void *client_data) {
27    /*! remove consecutive equal elements
28     * @see sort_and_uniq for parameters
29     */
30    for (int i = size()-2; i >= 0; --i) {
31        if (compare(str[i], str[i+1], client_data) == 0) {
32            remove(i+1);
33        }
34    }
35}
36
37
38/* ----------------------------------------
39 * conversion between
40 *
41 * - char ** (heap-allocated array of heap-allocated char*'s)
42 * - one string containing several substrings separated by a separator
43 *   (e.g. "name1,name2,name3")
44 */
45
46#if defined(WARN_TODO)
47#warning search for code which is splitting strings and use GBT_split_string there
48#warning rename to GBS_split_string and move to string functions
49#endif
50
51void GBT_splitNdestroy_string(ConstStrArray& names, char*& namelist, const char *separator, bool dropEmptyTokens) {
52    /*! Split 'namelist' into an array of substrings at each member of 'separator'.
53     *
54     * @param names pointers to split parts (into namelist)
55     * @param namelist string containing separator delimited parts
56     * @param separator contains all characters handled as separators
57     * @param dropEmptyTokens if true, empty tokens will be skipped
58     *
59     * Example:
60     * @code
61     * ConstStrArray array;
62     * char *list = strdup("Peter;Paul;Mary");
63     * GBT_splitNdestroy_string(array, list, ";", false);
64     * // array[1] contains "Paul"
65     * @endcode
66     *
67     * ownership of namelist is transferred to 'names'
68     */
69
70    names.set_memblock(namelist);
71
72    char *sep = namelist;
73    while (sep) {
74        size_t nonsepcount = strcspn(sep, separator);
75        if (nonsepcount || !dropEmptyTokens) {
76            // names.put(GB_strpartdup(sep, sep+nonsepcount-1));
77            names.put(sep);
78            sep += nonsepcount;
79        }
80        size_t sepcount = strspn(sep, separator);
81        sep[0] = 0;
82        if (sepcount) {
83            if (!dropEmptyTokens) {
84                // for (size_t s = 1; s<sepcount; ++s) names.put(strdup(""));
85                for (size_t s = 1; s<sepcount; ++s) names.put(sep);
86            }
87            sep += sepcount;
88        }
89        else {
90            sep = NULL;
91        }
92    }
93    namelist = NULL; // own it
94}
95
96void GBT_splitNdestroy_string(ConstStrArray& dest, char*& namelist, char separator) {
97    char separator_string[] = "x";
98    separator_string[0]   = separator;
99    GBT_splitNdestroy_string(dest, namelist, separator_string, false);
100}
101
102char *GBT_join_names(const CharPtrArray& names, char separator) {
103    /*! Joins a NULL-terminated array of 'char*' into one string
104     *
105     * @param names array of strings to join (maybe generated using GBT_split_string() or GBT_splitNdestroy_string)
106     * @param separator is put between the concatenated strings
107     *
108     * @return heap-copy of joined strings
109     */
110    GBS_strstruct *out = GBS_stropen(1000);
111
112    if (names[0]) {
113        GBS_strcat(out, names[0]);
114        arb_assert(strchr(names[0], separator) == 0); // otherwise you'll never be able to GBT_split_string
115        int n;
116        for (n = 1; names[n]; ++n) {
117            GBS_chrcat(out, separator);
118            GBS_strcat(out, names[n]);
119            arb_assert(strchr(names[n], separator) == 0); // otherwise you'll never be able to GBT_split_string
120        }
121    }
122
123    return GBS_strclose(out);
124}
125
126int GBT_names_index_of(const CharPtrArray& names, const char *search_for) {
127    // return index of 'search_for' or -1 if not found or given
128    int index = -1;
129    if (search_for) {
130        for (int i = 0; names[i]; ++i) {
131            if (strcmp(names[i], search_for) == 0) {
132                index = i;
133                break;
134            }
135        }
136    }
137    return index;
138}
139
140void GBT_names_erase(CharPtrArray& names, int index) {
141    if (index >= 0 && size_t(index)<names.size()) {
142        names.remove(index);
143    }
144}
145
146inline void move_last_elem(CharPtrArray& names, int before_pos) {
147    int last_idx = int(names.size())-1;
148    if (before_pos != -1 && before_pos < last_idx) {
149        GBT_names_move(names, last_idx, before_pos);
150    }
151}
152void GBT_names_add(ConstStrArray& names, int insert_before, const char *name) {
153    // insert a new 'name' before position 'insert_before'
154    // if 'insert_before' == -1 (or bigger than array size) -> append at end
155    names.put(name);
156    move_last_elem(names, insert_before);
157}
158
159void GBT_names_move(CharPtrArray& names, int old_index, int new_index) {
160    /*! moves array-entry from 'old_index' to 'new_index'
161     * -1 means "last entry"
162     * if new_index is out of bounds, it'll be moved to start of array
163     */
164    int size = (int)names.size();
165
166    if (old_index == -1) old_index        = size-1;
167    if (new_index == -1) new_index        = size-1;
168    else if (new_index >= size) new_index = 0;
169
170    if (old_index != new_index && new_index<size && old_index<size) {
171        if (old_index>new_index) {
172            for (int i = old_index-1; i >= new_index; --i) names.swap(i, i+1);
173        }
174        else {
175            for (int i = old_index; i < new_index; ++i) names.swap(i, i+1);
176        }
177    }
178}
179
180// --------------------------------------------------------------------------------
181
182#ifdef UNIT_TESTS
183#include <test_unit.h>
184
185void TEST_StrArray() {
186    StrArray array;
187
188    TEST_EXPECT(array.empty());
189    TEST_EXPECT_EQUAL(array.size(), 0);
190    TEST_EXPECT_NULL(array[0]);
191
192    array.put(strdup("first"));
193
194    TEST_REJECT(array.empty());
195    TEST_EXPECT_EQUAL(array.size(), 1);
196    TEST_EXPECT_EQUAL(array[0], "first");
197    TEST_EXPECT_NULL(array[1]);
198
199    array.put(strdup("second"));
200   
201    TEST_EXPECT_EQUAL(array.size(), 2);
202    TEST_EXPECT_EQUAL(array[0], "first");
203    TEST_EXPECT_EQUAL(array[1], "second");
204    TEST_EXPECT_NULL(array[2]);
205
206    array.remove(0);
207
208    TEST_EXPECT_EQUAL(array.size(), 1);
209    TEST_EXPECT_EQUAL(array[0], "second");
210    TEST_EXPECT_NULL(array[1]);
211   
212    array.remove(0);
213
214    TEST_EXPECT(array.empty());
215    TEST_EXPECT_EQUAL(array.size(), 0);
216    TEST_EXPECT_NULL(array[0]);
217}
218
219void TEST_StrArray_truncate() {
220    ConstStrArray parts;
221    GBT_split_string(parts, "test;word;bla", ';');
222
223    TEST_EXPECT_EQUAL(parts.size(), 3);
224    parts.resize(1000); TEST_EXPECT_EQUAL(parts.size(), 3);
225    parts.resize(2); TEST_EXPECT_EQUAL(parts.size(), 2);
226    parts.resize(1); TEST_EXPECT_EQUAL(parts.size(), 1);
227    parts.resize(0); TEST_EXPECT(parts.empty());
228}
229
230#define TEST_SPLIT_JOIN(str,sep)                        \
231    do {                                                \
232        ConstStrArray cnames;                           \
233        GBT_split_string(cnames, str, sep);             \
234        char *joined = GBT_join_names(cnames, sep);     \
235        TEST_EXPECT_EQUAL(str, joined);                 \
236        free(joined);                                   \
237    } while(0)
238
239void TEST_GBT_split_join_names() {
240    {                                               // simple split
241        ConstStrArray names;
242        GBT_split_string(names, "a*b*c", '*');
243        size_t   count = names.size();
244
245        TEST_EXPECT_EQUAL(count, 3U);
246        TEST_EXPECT_EQUAL(names[0], "a");
247        TEST_EXPECT_EQUAL(names[1], "b");
248        TEST_EXPECT_EQUAL(names[2], "c");
249    }
250    {                                               // split string containing empty tokens
251        ConstStrArray names;
252        GBT_split_string(names, "**a**b*c*", '*');
253        size_t   count = names.size();
254
255        TEST_EXPECT_EQUAL(count, 7U);
256        TEST_EXPECT_EQUAL(names[0], "");
257        TEST_EXPECT_EQUAL(names[1], "");
258        TEST_EXPECT_EQUAL(names[2], "a");
259        TEST_EXPECT_EQUAL(names[3], "");
260        TEST_EXPECT_EQUAL(names[4], "b");
261        TEST_EXPECT_EQUAL(names[5], "c");
262        TEST_EXPECT_EQUAL(names[6], "");
263        TEST_EXPECT_NULL(names[7]);
264    }
265
266    TEST_SPLIT_JOIN("a.b.c", '.');
267    TEST_SPLIT_JOIN("a.b.c", '*');
268   
269    TEST_SPLIT_JOIN("..a.b.c", '.');
270    TEST_SPLIT_JOIN("a.b.c..", '.');
271    TEST_SPLIT_JOIN("a..b..c", '.');
272    TEST_SPLIT_JOIN(".", '.');
273    TEST_SPLIT_JOIN("....", '.');
274}
275
276void TEST_GBT_names_index_of() {
277    ConstStrArray names;
278    GBT_split_string(names, "**a**b*c*", '*');
279
280    TEST_EXPECT_EQUAL(GBT_names_index_of(names, "a"), 2);
281    TEST_EXPECT_EQUAL(GBT_names_index_of(names, "b"), 4);
282    TEST_EXPECT_EQUAL(GBT_names_index_of(names, "c"), 5);
283    TEST_EXPECT_EQUAL(GBT_names_index_of(names, ""), 0);
284    TEST_EXPECT_EQUAL(GBT_names_index_of(names, "no"), -1);
285}
286TEST_PUBLISH(TEST_GBT_names_index_of);
287
288#define TEST_EXPECT_NAMES_JOIN_TO(names, sep, expected) \
289    do {                                                \
290        char *joined = GBT_join_names(names, sep);      \
291        TEST_EXPECT_EQUAL(joined, expected);            \
292        free(joined);                                   \
293    } while(0)                                          \
294   
295void TEST_GBT_names_erase() {
296    ConstStrArray names;
297    GBT_split_string(names, "a*b*c*d*e", '*');
298
299    TEST_EXPECT_EQUAL(names.size(), 5U);
300
301    GBT_names_erase(names, 0); 
302    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*d*e");
303
304    GBT_names_erase(names, 3);
305    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*d");
306
307    GBT_names_erase(names, 3);                      // index out of range
308    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*d");
309
310    GBT_names_erase(names, -1);                     // illegal index
311    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*d");
312
313    GBT_names_erase(names, 1);
314    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*d");
315}
316
317void TEST_GBT_names_move() {
318    ConstStrArray names;
319    GBT_split_string(names, "a*b*c*dee", '*');
320
321    GBT_names_move(names, 0, -1); // -1 means last
322    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "b*c*dee*a");
323    GBT_names_move(names, -1, 0); 
324    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*c*dee");
325    GBT_names_move(names, 2, 3); 
326    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*dee*c");
327    GBT_names_move(names, 2, 1); 
328    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*dee*b*c");
329
330    // test wrap arounds
331    GBT_names_move(names, 0, -1);
332    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "dee*b*c*a");
333    GBT_names_move(names, -1, 99999);
334    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*dee*b*c");
335}
336
337void TEST_GBT_names_add() { // test after GBT_names_move (cause add depends on move)
338    ConstStrArray names;
339    GBT_split_string(names, "a", '*');
340
341    GBT_names_add(names, -1, "b");                  // append at end
342    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b");
343
344    GBT_names_add(names, 2, "c");                   // append at end (using non-existing index)
345    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*c");
346
347    GBT_names_add(names, 99, "d");                  // append at end (using even bigger non-existing index)
348    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*c*d");
349
350    GBT_names_add(names, 2, "b2");                  // insert inside
351    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a*b*b2*c*d");
352
353    GBT_names_add(names, 0, "a0");                  // insert at beginning
354    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a0*a*b*b2*c*d");
355
356    GBT_names_add(names, 5, "d0");                  // insert before last
357    TEST_EXPECT_NAMES_JOIN_TO(names, '*', "a0*a*b*b2*c*d0*d");
358}
359TEST_PUBLISH(TEST_GBT_names_add);
360
361#endif // UNIT_TESTS
362
Note: See TracBrowser for help on using the repository browser.