baff5c4177b1eccd0b9bb3a14d3ed91a2d926d47
[alexxy/gromacs.git] / src / gromacs / onlinehelp / helpwritercontext.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013, by the GROMACS development team, led by
5  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6  * others, as listed in the AUTHORS file in the top-level source
7  * 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::HelpWriterContext.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_onlinehelp
41  */
42 #include "helpwritercontext.h"
43
44 #include <cctype>
45
46 #include <algorithm>
47 #include <string>
48 #include <vector>
49
50 #include <boost/shared_ptr.hpp>
51
52 #include "gromacs/onlinehelp/helpformat.h"
53 #include "gromacs/utility/exceptions.h"
54 #include "gromacs/utility/file.h"
55 #include "gromacs/utility/gmxassert.h"
56 #include "gromacs/utility/programinfo.h"
57 #include "gromacs/utility/stringutil.h"
58
59 namespace gmx
60 {
61
62 namespace
63 {
64
65 //! \addtogroup module_onlinehelp
66 //! \{
67
68 struct t_sandr
69 {
70     const char *search;
71     const char *replace;
72 };
73
74 /* The order of these arrays is significant. Text search and replace
75  * for each element occurs in order, so earlier changes can induce
76  * subsequent changes even though the original text might not appear
77  * to invoke the latter changes. */
78
79 //! List of replacements for console output.
80 const t_sandr sandrTty[] = {
81     { "[TT]", "" },
82     { "[tt]", "" },
83     { "[BB]", "" },
84     { "[bb]", "" },
85     { "[IT]", "" },
86     { "[it]", "" },
87     { "[MATH]", "" },
88     { "[math]", "" },
89     { "[CHEVRON]", "<" },
90     { "[chevron]", ">" },
91     { "[MAG]", "|" },
92     { "[mag]", "|" },
93     { "[INT]", "integral" },
94     { "[FROM]", " from " },
95     { "[from]", "" },
96     { "[TO]", " to " },
97     { "[to]", " of" },
98     { "[int]", "" },
99     { "[SUM]", "sum" },
100     { "[sum]", "" },
101     { "[SUB]", "_" },
102     { "[sub]", "" },
103     { "[SQRT]", "sqrt(" },
104     { "[sqrt]", ")" },
105     { "[EXP]", "exp(" },
106     { "[exp]", ")" },
107     { "[LN]", "ln(" },
108     { "[ln]", ")" },
109     { "[LOG]", "log(" },
110     { "[log]", ")" },
111     { "[COS]", "cos(" },
112     { "[cos]", ")" },
113     { "[SIN]", "sin(" },
114     { "[sin]", ")" },
115     { "[TAN]", "tan(" },
116     { "[tan]", ")" },
117     { "[COSH]", "cosh(" },
118     { "[cosh]", ")" },
119     { "[SINH]", "sinh(" },
120     { "[sinh]", ")" },
121     { "[TANH]", "tanh(" },
122     { "[tanh]", ")" },
123     { "[PAR]", "\n\n" },
124     { "[BR]", "\n"},
125     { "[GRK]", "" },
126     { "[grk]", "" }
127 };
128
129 //! List of replacements for man page output.
130 const t_sandr sandrMan[] = {
131     { "[TT]", "\\fB " },
132     { "[tt]", "\\fR" },
133     { "[BB]", "\\fB " },
134     { "[bb]", "\\fR" },
135     { "[IT]", "\\fI " },
136     { "[it]", "\\fR" },
137     { "[MATH]", "" },
138     { "[math]", "" },
139     { "[CHEVRON]", "<" },
140     { "[chevron]", ">" },
141     { "[MAG]", "|" },
142     { "[mag]", "|" },
143     { "[INT]", "integral" },
144     { "[FROM]", " from " },
145     { "[from]", "" },
146     { "[TO]", " to " },
147     { "[to]", " of" },
148     { "[int]", "" },
149     { "[SUM]", "sum" },
150     { "[sum]", "" },
151     { "[SUB]", "_" },
152     { "[sub]", "" },
153     { "[SQRT]", "sqrt(" },
154     { "[sqrt]", ")", },
155     { "[EXP]", "exp(" },
156     { "[exp]", ")" },
157     { "[LN]", "ln(" },
158     { "[ln]", ")" },
159     { "[LOG]", "log(" },
160     { "[log]", ")" },
161     { "[COS]", "cos(" },
162     { "[cos]", ")" },
163     { "[SIN]", "sin(" },
164     { "[sin]", ")" },
165     { "[TAN]", "tan(" },
166     { "[tan]", ")" },
167     { "[COSH]", "cosh(" },
168     { "[cosh]", ")" },
169     { "[SINH]", "sinh(" },
170     { "[sinh]", ")" },
171     { "[TANH]", "tanh(" },
172     { "[tanh]", ")" },
173     { "[PAR]", "\n\n" },
174     { "\n ",    "\n" },
175     { "<",    "" },
176     { ">",    "" },
177     { "^",    "" },
178     { "#",    "" },
179     { "[BR]", "\n"},
180     { "-",    "\\-"},
181     { "[GRK]", "" },
182     { "[grk]", "" }
183 };
184
185 //! List of replacements for HTML output.
186 const t_sandr sandrHtml[] = {
187     { "<",    "&lt;" },
188     { ">",    "&gt;" },
189     { "[TT]", "<tt>" },
190     { "[tt]", "</tt>" },
191     { "[BB]", "<b>" },
192     { "[bb]", "</b>" },
193     { "[IT]", "<it>" },
194     { "[it]", "</it>" },
195     { "[MATH]", "" },
196     { "[math]", "" },
197     { "[CHEVRON]", "<" },
198     { "[chevron]", ">" },
199     { "[MAG]", "|" },
200     { "[mag]", "|" },
201     { "[INT]", "integral" },
202     { "[FROM]", " from " },
203     { "[from]", "" },
204     { "[TO]", " to " },
205     { "[to]", " of" },
206     { "[int]", "" },
207     { "[SUM]", "sum" },
208     { "[sum]", "" },
209     { "[SUB]", "_" },
210     { "[sub]", "" },
211     { "[SQRT]", "sqrt(" },
212     { "[sqrt]", ")", },
213     { "[EXP]", "exp(" },
214     { "[exp]", ")" },
215     { "[LN]", "ln(" },
216     { "[ln]", ")" },
217     { "[LOG]", "log(" },
218     { "[log]", ")" },
219     { "[COS]", "cos(" },
220     { "[cos]", ")" },
221     { "[SIN]", "sin(" },
222     { "[sin]", ")" },
223     { "[TAN]", "tan(" },
224     { "[tan]", ")" },
225     { "[COSH]", "cosh(" },
226     { "[cosh]", ")" },
227     { "[SINH]", "sinh(" },
228     { "[sinh]", ")" },
229     { "[TANH]", "tanh(" },
230     { "[tanh]", ")" },
231     { "[PAR]", "<p>" },
232     { "[BR]", "<br>" },
233     { "[GRK]", "&"  },
234     { "[grk]", ";"  }
235 };
236
237 /*! \brief
238  * Replaces all entries from a list of replacements.
239  */
240 std::string repall(const std::string &s, int nsr, const t_sandr sa[])
241 {
242     std::string result(s);
243     for (int i = 0; i < nsr; ++i)
244     {
245         result = replaceAll(result, sa[i].search, sa[i].replace);
246     }
247     return result;
248 }
249
250 /*! \brief
251  * Replaces all entries from a list of replacements.
252  */
253 template <size_t nsr>
254 std::string repall(const std::string &s, const t_sandr (&sa)[nsr])
255 {
256     return repall(s, nsr, sa);
257 }
258
259 /*! \brief
260  * Custom output interface for HelpWriterContext::Impl::processMarkup().
261  *
262  * Provides an interface that is used to implement different types of output
263  * from HelpWriterContext::Impl::processMarkup().
264  */
265 class WrapperInterface
266 {
267     public:
268         virtual ~WrapperInterface() {}
269
270         /*! \brief
271          * Provides the wrapping settings.
272          *
273          * HelpWriterContext::Impl::processMarkup() may provide some default
274          * values for the settings if they are not set; this is the reason the
275          * return value is not const.
276          */
277         virtual TextLineWrapperSettings &settings() = 0;
278         //! Appends the given string to output.
279         virtual void wrap(const std::string &text)  = 0;
280 };
281
282 /*! \brief
283  * Wraps markup output into a single string.
284  */
285 class WrapperToString : public WrapperInterface
286 {
287     public:
288         //! Creates a wrapper with the given settings.
289         explicit WrapperToString(const TextLineWrapperSettings &settings)
290             : wrapper_(settings)
291         {
292         }
293
294         virtual TextLineWrapperSettings &settings()
295         {
296             return wrapper_.settings();
297         }
298         virtual void wrap(const std::string &text)
299         {
300             result_.append(wrapper_.wrapToString(text));
301         }
302         //! Returns the result string.
303         const std::string &result() const { return result_; }
304
305     private:
306         TextLineWrapper         wrapper_;
307         std::string             result_;
308 };
309
310 /*! \brief
311  * Wraps markup output into a vector of string (one line per element).
312  */
313 class WrapperToVector : public WrapperInterface
314 {
315     public:
316         //! Creates a wrapper with the given settings.
317         explicit WrapperToVector(const TextLineWrapperSettings &settings)
318             : wrapper_(settings)
319         {
320         }
321
322         virtual TextLineWrapperSettings &settings()
323         {
324             return wrapper_.settings();
325         }
326         virtual void wrap(const std::string &text)
327         {
328             const std::vector<std::string> &lines = wrapper_.wrapToVector(text);
329             result_.insert(result_.end(), lines.begin(), lines.end());
330         }
331         //! Returns a vector with the output lines.
332         const std::vector<std::string> &result() const { return result_; }
333
334     private:
335         TextLineWrapper          wrapper_;
336         std::vector<std::string> result_;
337 };
338
339 /*! \brief
340  * Make the string uppercase.
341  *
342  * \param[in] text  Input text.
343  * \returns   \p text with all characters transformed to uppercase.
344  * \throws    std::bad_alloc if out of memory.
345  */
346 std::string toUpperCase(const std::string &text)
347 {
348     std::string result(text);
349     transform(result.begin(), result.end(), result.begin(), toupper);
350     return result;
351 }
352
353 //! \}
354
355 }   // namespace
356
357 /********************************************************************
358  * HelpLinks::Impl
359  */
360
361 /*! \internal \brief
362  * Private implementation class for HelpLinks.
363  *
364  * \ingroup module_onlinehelp
365  */
366 class HelpLinks::Impl
367 {
368     public:
369         struct LinkItem
370         {
371             LinkItem(const std::string &linkName,
372                      const std::string &targetName)
373                 : linkName(linkName), targetName(targetName)
374             {
375             }
376             std::string         linkName;
377             std::string         targetName;
378         };
379
380         //! Shorthand for a list of links.
381         typedef std::vector<LinkItem> LinkList;
382
383         //! List of links.
384         LinkList        links_;
385 };
386
387 /********************************************************************
388  * HelpLinks
389  */
390
391 HelpLinks::HelpLinks() : impl_(new Impl)
392 {
393 }
394
395 HelpLinks::~HelpLinks()
396 {
397 }
398
399 void HelpLinks::addLink(const std::string &linkName,
400                         const std::string &targetName)
401 {
402     impl_->links_.push_back(Impl::LinkItem(linkName, targetName));
403 }
404
405 /********************************************************************
406  * HelpWriterContext::Impl
407  */
408
409 /*! \internal \brief
410  * Private implementation class for HelpWriterContext.
411  *
412  * \ingroup module_onlinehelp
413  */
414 class HelpWriterContext::Impl
415 {
416     public:
417         /*! \brief
418          * Shared, non-modifiable state for context objects.
419          *
420          * Contents of this structure are shared between all context objects
421          * that are created from a common parent.
422          * This state should not be modified after construction.
423          *
424          * \ingroup module_onlinehelp
425          */
426         struct SharedState
427         {
428             //! Initializes the state with the given parameters.
429             SharedState(File *file, HelpOutputFormat format,
430                         const HelpLinks *links)
431                 : file_(*file), format_(format), links_(links)
432             {
433             }
434
435             //! Output file to which the help is written.
436             File                   &file_;
437             //! Output format for the help output.
438             HelpOutputFormat        format_;
439             //! Links to use.
440             const HelpLinks        *links_;
441         };
442
443         //! Smart pointer type for managing the shared state.
444         typedef boost::shared_ptr<const SharedState> StatePointer;
445
446         //! Initializes the context with the given state.
447         explicit Impl(const StatePointer &state)
448             : state_(state)
449         {
450         }
451
452         /*! \brief
453          * Process markup and wrap lines within a block of text.
454          *
455          * \param[in] text     Text to process.
456          * \param     wrapper  Object used to wrap the text.
457          *
458          * The \p wrapper should take care of either writing the text to output
459          * or providing an interface for the caller to retrieve the output.
460          */
461         void processMarkup(const std::string &text,
462                            WrapperInterface  *wrapper) const;
463
464         //! Constant state shared by all child context objects.
465         StatePointer            state_;
466
467     private:
468         GMX_DISALLOW_ASSIGN(Impl);
469 };
470
471 void HelpWriterContext::Impl::processMarkup(const std::string &text,
472                                             WrapperInterface  *wrapper) const
473 {
474     const char *program = ProgramInfo::getInstance().programName().c_str();
475     std::string result(text);
476     result = replaceAll(result, "[PROGRAM]", program);
477     switch (state_->format_)
478     {
479         case eHelpOutputFormat_Console:
480         {
481             result = repall(result, sandrTty);
482             if (wrapper->settings().lineLength() == 0)
483             {
484                 wrapper->settings().setLineLength(78);
485             }
486             return wrapper->wrap(result);
487         }
488         case eHelpOutputFormat_Man:
489         {
490             result = repall(result, sandrMan);
491             return wrapper->wrap(result);
492         }
493         case eHelpOutputFormat_Html:
494         {
495             result = repall(result, sandrHtml);
496             if (state_->links_ != NULL)
497             {
498                 HelpLinks::Impl::LinkList::const_iterator link;
499                 for (link  = state_->links_->impl_->links_.begin();
500                      link != state_->links_->impl_->links_.end(); ++link)
501                 {
502                     std::string replacement
503                         = formatString("<a href=\"%s.html\">%s</a>",
504                                        link->targetName.c_str(),
505                                        link->linkName.c_str());
506                     result = replaceAllWords(result, link->linkName, replacement);
507                 }
508             }
509             return wrapper->wrap(result);
510         }
511         default:
512             GMX_THROW(InternalError("Invalid help output format"));
513     }
514 }
515
516 /********************************************************************
517  * HelpWriterContext
518  */
519
520 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format)
521     : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, NULL))))
522 {
523 }
524
525 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format,
526                                      const HelpLinks *links)
527     : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, links))))
528 {
529 }
530
531 HelpWriterContext::HelpWriterContext(Impl *impl)
532     : impl_(impl)
533 {
534 }
535
536 HelpWriterContext::HelpWriterContext(const HelpWriterContext &other)
537     : impl_(new Impl(*other.impl_))
538 {
539 }
540
541 HelpWriterContext::~HelpWriterContext()
542 {
543 }
544
545 HelpOutputFormat HelpWriterContext::outputFormat() const
546 {
547     return impl_->state_->format_;
548 }
549
550 File &HelpWriterContext::outputFile() const
551 {
552     return impl_->state_->file_;
553 }
554
555 std::string
556 HelpWriterContext::substituteMarkupAndWrapToString(
557         const TextLineWrapperSettings &settings, const std::string &text) const
558 {
559     WrapperToString wrapper(settings);
560     impl_->processMarkup(text, &wrapper);
561     return wrapper.result();
562 }
563
564 std::vector<std::string>
565 HelpWriterContext::substituteMarkupAndWrapToVector(
566         const TextLineWrapperSettings &settings, const std::string &text) const
567 {
568     WrapperToVector wrapper(settings);
569     impl_->processMarkup(text, &wrapper);
570     return wrapper.result();
571 }
572
573 void HelpWriterContext::writeTitle(const std::string &title) const
574 {
575     if (outputFormat() != eHelpOutputFormat_Console)
576     {
577         // TODO: Implement once the situation with Redmine issue #969 is more
578         // clear.
579         GMX_THROW(NotImplementedError(
580                           "This output format is not implemented"));
581     }
582     File &file = outputFile();
583     file.writeLine(toUpperCase(title));
584     file.writeLine();
585 }
586
587 void HelpWriterContext::writeTextBlock(const std::string &text) const
588 {
589     writeTextBlock(TextLineWrapperSettings(), text);
590 }
591
592 void HelpWriterContext::writeTextBlock(const TextLineWrapperSettings &settings,
593                                        const std::string             &text) const
594 {
595     outputFile().writeLine(substituteMarkupAndWrapToString(settings, text));
596 }
597
598 } // namespace gmx