Moved second half of gmxana tools to C++
[alexxy/gromacs.git] / src / gromacs / utility / stringutil.cpp
index c2ae4814ffc469f8312b3ac249319fd331ba267f..f019512d1b699ee5d48a97dc3f336b3543ab37dd 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2011,2012,2013, by the GROMACS development team, led by
- * David van der Spoel, Berk Hess, Erik Lindahl, and including many
- * others, as listed in the AUTHORS file in the top-level source
- * directory and at http://www.gromacs.org.
+ * Copyright (c) 2011,2012,2013,2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
  *
  * GROMACS is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * \author Teemu Murtola <teemu.murtola@gmail.com>
  * \ingroup module_utility
  */
+#include "gmxpre.h"
+
 #include "stringutil.h"
 
 #include <cctype>
-#include <cstdio>
 #include <cstdarg>
+#include <cstdio>
 #include <cstring>
 
 #include <algorithm>
 namespace gmx
 {
 
-bool endsWith(const std::string &str, const char *suffix)
+std::size_t
+countWords(const char *s)
+{
+    std::size_t nWords = 0;
+    // Use length variable to avoid N^2 complexity when executing strlen(s) every iteration
+    std::size_t length = std::strlen(s);
+
+    for (std::size_t i = 0; i < length; i++)
+    {
+        // If we found a new word, increase counter and step through the word
+        if (std::isalnum(s[i]))
+        {
+            ++nWords;
+            // If we hit string end, '\0' is not alphanumerical
+            while (std::isalnum(s[i]))
+            {
+                // This might increment i to the string end, and then the outer
+                // loop will increment i one unit beyond that, but since
+                // we compare to the string length in the outer loop this is fine.
+                i++;
+            }
+        }
+    }
+    return nWords;
+}
+
+
+std::size_t
+countWords(const std::string &str)
+{
+    // Under out beautiful C++ interface hides an ugly c-string implementation :-)
+    return countWords(str.c_str());
+}
+
+bool endsWith(const char *str, const char *suffix)
 {
-    if (suffix == NULL || suffix[0] == '\0')
+    if (isNullOrEmpty(suffix))
     {
         return true;
     }
-    size_t length = std::strlen(suffix);
-    return (str.length() >= length
-            && str.compare(str.length() - length, length, suffix) == 0);
+    const size_t strLength    = std::strlen(str);
+    const size_t suffixLength = std::strlen(suffix);
+    return (strLength >= suffixLength
+            && std::strcmp(&str[strLength - suffixLength], suffix) == 0);
 }
 
 std::string stripSuffixIfPresent(const std::string &str, const char *suffix)
@@ -79,6 +116,21 @@ std::string stripSuffixIfPresent(const std::string &str, const char *suffix)
     return str;
 }
 
+std::string stripString(const std::string &str)
+{
+    std::string::const_iterator start = str.begin();
+    std::string::const_iterator end   = str.end();
+    while (start != end && std::isspace(*start))
+    {
+        ++start;
+    }
+    while (start != end && std::isspace(*(end - 1)))
+    {
+        --end;
+    }
+    return std::string(start, end);
+}
+
 std::string formatString(const char *fmt, ...)
 {
     va_list           ap;
@@ -112,29 +164,45 @@ std::string formatString(const char *fmt, ...)
     }
 }
 
