1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2017 - ROLI Ltd.
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
StringArray()26 StringArray::StringArray() noexcept
27 {
28 }
29 
StringArray(const StringArray & other)30 StringArray::StringArray (const StringArray& other)
31     : strings (other.strings)
32 {
33 }
34 
StringArray(StringArray && other)35 StringArray::StringArray (StringArray&& other) noexcept
36     : strings (std::move (other.strings))
37 {
38 }
39 
StringArray(Array<String> && other)40 StringArray::StringArray (Array<String>&& other) noexcept
41     : strings (std::move (other))
42 {
43 }
44 
StringArray(const String & firstValue)45 StringArray::StringArray (const String& firstValue)
46 {
47     strings.add (firstValue);
48 }
49 
StringArray(const String * initialStrings,int numberOfStrings)50 StringArray::StringArray (const String* initialStrings, int numberOfStrings)
51 {
52     strings.addArray (initialStrings, numberOfStrings);
53 }
54 
StringArray(const char * const * initialStrings)55 StringArray::StringArray (const char* const* initialStrings)
56 {
57     strings.addNullTerminatedArray (initialStrings);
58 }
59 
StringArray(const char * const * initialStrings,int numberOfStrings)60 StringArray::StringArray (const char* const* initialStrings, int numberOfStrings)
61 {
62     strings.addArray (initialStrings, numberOfStrings);
63 }
64 
StringArray(const wchar_t * const * initialStrings)65 StringArray::StringArray (const wchar_t* const* initialStrings)
66 {
67     strings.addNullTerminatedArray (initialStrings);
68 }
69 
StringArray(const wchar_t * const * initialStrings,int numberOfStrings)70 StringArray::StringArray (const wchar_t* const* initialStrings, int numberOfStrings)
71 {
72     strings.addArray (initialStrings, numberOfStrings);
73 }
74 
StringArray(const std::initializer_list<const char * > & stringList)75 StringArray::StringArray (const std::initializer_list<const char*>& stringList)
76 {
77     strings.addArray (stringList);
78 }
79 
operator =(const StringArray & other)80 StringArray& StringArray::operator= (const StringArray& other)
81 {
82     strings = other.strings;
83     return *this;
84 }
85 
operator =(StringArray && other)86 StringArray& StringArray::operator= (StringArray&& other) noexcept
87 {
88     strings = std::move (other.strings);
89     return *this;
90 }
91 
~StringArray()92 StringArray::~StringArray()
93 {
94 }
95 
operator ==(const StringArray & other) const96 bool StringArray::operator== (const StringArray& other) const noexcept
97 {
98     return strings == other.strings;
99 }
100 
operator !=(const StringArray & other) const101 bool StringArray::operator!= (const StringArray& other) const noexcept
102 {
103     return ! operator== (other);
104 }
105 
swapWith(StringArray & other)106 void StringArray::swapWith (StringArray& other) noexcept
107 {
108     strings.swapWith (other.strings);
109 }
110 
clear()111 void StringArray::clear()
112 {
113     strings.clear();
114 }
115 
clearQuick()116 void StringArray::clearQuick()
117 {
118     strings.clearQuick();
119 }
120 
operator [](int index) const121 const String& StringArray::operator[] (int index) const noexcept
122 {
123     if (isPositiveAndBelow (index, strings.size()))
124         return strings.getReference (index);
125 
126     static String empty;
127     return empty;
128 }
129 
getReference(int index)130 String& StringArray::getReference (int index) noexcept
131 {
132     return strings.getReference (index);
133 }
134 
add(String newString)135 void StringArray::add (String newString)
136 {
137     // NB: the local temp copy is to avoid a dangling pointer if the
138     // argument being passed-in is a reference into this array.
139     strings.add (std::move (newString));
140 }
141 
insert(int index,String newString)142 void StringArray::insert (int index, String newString)
143 {
144     // NB: the local temp copy is to avoid a dangling pointer if the
145     // argument being passed-in is a reference into this array.
146     strings.insert (index, std::move (newString));
147 }
148 
addIfNotAlreadyThere(const String & newString,bool ignoreCase)149 bool StringArray::addIfNotAlreadyThere (const String& newString, bool ignoreCase)
150 {
151     if (contains (newString, ignoreCase))
152         return false;
153 
154     add (newString);
155     return true;
156 }
157 
addArray(const StringArray & otherArray,int startIndex,int numElementsToAdd)158 void StringArray::addArray (const StringArray& otherArray, int startIndex, int numElementsToAdd)
159 {
160     jassert (this != &otherArray); // can't add from our own elements!
161 
162     if (startIndex < 0)
163     {
164         jassertfalse;
165         startIndex = 0;
166     }
167 
168     if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size())
169         numElementsToAdd = otherArray.size() - startIndex;
170 
171     while (--numElementsToAdd >= 0)
172         strings.add (otherArray.strings.getReference (startIndex++));
173 }
174 
mergeArray(const StringArray & otherArray,bool ignoreCase)175 void StringArray::mergeArray (const StringArray& otherArray, bool ignoreCase)
176 {
177     jassert (this != &otherArray); // can't add from our own elements!
178 
179     for (auto& s : otherArray)
180         addIfNotAlreadyThere (s, ignoreCase);
181 }
182 
set(int index,String newString)183 void StringArray::set (int index, String newString)
184 {
185     strings.set (index, std::move (newString));
186 }
187 
contains(StringRef stringToLookFor,bool ignoreCase) const188 bool StringArray::contains (StringRef stringToLookFor, bool ignoreCase) const
189 {
190     return indexOf (stringToLookFor, ignoreCase) >= 0;
191 }
192 
indexOf(StringRef stringToLookFor,bool ignoreCase,int i) const193 int StringArray::indexOf (StringRef stringToLookFor, bool ignoreCase, int i) const
194 {
195     if (i < 0)
196         i = 0;
197 
198     auto numElements = size();
199 
200     if (ignoreCase)
201     {
202         for (; i < numElements; ++i)
203             if (strings.getReference(i).equalsIgnoreCase (stringToLookFor))
204                 return i;
205     }
206     else
207     {
208         for (; i < numElements; ++i)
209             if (stringToLookFor == strings.getReference (i))
210                 return i;
211     }
212 
213     return -1;
214 }
215 
move(int currentIndex,int newIndex)216 void StringArray::move (int currentIndex, int newIndex) noexcept
217 {
218     strings.move (currentIndex, newIndex);
219 }
220 
221 //==============================================================================
remove(int index)222 void StringArray::remove (int index)
223 {
224     strings.remove (index);
225 }
226 
removeString(StringRef stringToRemove,bool ignoreCase)227 void StringArray::removeString (StringRef stringToRemove, bool ignoreCase)
228 {
229     if (ignoreCase)
230     {
231         for (int i = size(); --i >= 0;)
232             if (strings.getReference(i).equalsIgnoreCase (stringToRemove))
233                 strings.remove (i);
234     }
235     else
236     {
237         for (int i = size(); --i >= 0;)
238             if (stringToRemove == strings.getReference (i))
239                 strings.remove (i);
240     }
241 }
242 
removeRange(int startIndex,int numberToRemove)243 void StringArray::removeRange (int startIndex, int numberToRemove)
244 {
245     strings.removeRange (startIndex, numberToRemove);
246 }
247 
248 //==============================================================================
removeEmptyStrings(bool removeWhitespaceStrings)249 void StringArray::removeEmptyStrings (bool removeWhitespaceStrings)
250 {
251     if (removeWhitespaceStrings)
252     {
253         for (int i = size(); --i >= 0;)
254             if (! strings.getReference(i).containsNonWhitespaceChars())
255                 strings.remove (i);
256     }
257     else
258     {
259         for (int i = size(); --i >= 0;)
260             if (strings.getReference(i).isEmpty())
261                 strings.remove (i);
262     }
263 }
264 
trim()265 void StringArray::trim()
266 {
267     for (auto& s : strings)
268         s = s.trim();
269 }
270 
271 //==============================================================================
sort(bool ignoreCase)272 void StringArray::sort (bool ignoreCase)
273 {
274     if (ignoreCase)
275         std::sort (strings.begin(), strings.end(),
276                    [] (const String& a, const String& b) { return a.compareIgnoreCase (b) < 0; });
277     else
278         std::sort (strings.begin(), strings.end());
279 }
280 
sortNatural()281 void StringArray::sortNatural()
282 {
283     std::sort (strings.begin(), strings.end(),
284                [] (const String& a, const String& b) { return a.compareNatural (b) < 0; });
285 }
286 
287 //==============================================================================
joinIntoString(StringRef separator,int start,int numberToJoin) const288 String StringArray::joinIntoString (StringRef separator, int start, int numberToJoin) const
289 {
290     auto last = (numberToJoin < 0) ? size()
291                                    : jmin (size(), start + numberToJoin);
292 
293     if (start < 0)
294         start = 0;
295 
296     if (start >= last)
297         return {};
298 
299     if (start == last - 1)
300         return strings.getReference (start);
301 
302     auto separatorBytes = separator.text.sizeInBytes() - sizeof (String::CharPointerType::CharType);
303     auto bytesNeeded = (size_t) (last - start - 1) * separatorBytes;
304 
305     for (int i = start; i < last; ++i)
306         bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType);
307 
308     String result;
309     result.preallocateBytes (bytesNeeded);
310 
311     auto dest = result.getCharPointer();
312 
313     while (start < last)
314     {
315         auto& s = strings.getReference (start);
316 
317         if (! s.isEmpty())
318             dest.writeAll (s.getCharPointer());
319 
320         if (++start < last && separatorBytes > 0)
321             dest.writeAll (separator.text);
322     }
323 
324     dest.writeNull();
325     return result;
326 }
327 
addTokens(StringRef text,const bool preserveQuotedStrings)328 int StringArray::addTokens (StringRef text, const bool preserveQuotedStrings)
329 {
330     return addTokens (text, " \n\r\t", preserveQuotedStrings ? "\"" : "");
331 }
332 
addTokens(StringRef text,StringRef breakCharacters,StringRef quoteCharacters)333 int StringArray::addTokens (StringRef text, StringRef breakCharacters, StringRef quoteCharacters)
334 {
335     int num = 0;
336 
337     if (text.isNotEmpty())
338     {
339         for (auto t = text.text;;)
340         {
341             auto tokenEnd = CharacterFunctions::findEndOfToken (t,
342                                                                 breakCharacters.text,
343                                                                 quoteCharacters.text);
344             strings.add (String (t, tokenEnd));
345             ++num;
346 
347             if (tokenEnd.isEmpty())
348                 break;
349 
350             t = ++tokenEnd;
351         }
352     }
353 
354     return num;
355 }
356 
addLines(StringRef sourceText)357 int StringArray::addLines (StringRef sourceText)
358 {
359     int numLines = 0;
360     auto text = sourceText.text;
361     bool finished = text.isEmpty();
362 
363     while (! finished)
364     {
365         for (auto startOfLine = text;;)
366         {
367             auto endOfLine = text;
368 
369             switch (text.getAndAdvance())
370             {
371                 case 0:     finished = true; break;
372                 case '\n':  break;
373                 case '\r':  if (*text == '\n') ++text; break;
374                 default:    continue;
375             }
376 
377             strings.add (String (startOfLine, endOfLine));
378             ++numLines;
379             break;
380         }
381     }
382 
383     return numLines;
384 }
385 
fromTokens(StringRef stringToTokenise,bool preserveQuotedStrings)386 StringArray StringArray::fromTokens (StringRef stringToTokenise, bool preserveQuotedStrings)
387 {
388     StringArray s;
389     s.addTokens (stringToTokenise, preserveQuotedStrings);
390     return s;
391 }
392 
fromTokens(StringRef stringToTokenise,StringRef breakCharacters,StringRef quoteCharacters)393 StringArray StringArray::fromTokens (StringRef stringToTokenise,
394                                      StringRef breakCharacters,
395                                      StringRef quoteCharacters)
396 {
397     StringArray s;
398     s.addTokens (stringToTokenise, breakCharacters, quoteCharacters);
399     return s;
400 }
401 
fromLines(StringRef stringToBreakUp)402 StringArray StringArray::fromLines (StringRef stringToBreakUp)
403 {
404     StringArray s;
405     s.addLines (stringToBreakUp);
406     return s;
407 }
408 
409 //==============================================================================
removeDuplicates(bool ignoreCase)410 void StringArray::removeDuplicates (bool ignoreCase)
411 {
412     for (int i = 0; i < size() - 1; ++i)
413     {
414         auto s = strings.getReference(i);
415 
416         for (int nextIndex = i + 1;;)
417         {
418             nextIndex = indexOf (s, ignoreCase, nextIndex);
419 
420             if (nextIndex < 0)
421                 break;
422 
423             strings.remove (nextIndex);
424         }
425     }
426 }
427 
appendNumbersToDuplicates(bool ignoreCase,bool appendNumberToFirstInstance,CharPointer_UTF8 preNumberString,CharPointer_UTF8 postNumberString)428 void StringArray::appendNumbersToDuplicates (bool ignoreCase,
429                                              bool appendNumberToFirstInstance,
430                                              CharPointer_UTF8 preNumberString,
431                                              CharPointer_UTF8 postNumberString)
432 {
433     if (preNumberString.getAddress() == nullptr)
434         preNumberString = CharPointer_UTF8 (" (");
435 
436     if (postNumberString.getAddress() == nullptr)
437         postNumberString = CharPointer_UTF8 (")");
438 
439     for (int i = 0; i < size() - 1; ++i)
440     {
441         auto& s = strings.getReference(i);
442         auto nextIndex = indexOf (s, ignoreCase, i + 1);
443 
444         if (nextIndex >= 0)
445         {
446             auto original = s;
447             int number = 0;
448 
449             if (appendNumberToFirstInstance)
450                 s = original + String (preNumberString) + String (++number) + String (postNumberString);
451             else
452                 ++number;
453 
454             while (nextIndex >= 0)
455             {
456                 set (nextIndex, (*this)[nextIndex] + String (preNumberString) + String (++number) + String (postNumberString));
457                 nextIndex = indexOf (original, ignoreCase, nextIndex + 1);
458             }
459         }
460     }
461 }
462 
ensureStorageAllocated(int minNumElements)463 void StringArray::ensureStorageAllocated (int minNumElements)
464 {
465     strings.ensureStorageAllocated (minNumElements);
466 }
467 
minimiseStorageOverheads()468 void StringArray::minimiseStorageOverheads()
469 {
470     strings.minimiseStorageOverheads();
471 }
472 
473 } // namespace juce
474