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.

508 lines
14KB

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