e3d404a186f00600e427edc24ecb87eeaefdfb38
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinehelpwriter.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements gmx::CommandLineHelpWriter.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_commandline
41  */
42 #include "cmdlinehelpwriter.h"
43
44 #include <string>
45
46 #include <boost/scoped_ptr.hpp>
47
48 #include "gromacs/commandline/cmdlinehelpcontext.h"
49 #include "gromacs/onlinehelp/helpformat.h"
50 #include "gromacs/onlinehelp/helpwritercontext.h"
51 #include "gromacs/options/basicoptions.h"
52 #include "gromacs/options/filenameoption.h"
53 #include "gromacs/options/options.h"
54 #include "gromacs/options/optionsvisitor.h"
55 #include "gromacs/options/timeunitmanager.h"
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/file.h"
58 #include "gromacs/utility/stringutil.h"
59
60 namespace gmx
61 {
62
63 namespace
64 {
65
66 //! \addtogroup module_commandline
67 //! \{
68
69 /********************************************************************
70  * OptionsFormatterInterface
71  */
72
73 /*! \brief
74  * Interface for output format specific formatting of options.
75  *
76  * \see OptionsFilter
77  */
78 class OptionsFormatterInterface
79 {
80     public:
81         virtual ~OptionsFormatterInterface() {}
82
83         //! Formats the description text block for a section.
84         virtual void formatDescription(const HelpWriterContext &context,
85                                        const Options           &section) = 0;
86         //! Formats a single file option.
87         virtual void formatFileOption(const HelpWriterContext  &context,
88                                       const FileNameOptionInfo &option) = 0;
89         //! Formats a single non-file, non-selection option.
90         virtual void formatOption(const HelpWriterContext &context,
91                                   const OptionInfo        &option) = 0;
92 };
93
94 /********************************************************************
95  * OptionsFilter
96  */
97
98 /*! \brief
99  * Output format independent processing of options.
100  *
101  * Together with code in CommandLineHelpWriter::writeHelp(), this class
102  * implements the common logic for writing out the help.
103  * An object implementing the OptionsFormatterInterface must be provided to the
104  * constructor, and does the actual formatting that is specific to the output
105  * format.
106  */
107 class OptionsFilter : public OptionsVisitor
108 {
109     public:
110         //! Specifies the type of output that formatSelected() produces.
111         enum FilterType
112         {
113             eSelectDescriptions,
114             eSelectFileOptions,
115             eSelectOtherOptions
116         };
117
118         /*! \brief
119          * Creates a new filtering object.
120          *
121          * \param[in] context   Help context to use to write the help.
122          * \param[in] formatter Output format specific formatter.
123          *
124          * Does not throw.
125          */
126         OptionsFilter(const HelpWriterContext   &context,
127                       OptionsFormatterInterface *formatter)
128             : context_(context), formatter_(*formatter),
129               bShowHidden_(false), filterType_(eSelectOtherOptions),
130               title_(NULL), bDidOutput_(false)
131         {
132         }
133
134         //! Sets whether hidden options will be shown.
135         void setShowHidden(bool bShowHidden)
136         {
137             bShowHidden_ = bShowHidden;
138         }
139
140         //! Formats selected options using the formatter.
141         void formatSelected(FilterType type, const Options &options,
142                             const char *title);
143
144         virtual void visitSubSection(const Options &section);
145         virtual void visitOption(const OptionInfo &option);
146
147     private:
148         void writeTitleIfSet()
149         {
150             if (title_ != NULL)
151             {
152                 context_.writeTitle(title_);
153             }
154         }
155
156         const HelpWriterContext        &context_;
157         OptionsFormatterInterface      &formatter_;
158         bool                            bShowHidden_;
159         FilterType                      filterType_;
160         const char                     *title_;
161         bool                            bDidOutput_;
162
163         GMX_DISALLOW_COPY_AND_ASSIGN(OptionsFilter);
164 };
165
166 void OptionsFilter::formatSelected(FilterType type, const Options &options,
167                                    const char *title)
168 {
169     filterType_ = type;
170     title_      = title;
171     bDidOutput_ = false;
172     visitSubSection(options);
173     if (bDidOutput_)
174     {
175         if (type != eSelectDescriptions)
176         {
177             context_.writeOptionListEnd();
178         }
179         context_.outputFile().writeLine();
180     }
181 }
182
183 void OptionsFilter::visitSubSection(const Options &section)
184 {
185     if (filterType_ == eSelectDescriptions)
186     {
187         if (!section.description().empty())
188         {
189             if (bDidOutput_)
190             {
191                 context_.outputFile().writeLine();
192             }
193             else
194             {
195                 writeTitleIfSet();
196             }
197             formatter_.formatDescription(context_, section);
198             bDidOutput_ = true;
199         }
200     }
201
202     OptionsIterator iterator(section);
203     iterator.acceptSubSections(this);
204     iterator.acceptOptions(this);
205 }
206
207 void OptionsFilter::visitOption(const OptionInfo &option)
208 {
209     if (!bShowHidden_ && option.isHidden())
210     {
211         return;
212     }
213     if (option.isType<FileNameOptionInfo>())
214     {
215         if (filterType_ == eSelectFileOptions)
216         {
217             if (!bDidOutput_)
218             {
219                 writeTitleIfSet();
220                 context_.writeOptionListStart();
221             }
222             formatter_.formatFileOption(context_,
223                                         *option.toType<FileNameOptionInfo>());
224             bDidOutput_ = true;
225         }
226         return;
227     }
228     if (filterType_ == eSelectOtherOptions)
229     {
230         if (!bDidOutput_)
231         {
232             writeTitleIfSet();
233             context_.writeOptionListStart();
234         }
235         formatter_.formatOption(context_, option);
236         bDidOutput_ = true;
237         return;
238     }
239 }
240
241 /********************************************************************
242  * CommonFormatterData
243  */
244
245 class CommonFormatterData
246 {
247     public:
248         explicit CommonFormatterData(const char *timeUnit)
249             : timeUnit(timeUnit)
250         {
251         }
252
253         const char             *timeUnit;
254 };
255
256 /********************************************************************
257  * Helper functions
258  */
259
260 //! Formats the default option value as a string.
261 std::string
262 defaultOptionValue(const OptionInfo &option)
263 {
264     if (option.valueCount() == 0
265         || (option.valueCount() == 1 && option.formatValue(0).empty()))
266     {
267         return option.formatDefaultValueIfSet();
268     }
269     else
270     {
271         std::string result;
272         for (int i = 0; i < option.valueCount(); ++i)
273         {
274             if (i != 0)
275             {
276                 result.append(" ");
277             }
278             result.append(option.formatValue(i));
279         }
280         return result;
281     }
282 }
283
284 //! Formats the flags for a file option as a string.
285 std::string
286 fileOptionFlagsAsString(const FileNameOptionInfo &option, bool bAbbrev)
287 {
288     std::string type;
289     if (option.isInputOutputFile())
290     {
291         type = bAbbrev ? "In/Out" : "Input/Output";
292     }
293     else if (option.isInputFile())
294     {
295         type = "Input";
296     }
297     else if (option.isOutputFile())
298     {
299         type = "Output";
300     }
301     if (!option.isRequired())
302     {
303         type += bAbbrev ? ", Opt." : ", Optional";
304     }
305     if (option.isLibraryFile())
306     {
307         type += bAbbrev ? ", Lib." : ", Library";
308     }
309     return type;
310 }
311
312 //! Formats the description for an option as a string.
313 std::string
314 descriptionWithOptionDetails(const CommonFormatterData &common,
315                              const OptionInfo          &option)
316 {
317     std::string             description(option.formatDescription());
318
319     const FloatOptionInfo  *floatOption  = option.toType<FloatOptionInfo>();
320     const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
321     if ((floatOption != NULL && floatOption->isTime())
322         || (doubleOption != NULL && doubleOption->isTime()))
323     {
324         // TODO: It could be nicer to have this in basicoptions.cpp.
325         description = replaceAll(description, "%t", common.timeUnit);
326     }
327
328     return description;
329 }
330
331 /********************************************************************
332  * OptionsExportFormatter
333  */
334
335 /*! \brief
336  * Formatter implementation for help export.
337  */
338 class OptionsExportFormatter : public OptionsFormatterInterface
339 {
340     public:
341         //! Creates a helper object for formatting options.
342         OptionsExportFormatter(const CommonFormatterData &common, bool bConsole);
343
344         virtual void formatDescription(const HelpWriterContext &context,
345                                        const Options           &section);
346         virtual void formatFileOption(const HelpWriterContext  &context,
347                                       const FileNameOptionInfo &option);
348         virtual void formatOption(const HelpWriterContext &context,
349                                   const OptionInfo        &option);
350
351     private:
352         const CommonFormatterData             &common_;
353         boost::scoped_ptr<TextTableFormatter>  consoleFormatter_;
354
355         GMX_DISALLOW_COPY_AND_ASSIGN(OptionsExportFormatter);
356 };
357
358 OptionsExportFormatter::OptionsExportFormatter(
359         const CommonFormatterData &common, bool bConsole)
360     : common_(common)
361 {
362     if (bConsole)
363     {
364         consoleFormatter_.reset(new TextTableFormatter());
365         consoleFormatter_->setFirstColumnIndent(1);
366         consoleFormatter_->setFoldLastColumnToNextLine(4);
367         consoleFormatter_->addColumn(NULL, 6, false);
368         consoleFormatter_->addColumn(NULL, 8, false);
369         consoleFormatter_->addColumn(NULL, 10, false);
370         consoleFormatter_->addColumn(NULL, 50, true);
371     }
372 }
373
374 void OptionsExportFormatter::formatDescription(
375         const HelpWriterContext &context, const Options &section)
376 {
377     // TODO: Print title for the section?
378     context.writeTextBlock(section.description());
379 }
380
381 void OptionsExportFormatter::formatFileOption(
382         const HelpWriterContext &context, const FileNameOptionInfo &option)
383 {
384     const bool  bAbbrev = (context.outputFormat() == eHelpOutputFormat_Console);
385     std::string value("<" + option.type() + ">");
386     if (option.maxValueCount() != 1)
387     {
388         value += " [...]";
389     }
390     if (option.minValueCount() == 0)
391     {
392         value = "[" + value + "]";
393     }
394     std::string defaultValue(defaultOptionValue(option));
395     std::string info = "(" + fileOptionFlagsAsString(option, bAbbrev) + ")";
396     if (!defaultValue.empty())
397     {
398         info = "(" + defaultValue + ") " + info;
399     }
400     std::string description(option.formatDescription());
401     if (context.outputFormat() == eHelpOutputFormat_Console)
402     {
403         consoleFormatter_->clear();
404         consoleFormatter_->addColumnLine(0, "-" + option.name());
405         consoleFormatter_->addColumnLine(1, value);
406         consoleFormatter_->addColumnLine(2, info);
407         consoleFormatter_->addColumnHelpTextBlock(3, context, description);
408         context.outputFile().writeString(consoleFormatter_->formatRow());
409     }
410     else
411     {
412         value += " " + info;
413         context.writeOptionItem("-" + option.name(), value, description);
414     }
415 }
416
417 void OptionsExportFormatter::formatOption(
418         const HelpWriterContext &context, const OptionInfo &option)
419 {
420     std::string name(option.name());
421     std::string value("<" + option.type() + ">");
422     std::string defaultValue(defaultOptionValue(option));
423     std::string description(descriptionWithOptionDetails(common_, option));
424     if (option.isType<BooleanOptionInfo>())
425     {
426         name  = "[no]" + name;
427         // Old command-line parser doesn't accept any values for these.
428         // value = "[" + value + "]";
429         value.clear();
430     }
431     if (context.outputFormat() == eHelpOutputFormat_Console)
432     {
433         consoleFormatter_->clear();
434         consoleFormatter_->addColumnLine(0, "-" + name);
435         consoleFormatter_->addColumnLine(1, value);
436         if (!defaultValue.empty())
437         {
438             consoleFormatter_->addColumnLine(2, "(" + defaultValue + ")");
439         }
440         consoleFormatter_->addColumnHelpTextBlock(3, context, description);
441         context.outputFile().writeString(consoleFormatter_->formatRow());
442     }
443     else
444     {
445         if (!defaultValue.empty())
446         {
447             value += " (" + defaultValue + ")";
448         }
449         context.writeOptionItem("-" + name, value, description);
450     }
451 }
452
453 //! \}
454
455 }   // namespace
456
457 /********************************************************************
458  * CommandLineHelpWriter::Impl
459  */
460
461 /*! \internal \brief
462  * Private implementation class for CommandLineHelpWriter.
463  *
464  * \ingroup module_commandline
465  */
466 class CommandLineHelpWriter::Impl
467 {
468     public:
469         //! Sets the Options object to use for generating help.
470         explicit Impl(const Options &options);
471
472         //! Options object to use for generating help.
473         const Options          &options_;
474         //! Time unit to show in descriptions.
475         std::string             timeUnit_;
476         //! Whether to write descriptions to output.
477         bool                    bShowDescriptions_;
478 };
479
480 CommandLineHelpWriter::Impl::Impl(const Options &options)
481     : options_(options), timeUnit_(TimeUnitManager().timeUnitAsString()),
482       bShowDescriptions_(false)
483 {
484 }
485
486 /********************************************************************
487  * CommandLineHelpWriter
488  */
489
490 CommandLineHelpWriter::CommandLineHelpWriter(const Options &options)
491     : impl_(new Impl(options))
492 {
493 }
494
495 CommandLineHelpWriter::~CommandLineHelpWriter()
496 {
497 }
498
499 CommandLineHelpWriter &CommandLineHelpWriter::setShowDescriptions(bool bSet)
500 {
501     impl_->bShowDescriptions_ = bSet;
502     return *this;
503 }
504
505 CommandLineHelpWriter &CommandLineHelpWriter::setTimeUnitString(const char *timeUnit)
506 {
507     impl_->timeUnit_ = timeUnit;
508     return *this;
509 }
510
511 void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext &context)
512 {
513     const HelpWriterContext &writerContext = context.writerContext();
514     const bool               bConsole
515         = (writerContext.outputFormat() == eHelpOutputFormat_Console);
516     CommonFormatterData      common(impl_->timeUnit_.c_str());
517     OptionsExportFormatter   formatter(common, bConsole);
518     OptionsFilter            filter(writerContext, &formatter);
519     filter.setShowHidden(context.showHidden());
520
521     File &file = writerContext.outputFile();
522     if (!bConsole)
523     {
524         // TODO: Write a proper synopsis, with all the options.
525         writerContext.writeTitle("Synopsis");
526         writerContext.writeTextBlock(context.moduleDisplayName());
527         file.writeLine("\n\n");
528     }
529
530     if (impl_->bShowDescriptions_)
531     {
532         filter.formatSelected(OptionsFilter::eSelectDescriptions,
533                               impl_->options_, "Description");
534     }
535     filter.formatSelected(OptionsFilter::eSelectFileOptions,
536                           impl_->options_, "File Options");
537     filter.formatSelected(OptionsFilter::eSelectOtherOptions,
538                           impl_->options_, "Options");
539 }
540
541 } // namespace gmx