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.
37 * Implements gmx::HelpWriterContext.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_onlinehelp
42 #include "helpwritercontext.h"
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"
64 //! \internal \addtogroup module_onlinehelp
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[] = {
94 { "[INT]", "integral" },
95 { "[FROM]", " from " },
104 { "[SQRT]", "sqrt(" },
118 { "[COSH]", "cosh(" },
120 { "[SINH]", "sinh(" },
122 { "[TANH]", "tanh(" },
126 /* [UL], [LI], [ul] cannot be implemented properly with the current
132 //! List of replacements for man page output.
133 const t_sandr sandrMan[] = {
142 { "[CHEVRON]", "<" },
143 { "[chevron]", ">" },
146 { "[INT]", "integral" },
147 { "[FROM]", " from " },
156 { "[SQRT]", "sqrt(" },
170 { "[COSH]", "cosh(" },
172 { "[SINH]", "sinh(" },
174 { "[TANH]", "tanh(" },
178 // The following three work only in the specific context in which they are
193 //! List of replacements for HTML output.
194 const t_sandr sandrHtml[] = {
205 { "[CHEVRON]", "<" },
206 { "[chevron]", ">" },
209 { "[INT]", "integral" },
210 { "[FROM]", " from " },
219 { "[SQRT]", "sqrt(" },
233 { "[COSH]", "cosh(" },
235 { "[SINH]", "sinh(" },
237 { "[TANH]", "tanh(" },
249 * Replaces all entries from a list of replacements.
251 std::string repall(const std::string &s, int nsr, const t_sandr sa[])
253 std::string result(s);
254 for (int i = 0; i < nsr; ++i)
256 result = replaceAll(result, sa[i].search, sa[i].replace);
262 * Replaces all entries from a list of replacements.
264 template <size_t nsr>
265 std::string repall(const std::string &s, const t_sandr (&sa)[nsr])
267 return repall(s, nsr, sa);
271 * Custom output interface for HelpWriterContext::Impl::processMarkup().
273 * Provides an interface that is used to implement different types of output
274 * from HelpWriterContext::Impl::processMarkup().
276 class WrapperInterface
279 virtual ~WrapperInterface() {}
282 * Provides the wrapping settings.
284 * HelpWriterContext::Impl::processMarkup() may provide some default
285 * values for the settings if they are not set; this is the reason the
286 * return value is not const.
288 virtual TextLineWrapperSettings &settings() = 0;
289 //! Appends the given string to output.
290 virtual void wrap(const std::string &text) = 0;
294 * Wraps markup output into a single string.
296 class WrapperToString : public WrapperInterface
299 //! Creates a wrapper with the given settings.
300 explicit WrapperToString(const TextLineWrapperSettings &settings)
305 virtual TextLineWrapperSettings &settings()
307 return wrapper_.settings();
309 virtual void wrap(const std::string &text)
311 result_.append(wrapper_.wrapToString(text));
313 //! Returns the result string.
314 const std::string &result() const { return result_; }
317 TextLineWrapper wrapper_;
322 * Wraps markup output into a vector of string (one line per element).
324 class WrapperToVector : public WrapperInterface
327 //! Creates a wrapper with the given settings.
328 explicit WrapperToVector(const TextLineWrapperSettings &settings)
333 virtual TextLineWrapperSettings &settings()
335 return wrapper_.settings();
337 virtual void wrap(const std::string &text)
339 const std::vector<std::string> &lines = wrapper_.wrapToVector(text);
340 result_.insert(result_.end(), lines.begin(), lines.end());
342 //! Returns a vector with the output lines.
343 const std::vector<std::string> &result() const { return result_; }
346 TextLineWrapper wrapper_;
347 std::vector<std::string> result_;
351 * Makes the string uppercase.
353 * \param[in] text Input text.
354 * \returns \p text with all characters transformed to uppercase.
355 * \throws std::bad_alloc if out of memory.
357 std::string toUpperCase(const std::string &text)
359 std::string result(text);
360 transform(result.begin(), result.end(), result.begin(), toupper);
368 /********************************************************************
373 * Private implementation class for HelpLinks.
375 * \ingroup module_onlinehelp
377 class HelpLinks::Impl
382 LinkItem(const std::string &linkName,
383 const std::string &replacement)
384 : linkName(linkName), replacement(replacement)
387 std::string linkName;
388 std::string replacement;
391 //! Shorthand for a list of links.
392 typedef std::vector<LinkItem> LinkList;
394 //! Initializes empty links with the given format.
395 explicit Impl(HelpOutputFormat format) : format_(format)
401 //! Output format for which the links are formatted.
402 HelpOutputFormat format_;
405 /********************************************************************
409 HelpLinks::HelpLinks(HelpOutputFormat format) : impl_(new Impl(format))
413 HelpLinks::~HelpLinks()
417 void HelpLinks::addLink(const std::string &linkName,
418 const std::string &targetName,
419 const std::string &displayName)
421 std::string replacement;
422 switch (impl_->format_)
424 case eHelpOutputFormat_Console:
425 replacement = repall(displayName, sandrTty);
427 case eHelpOutputFormat_Man:
428 replacement = repall(displayName, sandrMan);
430 case eHelpOutputFormat_Html:
431 replacement = formatString(
432 "<a href=\"%s.html\">%s</a>", targetName.c_str(),
433 repall(displayName, sandrHtml).c_str());
436 GMX_RELEASE_ASSERT(false, "Output format not implemented for links");
438 impl_->links_.push_back(Impl::LinkItem(linkName, replacement));
441 /********************************************************************
442 * HelpWriterContext::Impl
446 * Private implementation class for HelpWriterContext.
448 * \ingroup module_onlinehelp
450 class HelpWriterContext::Impl
454 * Shared, non-modifiable state for context objects.
456 * Contents of this structure are shared between all context objects
457 * that are created from a common parent.
458 * This state should not be modified after construction.
460 * \ingroup module_onlinehelp
464 //! Initializes the state with the given parameters.
465 SharedState(File *file, HelpOutputFormat format,
466 const HelpLinks *links)
467 : file_(*file), format_(format), links_(links)
471 //! Output file to which the help is written.
473 //! Output format for the help output.
474 HelpOutputFormat format_;
476 const HelpLinks *links_;
481 ReplaceItem(const std::string &search,
482 const std::string &replace)
483 : search(search), replace(replace)
490 //! Smart pointer type for managing the shared state.
491 typedef boost::shared_ptr<const SharedState> StatePointer;
492 //! Shorthand for a list of markup/other replacements.
493 typedef std::vector<ReplaceItem> ReplaceList;
495 //! Initializes the context with the given state.
496 explicit Impl(const StatePointer &state)
499 initDefaultReplacements();
502 //! Initializes default replacements for the chosen output format.
503 void initDefaultReplacements();
504 //! Adds a new replacement.
505 void addReplacement(const std::string &search,
506 const std::string &replace)
508 replacements_.push_back(ReplaceItem(search, replace));
511 //! Replaces links in a given string.
512 std::string replaceLinks(const std::string &input) const;
515 * Process markup and wrap lines within a block of text.
517 * \param[in] text Text to process.
518 * \param wrapper Object used to wrap the text.
520 * The \p wrapper should take care of either writing the text to output
521 * or providing an interface for the caller to retrieve the output.
523 void processMarkup(const std::string &text,
524 WrapperInterface *wrapper) const;
526 //! Constant state shared by all child context objects.
528 //! List of markup/other replacements.
529 ReplaceList replacements_;
532 GMX_DISALLOW_ASSIGN(Impl);
535 void HelpWriterContext::Impl::initDefaultReplacements()
537 const char *program = getProgramContext().programName();
538 addReplacement("[PROGRAM]", program);
541 std::string HelpWriterContext::Impl::replaceLinks(const std::string &input) const
543 std::string result(input);
544 if (state_->links_ != NULL)
546 HelpLinks::Impl::LinkList::const_iterator link;
547 for (link = state_->links_->impl_->links_.begin();
548 link != state_->links_->impl_->links_.end(); ++link)
550 result = replaceAllWords(result, link->linkName, link->replacement);
556 void HelpWriterContext::Impl::processMarkup(const std::string &text,
557 WrapperInterface *wrapper) const
559 std::string result(text);
560 for (ReplaceList::const_iterator i = replacements_.begin();
561 i != replacements_.end(); ++i)
563 result = replaceAll(result, i->search, i->replace);
565 switch (state_->format_)
567 case eHelpOutputFormat_Console:
569 result = repall(result, sandrTty);
570 result = replaceLinks(result);
571 return wrapper->wrap(result);
573 case eHelpOutputFormat_Man:
575 // Needs to be done first to avoid '-' -> '\-' messing up the links.
576 result = replaceLinks(result);
577 result = repall(result, sandrMan);
578 return wrapper->wrap(result);
580 case eHelpOutputFormat_Html:
582 result = repall(result, sandrHtml);
583 result = replaceLinks(result);
584 return wrapper->wrap(result);
587 GMX_THROW(InternalError("Invalid help output format"));
591 /********************************************************************
595 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format)
596 : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, NULL))))
600 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format,
601 const HelpLinks *links)
602 : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, links))))
606 GMX_RELEASE_ASSERT(links->impl_->format_ == format,
607 "Links must have the same output format as the context");
611 HelpWriterContext::HelpWriterContext(Impl *impl)
616 HelpWriterContext::HelpWriterContext(const HelpWriterContext &other)
617 : impl_(new Impl(*other.impl_))
621 HelpWriterContext::~HelpWriterContext()
625 void HelpWriterContext::setReplacement(const std::string &search,
626 const std::string &replace)
628 impl_->addReplacement(search, replace);
631 HelpOutputFormat HelpWriterContext::outputFormat() const
633 return impl_->state_->format_;
636 File &HelpWriterContext::outputFile() const
638 return impl_->state_->file_;
642 HelpWriterContext::substituteMarkupAndWrapToString(
643 const TextLineWrapperSettings &settings, const std::string &text) const
645 WrapperToString wrapper(settings);
646 impl_->processMarkup(text, &wrapper);
647 return wrapper.result();
650 std::vector<std::string>
651 HelpWriterContext::substituteMarkupAndWrapToVector(
652 const TextLineWrapperSettings &settings, const std::string &text) const
654 WrapperToVector wrapper(settings);
655 impl_->processMarkup(text, &wrapper);
656 return wrapper.result();
659 void HelpWriterContext::writeTitle(const std::string &title) const
661 File &file = outputFile();
662 switch (outputFormat())
664 case eHelpOutputFormat_Console:
665 file.writeLine(toUpperCase(title));
668 case eHelpOutputFormat_Man:
669 file.writeLine(formatString(".SH %s", toUpperCase(title).c_str()));
671 case eHelpOutputFormat_Html:
672 file.writeLine(formatString("<H3>%s</H3>", title.c_str()));
675 GMX_THROW(NotImplementedError(
676 "This output format is not implemented"));
680 void HelpWriterContext::writeTextBlock(const std::string &text) const
682 TextLineWrapperSettings settings;
683 if (outputFormat() == eHelpOutputFormat_Console)
685 settings.setLineLength(78);
687 outputFile().writeLine(substituteMarkupAndWrapToString(settings, text));
690 void HelpWriterContext::writeOptionListStart() const
692 if (outputFormat() == eHelpOutputFormat_Html)
694 outputFile().writeLine("<dl>");
698 void HelpWriterContext::writeOptionItem(const std::string &name,
699 const std::string &args,
700 const std::string &description) const
702 File &file = outputFile();
703 switch (outputFormat())
705 case eHelpOutputFormat_Console:
706 // TODO: Generalize this when there is need for it; the current,
707 // special implementation is in CommandLineHelpWriter.
708 GMX_THROW(NotImplementedError("Option item formatting for console output not implemented"));
710 case eHelpOutputFormat_Man:
711 file.writeLine(formatString(".BI \"\\%s\" \" %s\"", name.c_str(), args.c_str()));
712 file.writeString(" ");
713 writeTextBlock(description);
716 case eHelpOutputFormat_Html:
718 std::string substArgs =
719 substituteMarkupAndWrapToString(TextLineWrapperSettings(), args);
720 file.writeLine(formatString("<dt><b><tt>%s</tt></b> %s</dt>", name.c_str(),
722 file.writeLine("<dd>");
723 writeTextBlock(description);
724 file.writeLine("</dd>");
728 GMX_THROW(NotImplementedError(
729 "This output format is not implemented"));
733 void HelpWriterContext::writeOptionListEnd() const
735 if (outputFormat() == eHelpOutputFormat_Html)
737 outputFile().writeLine("</dl>");