Separated string formatting used only for help.
authorTeemu Murtola <teemu.murtola@gmail.com>
Sat, 12 May 2012 03:17:30 +0000 (06:17 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Tue, 29 May 2012 13:56:41 +0000 (16:56 +0300)
Moved string formatting routines used only in help printing to
src/gromacs/onlinehelp/ from src/gromacs/utility/format.*.
Also moved one function from cmdlinehelpwriter.cpp to the new file.
Subsequent commits will add more content to the new directory and will
use the same string formatting routines.

Added NOMINMAX define for Windows to avoid min/max macros.

Related to #666.

Change-Id: I9b52012df2fc718bc3d68c697140661a241a1467

14 files changed:
CMakeLists.txt
src/gromacs/CMakeLists.txt
src/gromacs/commandline/cmdlinehelpwriter.cpp
src/gromacs/onlinehelp/CMakeLists.txt [new file with mode: 0644]
src/gromacs/onlinehelp/helpformat.cpp [new file with mode: 0644]
src/gromacs/onlinehelp/helpformat.h [new file with mode: 0644]
src/gromacs/onlinehelp/tests/CMakeLists.txt [new file with mode: 0644]
src/gromacs/onlinehelp/tests/helpformat.cpp [new file with mode: 0644]
src/gromacs/onlinehelp/tests/refdata/TextTableFormatterTest_HandlesBasicCase.xml [moved from src/gromacs/utility/tests/refdata/TextTableFormatterTest_HandlesBasicCase.xml with 100% similarity]
src/gromacs/onlinehelp/tests/refdata/TextTableFormatterTest_HandlesEmptyColumns.xml [moved from src/gromacs/utility/tests/refdata/TextTableFormatterTest_HandlesEmptyColumns.xml with 100% similarity]
src/gromacs/onlinehelp/tests/refdata/TextTableFormatterTest_HandlesOverflowingLines.xml [moved from src/gromacs/utility/tests/refdata/TextTableFormatterTest_HandlesOverflowingLines.xml with 100% similarity]
src/gromacs/utility/format.cpp
src/gromacs/utility/format.h
src/gromacs/utility/tests/format.cpp

index bb6dad6122ac03dfddfd2ebcc4fe70e98f4837c3..1f2f4abdc4a7f19cd1c7e3a980a90f35d6c73a3b 100644 (file)
@@ -80,6 +80,9 @@ IF( WIN32 AND NOT CYGWIN)
   option(GMX_PREFER_STATIC_LIBS "When finding libraries prefer static system libraries (MT instead of MD)!" ON)
   mark_as_advanced(GMX_PREFER_STATIC_LIBS)
   SET(SHARED_LIBS_DEFAULT OFF)  #is currently not working on Windows
+  # This makes windows.h not declare min/max as macros that would break
+  # C++ code using std::min/std::max.
+  add_definitions(-DNOMINMAX)
 
   IF (GMX_PREFER_STATIC_LIBS)
     #Only setting Debug and Release flags. Others configurations current not used.
index 76eb5eafd4e0b3fe0339730587e5fa104b1b8464..9951d6d083edacaec8b6ff51e0542d71b83bd2b4 100644 (file)
@@ -7,6 +7,7 @@ add_subdirectory(gmxpreprocess)
 add_subdirectory(analysisdata)
 add_subdirectory(commandline)
 add_subdirectory(linearalgebra)
+add_subdirectory(onlinehelp)
 add_subdirectory(options)
 add_subdirectory(selection)
 add_subdirectory(trajectoryanalysis)
index ba51a84cc2e7637eb1a1eed5237fb517bd755ed3..9ca096f42767c45e1975162e29145fa72041c6b3 100644 (file)
@@ -39,9 +39,7 @@
 
 #include <string>
 
-#include "gromacs/legacyheaders/smalloc.h"
-#include "gromacs/legacyheaders/wman.h"
-
+#include "gromacs/onlinehelp/helpformat.h"
 #include "gromacs/options/basicoptioninfo.h"
 #include "gromacs/options/filenameoptioninfo.h"
 #include "gromacs/options/options.h"
@@ -60,22 +58,6 @@ namespace gmx
 namespace
 {
 
-std::string substituteMarkup(const std::string &text)
-{
-    char *resultStr = check_tty(text.c_str());
-    try
-    {
-        std::string result(resultStr);
-        sfree(resultStr);
-        return result;
-    }
-    catch (...)
-    {
-        sfree(resultStr);
-        throw;
-    }
-}
-
 /********************************************************************
  * DescriptionWriter
  */
@@ -108,10 +90,7 @@ void DescriptionWriter::visitSubSection(const Options &section)
             file_.writeLine(title);
             file_.writeLine();
         }
-        TextLineWrapper wrapper;
-        wrapper.setLineLength(78);
-        std::string description(substituteMarkup(section.description()));
-        file_.writeLine(wrapper.wrapToString(description));
+        writeHelpTextForConsole(&file_, section.description());
         file_.writeLine();
     }
     OptionsIterator(section).acceptSubSections(this);
@@ -218,7 +197,7 @@ void FileParameterWriter::visitOptionType(const FileNameOptionInfo &option)
     }
     bool bLongType = (type.length() > 12U);
     formatter_.addColumnLine(2, type);
