Bug 562310 Part 3: Extend nsCharSeparatedTokenizer to allow separator to be optional. r=roc
authorDaniel Holbert <dholbert@cs.stanford.edu>
Tue, 04 May 2010 07:43:48 -0700
changeset 41765 90fa6c2809b6f4d5412f0dead546d60b04163f97
parent 41764 4dcfc2768b01194a84999892be832615840eb9d0
child 41766 854dd58ccf7fc2448a5b3fa0a6f4564cb56980b8
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs562310
milestone1.9.3a5pre
Bug 562310 Part 3: Extend nsCharSeparatedTokenizer to allow separator to be optional. r=roc
xpcom/ds/nsCharSeparatedTokenizer.h
--- a/xpcom/ds/nsCharSeparatedTokenizer.h
+++ b/xpcom/ds/nsCharSeparatedTokenizer.h
@@ -54,20 +54,28 @@
  * "foo , bar hi , baz" -> "foo" "bar hi" "baz"
  * "foo, ,bar,baz" ->      "foo" "" "bar" "baz"
  * "foo,,bar,baz" ->       "foo" "" "bar" "baz"
  * "foo,bar,baz," ->       "foo" "bar" "baz"
  */
 class nsCharSeparatedTokenizer
 {
 public:
+    // Flags -- only one for now. If we need more, they should be defined to
+    // be 1<<1, 1<<2, etc. (They're masks, and aFlags/mFlags are bitfields.)
+    enum {
+        SEPARATOR_OPTIONAL = 1
+    };
+
     nsCharSeparatedTokenizer(const nsSubstring& aSource,
-                             PRUnichar aSeparatorChar)
+                             PRUnichar aSeparatorChar,
+                             PRUint32  aFlags = 0)
         : mLastTokenEndedWithSeparator(PR_FALSE),
-          mSeparatorChar(aSeparatorChar)
+          mSeparatorChar(aSeparatorChar),
+          mFlags(aFlags)
     {
         aSource.BeginReading(mIter);
         aSource.EndReading(mEnd);
 
         // Skip initial whitespace
         while (mIter != mEnd && isWhitespace(*mIter)) {
             ++mIter;
         }
@@ -94,49 +102,62 @@ public:
      */
     const nsDependentSubstring nextToken()
     {
         nsSubstring::const_char_iterator end = mIter, begin = mIter;
 
         NS_ASSERTION(mIter == mEnd || !isWhitespace(*mIter),
                      "Should be at beginning of token if there is one");
 
-        // Search until we hit separator or end.
+        // Search until we hit separator or end (or whitespace, if separator
+        // isn't required -- see clause with 'break' below).
         while (mIter != mEnd && *mIter != mSeparatorChar) {
           // Skip to end of current word.
           while (mIter != mEnd &&
                  !isWhitespace(*mIter) && *mIter != mSeparatorChar) {
               ++mIter;
           }
           end = mIter;
 
           // Skip whitespace after current word.
           while (mIter != mEnd && isWhitespace(*mIter)) {
               ++mIter;
           }
+          if (mFlags & SEPARATOR_OPTIONAL) {
+            // We've hit (and skipped) whitespace, and that's sufficient to end
+            // our token, regardless of whether we've reached a SeparatorChar.
+            break;
+          } // (else, we'll keep looping until we hit mEnd or SeparatorChar)
         }
-        mLastTokenEndedWithSeparator = mIter != mEnd;
 
-        // Skip separator (and any whitespace after it).
+        mLastTokenEndedWithSeparator = (mIter != mEnd &&
+                                        *mIter == mSeparatorChar);
+        NS_ASSERTION((mFlags & SEPARATOR_OPTIONAL) ||
+                     (mLastTokenEndedWithSeparator == (mIter != mEnd)),
+                     "If we require a separator and haven't hit the end of "
+                     "our string, then we shouldn't have left the loop "
+                     "unless we hit a separator");
+
+        // Skip separator (and any whitespace after it), if we're at one.
         if (mLastTokenEndedWithSeparator) {
-            NS_ASSERTION(*mIter == mSeparatorChar, "Ended loop too soon");
             ++mIter;
 
             while (mIter != mEnd && isWhitespace(*mIter)) {
                 ++mIter;
             }
         }
         
         return Substring(begin, end);
     }
 
 private:
     nsSubstring::const_char_iterator mIter, mEnd;
     PRPackedBool mLastTokenEndedWithSeparator;
     PRUnichar mSeparatorChar;
+    PRUint32  mFlags;
 
     PRBool isWhitespace(PRUnichar aChar)
     {
         return aChar <= ' ' &&
                (aChar == ' ' || aChar == '\n' ||
                 aChar == '\r'|| aChar == '\t');
     }
 };