]> git.saurik.com Git - wxWidgets.git/commitdiff
added wxJoin and wxSplit functions (modified patch 1638950)
authorVadim Zeitlin <vadim@wxwidgets.org>
Sun, 18 Mar 2007 14:43:41 +0000 (14:43 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sun, 18 Mar 2007 14:43:41 +0000 (14:43 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@44890 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
docs/latex/wx/function.tex
include/wx/arrstr.h
src/common/string.cpp
tests/arrays/arrays.cpp

index 97fa8ebd9c432d40a15b3b7d219e3c5b291f2d9e..212e6e800c392253c24db46dc2dd6bf2b86ed6a6 100644 (file)
@@ -58,6 +58,13 @@ Major new features in this release
 
 All:
 
+- Added wxJoin() and wxSplit() functions (Francesco Montorsi)
+- Added wxMutex::LockTimeout() (Aleksandr Napylov)
+- Added wxMemoryInputStream(wxInputStream&) ctor (Stas Sergeev)
+- Implemented wxMemoryInputStream::CanRead()
+
+All (GUI):
+
 - Added wxEventBlocker class (Francesco Montorsi).
 - Added wxFile/DirPickerCtrl::Get/SetFile/DirName() (Francesco Montorsi).
 - Added wxSizerFlags::Top() and Bottom().
@@ -65,13 +72,11 @@ All:
 - Fixed tab-related drawing and hit-testing bugs in wxRichTextCtrl.
 - Implemented background colour in wxRichTextCtrl.
 - Fixed crashes in helpview when opening a file.
-- Added wxMutex::LockTimeout() (Aleksandr Napylov)
 - Set locale to the default in all ports, not just wxGTK
 - Added wxJoystick::GetButtonState/Position() (Frank C Szczerba)
 - Added wxGridUpdateLocker helper class (Evgeniy Tarassov)
 - Support wxGRID_AUTOSIZE in wxGrid::SetRow/ColLabelSize() (Evgeniy Tarassov)
-- Added wxMemoryInputStream(wxInputStream&) ctor (Stas Sergeev)
-- Implemented wxMemoryInputStream::CanRead()
+- Add support for <data> tags to wxrc
 
 wxGTK:
 
index b3d9a075121bdf3f8a13ed35eae8a07a7b3cd160..8d04a686eb0cad4fa5993c75cc4e6dd0bdcc7335 100644 (file)
@@ -179,6 +179,7 @@ the corresponding topic.
 \helpref{wxIsPlatformLittleEndian}{wxisplatformlittleendian}\\
 \helpref{wxIsPlatform64Bit}{wxisplatform64bit}\\
 \helpref{wxIsWild}{wxiswild}\\
+\helpref{wxJoin}{wxjoin}\\
 \helpref{wxKill}{wxkill}\\
 \helpref{wxLaunchDefaultBrowser}{wxlaunchdefaultbrowser}\\
 \helpref{wxLEAVE\_CRIT\_SECT}{wxleavecritsect}\\
@@ -238,6 +239,7 @@ the corresponding topic.
 \helpref{wxShutdown}{wxshutdown}\\
 \helpref{wxSleep}{wxsleep}\\
 \helpref{wxSnprintf}{wxsnprintf}\\
+\helpref{wxSplit}{wxsplit}\\
 \helpref{wxSplitPath}{wxsplitfunction}\\
 \helpref{wxStartTimer}{wxstarttimer}\\
 \helpref{wxStaticCast}{wxstaticcast}\\
@@ -694,6 +696,23 @@ application. See \helpref{wxCloseEvent}{wxcloseevent} and \helpref{wxApp}{wxapp}
 <wx/app.h>
 
 
+\membersection{::wxJoin}\label{wxjoin}
+
+\func{wxString}{wxJoin}{\param{const wxArrayString\&}{ arr}, \param{const wxChar}{ sep}, \param{const wxChar}{ escape = '\\'}}
+
+Concatenate all lines of the given \helpref{wxArrayString}{wxarraystring} object using the separator \arg{sep} and returns
+the result as a \helpref{wxString}{string}.
+
+If the \arg{escape} character is non-\NULL, then it's used as prefix for each occurrence of \arg{sep}
+in the strings contained in \arg{arr} before joining them which is necessary
+in order to be able to recover the original array contents from the string
+later using \helpref{wxSplit}{wxsplit}.
+
+\wxheading{Include files}
+
+<wx/arrstr.h>
+
+
 \membersection{::wxKill}\label{wxkill}
 
 \func{int}{wxKill}{\param{long}{ pid}, \param{int}{ sig = wxSIGTERM}, \param{wxKillError }{*rc = NULL}, \param{int }{flags = 0}}
@@ -1287,6 +1306,27 @@ Sets the current working directory, returning true if the operation succeeded.
 Under MS Windows, the current drive is also changed if {\it dir} contains a drive specification.
 
 
+\membersection{::wxSplit}\label{wxsplit}
+
+\func{wxArrayString}{wxSplit}{\param{const wxString\&}{ str}, \param{const wxChar}{ sep}, \param{const wxChar}{ escape = '\\'}}
+
+Splits the given \helpref{wxString}{wxstring} object using the separator \arg{sep} and returns the
+result as a \helpref{wxArrayString}{wxarraystring}.
+
+If the \arg{escape} character is non-\NULL, then the occurrences of \arg{sep} immediately prefixed
+with \arg{escape} are not considered as separators.
+
+Note that empty tokens will be generated if there are two or more adjacent separators.
+
+\wxheading{See also}
+
+\helpref{wxJoin}{wxjoin}
+
+\wxheading{Include files}
+
+<wx/arrstr.h>
+
+
 \membersection{::wxSplitPath}\label{wxsplitfunction}
 
 \func{void}{wxSplitPath}{\param{const char *}{ fullname}, \param{wxString *}{ path}, \param{wxString *}{ name}, \param{wxString *}{ ext}}
index 570d4672e8c8101420722141d34edf2792a3cb2e..ad82252b903d9190b837b8d2db2a244ba76ab622 100644 (file)
@@ -56,7 +56,7 @@ public:
         wxArrayStringBase::Add(string, copies);
         return size() - copies;
     }
-};
+}
 
 class WXDLLIMPEXP_BASE wxSortedArrayString : public wxSortedArrayStringBase
 {
@@ -343,4 +343,21 @@ private:
     wxString* m_strings;
 };
 