-    formatter_.addColumnLine(3, substituteMarkup(option.description()));
+    formatter_.addColumnLine(3, substituteMarkupForConsole(option.description()));
 
     // Compute layout.
     if (name.length() > 6U || firstShortValue > 0)
@@ -325,7 +304,7 @@ void ParameterWriter::visitOption(const OptionInfo &option)
         values.append(option.formatValue(i));
     }
     formatter_.addColumnLine(2, values);
-    std::string description(substituteMarkup(option.description()));
+    std::string description(substituteMarkupForConsole(option.description()));
     const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
     if (doubleOption != NULL && doubleOption->isTime())
     {
@@ -392,7 +371,7 @@ void SelectionParameterWriter::visitOption(const OptionInfo &option)
     formatter_.clear();
     std::string name(formatString("-%s", option.name().c_str()));
     formatter_.addColumnLine(0, name);
-    formatter_.addColumnLine(1, substituteMarkup(option.description()));
+    formatter_.addColumnLine(1, substituteMarkupForConsole(option.description()));
     file_.writeString(formatter_.formatRow());
 
     // TODO: What to do with selection variables?
diff --git a/src/gromacs/onlinehelp/CMakeLists.txt b/src/gromacs/onlinehelp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..739d457
--- /dev/null
@@ -0,0 +1,6 @@
+file(GLOB ONLINEHELP_SOURCES *.cpp)
+set(LIBGROMACS_SOURCES ${LIBGROMACS_SOURCES} ${ONLINEHELP_SOURCES} PARENT_SCOPE)
+
+if (BUILD_TESTING)
+    add_subdirectory(tests)
+endif (BUILD_TESTING)
diff --git a/src/gromacs/onlinehelp/helpformat.cpp b/src/gromacs/onlinehelp/helpformat.cpp
new file mode 100644 (file)
index 0000000..0779dc9
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it 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.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in helpformat.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_onlinehelp
+ */
+#include "helpformat.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "gromacs/legacyheaders/smalloc.h"
+#include "gromacs/legacyheaders/wman.h"
+
+#include "gromacs/utility/file.h"
+#include "gromacs/utility/format.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+std::string substituteMarkupForConsole(const std::string &text)
+{
+    char *resultStr = check_tty(text.c_str());
+    try
+    {
+        std::string result(resultStr);
+        sfree(resultStr);
+        return result;
+    }
+    catch (...)
+    {
+        sfree(resultStr);
+        throw;
+    }
+}
+
+void writeHelpTextForConsole(File *file, const std::string &text)
+{
+    TextLineWrapper wrapper;
+    wrapper.setLineLength(78);
+    file->writeLine(wrapper.wrapToString(substituteMarkupForConsole(text)));
+}
+//! \endcond
+
+/********************************************************************
+ * TextTableFormatter::Impl
+ */
+
+/*! \internal \brief
+ * Private implementation class for TextTableFormatter.
+ *
+ * \ingroup module_onlinehelp
+ */
+class TextTableFormatter::Impl
+{
+    public:
+        struct ColumnData
+        {
+            ColumnData(const char *title, int width, bool bWrap)
+                : title_(title != NULL ? title : ""),
+                  width_(width), bWrap_(bWrap), firstLine_(0)
+            {
+                GMX_ASSERT(width >= 0, "Negative width not possible");
+                GMX_ASSERT(title_.length() <= static_cast<size_t>(width),
+                           "Title too long for column width");
+            }
+
+            //! Returns the title of the column.
+            const std::string &title() const { return title_; }
+            //! Returns the width of the column.
+            int width() const { return width_; }
+            /*! \brief
+             * Returns the first line offset for the current row.
+             *
+             * Note that the return value may be outside the printed lines if
+             * there is no text.
+             */
+            int firstLine() const { return firstLine_; }
+            /*! \brief
+             * Returns the index of the last line with text for the current row.
+             *
+             * If there is no text, returns -1.
+             */
+            int lastLine() const
+            {
+                if (lines_.empty())
+                {
+                    return -1;
+                }
+                return firstLine_ + static_cast<int>(lines_.size()) - 1;
+            }
+            /*! \brief
+             * Returns the text for a line.
+             *
+             * \param[in] line  Zero-based line index.
+             * \returns   Text for line \p line, or empty string if \p line has
+             *     no text for this column.
+             */
+            std::string textForLine(int line) const
+            {
+                // The second conditional matches if there are no lines
+                if (line < firstLine() || line > lastLine())
+                {
+                    return std::string();
+                }
+                return lines_[line - firstLine()];
+            }
+
+            //! Statit data: title of the column.
+            std::string                 title_;
+            //! Static data: width of the column.
+            int                         width_;
+            //! Static data: whether to automatically wrap input text.
+            bool                        bWrap_;
+            //! First line offset for the current row.
+            int                         firstLine_;
+            //! Text lines for the current row.
+            std::vector<std::string>    lines_;
+        };
+
+        //! Container type for column data.
+        typedef std::vector<ColumnData> ColumnList;
+
+        //! Initializes data for an empty formatter.
+        Impl();
+
+        /*! \brief
+         * Convenience method for checked access to data for a column.
+         *
+         * \param[in] index  Zero-based column index.
+         * \returns   \c columns_[index]
+         */
+        ColumnData &columnData(int index)
+        {
+            GMX_ASSERT(index >= 0 && index < static_cast<int>(columns_.size()),
+                       "Invalid column index");
+            return columns_[index];
+        }
+        //! \copydoc columnData()
+        const ColumnData &columnData(int index) const
+        {
+            return const_cast<Impl *>(this)->columnData(index);
+        }
+
+        //! Container for column data.
+        ColumnList              columns_;
+        //! If true, no output has yet been produced.
+        bool                    bFirstRow_;
+};
+
+TextTableFormatter::Impl::Impl()
+    : bFirstRow_(true)
+{
+}
+
+/********************************************************************
+ * TextTableFormatter
+ */
+
+TextTableFormatter::TextTableFormatter()
+    : impl_(new Impl)
+{
+}
+
+TextTableFormatter::~TextTableFormatter()
+{
+}
+
+void TextTableFormatter::addColumn(const char *title, int width, bool bWrap)
+{
+    impl_->columns_.push_back(Impl::ColumnData(title, width, bWrap));
+}
+
+bool TextTableFormatter::didOutput() const
+{
+    return !impl_->bFirstRow_;
+}
+
+void TextTableFormatter::clear()
+{
+    Impl::ColumnList::iterator i;
+    for (i = impl_->columns_.begin(); i != impl_->columns_.end(); ++i)
+    {
+        i->firstLine_ = 0;
+        i->lines_.clear();
+    }
+}
+
+void TextTableFormatter::addColumnLine(int index, const std::string &text)
+{
+    Impl::ColumnData &column = impl_->columnData(index);
+    TextLineWrapper wrapper;
+    if (column.bWrap_)
+    {
+        wrapper.setLineLength(column.width());
+    }
+    std::vector<std::string> lines(wrapper.wrapToVector(text));
+    column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
+}
+
+void TextTableFormatter::setColumnFirstLineOffset(int index, int firstLine)
+{
+    GMX_ASSERT(firstLine >= 0, "Invalid first line");
+    Impl::ColumnData &column = impl_->columnData(index);
+    column.firstLine_ = firstLine;
+}
+
+int TextTableFormatter::lastColumnLine(int index) const
+{
+    return impl_->columnData(index).lastLine();
+}
+
+std::string TextTableFormatter::formatRow()
+{
+    std::string result;
+    Impl::ColumnList::const_iterator column;
+    // Print a header if this is the first line.
+    if (impl_->bFirstRow_)
+    {
+        size_t totalWidth = 0;
+        for (column  = impl_->columns_.begin();
+             column != impl_->columns_.end();
+             ++column)
+        {
+            std::string title(column->title());
+            if (column != impl_->columns_.end() - 1)
+            {
+                title.resize(column->width() + 1, ' ');
+                totalWidth += title.length();
+            }
+            else
+            {
+                totalWidth += std::min(column->width(),
+                                       static_cast<int>(title.length() + 13));
+            }
+            result.append(title);
+        }
+        result.append("\n");
+        result.append(totalWidth, '-');
+        result.append("\n");
+    }
+
+    // Compute the last applicable line.
+    int lastLine = -1;
+    for (column  = impl_->columns_.begin();
+         column != impl_->columns_.end();
+         ++column)
+    {
+        lastLine = std::max(lastLine, column->lastLine());
+    }
+
+    // Format the actual row data.
+    for (int line = 0; line <= lastLine; ++line)
+    {
+        std::string lineResult;
+        size_t currentWidth = 0;
+        for (column  = impl_->columns_.begin();
+             column != impl_->columns_.end();
+             ++column)
+        {
+            std::string value(column->textForLine(line));
+            if (column != impl_->columns_.begin())
+            {
+                ++currentWidth;
+                if (!value.empty())
+                {
+                    lineResult.append(" ");
+                    if (lineResult.length() < currentWidth)
+                    {
+                        lineResult.resize(currentWidth, ' ');
+                    }
+                }
+            }
+            // TODO: Rewrap the text if wrapping is on and the previous columns
+            // overflow.
+            lineResult.append(value);
+            currentWidth += column->width();
+        }
+        result.append(lineResult);
+        result.append("\n");
+    }
+
+    impl_->bFirstRow_ = false;
+    clear();
+    return result;
+}
+
+} // namespace gmx
diff --git a/src/gromacs/onlinehelp/helpformat.h b/src/gromacs/onlinehelp/helpformat.h
new file mode 100644 (file)
index 0000000..35a9d61
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it 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.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares common string formatting routines for online help.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_ONLINEHELP_HELPFORMAT_H
+#define GMX_ONLINEHELP_HELPFORMAT_H
+
+#include <string>
+
+#include "../utility/common.h"
+
+namespace gmx
+{
+
+class File;
+
+/*! \cond libapi */
+/*! \libinternal \brief
+ * Substitute markup used in help text for console output.
+ *
+ * \param[in] text  Text to substitute.
+ * \returns   \p text with markup substituted.
+ * \throws    std::bad_alloc if out of memory.
+ *
+ * \inlibraryapi
+ */
+std::string substituteMarkupForConsole(const std::string &text);
+/*! \libinternal \brief
+ * Format a help text block for console output.
+ *
+ * \param     file  File to write the formatted text to.
+ * \param[in] text  Text to format.
+ * \throws    std::bad_alloc if out of memory.
+ * \throws    FileIOError on any I/O error.
+ *
+ * Calls substituteMarkupForConsole(), and also wraps the lines to 78
+ * characters.
+ *
+ * \inlibraryapi
+ */
+void writeHelpTextForConsole(File *file, const std::string &text);
+//! \endcond
+
+/*! \libinternal \brief
+ * Formats rows of a table for text output.
+ *
+ * This utility class formats tabular data, mainly for console output.
+ * Each row in the table can take multiple lines, and automatic text wrapping
+ * is supported.  If text overflows the allocated width, the remaining columns
+ * on that line become shifted.  To avoid this, it is possible to start the
+ * output for different columns from different lines (it is the caller's
+ * responsibility to check that overflows are avoided or are acceptable).
+ *
+ * Column definitions are first set with addColumn().
+ * To format a fow, first call clear().  Then call addColumnLine() to add text
+ * to each column (can be called multiple times on a single column to add
+ * multiple lines), and possibly setColumnFirstLineOffset() to adjust the line
+ * from which the column output should start.  Finally, call formatRow() to
+ * obtain the formatted row.
+ *
+ * A header (column titles and a horizontal line) is printed before the first
+ * line.
+ *
+ * Typical usage:
+ * \code
+gmx::TextTableFormatter formatter;
+formatter.addColumn("Name", 10, false);
+formatter.addColumn("Type", 10, false);
+formatter.addColumn("Description", 50, true);
+
+formatter.clear();
+formatter.addColumnLine(0, "name");
+formatter.addColumnLine(1, "type");
+formatter.addColumnLine(2, "Description for name");
+printf("%s", formatter.formatRow().c_str());
+
+formatter.clear();
+formatter.addColumnLine(0, "averylongname");
+formatter.addColumnLine(1, "type");
+formatter.setColumnFirstLineOffset(1, 1);
+formatter.addColumnLine(2, "Description for name");
+printf("%s", formatter.formatRow().c_str());
+
+// format other rows by repeating the above code
+ * \endcode
+ *
+ * Methods in this class may throw std::bad_alloc if out of memory.
+ * Other exceptions are not thrown.
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class TextTableFormatter
+{
+    public:
+        //! Constructs an empty formatter.
+        TextTableFormatter();
+        ~TextTableFormatter();
+
+        /*! \brief
+         * Adds a column to the table.
+         *
+         * \param[in]  title  Title string for the column (used for header).
+         * \param[in]  width  Width of the column (must be > 0).
+         * \param[in]  bWrap  Whether text that exceeds \p width is
+         *      automatically wrapped.
+         *
+         * The length of \p title must not exceed \p width.
+         */
+        void addColumn(const char *title, int width, bool bWrap);
+
+        /*! \brief
+         * Whether formatRow() has been successfully called.
+         *
+         * This method can be used to determine after-the-fact whether anything
+         * was written in the table.
+         *
+         * Does not throw.
+         */
+        bool didOutput() const;
+
+        /*! \brief
+         * Removes all text from all columns and resets the line offsets.
+         *
+         * Removes all text added using addColumnLine() and resets line offsets
+         * set with setColumnFirstLineOffset() to zero.
+         * Should be called before starting to add data for a row.
+         *
+         * Does not throw.
+         */
+        void clear();
+        /*! \brief
+         * Adds text to be printed in a column.
+         *
+         * \param[in]  index     Zero-based column index.
+         * \param[in]  text      Text to add.
+         *
+         * Can be called multiple times.  Additional calls append \p text as
+         * additional lines.  Any calls with \p text empty have no effect.
+         * To add an empty line, use "\n" as \p text.
+         *
+         * If \p text contains newlines, the text is automatically splitted to
+         * multiple lines.  The same happens if automatic wrapping is on for
+         * the column and the text contains lines that are longer than what
+         * fits the column.
+         */
+        void addColumnLine(int index, const std::string &text);
+        /*! \brief
+         * Sets the first line to which text is printed for a column.
+         *
+         * \param[in]  index     Zero-based column index.
+         * \param[in]  firstLine Zero-based line index from which to start the
+         *      output.
+         *
+         * Can be called if there is no text for column \p index.
+         * Does not affect the output in this case.
+         *
+         * Does not throw.
+         */
+        void setColumnFirstLineOffset(int index, int firstLine);
+        /*! \brief
+         * Formats the lines for the current row.
+         *
+         * \returns  Current row formatted as a single string
+         *      (contains newlines).
+         *
+         * Formats the data as set after the previous clear()/formatRow() using
+         * addColumnLine() and setColumnFirstLineOffset().
+         *
+         * If this is the first line to be formatted, a header is also added to
+         * the beginning of the returned string.
+         *
+         * The return value always terminates with a newline.
+         *
+         * Calls clear() on successful return.
+         */
+        std::string formatRow();
+
+        /*! \brief
+         * Returns the last line on which column \p index has text.
+         *
+         * \param[in] index  Zero-based column index.
+         * \returns   Last line index (zero-based) on which \p index has text.
+         *
+         * The return value is the sum of the number of lines added with
+         * addColumnLine() (taking into account possible wrapping) and the line
+         * offset set with setColumnFirstLineOffset().
+         *
+         * Does not throw.
+         */
+        int lastColumnLine(int index) const;
+
+    private:
+        class Impl;
+
+        PrivateImplPointer<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/gromacs/onlinehelp/tests/CMakeLists.txt b/src/gromacs/onlinehelp/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5a8cfd9
--- /dev/null
@@ -0,0 +1,2 @@
+gmx_add_unit_test(OnlineHelpUnitTests onlinehelp-test
+                  helpformat.cpp)
diff --git a/src/gromacs/onlinehelp/tests/helpformat.cpp b/src/gromacs/onlinehelp/tests/helpformat.cpp
new file mode 100644 (file)
index 0000000..7cb6f4a
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it 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.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests for help string formatting functions and classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_onlinehelp
+ */
+#include <gtest/gtest.h>
+
+#include "gromacs/onlinehelp/helpformat.h"
+
+#include "testutils/stringtest.h"
+
+namespace
+{
+
+const char g_wrapText[] = "A quick brown fox jumps over the lazy dog";
+const char g_wrapText2[] = "A quick brown fox jumps\nover the lazy dog";
+
+/********************************************************************
+ * Tests for TextTableFormatter
+ */
+
+class TextTableFormatterTest : public gmx::test::StringTestBase
+{
+    public:
+        TextTableFormatterTest();
+
+        gmx::TextTableFormatter formatter_;
+};
+
+TextTableFormatterTest::TextTableFormatterTest()
+{
+    formatter_.addColumn("Col1", 4, false);
+    formatter_.addColumn("Col2", 4, false);
+    formatter_.addColumn("Col3Wrap", 14, true);
+    formatter_.addColumn("Col4Wrap", 14, true);
+}
+
+TEST_F(TextTableFormatterTest, HandlesBasicCase)
+{
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foo");
+    formatter_.addColumnLine(1, "bar");
+    formatter_.addColumnLine(2, g_wrapText);
+    formatter_.addColumnLine(3, g_wrapText2);
+    checkText(formatter_.formatRow(), "FormattedTable");
+}
+
+TEST_F(TextTableFormatterTest, HandlesOverflowingLines)
+{
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foobar");
+    formatter_.addColumnLine(1, "barfoo");
+    formatter_.addColumnLine(2, g_wrapText);
+    formatter_.addColumnLine(3, g_wrapText2);
+    checkText(formatter_.formatRow(), "FormattedTable");
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foobar");
+    formatter_.addColumnLine(1, "barfoo");
+    formatter_.setColumnFirstLineOffset(1, 1);
+    formatter_.addColumnLine(2, g_wrapText);
+    formatter_.addColumnLine(3, g_wrapText2);
+    checkText(formatter_.formatRow(), "FormattedRow2");
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foobar");
+    formatter_.addColumnLine(1, "barfoo");
+    formatter_.setColumnFirstLineOffset(1, 1);
+    formatter_.addColumnLine(2, g_wrapText);
+    formatter_.setColumnFirstLineOffset(2, 2);
+    formatter_.addColumnLine(3, g_wrapText2);
+    checkText(formatter_.formatRow(), "FormattedRow3");
+}
+
+TEST_F(TextTableFormatterTest, HandlesEmptyColumns)
+{
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foo");
+    formatter_.addColumnLine(1, "bar");
+    formatter_.addColumnLine(3, "Text");
+    checkText(formatter_.formatRow(), "FormattedTable");
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foo");
+    formatter_.addColumnLine(1, "bar");
+    formatter_.setColumnFirstLineOffset(2, 1);
+    formatter_.addColumnLine(3, "Text");
+    checkText(formatter_.formatRow(), "FormattedRow2");
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foo");
+    formatter_.addColumnLine(1, "bar");
+    formatter_.addColumnLine(2, "");
+    formatter_.setColumnFirstLineOffset(2, 1);
+    formatter_.addColumnLine(3, "Text");
+    checkText(formatter_.formatRow(), "FormattedRow3");
+}
+
+} // namespace
index 7702d8c5c8728b2ea156c892ce260c3d66fc6ea9..93fd3c36e258d3bb5aa762182fe4b8c201cfd0e1 100644 (file)
@@ -277,247 +277,4 @@ TextLineWrapper::wrapToVector(const std::string &input) const
     return result;
 }
 
