The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

485 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. //==============================================================================
  19. StringArray::StringArray() noexcept
  20. {
  21. }
  22. StringArray::StringArray (const StringArray& other)
  23. : strings (other.strings)
  24. {
  25. }
  26. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  27. StringArray::StringArray (StringArray&& other) noexcept
  28. : strings (static_cast <Array <String>&&> (other.strings))
  29. {
  30. }
  31. #endif
  32. StringArray::StringArray (const String& firstValue)
  33. {
  34. strings.add (firstValue);
  35. }
  36. namespace StringArrayHelpers
  37. {
  38. template <typename CharType>
  39. void addArray (Array<String>& dest, const CharType* const* strings)
  40. {
  41. if (strings != nullptr)
  42. while (*strings != nullptr)
  43. dest.add (*strings++);
  44. }
  45. template <typename Type>
  46. void addArray (Array<String>& dest, const Type* const strings, const int numberOfStrings)
  47. {
  48. for (int i = 0; i < numberOfStrings; ++i)
  49. dest.add (strings [i]);
  50. }
  51. }
  52. StringArray::StringArray (const String* initialStrings, int numberOfStrings)
  53. {
  54. StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings);
  55. }
  56. StringArray::StringArray (const char* const* const initialStrings)
  57. {
  58. StringArrayHelpers::addArray (strings, initialStrings);
  59. }
  60. StringArray::StringArray (const char* const* const initialStrings, const int numberOfStrings)
  61. {
  62. StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings);
  63. }
  64. StringArray::StringArray (const wchar_t* const* const initialStrings)
  65. {
  66. StringArrayHelpers::addArray (strings, initialStrings);
  67. }
  68. StringArray::StringArray (const wchar_t* const* const initialStrings, const int numberOfStrings)
  69. {
  70. StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings);
  71. }
  72. StringArray& StringArray::operator= (const StringArray& other)
  73. {
  74. strings = other.strings;
  75. return *this;
  76. }
  77. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  78. StringArray& StringArray::operator= (StringArray&& other) noexcept
  79. {
  80. strings = static_cast <Array<String>&&> (other.strings);
  81. return *this;
  82. }
  83. #endif
  84. StringArray::~StringArray()
  85. {
  86. }
  87. bool StringArray::operator== (const StringArray& other) const noexcept
  88. {
  89. if (other.size() != size())
  90. return false;
  91. for (int i = size(); --i >= 0;)
  92. if (other.strings.getReference(i) != strings.getReference(i))
  93. return false;
  94. return true;
  95. }
  96. bool StringArray::operator!= (const StringArray& other) const noexcept
  97. {
  98. return ! operator== (other);
  99. }
  100. void StringArray::swapWith (StringArray& other) noexcept
  101. {
  102. strings.swapWithArray (other.strings);
  103. }
  104. void StringArray::clear()
  105. {
  106. strings.clear();
  107. }
  108. const String& StringArray::operator[] (const int index) const noexcept
  109. {
  110. if (isPositiveAndBelow (index, strings.size()))
  111. return strings.getReference (index);
  112. return String::empty;
  113. }
  114. String& StringArray::getReference (const int index) noexcept
  115. {
  116. jassert (isPositiveAndBelow (index, strings.size()));
  117. return strings.getReference (index);
  118. }
  119. void StringArray::add (const String& newString)
  120. {
  121. strings.add (newString);
  122. }
  123. void StringArray::insert (const int index, const String& newString)
  124. {
  125. strings.insert (index, newString);
  126. }
  127. void StringArray::addIfNotAlreadyThere (const String& newString, const bool ignoreCase)
  128. {
  129. if (! contains (newString, ignoreCase))
  130. add (newString);
  131. }
  132. void StringArray::addArray (const StringArray& otherArray, int startIndex, int numElementsToAdd)
  133. {
  134. if (startIndex < 0)
  135. {
  136. jassertfalse;
  137. startIndex = 0;
  138. }
  139. if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size())
  140. numElementsToAdd = otherArray.size() - startIndex;
  141. while (--numElementsToAdd >= 0)
  142. strings.add (otherArray.strings.getReference (startIndex++));
  143. }
  144. void StringArray::set (const int index, const String& newString)
  145. {
  146. strings.set (index, newString);
  147. }
  148. bool StringArray::contains (const String& stringToLookFor, const bool ignoreCase) const
  149. {
  150. if (ignoreCase)
  151. {
  152. for (int i = size(); --i >= 0;)
  153. if (strings.getReference(i).equalsIgnoreCase (stringToLookFor))
  154. return true;
  155. }
  156. else
  157. {
  158. for (int i = size(); --i >= 0;)
  159. if (stringToLookFor == strings.getReference(i))
  160. return true;
  161. }
  162. return false;
  163. }
  164. int StringArray::indexOf (const String& stringToLookFor, const bool ignoreCase, int i) const
  165. {
  166. if (i < 0)
  167. i = 0;
  168. const int numElements = size();
  169. if (ignoreCase)
  170. {
  171. while (i < numElements)
  172. {
  173. if (strings.getReference(i).equalsIgnoreCase (stringToLookFor))
  174. return i;
  175. ++i;
  176. }
  177. }
  178. else
  179. {
  180. while (i < numElements)
  181. {
  182. if (stringToLookFor == strings.getReference (i))
  183. return i;
  184. ++i;
  185. }
  186. }
  187. return -1;
  188. }
  189. //==============================================================================
  190. void StringArray::remove (const int index)
  191. {
  192. strings.remove (index);
  193. }
  194. void StringArray::removeString (const String& stringToRemove,
  195. const bool ignoreCase)
  196. {
  197. if (ignoreCase)
  198. {
  199. for (int i = size(); --i >= 0;)
  200. if (strings.getReference(i).equalsIgnoreCase (stringToRemove))
  201. strings.remove (i);
  202. }
  203. else
  204. {
  205. for (int i = size(); --i >= 0;)
  206. if (stringToRemove == strings.getReference (i))
  207. strings.remove (i);
  208. }
  209. }
  210. void StringArray::removeRange (int startIndex, int numberToRemove)
  211. {
  212. strings.removeRange (startIndex, numberToRemove);
  213. }
  214. //==============================================================================
  215. void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings)
  216. {
  217. if (removeWhitespaceStrings)
  218. {
  219. for (int i = size(); --i >= 0;)
  220. if (! strings.getReference(i).containsNonWhitespaceChars())
  221. strings.remove (i);
  222. }
  223. else
  224. {
  225. for (int i = size(); --i >= 0;)
  226. if (strings.getReference(i).isEmpty())
  227. strings.remove (i);
  228. }
  229. }
  230. void StringArray::trim()
  231. {
  232. for (int i = size(); --i >= 0;)
  233. {
  234. String& s = strings.getReference(i);
  235. s = s.trim();
  236. }
  237. }
  238. //==============================================================================
  239. struct InternalStringArrayComparator_CaseSensitive
  240. {
  241. static int compareElements (String& first, String& second) { return first.compare (second); }
  242. };
  243. struct InternalStringArrayComparator_CaseInsensitive
  244. {
  245. static int compareElements (String& first, String& second) { return first.compareIgnoreCase (second); }
  246. };
  247. void StringArray::sort (const bool ignoreCase)
  248. {
  249. if (ignoreCase)
  250. {
  251. InternalStringArrayComparator_CaseInsensitive comp;
  252. strings.sort (comp);
  253. }
  254. else
  255. {
  256. InternalStringArrayComparator_CaseSensitive comp;
  257. strings.sort (comp);
  258. }
  259. }
  260. void StringArray::move (const int currentIndex, int newIndex) noexcept
  261. {
  262. strings.move (currentIndex, newIndex);
  263. }
  264. //==============================================================================
  265. String StringArray::joinIntoString (const String& separator, int start, int numberToJoin) const
  266. {
  267. const int last = (numberToJoin < 0) ? size()
  268. : jmin (size(), start + numberToJoin);
  269. if (start < 0)
  270. start = 0;
  271. if (start >= last)
  272. return String::empty;
  273. if (start == last - 1)
  274. return strings.getReference (start);
  275. const size_t separatorBytes = separator.getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType);
  276. size_t bytesNeeded = separatorBytes * (size_t) (last - start - 1);
  277. for (int i = start; i < last; ++i)
  278. bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType);
  279. String result;
  280. result.preallocateBytes (bytesNeeded);
  281. String::CharPointerType dest (result.getCharPointer());
  282. while (start < last)
  283. {
  284. const String& s = strings.getReference (start);
  285. if (! s.isEmpty())
  286. dest.writeAll (s.getCharPointer());
  287. if (++start < last && separatorBytes > 0)
  288. dest.writeAll (separator.getCharPointer());
  289. }
  290. dest.writeNull();
  291. return result;
  292. }
  293. int StringArray::addTokens (const String& text, const bool preserveQuotedStrings)
  294. {
  295. return addTokens (text, " \n\r\t", preserveQuotedStrings ? "\"" : "");
  296. }
  297. int StringArray::addTokens (const String& text, const String& breakCharacters, const String& quoteCharacters)
  298. {
  299. int num = 0;
  300. String::CharPointerType t (text.getCharPointer());
  301. if (! t.isEmpty())
  302. {
  303. for (;;)
  304. {
  305. String::CharPointerType tokenEnd (CharacterFunctions::findEndOfToken (t,
  306. breakCharacters.getCharPointer(),
  307. quoteCharacters.getCharPointer()));
  308. strings.add (String (t, tokenEnd));
  309. ++num;
  310. if (tokenEnd.isEmpty())
  311. break;
  312. t = ++tokenEnd;
  313. }
  314. }
  315. return num;
  316. }
  317. int StringArray::addLines (const String& sourceText)
  318. {
  319. int numLines = 0;
  320. String::CharPointerType text (sourceText.getCharPointer());
  321. bool finished = text.isEmpty();
  322. while (! finished)
  323. {
  324. for (String::CharPointerType startOfLine (text);;)
  325. {
  326. const String::CharPointerType endOfLine (text);
  327. switch (text.getAndAdvance())
  328. {
  329. case 0: finished = true; break;
  330. case '\n': break;
  331. case '\r': if (*text == '\n') ++text; break;
  332. default: continue;
  333. }
  334. strings.add (String (startOfLine, endOfLine));
  335. ++numLines;
  336. break;
  337. }
  338. }
  339. return numLines;
  340. }
  341. //==============================================================================
  342. void StringArray::removeDuplicates (const bool ignoreCase)
  343. {
  344. for (int i = 0; i < size() - 1; ++i)
  345. {
  346. const String s (strings.getReference(i));
  347. int nextIndex = i + 1;
  348. for (;;)
  349. {
  350. nextIndex = indexOf (s, ignoreCase, nextIndex);
  351. if (nextIndex < 0)
  352. break;
  353. strings.remove (nextIndex);
  354. }
  355. }
  356. }
  357. void StringArray::appendNumbersToDuplicates (const bool ignoreCase,
  358. const bool appendNumberToFirstInstance,
  359. CharPointer_UTF8 preNumberString,
  360. CharPointer_UTF8 postNumberString)
  361. {
  362. CharPointer_UTF8 defaultPre (" ("), defaultPost (")");
  363. if (preNumberString.getAddress() == nullptr)
  364. preNumberString = defaultPre;
  365. if (postNumberString.getAddress() == nullptr)
  366. postNumberString = defaultPost;
  367. for (int i = 0; i < size() - 1; ++i)
  368. {
  369. String& s = strings.getReference(i);
  370. int nextIndex = indexOf (s, ignoreCase, i + 1);
  371. if (nextIndex >= 0)
  372. {
  373. const String original (s);
  374. int number = 0;
  375. if (appendNumberToFirstInstance)
  376. s = original + String (preNumberString) + String (++number) + String (postNumberString);
  377. else
  378. ++number;
  379. while (nextIndex >= 0)
  380. {
  381. set (nextIndex, (*this)[nextIndex] + String (preNumberString) + String (++number) + String (postNumberString));
  382. nextIndex = indexOf (original, ignoreCase, nextIndex + 1);
  383. }
  384. }
  385. }
  386. }
  387. void StringArray::minimiseStorageOverheads()
  388. {
  389. strings.minimiseStorageOverheads();
  390. }