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.

582 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "../basics/juce_StandardHeader.h"
  24. BEGIN_JUCE_NAMESPACE
  25. #include "juce_StringArray.h"
  26. //==============================================================================
  27. StringArray::StringArray() throw()
  28. {
  29. }
  30. StringArray::StringArray (const StringArray& other) throw()
  31. {
  32. addArray (other);
  33. }
  34. StringArray::StringArray (const juce_wchar** const strings,
  35. const int numberOfStrings) throw()
  36. {
  37. for (int i = 0; i < numberOfStrings; ++i)
  38. add (strings [i]);
  39. }
  40. StringArray::StringArray (const char** const strings,
  41. const int numberOfStrings) throw()
  42. {
  43. for (int i = 0; i < numberOfStrings; ++i)
  44. add (strings [i]);
  45. }
  46. StringArray::StringArray (const juce_wchar** const strings) throw()
  47. {
  48. int i = 0;
  49. while (strings[i] != 0)
  50. add (strings [i++]);
  51. }
  52. StringArray::StringArray (const char** const strings) throw()
  53. {
  54. int i = 0;
  55. while (strings[i] != 0)
  56. add (strings [i++]);
  57. }
  58. const StringArray& StringArray::operator= (const StringArray& other) throw()
  59. {
  60. if (this != &other)
  61. {
  62. clear();
  63. addArray (other);
  64. }
  65. return *this;
  66. }
  67. StringArray::~StringArray() throw()
  68. {
  69. clear();
  70. }
  71. bool StringArray::operator== (const StringArray& other) const throw()
  72. {
  73. if (other.size() != size())
  74. return false;
  75. for (int i = size(); --i >= 0;)
  76. {
  77. if (*(String*) other.strings.getUnchecked(i)
  78. != *(String*) strings.getUnchecked(i))
  79. {
  80. return false;
  81. }
  82. }
  83. return true;
  84. }
  85. bool StringArray::operator!= (const StringArray& other) const throw()
  86. {
  87. return ! operator== (other);
  88. }
  89. void StringArray::clear() throw()
  90. {
  91. for (int i = size(); --i >= 0;)
  92. {
  93. String* const s = (String*) strings.getUnchecked(i);
  94. delete s;
  95. }
  96. strings.clear();
  97. }
  98. const String& StringArray::operator[] (const int index) const throw()
  99. {
  100. if (index >= 0 && index < strings.size())
  101. return *(const String*) (strings.getUnchecked (index));
  102. return String::empty;
  103. }
  104. void StringArray::add (const String& newString) throw()
  105. {
  106. strings.add (new String (newString));
  107. }
  108. void StringArray::insert (const int index,
  109. const String& newString) throw()
  110. {
  111. strings.insert (index, new String (newString));
  112. }
  113. void StringArray::addIfNotAlreadyThere (const String& newString,
  114. const bool ignoreCase) throw()
  115. {
  116. if (! contains (newString, ignoreCase))
  117. add (newString);
  118. }
  119. void StringArray::addArray (const StringArray& otherArray,
  120. int startIndex,
  121. int numElementsToAdd) throw()
  122. {
  123. if (startIndex < 0)
  124. {
  125. jassertfalse
  126. startIndex = 0;
  127. }
  128. if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size())
  129. numElementsToAdd = otherArray.size() - startIndex;
  130. while (--numElementsToAdd >= 0)
  131. strings.add (new String (*(const String*) otherArray.strings.getUnchecked (startIndex++)));
  132. }
  133. void StringArray::set (const int index,
  134. const String& newString) throw()
  135. {
  136. String* const s = (String*) strings [index];
  137. if (s != 0)
  138. {
  139. *s = newString;
  140. }
  141. else if (index >= 0)
  142. {
  143. add (newString);
  144. }
  145. }
  146. bool StringArray::contains (const String& stringToLookFor,
  147. const bool ignoreCase) const throw()
  148. {
  149. if (ignoreCase)
  150. {
  151. for (int i = size(); --i >= 0;)
  152. if (stringToLookFor.equalsIgnoreCase (*(const String*)(strings.getUnchecked(i))))
  153. return true;
  154. }
  155. else
  156. {
  157. for (int i = size(); --i >= 0;)
  158. if (stringToLookFor == *(const String*)(strings.getUnchecked(i)))
  159. return true;
  160. }
  161. return false;
  162. }
  163. int StringArray::indexOf (const String& stringToLookFor,
  164. const bool ignoreCase,
  165. int i) const throw()
  166. {
  167. if (i < 0)
  168. i = 0;
  169. const int numElements = size();
  170. if (ignoreCase)
  171. {
  172. while (i < numElements)
  173. {
  174. if (stringToLookFor.equalsIgnoreCase (*(const String*) strings.getUnchecked (i)))
  175. return i;
  176. ++i;
  177. }
  178. }
  179. else
  180. {
  181. while (i < numElements)
  182. {
  183. if (stringToLookFor == *(const String*) strings.getUnchecked (i))
  184. return i;
  185. ++i;
  186. }
  187. }
  188. return -1;
  189. }
  190. //==============================================================================
  191. void StringArray::remove (const int index) throw()
  192. {
  193. String* const s = (String*) strings [index];
  194. if (s != 0)
  195. {
  196. strings.remove (index);
  197. delete s;
  198. }
  199. }
  200. void StringArray::removeString (const String& stringToRemove,
  201. const bool ignoreCase) throw()
  202. {
  203. if (ignoreCase)
  204. {
  205. for (int i = size(); --i >= 0;)
  206. if (stringToRemove.equalsIgnoreCase (*(const String*) strings.getUnchecked (i)))
  207. remove (i);
  208. }
  209. else
  210. {
  211. for (int i = size(); --i >= 0;)
  212. if (stringToRemove == *(const String*) strings.getUnchecked (i))
  213. remove (i);
  214. }
  215. }
  216. //==============================================================================
  217. void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) throw()
  218. {
  219. if (removeWhitespaceStrings)
  220. {
  221. for (int i = size(); --i >= 0;)
  222. if (((const String*) strings.getUnchecked(i))->trim().isEmpty())
  223. remove (i);
  224. }
  225. else
  226. {
  227. for (int i = size(); --i >= 0;)
  228. if (((const String*) strings.getUnchecked(i))->isEmpty())
  229. remove (i);
  230. }
  231. }
  232. void StringArray::trim() throw()
  233. {
  234. for (int i = size(); --i >= 0;)
  235. {
  236. String& s = *(String*) strings.getUnchecked(i);
  237. s = s.trim();
  238. }
  239. }
  240. //==============================================================================
  241. class InternalStringArrayComparator
  242. {
  243. public:
  244. static int compareElements (void* const first, void* const second) throw()
  245. {
  246. return ((const String*) first)->compare (*(const String*) second);
  247. }
  248. };
  249. class InsensitiveInternalStringArrayComparator
  250. {
  251. public:
  252. static int compareElements (void* const first, void* const second) throw()
  253. {
  254. return ((const String*) first)->compareIgnoreCase (*(const String*) second);
  255. }
  256. };
  257. void StringArray::sort (const bool ignoreCase) throw()
  258. {
  259. if (ignoreCase)
  260. {
  261. InsensitiveInternalStringArrayComparator comp;
  262. strings.sort (comp);
  263. }
  264. else
  265. {
  266. InternalStringArrayComparator comp;
  267. strings.sort (comp);
  268. }
  269. }
  270. void StringArray::move (const int currentIndex, int newIndex) throw()
  271. {
  272. strings.move (currentIndex, newIndex);
  273. }
  274. //==============================================================================
  275. const String StringArray::joinIntoString (const String& separator,
  276. int start,
  277. int numberToJoin) const throw()
  278. {
  279. const int last = (numberToJoin < 0) ? size()
  280. : jmin (size(), start + numberToJoin);
  281. if (start < 0)
  282. start = 0;
  283. if (start >= last)
  284. return String::empty;
  285. if (start == last - 1)
  286. return *(const String*) strings.getUnchecked (start);
  287. const int separatorLen = separator.length();
  288. int charsNeeded = separatorLen * (last - start - 1);
  289. for (int i = start; i < last; ++i)
  290. charsNeeded += ((const String*) strings.getUnchecked(i))->length();
  291. String result;
  292. result.preallocateStorage (charsNeeded);
  293. tchar* dest = (tchar*) (const tchar*) result;
  294. while (start < last)
  295. {
  296. const String& s = *(const String*) strings.getUnchecked (start);
  297. const int len = s.length();
  298. if (len > 0)
  299. {
  300. s.copyToBuffer (dest, len);
  301. dest += len;
  302. }
  303. if (++start < last && separatorLen > 0)
  304. {
  305. separator.copyToBuffer (dest, separatorLen);
  306. dest += separatorLen;
  307. }
  308. }
  309. *dest = 0;
  310. return result;
  311. }
  312. int StringArray::addTokens (const tchar* const text,
  313. const bool preserveQuotedStrings) throw()
  314. {
  315. return addTokens (text,
  316. T(" \n\r\t"),
  317. preserveQuotedStrings ? T("\"") : 0);
  318. }
  319. int StringArray::addTokens (const tchar* const text,
  320. const tchar* breakCharacters,
  321. const tchar* quoteCharacters) throw()
  322. {
  323. int num = 0;
  324. if (text != 0 && *text != 0)
  325. {
  326. if (breakCharacters == 0)
  327. breakCharacters = T("");
  328. if (quoteCharacters == 0)
  329. quoteCharacters = T("");
  330. bool insideQuotes = false;
  331. tchar currentQuoteChar = 0;
  332. int i = 0;
  333. int tokenStart = 0;
  334. for (;;)
  335. {
  336. const tchar c = text[i];
  337. bool isBreak = (c == 0);
  338. if (! (insideQuotes || isBreak))
  339. {
  340. const tchar* b = breakCharacters;
  341. while (*b != 0)
  342. {
  343. if (*b++ == c)
  344. {
  345. isBreak = true;
  346. break;
  347. }
  348. }
  349. }
  350. if (! isBreak)
  351. {
  352. bool isQuote = false;
  353. const tchar* q = quoteCharacters;
  354. while (*q != 0)
  355. {
  356. if (*q++ == c)
  357. {
  358. isQuote = true;
  359. break;
  360. }
  361. }
  362. if (isQuote)
  363. {
  364. if (insideQuotes)
  365. {
  366. // only break out of quotes-mode if we find a matching quote to the
  367. // one that we opened with..
  368. if (currentQuoteChar == c)
  369. insideQuotes = false;
  370. }
  371. else
  372. {
  373. insideQuotes = true;
  374. currentQuoteChar = c;
  375. }
  376. }
  377. }
  378. else
  379. {
  380. add (String (text + tokenStart, i - tokenStart));
  381. ++num;
  382. tokenStart = i + 1;
  383. }
  384. if (c == 0)
  385. break;
  386. ++i;
  387. }
  388. }
  389. return num;
  390. }
  391. int StringArray::addLines (const tchar* text) throw()
  392. {
  393. int numLines = 0;
  394. if (text != 0)
  395. {
  396. while (*text != 0)
  397. {
  398. const tchar* const startOfLine = text;
  399. while (*text != 0)
  400. {
  401. if (*text == T('\r'))
  402. {
  403. ++text;
  404. if (*text == T('\n'))
  405. ++text;
  406. break;
  407. }
  408. if (*text == T('\n'))
  409. {
  410. ++text;
  411. break;
  412. }
  413. ++text;
  414. }
  415. const tchar* endOfLine = text;
  416. if (endOfLine > startOfLine && (*(endOfLine - 1) == T('\r') || *(endOfLine - 1) == T('\n')))
  417. --endOfLine;
  418. if (endOfLine > startOfLine && (*(endOfLine - 1) == T('\r') || *(endOfLine - 1) == T('\n')))
  419. --endOfLine;
  420. add (String (startOfLine, jmax (0, (int) (endOfLine - startOfLine))));
  421. ++numLines;
  422. }
  423. }
  424. return numLines;
  425. }
  426. //==============================================================================
  427. void StringArray::removeDuplicates (const bool ignoreCase) throw()
  428. {
  429. for (int i = 0; i < size() - 1; ++i)
  430. {
  431. const String& s = *(String*) strings.getUnchecked(i);
  432. int nextIndex = i + 1;
  433. for (;;)
  434. {
  435. nextIndex = indexOf (s, ignoreCase, nextIndex);
  436. if (nextIndex < 0)
  437. break;
  438. remove (nextIndex);
  439. }
  440. }
  441. }
  442. void StringArray::appendNumbersToDuplicates (const bool ignoreCase,
  443. const bool appendNumberToFirstInstance,
  444. const tchar* const preNumberString,
  445. const tchar* const postNumberString) throw()
  446. {
  447. for (int i = 0; i < size() - 1; ++i)
  448. {
  449. String& s = *(String*) strings.getUnchecked(i);
  450. int nextIndex = indexOf (s, ignoreCase, i + 1);
  451. if (nextIndex >= 0)
  452. {
  453. const String original (s);
  454. int number = 0;
  455. if (appendNumberToFirstInstance)
  456. s = original + preNumberString + String (++number) + postNumberString;
  457. else
  458. ++number;
  459. while (nextIndex >= 0)
  460. {
  461. set (nextIndex, (*this)[nextIndex] + preNumberString + String (++number) + postNumberString);
  462. nextIndex = indexOf (original, ignoreCase, nextIndex + 1);
  463. }
  464. }
  465. }
  466. }
  467. void StringArray::minimiseStorageOverheads() throw()
  468. {
  469. strings.minimiseStorageOverheads();
  470. }
  471. END_JUCE_NAMESPACE