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.

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