-/********************************************************************
- * TextTableFormatter::Impl
- */
-
-/*! \internal \brief
- * Private implementation class for TextTableFormatter.
- *
- * \ingroup module_utility
- */
-class TextTableFormatter::Impl
-{
-    public:
-        struct ColumnData
-        {
-            ColumnData(const char *title, int width, bool bWrap)
-                : title_(title != NULL ? title : ""),
-                  width_(width), bWrap_(bWrap), firstLine_(0)
-            {
-                GMX_ASSERT(width >= 0, "Negative width not possible");
-                GMX_ASSERT(title_.length() <= static_cast<size_t>(width),
-                           "Title too long for column width");
-            }
-
-            //! Returns the title of the column.
-            const std::string &title() const { return title_; }
-            //! Returns the width of the column.
-            int width() const { return width_; }
-            /*! \brief
-             * Returns the first line offset for the current row.
-             *
-             * Note that the return value may be outside the printed lines if
-             * there is no text.
-             */
-            int firstLine() const { return firstLine_; }
-            /*! \brief
-             * Returns the index of the last line with text for the current row.
-             *
-             * If there is no text, returns -1.
-             */
-            int lastLine() const
-            {
-                if (lines_.empty())
-                {
-                    return -1;
-                }
-                return firstLine_ + static_cast<int>(lines_.size()) - 1;
-            }
-            /*! \brief
-             * Returns the text for a line.
-             *
-             * \param[in] line  Zero-based line index.
-             * \returns   Text for line \p line, or empty string if \p line has
-             *     no text for this column.
-             */
-            std::string textForLine(int line) const
-            {
-                // The second conditional matches if there are no lines
-                if (line < firstLine() || line > lastLine())
-                {
-                    return std::string();
-                }
-                return lines_[line - firstLine()];
-            }
-
-            //! Statit data: title of the column.
-            std::string                 title_;
-            //! Static data: width of the column.
-            int                         width_;
-            //! Static data: whether to automatically wrap input text.
-            bool                        bWrap_;
-            //! First line offset for the current row.
-            int                         firstLine_;
-            //! Text lines for the current row.
-            std::vector<std::string>    lines_;
-        };
-
-        //! Container type for column data.
-        typedef std::vector<ColumnData> ColumnList;
-
-        //! Initializes data for an empty formatter.
-        Impl();
-
-        /*! \brief
-         * Convenience method for checked access to data for a column.
-         *
-         * \param[in] index  Zero-based column index.
-         * \returns   \c columns_[index]
-         */
-        ColumnData &columnData(int index)
-        {
-            GMX_ASSERT(index >= 0 && index < static_cast<int>(columns_.size()),
-                       "Invalid column index");
-            return columns_[index];
-        }
-        //! \copydoc columnData()
-        const ColumnData &columnData(int index) const
-        {
-            return const_cast<Impl *>(this)->columnData(index);
-        }
-
-        //! Container for column data.
-        ColumnList              columns_;
-        //! If true, no output has yet been produced.
-        bool                    bFirstRow_;
-};
-
-TextTableFormatter::Impl::Impl()
-    : bFirstRow_(true)
-{
-}
-
-/********************************************************************
- * TextTableFormatter
- */
-
-TextTableFormatter::TextTableFormatter()
-    : impl_(new Impl)
-{
-}
-
-TextTableFormatter::~TextTableFormatter()
-{
-}
-
-void TextTableFormatter::addColumn(const char *title, int width, bool bWrap)
-{
-    impl_->columns_.push_back(Impl::ColumnData(title, width, bWrap));
-}
-
-bool TextTableFormatter::didOutput() const
-{
-    return !impl_->bFirstRow_;
-}
-
-void TextTableFormatter::clear()
-{
-    Impl::ColumnList::iterator i;
-    for (i = impl_->columns_.begin(); i != impl_->columns_.end(); ++i)
-    {
-        i->firstLine_ = 0;
-        i->lines_.clear();
-    }
-}
-
-void TextTableFormatter::addColumnLine(int index, const std::string &text)
-{
-    Impl::ColumnData &column = impl_->columnData(index);
-    TextLineWrapper wrapper;
-    if (column.bWrap_)
-    {
-        wrapper.setLineLength(column.width());
-    }
-    std::vector<std::string> lines(wrapper.wrapToVector(text));
-    column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
-}
-
-void TextTableFormatter::setColumnFirstLineOffset(int index, int firstLine)
-{
-    GMX_ASSERT(firstLine >= 0, "Invalid first line");
-    Impl::ColumnData &column = impl_->columnData(index);
-    column.firstLine_ = firstLine;
-}
-
-int TextTableFormatter::lastColumnLine(int index) const
-{
-    return impl_->columnData(index).lastLine();
-}
-
-std::string TextTableFormatter::formatRow()
-{
-    std::string result;
-    Impl::ColumnList::const_iterator column;
-    // Print a header if this is the first line.
-    if (impl_->bFirstRow_)
-    {
-        size_t totalWidth = 0;
-        for (column  = impl_->columns_.begin();
-             column != impl_->columns_.end();
-             ++column)
-        {
-            std::string title(column->title());
-            if (column != impl_->columns_.end() - 1)
-            {
-                title.resize(column->width() + 1, ' ');
-                totalWidth += title.length();
-            }
-            else
-            {
-                totalWidth += std::min(column->width(),
-                                       static_cast<int>(title.length() + 13));
-            }
-            result.append(title);
-        }
-        result.append("\n");
-        result.append(totalWidth, '-');
-        result.append("\n");
-    }
-
-    // Compute the last applicable line.
-    int lastLine = -1;
-    for (column  = impl_->columns_.begin();
-         column != impl_->columns_.end();
-         ++column)
-    {
-        lastLine = std::max(lastLine, column->lastLine());
-    }
-
-    // Format the actual row data.
-    for (int line = 0; line <= lastLine; ++line)
-    {
-        std::string lineResult;
-        size_t currentWidth = 0;
-        for (column  = impl_->columns_.begin();
-             column != impl_->columns_.end();
-             ++column)
-        {
-            std::string value(column->textForLine(line));
-            if (column != impl_->columns_.begin())
-            {
-                ++currentWidth;
-                if (!value.empty())
-                {
-                    lineResult.append(" ");
-                    if (lineResult.length() < currentWidth)
-                    {
-                        lineResult.resize(currentWidth, ' ');
-                    }
-                }
-            }
-            // TODO: Rewrap the text if wrapping is on and the previous columns
-            // overflow.
-            lineResult.append(value);
-            currentWidth += column->width();
-        }
-        result.append(lineResult);
-        result.append("\n");
-    }
-
-    impl_->bFirstRow_ = false;
-    clear();
-    return result;
-}
-
 } // namespace gmx