-#endif
+
+// ----------------------------------------------------------------------------
+// helper functions for working with arrays
+// ----------------------------------------------------------------------------
+
+// by default, these functions use the escape character to escape the
+// separators occuring inside the string to be joined, this can be disabled by
+// passing '\0' as escape
+
+WXDLLIMPEXP_BASE wxString wxJoin(const wxArrayString& arr,
+                                 const wxChar sep,
+                                 const wxChar escape = wxT('\\'));
+
+WXDLLIMPEXP_BASE wxArrayString wxSplit(const wxString& str,
+                                       const wxChar sep,
+                                       const wxChar escape = wxT('\\'));
+
+#endif // _WX_ARRSTR_H
index 2a495e50c294f7239e2da7dd328dada8f75ab989..68da706c984229ffd6a5c1e85577b3b4d3126505 100644 (file)
@@ -2527,3 +2527,106 @@ int wxCMPFUNC_CONV wxStringSortDescending(wxString* s1, wxString* s2)
 {
     return -s1->Cmp(*s2);
 }
+
+
+
+// ===========================================================================
+// wxJoin and wxSplit
+// ===========================================================================
+
+#include "wx/tokenzr.h"
+
+wxString wxJoin(const wxArrayString& arr, const wxChar sep, const wxChar escape)
+{
+    size_t count = arr.size();
+    if ( count == 0 )
+        return wxEmptyString;
+
+    wxString str;
+
+    // pre-allocate memory using the estimation of the average length of the
+    // strings in the given array: this is very imprecise, of course, but
+    // better than nothing
+    str.reserve(count*(arr[0].length() + arr[count-1].length()) / 2);
+
+    if ( escape == wxT('\0') )
+    {
+        // escaping is disabled:
+        for ( size_t i = 0; i < count; i++ )
+        {
+            if ( i )
+                str += sep;
+            str += arr[i];
+        }
+    }
+    else // use escape character
+    {
+        for ( size_t n = 0; n < count; n++ )
+        {
+            if ( n )
+                str += sep;
+
+            for ( wxString::const_iterator i = arr[n].begin(),
+                                         end = arr[n].end();
+                  i != end;
+                  ++i )
+            {
+                const wxChar ch = *i;
+                if ( ch == sep )
+                    str += escape;      // escape this separator
+                str += ch;
+            }
+        }
+    }
+
+    str.Shrink(); // release extra memory if we allocated too much
+    return str;
+}
+
+wxArrayString wxSplit(const wxString& str, const wxChar sep, const wxChar escape)
+{
+    if ( escape == wxT('\0') )
+    {
+        // simple case: we don't need to honour the escape character
+        return wxStringTokenize(str, sep, wxTOKEN_RET_EMPTY_ALL);
+    }
+
+    wxArrayString ret;
+    wxString curr;
+    wxChar prev = wxT('\0');
+
+    for ( wxString::const_iterator i = str.begin(),
+                                 end = str.end();
+          i != end;
+          ++i )
+    {
+        const wxChar ch = *i;
+
+        if ( ch == sep )
+        {
+            if ( prev == escape )
+            {
+                // remove the escape character and don't consider this
+                // occurrence of 'sep' as a real separator
+                *curr.rbegin() = sep;
+            }
+            else // real separator
+            {
+                ret.push_back(curr);
+                curr.clear();
+            }
+        }
+        else // normal character
+        {
+            curr += ch;
+        }
+
+        prev = ch;
+    }
+
+    // add the last token
+    if ( !curr.empty() || prev == sep )
+        ret.Add(curr);
+
+    return ret;
+}
index a776a2c1db82ce1f22cec37f260a67f9b3cef6fc..8d05e0ca8dae15ec965aa2fbdc60939ba7df104c 100644 (file)
@@ -151,6 +151,10 @@ public:
 private:
     CPPUNIT_TEST_SUITE( ArraysTestCase );
         CPPUNIT_TEST( wxStringArrayTest );
