/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-7 by Raw Material Software ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License, as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with JUCE; if not, visit www.gnu.org/licenses or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ If you'd like to release a closed-source product which uses JUCE, commercial licenses are also available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ #include "../basics/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE #include "juce_StringArray.h" //============================================================================== StringArray::StringArray() throw() { } StringArray::StringArray (const StringArray& other) throw() { addArray (other); } StringArray::StringArray (const juce_wchar** const strings, const int numberOfStrings) throw() { for (int i = 0; i < numberOfStrings; ++i) add (strings [i]); } StringArray::StringArray (const char** const strings, const int numberOfStrings) throw() { for (int i = 0; i < numberOfStrings; ++i) add (strings [i]); } StringArray::StringArray (const juce_wchar** const strings) throw() { int i = 0; while (strings[i] != 0) add (strings [i++]); } StringArray::StringArray (const char** const strings) throw() { int i = 0; while (strings[i] != 0) add (strings [i++]); } const StringArray& StringArray::operator= (const StringArray& other) throw() { if (this != &other) { clear(); addArray (other); } return *this; } StringArray::~StringArray() throw() { clear(); } bool StringArray::operator== (const StringArray& other) const throw() { if (other.size() != size()) return false; for (int i = size(); --i >= 0;) { if (*(String*) other.strings.getUnchecked(i) != *(String*) strings.getUnchecked(i)) { return false; } } return true; } bool StringArray::operator!= (const StringArray& other) const throw() { return ! operator== (other); } void StringArray::clear() throw() { for (int i = size(); --i >= 0;) { String* const s = (String*) strings.getUnchecked(i); delete s; } strings.clear(); } const String& StringArray::operator[] (const int index) const throw() { if (index >= 0 && index < strings.size()) return *(const String*) (strings.getUnchecked (index)); return String::empty; } void StringArray::add (const String& newString) throw() { strings.add (new String (newString)); } void StringArray::insert (const int index, const String& newString) throw() { strings.insert (index, new String (newString)); } void StringArray::addIfNotAlreadyThere (const String& newString, const bool ignoreCase) throw() { if (! contains (newString, ignoreCase)) add (newString); } void StringArray::addArray (const StringArray& otherArray, int startIndex, int numElementsToAdd) throw() { if (startIndex < 0) { jassertfalse startIndex = 0; } if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size()) numElementsToAdd = otherArray.size() - startIndex; while (--numElementsToAdd >= 0) strings.add (new String (*(const String*) otherArray.strings.getUnchecked (startIndex++))); } void StringArray::set (const int index, const String& newString) throw() { String* const s = (String*) strings [index]; if (s != 0) { *s = newString; } else if (index >= 0) { add (newString); } } bool StringArray::contains (const String& stringToLookFor, const bool ignoreCase) const throw() { if (ignoreCase) { for (int i = size(); --i >= 0;) if (stringToLookFor.equalsIgnoreCase (*(const String*)(strings.getUnchecked(i)))) return true; } else { for (int i = size(); --i >= 0;) if (stringToLookFor == *(const String*)(strings.getUnchecked(i))) return true; } return false; } int StringArray::indexOf (const String& stringToLookFor, const bool ignoreCase, int i) const throw() { if (i < 0) i = 0; const int numElements = size(); if (ignoreCase) { while (i < numElements) { if (stringToLookFor.equalsIgnoreCase (*(const String*) strings.getUnchecked (i))) return i; ++i; } } else { while (i < numElements) { if (stringToLookFor == *(const String*) strings.getUnchecked (i)) return i; ++i; } } return -1; } //============================================================================== void StringArray::remove (const int index) throw() { String* const s = (String*) strings [index]; if (s != 0) { strings.remove (index); delete s; } } void StringArray::removeString (const String& stringToRemove, const bool ignoreCase) throw() { if (ignoreCase) { for (int i = size(); --i >= 0;) if (stringToRemove.equalsIgnoreCase (*(const String*) strings.getUnchecked (i))) remove (i); } else { for (int i = size(); --i >= 0;) if (stringToRemove == *(const String*) strings.getUnchecked (i)) remove (i); } } //============================================================================== void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) throw() { if (removeWhitespaceStrings) { for (int i = size(); --i >= 0;) if (((const String*) strings.getUnchecked(i))->trim().isEmpty()) remove (i); } else { for (int i = size(); --i >= 0;) if (((const String*) strings.getUnchecked(i))->isEmpty()) remove (i); } } void StringArray::trim() throw() { for (int i = size(); --i >= 0;) { String& s = *(String*) strings.getUnchecked(i); s = s.trim(); } } //============================================================================== class InternalStringArrayComparator { public: static int compareElements (void* const first, void* const second) throw() { return ((const String*) first)->compare (*(const String*) second); } }; class InsensitiveInternalStringArrayComparator { public: static int compareElements (void* const first, void* const second) throw() { return ((const String*) first)->compareIgnoreCase (*(const String*) second); } }; void StringArray::sort (const bool ignoreCase) throw() { if (ignoreCase) { InsensitiveInternalStringArrayComparator comp; strings.sort (comp); } else { InternalStringArrayComparator comp; strings.sort (comp); } } void StringArray::move (const int currentIndex, int newIndex) throw() { strings.move (currentIndex, newIndex); } //============================================================================== const String StringArray::joinIntoString (const String& separator, int start, int numberToJoin) const throw() { const int last = (numberToJoin < 0) ? size() : jmin (size(), start + numberToJoin); if (start < 0) start = 0; if (start >= last) return String::empty; if (start == last - 1) return *(const String*) strings.getUnchecked (start); const int separatorLen = separator.length(); int charsNeeded = separatorLen * (last - start - 1); for (int i = start; i < last; ++i) charsNeeded += ((const String*) strings.getUnchecked(i))->length(); String result; result.preallocateStorage (charsNeeded); tchar* dest = (tchar*) (const tchar*) result; while (start < last) { const String& s = *(const String*) strings.getUnchecked (start); const int len = s.length(); if (len > 0) { s.copyToBuffer (dest, len); dest += len; } if (++start < last && separatorLen > 0) { separator.copyToBuffer (dest, separatorLen); dest += separatorLen; } } *dest = 0; return result; } int StringArray::addTokens (const tchar* const text, const bool preserveQuotedStrings) throw() { return addTokens (text, T(" \n\r\t"), preserveQuotedStrings ? T("\"") : 0); } int StringArray::addTokens (const tchar* const text, const tchar* breakCharacters, const tchar* quoteCharacters) throw() { int num = 0; if (text != 0 && *text != 0) { if (breakCharacters == 0) breakCharacters = T(""); if (quoteCharacters == 0) quoteCharacters = T(""); bool insideQuotes = false; tchar currentQuoteChar = 0; int i = 0; int tokenStart = 0; for (;;) { const tchar c = text[i]; bool isBreak = (c == 0); if (! (insideQuotes || isBreak)) { const tchar* b = breakCharacters; while (*b != 0) { if (*b++ == c) { isBreak = true; break; } } } if (! isBreak) { bool isQuote = false; const tchar* q = quoteCharacters; while (*q != 0) { if (*q++ == c) { isQuote = true; break; } } if (isQuote) { if (insideQuotes) { // only break out of quotes-mode if we find a matching quote to the // one that we opened with.. if (currentQuoteChar == c) insideQuotes = false; } else { insideQuotes = true; currentQuoteChar = c; } } } else { add (String (text + tokenStart, i - tokenStart)); ++num; tokenStart = i + 1; } if (c == 0) break; ++i; } } return num; } int StringArray::addLines (const tchar* text) throw() { int numLines = 0; if (text != 0) { while (*text != 0) { const tchar* const startOfLine = text; while (*text != 0) { if (*text == T('\r')) { ++text; if (*text == T('\n')) ++text; break; } if (*text == T('\n')) { ++text; break; } ++text; } const tchar* endOfLine = text; if (endOfLine > startOfLine && (*(endOfLine - 1) == T('\r') || *(endOfLine - 1) == T('\n'))) --endOfLine; if (endOfLine > startOfLine && (*(endOfLine - 1) == T('\r') || *(endOfLine - 1) == T('\n'))) --endOfLine; add (String (startOfLine, jmax (0, (int) (endOfLine - startOfLine)))); ++numLines; } } return numLines; } //============================================================================== void StringArray::removeDuplicates (const bool ignoreCase) throw() { for (int i = 0; i < size() - 1; ++i) { const String& s = *(String*) strings.getUnchecked(i); int nextIndex = i + 1; for (;;) { nextIndex = indexOf (s, ignoreCase, nextIndex); if (nextIndex < 0) break; remove (nextIndex); } } } void StringArray::appendNumbersToDuplicates (const bool ignoreCase, const bool appendNumberToFirstInstance, const tchar* const preNumberString, const tchar* const postNumberString) throw() { for (int i = 0; i < size() - 1; ++i) { String& s = *(String*) strings.getUnchecked(i); int nextIndex = indexOf (s, ignoreCase, i + 1); if (nextIndex >= 0) { const String original (s); int number = 0; if (appendNumberToFirstInstance) s = original + preNumberString + String (++number) + postNumberString; else ++number; while (nextIndex >= 0) { set (nextIndex, (*this)[nextIndex] + preNumberString + String (++number) + postNumberString); nextIndex = indexOf (original, ignoreCase, nextIndex + 1); } } } } void StringArray::minimiseStorageOverheads() throw() { strings.minimiseStorageOverheads(); } END_JUCE_NAMESPACE