index 5036b88db67b8bb7691e6932dd6b465de0975f38..c0a194ed50a7dcedf751f03c9340cf2c7a26ca1d 100644 (file)
@@ -216,161 +216,6 @@ class TextLineWrapper
         PrivateImplPointer<Impl> impl_;
 };
 
-/*! \brief
- * Formats rows of a table for text output.
- *
- * This utility class formats tabular data, mainly for console output.
- * Each row in the table can take multiple lines, and automatic text wrapping
- * is supported.  If text overflows the allocated width, the remaining columns
- * on that line become shifted.  To avoid this, it is possible to start the
- * output for different columns from different lines (it is the caller's
- * responsibility to check that overflows are avoided or are acceptable).
- *
- * Column definitions are first set with addColumn().
- * To format a fow, first call clear().  Then call addColumnLine() to add text
- * to each column (can be called multiple times on a single column to add
- * multiple lines), and possibly setColumnFirstLineOffset() to adjust the line
- * from which the column output should start.  Finally, call formatRow() to
- * obtain the formatted row.
- *
- * A header (column titles and a horizontal line) is printed before the first
- * line.
- *
- * Typical usage:
- * \code
-gmx::TextTableFormatter formatter;
-formatter.addColumn("Name", 10, false);
-formatter.addColumn("Type", 10, false);
-formatter.addColumn("Description", 50, true);
-
-formatter.clear();
-formatter.addColumnLine(0, "name");
-formatter.addColumnLine(1, "type");
-formatter.addColumnLine(2, "Description for name");
-printf("%s", formatter.formatRow().c_str());
-
-formatter.clear();
-formatter.addColumnLine(0, "averylongname");
-formatter.addColumnLine(1, "type");
-formatter.setColumnFirstLineOffset(1, 1);
-formatter.addColumnLine(2, "Description for name");
-printf("%s", formatter.formatRow().c_str());
-
-// format other rows by repeating the above code
- * \endcode
- *
- * Methods in this class may throw std::bad_alloc if out of memory.
- * Other exceptions are not thrown.
- *
- * \inpublicapi
- * \ingroup module_utility
- */
-class TextTableFormatter
-{
-    public:
-        //! Constructs an empty formatter.
-        TextTableFormatter();
-        ~TextTableFormatter();
-
-        /*! \brief
-         * Adds a column to the table.
-         *
-         * \param[in]  title  Title string for the column (used for header).
-         * \param[in]  width  Width of the column (must be > 0).
-         * \param[in]  bWrap  Whether text that exceeds \p width is
-         *      automatically wrapped.
-         *
-         * The length of \p title must not exceed \p width.
-         */
-        void addColumn(const char *title, int width, bool bWrap);
-
-        /*! \brief
-         * Whether formatRow() has been successfully called.
-         *
-         * This method can be used to determine after-the-fact whether anything
-         * was written in the table.
-         *
-         * Does not throw.
-         */
-        bool didOutput() const;
-
-        /*! \brief
-         * Removes all text from all columns and resets the line offsets.
-         *
-         * Removes all text added using addColumnLine() and resets line offsets
-         * set with setColumnFirstLineOffset() to zero.
-         * Should be called before starting to add data for a row.
-         *
-         * Does not throw.
-         */
-        void clear();
-        /*! \brief
-         * Adds text to be printed in a column.
-         *
-         * \param[in]  index     Zero-based column index.
-         * \param[in]  text      Text to add.
-         *
-         * Can be called multiple times.  Additional calls append \p text as
-         * additional lines.  Any calls with \p text empty have no effect.
-         * To add an empty line, use "\n" as \p text.
-         *
-         * If \p text contains newlines, the text is automatically splitted to
-         * multiple lines.  The same happens if automatic wrapping is on for
-         * the column and the text contains lines that are longer than what
-         * fits the column.
-         */
-        void addColumnLine(int index, const std::string &text);
-        /*! \brief
-         * Sets the first line to which text is printed for a column.
-         *
-         * \param[in]  index     Zero-based column index.
-         * \param[in]  firstLine Zero-based line index from which to start the
-         *      output.
-         *
-         * Can be called if there is no text for column \p index.
-         * Does not affect the output in this case.
-         *
-         * Does not throw.
-         */
-        void setColumnFirstLineOffset(int index, int firstLine);
-        /*! \brief
-         * Formats the lines for the current row.
-         *
-         * \returns  Current row formatted as a single string
-         *      (contains newlines).
-         *
-         * Formats the data as set after the previous clear()/formatRow() using
-         * addColumnLine() and setColumnFirstLineOffset().
-         *
-         * If this is the first line to be formatted, a header is also added to
-         * the beginning of the returned string.
-         *
-         * The return value always terminates with a newline.
-         *
-         * Calls clear() on successful return.
-         */
-        std::string formatRow();
-
-        /*! \brief
-         * Returns the last line on which column \p index has text.
-         *
-         * \param[in] index  Zero-based column index.
-         * \returns   Last line index (zero-based) on which \p index has text.
-         *
-         * The return value is the sum of the number of lines added with
-         * addColumnLine() (taking into account possible wrapping) and the line
-         * offset set with setColumnFirstLineOffset().
-         *
-         * Does not throw.
-         */
-        int lastColumnLine(int index) const;
-
-    private:
-        class Impl;
-
-        PrivateImplPointer<Impl> impl_;
-};
-
 } // namespace gmx
 
 #endif
