Refactor ProgramInfo use
[gromacs.git] / src / gromacs / onlinehelp / helpwritercontext.cpp
blobfd93b4363a5c51339d7d9ba393263bbb07c85a07
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 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.
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.
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.
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.
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.
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.
35 /*! \internal \file
36 * \brief
37 * Implements gmx::HelpWriterContext.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_onlinehelp
42 #include "helpwritercontext.h"
44 #include <cctype>
46 #include <algorithm>
47 #include <string>
48 #include <vector>
50 #include <boost/shared_ptr.hpp>
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/file.h"
54 #include "gromacs/utility/gmxassert.h"
55 #include "gromacs/utility/programcontext.h"
56 #include "gromacs/utility/stringutil.h"
58 namespace gmx
61 namespace
64 //! \addtogroup module_onlinehelp
65 //! \{
67 struct t_sandr
69 const char *search;
70 const char *replace;
73 /* The order of these arrays is significant. Text search and replace
74 * for each element occurs in order, so earlier changes can induce
75 * subsequent changes even though the original text might not appear
76 * to invoke the latter changes.
77 * TODO: Get rid of this behavior. It makes it very difficult to manage
78 * replacements coming from multiple sources (e.g., hyperlinks).*/
80 //! List of replacements for console output.
81 const t_sandr sandrTty[] = {
82 { "[TT]", "" },
83 { "[tt]", "" },
84 { "[BB]", "" },
85 { "[bb]", "" },
86 { "[IT]", "" },
87 { "[it]", "" },
88 { "[MATH]", "" },
89 { "[math]", "" },
90 { "[CHEVRON]", "<" },
91 { "[chevron]", ">" },
92 { "[MAG]", "|" },
93 { "[mag]", "|" },
94 { "[INT]", "integral" },
95 { "[FROM]", " from " },
96 { "[from]", "" },
97 { "[TO]", " to " },
98 { "[to]", " of" },
99 { "[int]", "" },
100 { "[SUM]", "sum" },
101 { "[sum]", "" },
102 { "[SUB]", "_" },
103 { "[sub]", "" },
104 { "[SQRT]", "sqrt(" },
105 { "[sqrt]", ")" },
106 { "[EXP]", "exp(" },
107 { "[exp]", ")" },
108 { "[LN]", "ln(" },
109 { "[ln]", ")" },
110 { "[LOG]", "log(" },
111 { "[log]", ")" },
112 { "[COS]", "cos(" },
113 { "[cos]", ")" },
114 { "[SIN]", "sin(" },
115 { "[sin]", ")" },
116 { "[TAN]", "tan(" },
117 { "[tan]", ")" },
118 { "[COSH]", "cosh(" },
119 { "[cosh]", ")" },
120 { "[SINH]", "sinh(" },
121 { "[sinh]", ")" },
122 { "[TANH]", "tanh(" },
123 { "[tanh]", ")" },
124 { "[PAR]", "\n\n" },
125 { "[BR]", "\n"},
126 { "[GRK]", "" },
127 { "[grk]", "" }
130 //! List of replacements for man page output.
131 const t_sandr sandrMan[] = {
132 { "[TT]", "\\fB " },
133 { "[tt]", "\\fR" },
134 { "[BB]", "\\fB " },
135 { "[bb]", "\\fR" },
136 { "[IT]", "\\fI " },
137 { "[it]", "\\fR" },
138 { "[MATH]", "" },
139 { "[math]", "" },
140 { "[CHEVRON]", "<" },
141 { "[chevron]", ">" },
142 { "[MAG]", "|" },
143 { "[mag]", "|" },
144 { "[INT]", "integral" },
145 { "[FROM]", " from " },
146 { "[from]", "" },
147 { "[TO]", " to " },
148 { "[to]", " of" },
149 { "[int]", "" },
150 { "[SUM]", "sum" },
151 { "[sum]", "" },
152 { "[SUB]", "_" },
153 { "[sub]", "" },
154 { "[SQRT]", "sqrt(" },
155 { "[sqrt]", ")", },
156 { "[EXP]", "exp(" },
157 { "[exp]", ")" },
158 { "[LN]", "ln(" },
159 { "[ln]", ")" },
160 { "[LOG]", "log(" },
161 { "[log]", ")" },
162 { "[COS]", "cos(" },
163 { "[cos]", ")" },
164 { "[SIN]", "sin(" },
165 { "[sin]", ")" },
166 { "[TAN]", "tan(" },
167 { "[tan]", ")" },
168 { "[COSH]", "cosh(" },
169 { "[cosh]", ")" },
170 { "[SINH]", "sinh(" },
171 { "[sinh]", ")" },
172 { "[TANH]", "tanh(" },
173 { "[tanh]", ")" },
174 { "[PAR]", "\n\n" },
175 { "\n ", "\n" },
176 { "<", "" },
177 { ">", "" },
178 { "^", "" },
179 { "#", "" },
180 { "[BR]", "\n"},
181 { "-", "\\-"},
182 { "[GRK]", "" },
183 { "[grk]", "" }
186 //! List of replacements for HTML output.
187 const t_sandr sandrHtml[] = {
188 { "<", "&lt;" },
189 { ">", "&gt;" },
190 { "[TT]", "<tt>" },
191 { "[tt]", "</tt>" },
192 { "[BB]", "<b>" },
193 { "[bb]", "</b>" },
194 { "[IT]", "<it>" },
195 { "[it]", "</it>" },
196 { "[MATH]", "" },
197 { "[math]", "" },
198 { "[CHEVRON]", "&lt;" },
199 { "[chevron]", "&gt;" },
200 { "[MAG]", "|" },
201 { "[mag]", "|" },
202 { "[INT]", "integral" },
203 { "[FROM]", " from " },
204 { "[from]", "" },
205 { "[TO]", " to " },
206 { "[to]", " of" },
207 { "[int]", "" },
208 { "[SUM]", "sum" },
209 { "[sum]", "" },
210 { "[SUB]", "_" },
211 { "[sub]", "" },
212 { "[SQRT]", "sqrt(" },
213 { "[sqrt]", ")", },
214 { "[EXP]", "exp(" },
215 { "[exp]", ")" },
216 { "[LN]", "ln(" },
217 { "[ln]", ")" },
218 { "[LOG]", "log(" },
219 { "[log]", ")" },
220 { "[COS]", "cos(" },
221 { "[cos]", ")" },
222 { "[SIN]", "sin(" },
223 { "[sin]", ")" },
224 { "[TAN]", "tan(" },
225 { "[tan]", ")" },
226 { "[COSH]", "cosh(" },
227 { "[cosh]", ")" },
228 { "[SINH]", "sinh(" },
229 { "[sinh]", ")" },
230 { "[TANH]", "tanh(" },
231 { "[tanh]", ")" },
232 { "[PAR]", "<p>" },
233 { "[BR]", "<br>" },
234 { "[GRK]", "&" },
235 { "[grk]", ";" }
238 /*! \brief
239 * Replaces all entries from a list of replacements.
241 std::string repall(const std::string &s, int nsr, const t_sandr sa[])
243 std::string result(s);
244 for (int i = 0; i < nsr; ++i)
246 result = replaceAll(result, sa[i].search, sa[i].replace);
248 return result;
251 /*! \brief
252 * Replaces all entries from a list of replacements.
254 template <size_t nsr>
255 std::string repall(const std::string &s, const t_sandr (&sa)[nsr])
257 return repall(s, nsr, sa);
260 /*! \brief
261 * Custom output interface for HelpWriterContext::Impl::processMarkup().
263 * Provides an interface that is used to implement different types of output
264 * from HelpWriterContext::Impl::processMarkup().
266 class WrapperInterface
268 public:
269 virtual ~WrapperInterface() {}
271 /*! \brief
272 * Provides the wrapping settings.
274 * HelpWriterContext::Impl::processMarkup() may provide some default
275 * values for the settings if they are not set; this is the reason the
276 * return value is not const.
278 virtual TextLineWrapperSettings &settings() = 0;
279 //! Appends the given string to output.
280 virtual void wrap(const std::string &text) = 0;
283 /*! \brief
284 * Wraps markup output into a single string.
286 class WrapperToString : public WrapperInterface
288 public:
289 //! Creates a wrapper with the given settings.
290 explicit WrapperToString(const TextLineWrapperSettings &settings)
291 : wrapper_(settings)
295 virtual TextLineWrapperSettings &settings()
297 return wrapper_.settings();
299 virtual void wrap(const std::string &text)
301 result_.append(wrapper_.wrapToString(text));
303 //! Returns the result string.
304 const std::string &result() const { return result_; }
306 private:
307 TextLineWrapper wrapper_;
308 std::string result_;
311 /*! \brief
312 * Wraps markup output into a vector of string (one line per element).
314 class WrapperToVector : public WrapperInterface
316 public:
317 //! Creates a wrapper with the given settings.
318 explicit WrapperToVector(const TextLineWrapperSettings &settings)
319 : wrapper_(settings)
323 virtual TextLineWrapperSettings &settings()
325 return wrapper_.settings();
327 virtual void wrap(const std::string &text)
329 const std::vector<std::string> &lines = wrapper_.wrapToVector(text);
330 result_.insert(result_.end(), lines.begin(), lines.end());
332 //! Returns a vector with the output lines.
333 const std::vector<std::string> &result() const { return result_; }
335 private:
336 TextLineWrapper wrapper_;
337 std::vector<std::string> result_;
340 /*! \brief
341 * Makes the string uppercase.
343 * \param[in] text Input text.
344 * \returns \p text with all characters transformed to uppercase.
345 * \throws std::bad_alloc if out of memory.
347 std::string toUpperCase(const std::string &text)
349 std::string result(text);
350 transform(result.begin(), result.end(), result.begin(), toupper);
351 return result;
354 //! \}
356 } // namespace
358 /********************************************************************
359 * HelpLinks::Impl
362 /*! \internal \brief
363 * Private implementation class for HelpLinks.
365 * \ingroup module_onlinehelp
367 class HelpLinks::Impl
369 public:
370 struct LinkItem
372 LinkItem(const std::string &linkName,
373 const std::string &replacement)
374 : linkName(linkName), replacement(replacement)
377 std::string linkName;
378 std::string replacement;
381 //! Shorthand for a list of links.
382 typedef std::vector<LinkItem> LinkList;
384 //! Initializes empty links with the given format.
385 explicit Impl(HelpOutputFormat format) : format_(format)
389 //! List of links.
390 LinkList links_;
391 //! Output format for which the links are formatted.
392 HelpOutputFormat format_;
395 /********************************************************************
396 * HelpLinks
399 HelpLinks::HelpLinks(HelpOutputFormat format) : impl_(new Impl(format))
403 HelpLinks::~HelpLinks()
407 void HelpLinks::addLink(const std::string &linkName,
408 const std::string &targetName,
409 const std::string &displayName)
411 std::string replacement;
412 switch (impl_->format_)
414 case eHelpOutputFormat_Console:
415 replacement = repall(displayName, sandrTty);
416 break;
417 case eHelpOutputFormat_Man:
418 replacement = repall(displayName, sandrMan);
419 break;
420 case eHelpOutputFormat_Html:
421 replacement = formatString(
422 "<a href=\"%s.html\">%s</a>", targetName.c_str(),
423 repall(displayName, sandrHtml).c_str());
424 break;
425 default:
426 GMX_RELEASE_ASSERT(false, "Output format not implemented for links");
428 impl_->links_.push_back(Impl::LinkItem(linkName, replacement));
431 /********************************************************************
432 * HelpWriterContext::Impl
435 /*! \internal \brief
436 * Private implementation class for HelpWriterContext.
438 * \ingroup module_onlinehelp
440 class HelpWriterContext::Impl
442 public:
443 /*! \brief
444 * Shared, non-modifiable state for context objects.
446 * Contents of this structure are shared between all context objects
447 * that are created from a common parent.
448 * This state should not be modified after construction.
450 * \ingroup module_onlinehelp
452 struct SharedState
454 //! Initializes the state with the given parameters.
455 SharedState(File *file, HelpOutputFormat format,
456 const HelpLinks *links)
457 : file_(*file), format_(format), links_(links)
461 //! Output file to which the help is written.
462 File &file_;
463 //! Output format for the help output.
464 HelpOutputFormat format_;
465 //! Links to use.
466 const HelpLinks *links_;
469 struct ReplaceItem
471 ReplaceItem(const std::string &search,
472 const std::string &replace)
473 : search(search), replace(replace)
476 std::string search;
477 std::string replace;
480 //! Smart pointer type for managing the shared state.
481 typedef boost::shared_ptr<const SharedState> StatePointer;
482 //! Shorthand for a list of markup/other replacements.
483 typedef std::vector<ReplaceItem> ReplaceList;
485 //! Initializes the context with the given state.
486 explicit Impl(const StatePointer &state)
487 : state_(state)
489 initDefaultReplacements();
492 //! Initializes default replacements for the chosen output format.
493 void initDefaultReplacements();
494 //! Adds a new replacement.
495 void addReplacement(const std::string &search,
496 const std::string &replace)
498 replacements_.push_back(ReplaceItem(search, replace));
501 //! Replaces links in a given string.
502 std::string replaceLinks(const std::string &input) const;
504 /*! \brief
505 * Process markup and wrap lines within a block of text.
507 * \param[in] text Text to process.
508 * \param wrapper Object used to wrap the text.
510 * The \p wrapper should take care of either writing the text to output
511 * or providing an interface for the caller to retrieve the output.
513 void processMarkup(const std::string &text,
514 WrapperInterface *wrapper) const;
516 //! Constant state shared by all child context objects.
517 StatePointer state_;
518 //! List of markup/other replacements.
519 ReplaceList replacements_;
521 private:
522 GMX_DISALLOW_ASSIGN(Impl);
525 void HelpWriterContext::Impl::initDefaultReplacements()
527 const char *program = getProgramContext().programName();
528 addReplacement("[PROGRAM]", program);
531 std::string HelpWriterContext::Impl::replaceLinks(const std::string &input) const
533 std::string result(input);
534 if (state_->links_ != NULL)
536 HelpLinks::Impl::LinkList::const_iterator link;
537 for (link = state_->links_->impl_->links_.begin();
538 link != state_->links_->impl_->links_.end(); ++link)
540 result = replaceAllWords(result, link->linkName, link->replacement);
543 return result;
546 void HelpWriterContext::Impl::processMarkup(const std::string &text,
547 WrapperInterface *wrapper) const
549 std::string result(text);
550 for (ReplaceList::const_iterator i = replacements_.begin();
551 i != replacements_.end(); ++i)
553 result = replaceAll(result, i->search, i->replace);
555 switch (state_->format_)
557 case eHelpOutputFormat_Console:
559 result = repall(result, sandrTty);
560 result = replaceLinks(result);
561 return wrapper->wrap(result);
563 case eHelpOutputFormat_Man:
565 // Needs to be done first to avoid '-' -> '\-' messing up the links.
566 result = replaceLinks(result);
567 result = repall(result, sandrMan);
568 return wrapper->wrap(result);
570 case eHelpOutputFormat_Html:
572 result = repall(result, sandrHtml);
573 result = replaceLinks(result);
574 return wrapper->wrap(result);
576 default:
577 GMX_THROW(InternalError("Invalid help output format"));
581 /********************************************************************
582 * HelpWriterContext
585 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format)
586 : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, NULL))))
590 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format,
591 const HelpLinks *links)
592 : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, links))))
594 if (links != NULL)
596 GMX_RELEASE_ASSERT(links->impl_->format_ == format,
597 "Links must have the same output format as the context");
601 HelpWriterContext::HelpWriterContext(Impl *impl)
602 : impl_(impl)
606 HelpWriterContext::HelpWriterContext(const HelpWriterContext &other)
607 : impl_(new Impl(*other.impl_))
611 HelpWriterContext::~HelpWriterContext()
615 void HelpWriterContext::setReplacement(const std::string &search,
616 const std::string &replace)
618 impl_->addReplacement(search, replace);
621 HelpOutputFormat HelpWriterContext::outputFormat() const
623 return impl_->state_->format_;
626 File &HelpWriterContext::outputFile() const
628 return impl_->state_->file_;
631 std::string
632 HelpWriterContext::substituteMarkupAndWrapToString(
633 const TextLineWrapperSettings &settings, const std::string &text) const
635 WrapperToString wrapper(settings);
636 impl_->processMarkup(text, &wrapper);
637 return wrapper.result();
640 std::vector<std::string>
641 HelpWriterContext::substituteMarkupAndWrapToVector(
642 const TextLineWrapperSettings &settings, const std::string &text) const
644 WrapperToVector wrapper(settings);
645 impl_->processMarkup(text, &wrapper);
646 return wrapper.result();
649 void HelpWriterContext::writeTitle(const std::string &title) const
651 File &file = outputFile();
652 switch (outputFormat())
654 case eHelpOutputFormat_Console:
655 file.writeLine(toUpperCase(title));
656 file.writeLine();
657 break;
658 case eHelpOutputFormat_Man:
659 file.writeLine(formatString(".SH %s", toUpperCase(title).c_str()));
660 break;
661 case eHelpOutputFormat_Html:
662 file.writeLine(formatString("<H3>%s</H3>", title.c_str()));
663 break;
664 default:
665 GMX_THROW(NotImplementedError(
666 "This output format is not implemented"));
670 void HelpWriterContext::writeTextBlock(const std::string &text) const
672 TextLineWrapperSettings settings;
673 if (outputFormat() == eHelpOutputFormat_Console)
675 settings.setLineLength(78);
677 outputFile().writeLine(substituteMarkupAndWrapToString(settings, text));
680 void HelpWriterContext::writeOptionListStart() const
682 if (outputFormat() == eHelpOutputFormat_Html)
684 outputFile().writeLine("<dl>");
688 void HelpWriterContext::writeOptionItem(const std::string &name,
689 const std::string &args,
690 const std::string &description) const
692 File &file = outputFile();
693 switch (outputFormat())
695 case eHelpOutputFormat_Console:
696 // TODO: Generalize this when there is need for it; the current,
697 // special implementation is in CommandLineHelpWriter.
698 GMX_THROW(NotImplementedError("Option item formatting for console output not implemented"));
699 break;
700 case eHelpOutputFormat_Man:
701 file.writeLine(formatString(".BI \"\\%s\" \" %s\"", name.c_str(), args.c_str()));
702 file.writeString(" ");
703 writeTextBlock(description);
704 file.writeLine();
705 break;
706 case eHelpOutputFormat_Html:
708 std::string substArgs =
709 substituteMarkupAndWrapToString(TextLineWrapperSettings(), args);
710 file.writeLine(formatString("<dt><b><tt>%s</tt></b> %s</dt>", name.c_str(),
711 substArgs.c_str()));
712 file.writeLine("<dd>");
713 writeTextBlock(description);
714 file.writeLine("</dd>");
715 break;
717 default:
718 GMX_THROW(NotImplementedError(
719 "This output format is not implemented"));
723 void HelpWriterContext::writeOptionListEnd() const
725 if (outputFormat() == eHelpOutputFormat_Html)
727 outputFile().writeLine("</dl>");
731 } // namespace gmx