1 /*
2   ==============================================================================
3 
4    This file is part of the Water library.
5    Copyright (c) 2016 ROLI Ltd.
6    Copyright (C) 2017 Filipe Coelho <falktx@falktx.com>
7 
8    Permission is granted to use this software under the terms of the ISC license
9    http://www.isc.org/downloads/software-support-policy/isc-license/
10 
11    Permission to use, copy, modify, and/or distribute this software for any
12    purpose with or without fee is hereby granted, provided that the above
13    copyright notice and this permission notice appear in all copies.
14 
15    THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
16    TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17    FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
18    OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
19    USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20    TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21    OF THIS SOFTWARE.
22 
23   ==============================================================================
24 */
25 
26 #ifndef WATER_STRINGARRAY_H_INCLUDED
27 #define WATER_STRINGARRAY_H_INCLUDED
28 
29 #include "String.h"
30 #include "../containers/Array.h"
31 
32 namespace water {
33 
34 //==============================================================================
35 /**
36     A special array for holding a list of strings.
37 
38     @see String, StringPairArray
39 */
40 class StringArray
41 {
42 public:
43     //==============================================================================
44     /** Creates an empty string array */
45     StringArray() noexcept;
46 
47     /** Creates a copy of another string array */
48     StringArray (const StringArray&);
49 
50    #if WATER_COMPILER_SUPPORTS_MOVE_SEMANTICS
51     StringArray (StringArray&&) noexcept;
52    #endif
53 
54     /** Creates an array containing a single string. */
55     explicit StringArray (const String& firstValue);
56 
57     /** Creates an array from a raw array of strings.
58         @param strings          an array of strings to add
59         @param numberOfStrings  how many items there are in the array
60     */
61     StringArray (const String* strings, int numberOfStrings);
62 
63     /** Creates a copy of an array of string literals.
64         @param strings          an array of strings to add. Null pointers in the array will be
65                                 treated as empty strings
66         @param numberOfStrings  how many items there are in the array
67     */
68     StringArray (const char* const* strings, int numberOfStrings);
69 
70     /** Creates a copy of a null-terminated array of string literals.
71 
72         Each item from the array passed-in is added, until it encounters a null pointer,
73         at which point it stops.
74     */
75     explicit StringArray (const char* const* strings);
76 
77     /** Destructor. */
78     ~StringArray();
79 
80     /** Copies the contents of another string array into this one */
81     StringArray& operator= (const StringArray&);
82 
83    #if WATER_COMPILER_SUPPORTS_MOVE_SEMANTICS
84     StringArray& operator= (StringArray&&) noexcept;
85    #endif
86 
87     /** Swaps the contents of this and another StringArray. */
88     void swapWith (StringArray&) noexcept;
89 
90     //==============================================================================
91     /** Compares two arrays.
92         Comparisons are case-sensitive.
93         @returns    true only if the other array contains exactly the same strings in the same order
94     */
95     bool operator== (const StringArray&) const noexcept;
96 
97     /** Compares two arrays.
98         Comparisons are case-sensitive.
99         @returns    false if the other array contains exactly the same strings in the same order
100     */
101     bool operator!= (const StringArray&) const noexcept;
102 
103     //==============================================================================
104     /** Returns the number of strings in the array */
size()105     inline int size() const noexcept                                    { return strings.size(); }
106 
107     /** Returns true if the array is empty, false otherwise. */
isEmpty()108     inline bool isEmpty() const noexcept                                { return size() == 0; }
109 
110     /** Returns one of the strings from the array.
111 
112         If the index is out-of-range, an empty string is returned.
113 
114         Obviously the reference returned shouldn't be stored for later use, as the
115         string it refers to may disappear when the array changes.
116     */
117     const String& operator[] (int index) const noexcept;
118 
119     /** Returns a reference to one of the strings in the array.
120         This lets you modify a string in-place in the array, but you must be sure that
121         the index is in-range.
122     */
123     String& getReference (int index) noexcept;
124 
125     /** Returns a pointer to the first String in the array.
126         This method is provided for compatibility with standard C++ iteration mechanisms.
127     */
begin()128     inline String* begin() const noexcept       { return strings.begin(); }
129 
130     /** Returns a pointer to the String which follows the last element in the array.
131         This method is provided for compatibility with standard C++ iteration mechanisms.
132     */
end()133     inline String* end() const noexcept         { return strings.end(); }
134 
135     /** Searches for a string in the array.
136 
137         The comparison will be case-insensitive if the ignoreCase parameter is true.
138 
139         @returns    true if the string is found inside the array
140     */
141     bool contains (StringRef stringToLookFor,
142                    bool ignoreCase = false) const;
143 
144     /** Searches for a string in the array.
145 
146         The comparison will be case-insensitive if the ignoreCase parameter is true.
147 
148         @param stringToLookFor  the string to try to find
149         @param ignoreCase       whether the comparison should be case-insensitive
150         @param startIndex       the first index to start searching from
151         @returns                the index of the first occurrence of the string in this array,
152                                 or -1 if it isn't found.
153     */
154     int indexOf (StringRef stringToLookFor,
155                  bool ignoreCase = false,
156                  int startIndex = 0) const;
157 
158     //==============================================================================
159     /** Appends a string at the end of the array. */
160     bool add (const String& stringToAdd);
161 
162    #if WATER_COMPILER_SUPPORTS_MOVE_SEMANTICS
163     /** Appends a string at the end of the array. */
164     bool add (String&& stringToAdd);
165    #endif
166 
167     /** Inserts a string into the array.
168 
169         This will insert a string into the array at the given index, moving
170         up the other elements to make room for it.
171         If the index is less than zero or greater than the size of the array,
172         the new string will be added to the end of the array.
173     */
174     bool insert (int index, const String& stringToAdd);
175 
176     /** Adds a string to the array as long as it's not already in there.
177         The search can optionally be case-insensitive.
178 
179         @return true if the string has been added, false otherwise.
180     */
181     bool addIfNotAlreadyThere (const String& stringToAdd, bool ignoreCase = false);
182 
183     /** Replaces one of the strings in the array with another one.
184 
185         If the index is higher than the array's size, the new string will be
186         added to the end of the array; if it's less than zero nothing happens.
187     */
188     void set (int index, const String& newString);
189 
190     /** Appends some strings from another array to the end of this one.
191 
192         @param other                the array to add
193         @param startIndex           the first element of the other array to add
194         @param numElementsToAdd     the maximum number of elements to add (if this is
195                                     less than zero, they are all added)
196     */
197     void addArray (const StringArray& other,
198                    int startIndex = 0,
199                    int numElementsToAdd = -1);
200 
201     /** Merges the strings from another array into this one.
202         This will not add a string that already exists.
203 
204         @param other                the array to add
205         @param ignoreCase           ignore case when merging
206     */
207     void mergeArray (const StringArray& other,
208                      bool ignoreCase = false);
209 
210     /** Breaks up a string into tokens and adds them to this array.
211 
212         This will tokenise the given string using whitespace characters as the
213         token delimiters, and will add these tokens to the end of the array.
214         @returns    the number of tokens added
215         @see fromTokens
216     */
217     int addTokens (StringRef stringToTokenise, bool preserveQuotedStrings);
218 
219     /** Breaks up a string into tokens and adds them to this array.
220 
221         This will tokenise the given string (using the string passed in to define the
222         token delimiters), and will add these tokens to the end of the array.
223 
224         @param stringToTokenise     the string to tokenise
225         @param breakCharacters      a string of characters, any of which will be considered
226                                     to be a token delimiter.
227         @param quoteCharacters      if this string isn't empty, it defines a set of characters
228                                     which are treated as quotes. Any text occurring
229                                     between quotes is not broken up into tokens.
230         @returns    the number of tokens added
231         @see fromTokens
232     */
233     int addTokens (StringRef stringToTokenise,
234                    StringRef breakCharacters,
235                    StringRef quoteCharacters);
236 
237     /** Breaks up a string into lines and adds them to this array.
238 
239         This breaks a string down into lines separated by \\n or \\r\\n, and adds each line
240         to the array. Line-break characters are omitted from the strings that are added to
241         the array.
242     */
243     int addLines (StringRef stringToBreakUp);
244 
245     /** Returns an array containing the tokens in a given string.
246 
247         This will tokenise the given string using whitespace characters as the
248         token delimiters, and return the parsed tokens as an array.
249         @see addTokens
250     */
251     static StringArray fromTokens (StringRef stringToTokenise,
252                                    bool preserveQuotedStrings);
253 
254     /** Returns an array containing the tokens in a given string.
255 
256         This will tokenise the given string using the breakCharacters string to define
257         the token delimiters, and will return the parsed tokens as an array.
258 
259         @param stringToTokenise     the string to tokenise
260         @param breakCharacters      a string of characters, any of which will be considered
261                                     to be a token delimiter.
262         @param quoteCharacters      if this string isn't empty, it defines a set of characters
263                                     which are treated as quotes. Any text occurring
264                                     between quotes is not broken up into tokens.
265         @see addTokens
266     */
267     static StringArray fromTokens (StringRef stringToTokenise,
268                                    StringRef breakCharacters,
269                                    StringRef quoteCharacters);
270 
271     /** Returns an array containing the lines in a given string.
272 
273         This breaks a string down into lines separated by \\n or \\r\\n, and returns an
274         array containing these lines. Line-break characters are omitted from the strings that
275         are added to the array.
276     */
277     static StringArray fromLines (StringRef stringToBreakUp);
278 
279     //==============================================================================
280     /** Removes all elements from the array. */
281     void clear();
282 
283     /** Removes all elements from the array without freeing the array's allocated storage.
284         @see clear
285     */
286     void clearQuick();
287 
288     /** Removes a string from the array.
289         If the index is out-of-range, no action will be taken.
290     */
291     void remove (int index);
292 
293     /** Finds a string in the array and removes it.
294         This will remove all occurrences of the given string from the array.
295         The comparison may be case-insensitive depending on the ignoreCase parameter.
296     */
297     void removeString (StringRef stringToRemove,
298                        bool ignoreCase = false);
299 
300     /** Removes a range of elements from the array.
301 
302         This will remove a set of elements, starting from the given index,
303         and move subsequent elements down to close the gap.
304 
305         If the range extends beyond the bounds of the array, it will
306         be safely clipped to the size of the array.
307 
308         @param startIndex       the index of the first element to remove
309         @param numberToRemove   how many elements should be removed
310     */
311     void removeRange (int startIndex, int numberToRemove);
312 
313     /** Removes any duplicated elements from the array.
314 
315         If any string appears in the array more than once, only the first occurrence of
316         it will be retained.
317 
318         @param ignoreCase   whether to use a case-insensitive comparison
319     */
320     void removeDuplicates (bool ignoreCase);
321 
322     /** Removes empty strings from the array.
323         @param removeWhitespaceStrings  if true, strings that only contain whitespace
324                                         characters will also be removed
325     */
326     void removeEmptyStrings (bool removeWhitespaceStrings = true);
327 
328     /** Deletes any whitespace characters from the starts and ends of all the strings. */
329     void trim();
330 
331     /** Adds numbers to the strings in the array, to make each string unique.
332 
333         This will add numbers to the ends of groups of similar strings.
334         e.g. if there are two "moose" strings, they will become "moose (1)" and "moose (2)"
335 
336         @param ignoreCaseWhenComparing      whether the comparison used is case-insensitive
337         @param appendNumberToFirstInstance  whether the first of a group of similar strings
338                                             also has a number appended to it.
339         @param preNumberString              when adding a number, this string is added before the number.
340                                             If you pass nullptr, a default string will be used, which adds
341                                             brackets around the number.
342         @param postNumberString             this string is appended after any numbers that are added.
343                                             If you pass nullptr, a default string will be used, which adds
344                                             brackets around the number.
345     */
346     void appendNumbersToDuplicates (bool ignoreCaseWhenComparing,
347                                     bool appendNumberToFirstInstance,
348                                     CharPointer_UTF8 preNumberString = CharPointer_UTF8 (nullptr),
349                                     CharPointer_UTF8 postNumberString = CharPointer_UTF8 (nullptr));
350 
351     //==============================================================================
352     /** Joins the strings in the array together into one string.
353 
354         This will join a range of elements from the array into a string, separating
355         them with a given string.
356 
357         e.g. joinIntoString (",") will turn an array of "a" "b" and "c" into "a,b,c".
358 
359         @param separatorString      the string to insert between all the strings
360         @param startIndex           the first element to join
361         @param numberOfElements     how many elements to join together. If this is less
362                                     than zero, all available elements will be used.
363     */
364     String joinIntoString (StringRef separatorString,
365                            int startIndex = 0,
366                            int numberOfElements = -1) const;
367 
368     //==============================================================================
369     /** Sorts the array into alphabetical order.
370         @param ignoreCase       if true, the comparisons used will be case-sensitive.
371     */
372     void sort (bool ignoreCase);
373 
374     /** Sorts the array using extra language-aware rules to do a better job of comparing
375         words containing spaces and numbers.
376         @see String::compareNatural()
377     */
378     void sortNatural();
379 
380     //==============================================================================
381     /** Increases the array's internal storage to hold a minimum number of elements.
382 
383         Calling this before adding a large known number of elements means that
384         the array won't have to keep dynamically resizing itself as the elements
385         are added, and it'll therefore be more efficient.
386     */
387     void ensureStorageAllocated (int minNumElements);
388 
389     /** Reduces the amount of storage being used by the array.
390 
391         Arrays typically allocate slightly more storage than they need, and after
392         removing elements, they may have quite a lot of unused space allocated.
393         This method will reduce the amount of allocated storage to a minimum.
394     */
395     void minimiseStorageOverheads();
396 
397     /** This is the array holding the actual strings. This is public to allow direct access
398         to array methods that may not already be provided by the StringArray class.
399     */
400     Array<String> strings;
401 };
402 
403 }
404 
405 #endif // WATER_STRINGARRAY_H_INCLUDED
406