-std::string concatenateStrings(const char *const *sarray, size_t count)
+std::vector<std::string> splitString(const std::string &str)
 {
-    std::string result;
-
-    for (size_t i = 0; i < count && sarray[i] != NULL; ++i)
+    std::vector<std::string>          result;
+    std::string::const_iterator       currPos = str.begin();
+    const std::string::const_iterator end     = str.end();
+    while (currPos != end)
     {
-        if (sarray[i][0] != '\0')
+        while (currPos != end && std::isspace(*currPos))
         {
-            result.append(sarray[i]);
-            char lastchar = sarray[i][std::strlen(sarray[i])-1];
-            if (!std::isspace(lastchar))
-            {
-                result.append(" ");
-            }
+            ++currPos;
+        }
+        const std::string::const_iterator startPos = currPos;
+        while (currPos != end && !std::isspace(*currPos))
+        {
+            ++currPos;
+        }
+        if (startPos != end)
+        {
+            result.push_back(std::string(startPos, currPos));
         }
     }
-    result.resize(result.find_last_not_of(" \n\r\t") + 1);
     return result;
 }
 
 namespace
 {
 
+/*! \brief
+ * Helper function to identify word boundaries for replaceAllWords().
+ *
+ * \returns  `true` if the character is considered part of a word.
+ *
+ * \ingroup module_utility
+ */
+bool isWordChar(char c)
+{
+    return std::isalnum(c) || c == '-' || c == '_';
+}
+
 /*! \brief
  * Common implementation for string replacement functions.
  *
@@ -162,8 +230,8 @@ replaceInternal(const std::string &input, const char *from, const char *to,
         size_t matchEnd = matchPos + matchLength;
         if (bWholeWords)
         {
-            if (!((matchPos == 0 || !std::isalnum(input[matchPos-1]))
-                  && (matchEnd == input.length() || !std::isalnum(input[matchEnd]))))
+            if (!((matchPos == 0 || !isWordChar(input[matchPos-1]))
+                  && (matchEnd == input.length() || !isWordChar(input[matchEnd]))))
             {
                 matchPos = input.find(from, matchPos + 1);
                 continue;
@@ -187,12 +255,26 @@ replaceAll(const std::string &input, const char *from, const char *to)
     return replaceInternal(input, from, to, false);
 }
 
+std::string
+replaceAll(const std::string &input, const std::string &from,
+           const std::string &to)
+{
+    return replaceInternal(input, from.c_str(), to.c_str(), false);
+}
+
 std::string
 replaceAllWords(const std::string &input, const char *from, const char *to)
 {
     return replaceInternal(input, from, to, true);
 }
 
+std::string
+replaceAllWords(const std::string &input, const std::string &from,
+                const std::string &to)
+{
+    return replaceInternal(input, from.c_str(), to.c_str(), true);
+}
+
 
 /********************************************************************
  * TextLineWrapperSettings
@@ -200,7 +282,7 @@ replaceAllWords(const std::string &input, const char *from, const char *to)
 
 TextLineWrapperSettings::TextLineWrapperSettings()
     : maxLength_(0), indent_(0), firstLineIndent_(-1),
-      bStripLeadingWhitespace_(true), continuationChar_('\0')
+      bKeepFinalSpaces_(false), continuationChar_('\0')
 {
 }
 
@@ -209,13 +291,19 @@ TextLineWrapperSettings::TextLineWrapperSettings()
  * TextLineWrapper
  */
 
+bool TextLineWrapper::isTrivial() const
+{
+    return settings_.lineLength() == 0 && settings_.indent() == 0
+           && settings_.firstLineIndent_ <= 0;
+}
+
 size_t
 TextLineWrapper::findNextLine(const char *input, size_t lineStart) const
 {
     size_t inputLength = std::strlen(input);
     bool   bFirstLine  = (lineStart == 0 || input[lineStart - 1] == '\n');
     // Ignore leading whitespace if necessary.
-    if (!bFirstLine || settings_.bStripLeadingWhitespace_)
+    if (!bFirstLine)
     {
         lineStart += std::strspn(input + lineStart, " ");
         if (lineStart >= inputLength)
@@ -260,7 +348,7 @@ TextLineWrapper::formatLine(const std::string &input,
     size_t inputLength = input.length();
     bool   bFirstLine  = (lineStart == 0 || input[lineStart - 1] == '\n');
     // Strip leading whitespace if necessary.
-    if (!bFirstLine || settings_.bStripLeadingWhitespace_)
+    if (!bFirstLine)
     {
         lineStart = input.find_first_not_of(' ', lineStart);
         if (lineStart >= inputLength)
@@ -271,12 +359,19 @@ TextLineWrapper::formatLine(const std::string &input,
     int  indent        = (bFirstLine ? settings_.firstLineIndent() : settings_.indent());
     bool bContinuation = (lineEnd < inputLength && input[lineEnd - 1] != '\n');
     // Strip trailing whitespace.
-    while (lineEnd > lineStart && std::isspace(input[lineEnd - 1]))
+    if (!settings_.bKeepFinalSpaces_ || lineEnd < inputLength || input[inputLength - 1] == '\n')
     {
-        --lineEnd;
+        while (lineEnd > lineStart && std::isspace(input[lineEnd - 1]))
+        {
+            --lineEnd;
+        }
     }
 
-    size_t      lineLength = lineEnd - lineStart;
+    const size_t lineLength = lineEnd - lineStart;
+    if (lineLength == 0)
+    {
+        return std::string();
+    }
     std::string result(indent, ' ');
     result.append(input, lineStart, lineLength);
     if (bContinuation && settings_.continuationChar_ != '\0')