+        CPPUNIT_TEST( wxStringArraySplitTest );
+        CPPUNIT_TEST( wxStringArrayJoinTest );
+        CPPUNIT_TEST( wxStringArraySplitJoinTest );
+
         CPPUNIT_TEST( wxObjArrayTest );
         CPPUNIT_TEST( wxArrayUShortTest );
         CPPUNIT_TEST( wxArrayIntTest );
@@ -160,6 +164,9 @@ private:
     CPPUNIT_TEST_SUITE_END();
 
     void wxStringArrayTest();
+    void wxStringArraySplitTest();
+    void wxStringArrayJoinTest();
+    void wxStringArraySplitJoinTest();
     void wxObjArrayTest();
     void wxArrayUShortTest();
     void wxArrayIntTest();
@@ -296,6 +303,159 @@ void ArraysTestCase::wxStringArrayTest()
                                           _T("a") ) );
 }
 
+void ArraysTestCase::wxStringArraySplitTest()
+{
+    // test wxSplit:
+
+    {
+        wxString str = wxT(",,,,first,second,third,,");
+        const wxChar *expected[] =
+            { wxT(""), wxT(""), wxT(""), wxT(""), wxT("first"),
+              wxT("second"), wxT("third"), wxT(""), wxT("") };
+
+        wxArrayString exparr(WXSIZEOF(expected), expected);
+        wxArrayString realarr(wxSplit(str, wxT(',')));
+        CPPUNIT_ASSERT( exparr == realarr );
+    }
+
+    {
+        wxString str = wxT(",\\,first,second,third,");
+        const wxChar *expected[] =
+            { wxT(""), wxT(",first"), wxT("second"), wxT("third"), wxT("") };
+        const wxChar *expected2[] =
+            { wxT(""), wxT("\\"), wxT("first"), wxT("second"), wxT("third"), wxT("") };
+
+        // escaping on:
+        wxArrayString exparr(WXSIZEOF(expected), expected);
+        wxArrayString realarr(wxSplit(str, wxT(','), wxT('\\')));
+        CPPUNIT_ASSERT( exparr == realarr );
+
+        // escaping turned off:
+        wxArrayString exparr2(WXSIZEOF(expected2), expected2);
+        wxArrayString realarr2(wxSplit(str, wxT(','), wxT('\0')));
+        CPPUNIT_ASSERT( exparr2 == realarr2 );
+    }
+
+    {
+        // test is escape characters placed before non-separator character are
+        // just ignored as they should:
+        wxString str = wxT(",\\,,fir\\st,se\\cond\\,,\\third");
+        const wxChar *expected[] =
+            { wxT(""), wxT(","), wxT("fir\\st"), wxT("se\\cond,"), wxT("\\third") };
+        const wxChar *expected2[] =
+            { wxT(""), wxT("\\"), wxT(""), wxT("fir\\st"), wxT("se\\cond\\"),
+              wxT(""), wxT("\\third") };
+
+        // escaping on:
+        wxArrayString exparr(WXSIZEOF(expected), expected);
+        wxArrayString realarr(wxSplit(str, wxT(','), wxT('\\')));
+        CPPUNIT_ASSERT( exparr == realarr );
+
+        // escaping turned off:
+        wxArrayString exparr2(WXSIZEOF(expected2), expected2);
+        wxArrayString realarr2(wxSplit(str, wxT(','), wxT('\0')));
+        CPPUNIT_ASSERT( exparr2 == realarr2 );
+    }
+}
+
+void ArraysTestCase::wxStringArrayJoinTest()
+{
+    // test wxJoin:
+
+    {
+        const wxChar *arr[] = { wxT("first"), wxT(""), wxT("second"), wxT("third") };
+        wxString expected = wxT("first,,second,third");
+
+        wxArrayString arrstr(WXSIZEOF(arr), arr);
+        wxString result = wxJoin(arrstr, wxT(','));
+        CPPUNIT_ASSERT( expected == result );
+    }
+
+    {
+        const wxChar *arr[] = { wxT("first, word"), wxT(",,second"), wxT("third,,") };
+        wxString expected = wxT("first\\, word,\\,\\,second,third\\,\\,");
+        wxString expected2 = wxT("first, word,,,second,third,,");
+
+        // escaping on:
+        wxArrayString arrstr(WXSIZEOF(arr), arr);
+        wxString result = wxJoin(arrstr, wxT(','), wxT('\\'));
+        CPPUNIT_ASSERT( expected == result );
+
+        // escaping turned off:
+        wxString result2 = wxJoin(arrstr, wxT(','), wxT('\0'));
+        CPPUNIT_ASSERT( expected2 == result2 );
+    }
+
+    {
+        // test is escape characters placed in the original array are just ignored as they should:
+        const wxChar *arr[] = { wxT("first\\, wo\\rd"), wxT("seco\\nd"), wxT("\\third\\") };
+        wxString expected = wxT("first\\\\, wo\\rd,seco\\nd,\\third\\");
+        wxString expected2 = wxT("first\\, wo\\rd,seco\\nd,\\third\\");
+
+        // escaping on:
+        wxArrayString arrstr(WXSIZEOF(arr), arr);
+        wxString result = wxJoin(arrstr, wxT(','), wxT('\\'));
+        CPPUNIT_ASSERT( expected == result );
+
+        // escaping turned off:
+        wxString result2 = wxJoin(arrstr, wxT(','), wxT('\0'));
+        CPPUNIT_ASSERT( expected2 == result2 );
+    }
+}
+
+void ArraysTestCase::wxStringArraySplitJoinTest()
+{
+    wxChar separators[] = { wxT('a'), wxT(','), wxT('_'), wxT(' '), wxT('\\'),
+                            wxT('&'), wxT('{'), wxT('A'), wxT('<'), wxT('>'),
+                            wxT('\''), wxT('\n'), wxT('!'), wxT('-') };
+
+    // test with a string: split it and then rejoin it:
+
+    wxString str = wxT("This is a long, long test; if wxSplit and wxJoin do work ")
+                   wxT("correctly, then splitting and joining this 'text' _multiple_ ")
+                   wxT("times shouldn't cause any loss of content.\n")
+                   wxT("This is some latex code: ")
+                   wxT("\\func{wxString}{wxJoin}{")
+                   wxT("\\param{const wxArray String\\&}{ arr}, ")
+                   wxT("\\param{const wxChar}{ sep}, ")
+                   wxT("\\param{const wxChar}{ escape = '\\'}}.\n")
+                   wxT("This is some HTML code: ")
+                   wxT("<html><head><meta http-equiv=\"content-type\">")
+                   wxT("<title>Initial page of Mozilla Firefox</title>")
+                   wxT("</meta></head></html>");
+
+    for (size_t i=0; i < WXSIZEOF(separators); i++)
+    {
+        wxArrayString arr = wxSplit(str, separators[i]);
+        CPPUNIT_ASSERT( str == wxJoin(arr, separators[i]) );
+    }
+
+
+    // test with an array: join it and then resplit it:
+
+    const wxChar *arr[] =
+        {
+            wxT("first, second!"), wxT("this is the third!!"),
+            wxT("\nThat's the fourth token\n\n"), wxT(" - fifth\ndummy\ntoken - "),
+            wxT("_sixth__token__with_underscores"), wxT("The! Last! One!")
+        };
+    wxArrayString theArr(WXSIZEOF(arr), arr);
+
+    for (size_t i=0; i < WXSIZEOF(separators); i++)
+    {
+        wxString string = wxJoin(theArr, separators[i]);
+        CPPUNIT_ASSERT( theArr == wxSplit(string, separators[i]) );
+    }
+
+    wxArrayString emptyArray;
+    wxString string = wxJoin(emptyArray, _T(';'));
+    CPPUNIT_ASSERT( string.empty() );
+
+    CPPUNIT_ASSERT( wxSplit(string, _T(';')).empty() );
+
+    CPPUNIT_ASSERT_EQUAL( (size_t)2, wxSplit(_T(";"), _T(';')).size() );
+}
+
 void ArraysTestCase::wxObjArrayTest()
 {
     {