SeqAn3
format_html.hpp
Go to the documentation of this file.
1 // ============================================================================
2 // SeqAn - The Library for Sequence Analysis
3 // ============================================================================
4 //
5 // Copyright (c) 2006-2018, Knut Reinert & Freie Universitaet Berlin
6 // Copyright (c) 2016-2018, Knut Reinert & MPI Molekulare Genetik
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // * Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // * Redistributions in binary form must reproduce the above copyright
15 // notice, this list of conditions and the following disclaimer in the
16 // documentation and/or other materials provided with the distribution.
17 // * Neither the name of Knut Reinert or the FU Berlin nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
25 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 // DAMAGE.
32 //
33 // ============================================================================
34 
40 #pragma once
41 
42 #include <iostream>
43 
46 #include <seqan3/version.hpp>
47 
48 namespace seqan3::detail
49 {
50 
61 class format_html : public format_base
62 {
63 public:
76  template <typename option_type, typename validator_type>
77  void add_option(option_type & value,
78  char const short_id,
79  std::string const & long_id,
80  std::string const & desc,
81  option_spec const & spec,
82  validator_type && validator)
83  {
84  parser_set_up_calls.push_back([=, &value]()
85  {
86  if (!(spec & option_spec::HIDDEN) && (!(spec & option_spec::ADVANCED)))
87  print_list_item(prep_id_for_help(short_id, long_id) + " " + option_type_and_list_info(value),
88  (desc + " " + validator.get_help_page_message()));
89  });
90  }
91 
99  void add_flag(bool & /*value*/,
100  char const short_id,
101  std::string const & long_id,
102  std::string const & desc,
103  option_spec const & spec)
104  {
105  parser_set_up_calls.push_back([=] ()
106  {
107  if (!(spec & option_spec::HIDDEN) && (!(spec & option_spec::ADVANCED)))
108  print_list_item(prep_id_for_help(short_id, long_id), desc);
109  });
110  }
111 
121  template <typename option_type, typename validator_type>
122  void add_positional_option(option_type & value,
123  std::string const & desc,
124  validator_type && validator)
125  {
126  ++positional_option_count;
127 
128  positional_option_calls.push_back([=, &value]()
129  {
130  std::string key{"\\fBARGUMENT " + std::to_string(positional_option_count) + "\\fP " + option_type_and_list_info(value)};
131  print_list_item(key, (desc + " " + validator.get_help_page_message()));
132  });
133  }
134 
138  void parse(argument_parser_meta_data const & parser_meta)
139  {
140  print_header();
141 
142  print_section("Synopsis");
143  _print_synopsis();
144 
145  if (!parser_meta.description.empty())
146  {
147  print_section("Description");
148  for (auto desc : parser_meta.description)
149  print_line(desc);
150  }
151 
152  // add positional options if specified
153  if (!positional_option_calls.empty())
154  print_section("Positional Arguments");
155 
156  // each call will evaluate the function print_list_item()
157  for (auto f : positional_option_calls)
158  f();
159 
160  // add options and flags if specified
161  if (!parser_set_up_calls.empty())
162  print_section("Options");
163 
164  // each call will evaluate the function print_list_item()
165  for (auto f : parser_set_up_calls)
166  f();
167 
168  print_footer();
169 
170  throw parser_interruption();
171  }
172 
176  void add_section(std::string const & title)
177  {
178  parser_set_up_calls.push_back([=] ()
179  {
180  print_section(title);
181  });
182  }
186  void add_subsection(std::string const & title)
187  {
188  parser_set_up_calls.push_back([=] ()
189  {
190  print_subsection(title);
191  });
192  }
193 
199  void add_line(std::string const & text, bool const line_is_paragraph)
200  {
201  parser_set_up_calls.push_back([=] ()
202  {
203  print_line(text, line_is_paragraph);
204  });
205  }
206 
212  void add_list_item(std::string const & key, std::string const & description)
213  {
214  parser_set_up_calls.push_back([=] ()
215  {
216  print_list_item(key, description);
217  });
218  }
219 
220 private:
222  void maybe_close_list()
223  {
224  if (is_dl)
225  {
226  std::cout << "</dl>\n";
227  is_dl = false;
228  }
229  }
230 
232  void maybe_close_paragraph()
233  {
234  if (is_p)
235  {
236  std::cout << "</p>\n";
237  is_p = false;
238  }
239  }
240 
242  void print_header()
243  {
244  // Print HTML boilerplate header.
245  std::cout << "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
246  << "http://www.w3.org/TR/html4/strict.dtd\">\n"
247  << "<html lang=\"en\">\n"
248  << "<head>\n"
249  << "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n"
250  << "<title>" << escape_special_xml_chars(meta.app_name) << " &mdash; "
251  << escape_special_xml_chars(meta.short_description) << "</title>\n"
252  << "</head>\n"
253  << "<body>\n";
254 
255  std::cout << "<h1>" << to_html(meta.app_name) << "</h1>\n"
256  << "<div>" << to_html(meta.short_description) << "</div>\n";
257  }
258 
260  void _print_synopsis()
261  {
262  for (unsigned i = 0; i < meta.synopsis.size(); ++i)
263  {
264  std::string text = "\\fB";
265  text.append(meta.app_name);
266  text.append("\\fP ");
267  text.append(meta.synopsis[i]);
268 
269  print_line(text, false);
270  }
271  }
272 
276  void print_section(std::string const & title)
277  {
278  // SEQAN_ASSERT_NOT_MSG(isDl && isP, "Current <dl> and <p> are mutually exclusive.");
279  maybe_close_list();
280  maybe_close_paragraph();
281  std::cout << "<h2>" << to_html(title) << "</h2>\n";
282  }
283 
287  void print_subsection(std::string const & title)
288  {
289  // SEQAN_ASSERT_NOT_MSG(isDl && isP, "Current <dl> and <p> are mutually exclusive.");
290  maybe_close_list();
291  maybe_close_paragraph();
292  std::cout << "<h3>" << to_html(title) << "</h3>\n";
293  }
294 
300  void print_line(std::string const & text, bool line_is_paragraph)
301  {
302  // SEQAN_ASSERT_NOT_MSG(isDl && isP, "Current <dl> and <p> are mutually exclusive.");
303  maybe_close_list();
304  if (!is_p) // open parapgraph
305  {
306  std::cout << "<p>\n";
307  is_p = true;
308  }
309  std::cout << to_html(text) << "\n";
310  if (line_is_paragraph)
311  maybe_close_paragraph();
312  else
313  std::cout << "<br />\n";
314  }
315 
319  void print_line(std::string const & text)
320  {
321  print_line(text, true);
322  }
323 
333  void print_list_item(std::string const & term, std::string const & desc)
334  {
335  // SEQAN_ASSERT_NOT_MSG(isDl && isP, "Current <dl> and <p> are mutually exclusive.");
336  maybe_close_paragraph();
337 
338  if (!is_dl)
339  {
340  std::cout << "<dl>\n";
341  is_dl = true;
342  }
343  std::cout << "<dt>" << to_html(term) << "</dt>\n"
344  << "<dd>" << to_html(desc) << "</dd>\n";
345  }
346 
348  void print_footer()
349  {
350  maybe_close_list();
351 
352  // Print version, date and url.
353  std::cout << "<h2>Version</h2>\n"
354  << "<strong>Last update:</strong> " << to_html(meta.date) << "<br>\n<strong>"
355  << meta.app_name << " version:</strong> " << meta.version << "<br>\n"
356  << "<strong>SeqAn version:</strong> " << SEQAN3_VERSION_MAJOR << '.' << SEQAN3_VERSION_MINOR << '.'
358 
359  if (!meta.url.empty())
360  {
361  std::cout << "<h2>Url</h2>\n"
362  << meta.url << "<br>\n";
363  }
364  std::cout << "<br>\n";
365 
366  // Print legal stuff
367  if ((!meta.short_copyright.empty()) || (!meta.long_copyright.empty()) || (!meta.citation.empty()))
368  {
369  std::cout << "<h2>Legal</h2>\n<strong>";
370 
371  if (!meta.short_copyright.empty())
372  std::cout << meta.app_name << " Copyright: </strong>"
373  << meta.short_copyright << "<br>\n<strong>";
374 
375  std::cout << "SeqAn Copyright:</strong> 2006-2015 Knut Reinert, FU-Berlin; released under the 3-clause BSDL.<br>\n<strong>";
376 
377  if (!meta.citation.empty())
378  std::cout << "In your academic works please cite:</strong> " << meta.citation << "<br>\n";
379  else
380  std::cout << "</strong>";
381 
382  if (!meta.long_copyright.empty())
383  std::cout << "For full copyright and/or warranty information see <tt>--copyright</tt>.\n";
384  }
385 
386  // Print HTML boilerplate footer.
387  std::cout << "</body></html>";
388  }
389 
394  std::string to_html(std::string const & input)
395  {
396  std::string buffer = escape_special_xml_chars(input);
397  std::string result;
398  std::vector<std::string> open_tags; // acts as a stack of html tags
399 
400  for (auto it = input.begin(); it != input.end(); ++it)
401  {
402  if (*it == '\\')
403  {
404  // Handle escape sequence, we interpret only "\-", "\fI", and "\fB".
405  ++it;
406  assert(!(it == input.end()));
407  if (*it == '-')
408  {
409  result.push_back(*it);
410  }
411  else if (*it == 'f')
412  {
413  ++it;
414  assert(!(it == input.end()));
415  if (*it == 'I')
416  {
417  open_tags.push_back("em");
418  result.append("<em>");
419  }
420  else if (*it == 'B')
421  {
422  open_tags.push_back("strong");
423  result.append("<strong>");
424  }
425  else if (*it == 'P')
426  {
427  assert(!open_tags.empty());
428  result.append("</");
429  result.append(open_tags.back());
430  result.append(">");
431  open_tags.pop_back();
432  }
433  else
434  {
435  result.append("\\f");
436  result.push_back(*it);
437  }
438  }
439  else
440  {
441  result.push_back('\\');
442  result.push_back(*it);
443  }
444  }
445  else
446  {
447  result.push_back(*it);
448  }
449  }
450 
451  return result;
452  }
453 
468  argument_parser_meta_data meta;
470  std::vector<std::function<void()>> parser_set_up_calls;
472  std::vector<std::function<void()>> positional_option_calls; // singled out to be printed on top
474  bool is_dl{false};
476  bool is_p{false};
478  unsigned positional_option_count{0};
479 };
480 
481 } // namespace seqan3
#define SEQAN3_VERSION_MINOR
The minor version as MACRO.
Definition: version.hpp:49
Contains the format_base struct containing all helper functions that are needed in all formats...
Definition: auxiliary.hpp:72
Checks if program is run interactively and retrieves dimensions of terminal (Transferred from seqan2)...
Definition: aligned_sequence_concept.hpp:288
Definition: auxiliary.hpp:68
option_spec
Used to further specify argument_parser options/flags.
Definition: auxiliary.hpp:60
#define SEQAN3_VERSION_PATCH
The patch version as MACRO.
Definition: version.hpp:51
#define SEQAN3_VERSION_MAJOR
The major version as MACRO.
Definition: version.hpp:47
Contains SeqAn version macros and global variables.