index 39ef01207359755c712824fbb3504dffb84e2b12..1fb49d0a9cc148c37a600e43bbc336514f83532e 100644 (file)
@@ -228,81 +228,4 @@ TEST_F(TextLineWrapperTest, WrapsCorrectlyWithExtraWhitespace)
     checkText(wrapper.wrapToString(g_wrapTextWhitespace), "WrappedAt14");
 }
 
-/********************************************************************
- * Tests for TextTableFormatter
- */
-
-class TextTableFormatterTest : public gmx::test::StringTestBase
-{
-    public:
-        TextTableFormatterTest();
-
-        gmx::TextTableFormatter formatter_;
-};
-
-TextTableFormatterTest::TextTableFormatterTest()
-{
-    formatter_.addColumn("Col1", 4, false);
-    formatter_.addColumn("Col2", 4, false);
-    formatter_.addColumn("Col3Wrap", 14, true);
-    formatter_.addColumn("Col4Wrap", 14, true);
-}
-
-TEST_F(TextTableFormatterTest, HandlesBasicCase)
-{
-    formatter_.clear();
-    formatter_.addColumnLine(0, "foo");
-    formatter_.addColumnLine(1, "bar");
-    formatter_.addColumnLine(2, g_wrapText);
-    formatter_.addColumnLine(3, g_wrapText2);
-    checkText(formatter_.formatRow(), "FormattedTable");
-}
-
-TEST_F(TextTableFormatterTest, HandlesOverflowingLines)
-{
-    formatter_.clear();
-    formatter_.addColumnLine(0, "foobar");
-    formatter_.addColumnLine(1, "barfoo");
-    formatter_.addColumnLine(2, g_wrapText);
-    formatter_.addColumnLine(3, g_wrapText2);
-    checkText(formatter_.formatRow(), "FormattedTable");
-    formatter_.clear();
-    formatter_.addColumnLine(0, "foobar");
-    formatter_.addColumnLine(1, "barfoo");
-    formatter_.setColumnFirstLineOffset(1, 1);
-    formatter_.addColumnLine(2, g_wrapText);
-    formatter_.addColumnLine(3, g_wrapText2);
-    checkText(formatter_.formatRow(), "FormattedRow2");
-    formatter_.clear();
-    formatter_.addColumnLine(0, "foobar");
-    formatter_.addColumnLine(1, "barfoo");
-    formatter_.setColumnFirstLineOffset(1, 1);
-    formatter_.addColumnLine(2, g_wrapText);
-    formatter_.setColumnFirstLineOffset(2, 2);
-    formatter_.addColumnLine(3, g_wrapText2);
-    checkText(formatter_.formatRow(), "FormattedRow3");
-}
-
-TEST_F(TextTableFormatterTest, HandlesEmptyColumns)
-{
-    formatter_.clear();
-    formatter_.addColumnLine(0, "foo");
-    formatter_.addColumnLine(1, "bar");
-    formatter_.addColumnLine(3, "Text");
-    checkText(formatter_.formatRow(), "FormattedTable");
-    formatter_.clear();
-    formatter_.addColumnLine(0, "foo");
-    formatter_.addColumnLine(1, "bar");
-    formatter_.setColumnFirstLineOffset(2, 1);
-    formatter_.addColumnLine(3, "Text");
-    checkText(formatter_.formatRow(), "FormattedRow2");
-    formatter_.clear();
-    formatter_.addColumnLine(0, "foo");
-    formatter_.addColumnLine(1, "bar");
-    formatter_.addColumnLine(2, "");
-    formatter_.setColumnFirstLineOffset(2, 1);
-    formatter_.addColumnLine(3, "Text");
-    checkText(formatter_.formatRow(), "FormattedRow3");
-}
-
 } // namespace