1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1998 - 2020 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 
17 #include <deal.II/base/logstream.h>
18 #include <deal.II/base/memory_consumption.h>
19 #include <deal.II/base/path_search.h>
20 #include <deal.II/base/patterns.h>
21 #include <deal.II/base/utilities.h>
22 
23 DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
24 #define BOOST_BIND_GLOBAL_PLACEHOLDERS
25 #include <boost/io/ios_state.hpp>
26 #include <boost/property_tree/json_parser.hpp>
27 #include <boost/property_tree/ptree.hpp>
28 #include <boost/property_tree/xml_parser.hpp>
29 #undef BOOST_BIND_GLOBAL_PLACEHOLDERS
30 DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
31 
32 #include <algorithm>
33 #include <cctype>
34 #include <cstdlib>
35 #include <cstring>
36 #include <fstream>
37 #include <iomanip>
38 #include <iostream>
39 #include <limits>
40 #include <sstream>
41 
42 
43 DEAL_II_NAMESPACE_OPEN
44 
45 
46 
47 // TODO[WB]: various functions here could be simplified by using namespace
48 // Utilities
49 
50 namespace Patterns
51 {
52   namespace internal
53   {
54     std::string
escape(const std::string & input,const PatternBase::OutputStyle style)55     escape(const std::string &input, const PatternBase::OutputStyle style)
56     {
57       switch (style)
58         {
59           case PatternBase::Machine:
60           case PatternBase::Text:
61             return input;
62           case PatternBase::LaTeX:
63             {
64               std::string u;
65               u.reserve(input.size());
66               for (const auto c : input)
67                 {
68                   switch (c)
69                     {
70                       case '#':
71                       case '$':
72                       case '%':
73                       case '&':
74                       case '_':
75                       case '{':
76                       case '}':
77                         // simple escaping:
78                         u.push_back('\\');
79                         u.push_back(c);
80                         break;
81 
82                       case '\\':
83                         u.append("\\textbackslash{}");
84                         break;
85 
86                       case '^':
87                         u.append("\\^{}");
88                         break;
89 
90                       case '~':
91                         u.append("\\~{}");
92                         break;
93 
94                       default:
95                         // all other chars are just copied:
96                         u.push_back(c);
97                     }
98                 }
99               return u;
100             }
101           default:
102             Assert(false, ExcNotImplemented());
103         }
104       return "";
105     }
106 
107   } // namespace internal
108 
109 
110   namespace
111   {
112     /**
113      * Read to the end of the stream and
114      * return whether all there is is
115      * whitespace or whether there are other
116      * characters as well.
117      */
118     bool
has_only_whitespace(std::istream & in)119     has_only_whitespace(std::istream &in)
120     {
121       while (in)
122         {
123           char c;
124 
125           // skip if we've reached the end of
126           // the line
127           if (!(in >> c))
128             break;
129 
130           if ((c != ' ') && (c != '\t'))
131             return false;
132         }
133       return true;
134     }
135   } // namespace
136 
137 
138 
139   std::unique_ptr<PatternBase>
pattern_factory(const std::string & description)140   pattern_factory(const std::string &description)
141   {
142     std::unique_ptr<PatternBase> p;
143 
144     p = Integer::create(description);
145     if (p != nullptr)
146       return p;
147 
148     p = Double::create(description);
149     if (p != nullptr)
150       return p;
151 
152     p = Selection::create(description);
153     if (p != nullptr)
154       return p;
155 
156     p = List::create(description);
157     if (p != nullptr)
158       return p;
159 
160     p = Map::create(description);
161     if (p != nullptr)
162       return p;
163 
164     p = MultipleSelection::create(description);
165     if (p != nullptr)
166       return p;
167 
168     p = Bool::create(description);
169     if (p != nullptr)
170       return p;
171 
172     p = Anything::create(description);
173     if (p != nullptr)
174       return p;
175 
176     p = FileName::create(description);
177     if (p != nullptr)
178       return p;
179 
180     p = DirectoryName::create(description);
181     if (p != nullptr)
182       return p;
183 
184     Assert(false, ExcNotImplemented());
185 
186     return p;
187   }
188 
189 
190 
191   std::size_t
memory_consumption() const192   PatternBase::memory_consumption() const
193   {
194     if (dynamic_cast<const Integer *>(this) != nullptr)
195       return sizeof(Integer);
196     else if (dynamic_cast<const Double *>(this) != nullptr)
197       return sizeof(Double);
198     else if (dynamic_cast<const Bool *>(this) != nullptr)
199       return sizeof(Bool);
200     else if (dynamic_cast<const Anything *>(this) != nullptr)
201       return sizeof(Anything);
202     else
203       return sizeof(*this) + 32;
204   }
205 
206 
207 
208   const int Integer::min_int_value = std::numeric_limits<int>::min();
209   const int Integer::max_int_value = std::numeric_limits<int>::max();
210 
211   const char *Integer::description_init = "[Integer";
212 
Integer(const int lower_bound,const int upper_bound)213   Integer::Integer(const int lower_bound, const int upper_bound)
214     : lower_bound(lower_bound)
215     , upper_bound(upper_bound)
216   {}
217 
218 
219 
220   bool
match(const std::string & test_string) const221   Integer::match(const std::string &test_string) const
222   {
223     std::istringstream str(test_string);
224 
225     int i;
226     if (!(str >> i))
227       return false;
228 
229     if (!has_only_whitespace(str))
230       return false;
231     // check whether valid bounds
232     // were specified, and if so
233     // enforce their values
234     if (lower_bound <= upper_bound)
235       return ((lower_bound <= i) && (upper_bound >= i));
236     else
237       return true;
238   }
239 
240 
241 
242   std::string
description(const OutputStyle style) const243   Integer::description(const OutputStyle style) const
244   {
245     switch (style)
246       {
247         case Machine:
248           {
249             // check whether valid bounds
250             // were specified, and if so
251             // output their values
252             if (lower_bound <= upper_bound)
253               {
254                 std::ostringstream description;
255 
256                 description << description_init << " range " << lower_bound
257                             << "..." << upper_bound << " (inclusive)]";
258                 return description.str();
259               }
260             else
261               // if no bounds were given, then
262               // return generic string
263               return "[Integer]";
264           }
265         case Text:
266           {
267             if (lower_bound <= upper_bound)
268               {
269                 std::ostringstream description;
270 
271                 description << "An integer n such that " << lower_bound
272                             << " <= n <= " << upper_bound;
273 
274                 return description.str();
275               }
276             else
277               return "An integer";
278           }
279         case LaTeX:
280           {
281             if (lower_bound <= upper_bound)
282               {
283                 std::ostringstream description;
284 
285                 description << "An integer $n$ such that $" << lower_bound
286                             << "\\leq n \\leq " << upper_bound << "$";
287 
288                 return description.str();
289               }
290             else
291               return "An integer";
292           }
293         default:
294           AssertThrow(false, ExcNotImplemented());
295       }
296     // Should never occur without an exception, but prevent compiler from
297     // complaining
298     return "";
299   }
300 
301 
302 
303   std::unique_ptr<PatternBase>
clone() const304   Integer::clone() const
305   {
306     return std::unique_ptr<PatternBase>(new Integer(lower_bound, upper_bound));
307   }
308 
309 
310 
311   std::unique_ptr<Integer>
create(const std::string & description)312   Integer::create(const std::string &description)
313   {
314     if (description.compare(0,
315                             std::strlen(description_init),
316                             description_init) == 0)
317       {
318         std::istringstream is(description);
319 
320         if (is.str().size() > strlen(description_init) + 1)
321           {
322             // TODO: verify that description matches the pattern "^\[Integer
323             // range \d+\.\.\.\d+\]$"
324             int lower_bound, upper_bound;
325 
326             is.ignore(strlen(description_init) + strlen(" range "));
327 
328             if (!(is >> lower_bound))
329               return std::make_unique<Integer>();
330 
331             is.ignore(strlen("..."));
332 
333             if (!(is >> upper_bound))
334               return std::make_unique<Integer>();
335 
336             return std::make_unique<Integer>(lower_bound, upper_bound);
337           }
338         else
339           return std::make_unique<Integer>();
340       }
341     else
342       return std::unique_ptr<Integer>();
343   }
344 
345 
346 
347   const double Double::min_double_value = -std::numeric_limits<double>::max();
348   const double Double::max_double_value = std::numeric_limits<double>::max();
349 
350   const char *Double::description_init = "[Double";
351 
Double(const double lower_bound,const double upper_bound)352   Double::Double(const double lower_bound, const double upper_bound)
353     : lower_bound(lower_bound)
354     , upper_bound(upper_bound)
355   {}
356 
357 
358 
359   bool
match(const std::string & test_string) const360   Double::match(const std::string &test_string) const
361   {
362     std::istringstream str(test_string);
363 
364     double d;
365     str >> d;
366     if (str.fail())
367       return false;
368 
369     if (!has_only_whitespace(str))
370       return false;
371     // check whether valid bounds
372     // were specified, and if so
373     // enforce their values
374     if (lower_bound <= upper_bound)
375       return ((lower_bound <= d) && (upper_bound >= d));
376     else
377       return true;
378   }
379 
380 
381 
382   std::string
description(const OutputStyle style) const383   Double::description(const OutputStyle style) const
384   {
385     switch (style)
386       {
387         case Machine:
388           {
389             std::ostringstream description;
390 
391             if (lower_bound <= upper_bound)
392               {
393                 // bounds are valid
394                 description << description_init << " ";
395                 // We really want to compare with ==, but -Wfloat-equal would
396                 // create a warning here, so work around it.
397                 if (0 == std::memcmp(&lower_bound,
398                                      &min_double_value,
399                                      sizeof(lower_bound)))
400                   description << "-MAX_DOUBLE";
401                 else
402                   description << lower_bound;
403                 description << "...";
404                 if (0 == std::memcmp(&upper_bound,
405                                      &max_double_value,
406                                      sizeof(upper_bound)))
407                   description << "MAX_DOUBLE";
408                 else
409                   description << upper_bound;
410                 description << " (inclusive)]";
411                 return description.str();
412               }
413             else
414               {
415                 // invalid bounds, assume unbounded double:
416                 description << description_init << "]";
417                 return description.str();
418               }
419           }
420         case Text:
421           {
422             if (lower_bound <= upper_bound)
423               {
424                 std::ostringstream description;
425 
426                 description << "A floating point number v such that ";
427                 if (0 == std::memcmp(&lower_bound,
428                                      &min_double_value,
429                                      sizeof(lower_bound)))
430                   description << "-MAX_DOUBLE";
431                 else
432                   description << lower_bound;
433                 description << " <= v <= ";
434                 if (0 == std::memcmp(&upper_bound,
435                                      &max_double_value,
436                                      sizeof(upper_bound)))
437                   description << "MAX_DOUBLE";
438                 else
439                   description << upper_bound;
440 
441                 return description.str();
442               }
443             else
444               return "A floating point number";
445           }
446         case LaTeX:
447           {
448             if (lower_bound <= upper_bound)
449               {
450                 std::ostringstream description;
451 
452                 description << "A floating point number $v$ such that $";
453                 if (0 == std::memcmp(&lower_bound,
454                                      &min_double_value,
455                                      sizeof(lower_bound)))
456                   description << "-\\text{MAX\\_DOUBLE}";
457                 else
458                   description << lower_bound;
459                 description << " \\leq v \\leq ";
460                 if (0 == std::memcmp(&upper_bound,
461                                      &max_double_value,
462                                      sizeof(upper_bound)))
463                   description << "\\text{MAX\\_DOUBLE}";
464                 else
465                   description << upper_bound;
466                 description << "$";
467 
468                 return description.str();
469               }
470             else
471               return "A floating point number";
472           }
473         default:
474           AssertThrow(false, ExcNotImplemented());
475       }
476     // Should never occur without an exception, but prevent compiler from
477     // complaining
478     return "";
479   }
480 
481 
482   std::unique_ptr<PatternBase>
clone() const483   Double::clone() const
484   {
485     return std::unique_ptr<PatternBase>(new Double(lower_bound, upper_bound));
486   }
487 
488 
489 
490   std::unique_ptr<Double>
create(const std::string & description)491   Double::create(const std::string &description)
492   {
493     const std::string description_init_str = description_init;
494     if (description.compare(0,
495                             description_init_str.size(),
496                             description_init_str) != 0)
497       return std::unique_ptr<Double>();
498     if (*description.rbegin() != ']')
499       return std::unique_ptr<Double>();
500 
501     std::string temp = description.substr(description_init_str.size());
502     if (temp == "]")
503       return std::make_unique<Double>(1.0,
504                                       -1.0); // return an invalid range
505 
506     if (temp.find("...") != std::string::npos)
507       temp.replace(temp.find("..."), 3, " ");
508 
509     double lower_bound = min_double_value, upper_bound = max_double_value;
510 
511     std::istringstream is(temp);
512     if (0 == temp.compare(0, std::strlen(" -MAX_DOUBLE"), " -MAX_DOUBLE"))
513       is.ignore(std::strlen(" -MAX_DOUBLE"));
514     else
515       {
516         // parse lower bound and give up if not a double
517         if (!(is >> lower_bound))
518           return std::unique_ptr<Double>();
519       }
520 
521     // ignore failure here and assume we got MAX_DOUBLE as upper bound:
522     is >> upper_bound;
523     if (is.fail())
524       upper_bound = max_double_value;
525 
526     return std::make_unique<Double>(lower_bound, upper_bound);
527   }
528 
529 
530 
531   const char *Selection::description_init = "[Selection";
532 
533 
Selection(const std::string & seq)534   Selection::Selection(const std::string &seq)
535     : sequence(seq)
536   {
537     while (sequence.find(" |") != std::string::npos)
538       sequence.replace(sequence.find(" |"), 2, "|");
539     while (sequence.find("| ") != std::string::npos)
540       sequence.replace(sequence.find("| "), 2, "|");
541   }
542 
543 
544 
545   bool
match(const std::string & test_string) const546   Selection::match(const std::string &test_string) const
547   {
548     std::string tmp(sequence);
549 
550     // remove whitespace at beginning
551     while ((tmp.length() != 0) && (std::isspace(tmp[0])))
552       tmp.erase(0, 1);
553 
554     // check the different possibilities
555     while (tmp.find('|') != std::string::npos)
556       {
557         if (test_string == std::string(tmp, 0, tmp.find('|')))
558           return true;
559 
560         tmp.erase(0, tmp.find('|') + 1);
561       }
562 
563     // remove whitespace at the end
564     while ((tmp.length() != 0) && (std::isspace(*(tmp.end() - 1))))
565       tmp.erase(tmp.end() - 1);
566 
567     // check last choice, not finished by |
568     if (test_string == tmp)
569       return true;
570 
571     // not found
572     return false;
573   }
574 
575 
576 
577   std::string
description(const OutputStyle style) const578   Selection::description(const OutputStyle style) const
579   {
580     switch (style)
581       {
582         case Machine:
583           {
584             std::ostringstream description;
585 
586             description << description_init << " " << sequence << " ]";
587 
588             return description.str();
589           }
590         case Text:
591         case LaTeX:
592           {
593             std::ostringstream description;
594 
595             description << "Any one of "
596                         << internal::escape(
597                              Utilities::replace_in_string(sequence, "|", ", "),
598                              style);
599 
600             return description.str();
601           }
602         default:
603           AssertThrow(false, ExcNotImplemented());
604       }
605     // Should never occur without an exception, but prevent compiler from
606     // complaining
607     return "";
608   }
609 
610 
611 
612   std::unique_ptr<PatternBase>
clone() const613   Selection::clone() const
614   {
615     return std::unique_ptr<PatternBase>(new Selection(sequence));
616   }
617 
618 
619   std::size_t
memory_consumption() const620   Selection::memory_consumption() const
621   {
622     return (sizeof(PatternBase) +
623             MemoryConsumption::memory_consumption(sequence));
624   }
625 
626 
627 
628   std::unique_ptr<Selection>
create(const std::string & description)629   Selection::create(const std::string &description)
630   {
631     if (description.compare(0,
632                             std::strlen(description_init),
633                             description_init) == 0)
634       {
635         std::string sequence(description);
636 
637         sequence.erase(0, std::strlen(description_init) + 1);
638         sequence.erase(sequence.length() - 2, 2);
639 
640         return std::make_unique<Selection>(sequence);
641       }
642     else
643       return std::unique_ptr<Selection>();
644   }
645 
646 
647 
648   const unsigned int List::max_int_value =
649     std::numeric_limits<unsigned int>::max();
650 
651   const char *List::description_init = "[List";
652 
653 
List(const PatternBase & p,const unsigned int min_elements,const unsigned int max_elements,const std::string & separator)654   List::List(const PatternBase &p,
655              const unsigned int min_elements,
656              const unsigned int max_elements,
657              const std::string &separator)
658     : pattern(p.clone())
659     , min_elements(min_elements)
660     , max_elements(max_elements)
661     , separator(separator)
662   {
663     Assert(min_elements <= max_elements,
664            ExcInvalidRange(min_elements, max_elements));
665     Assert(separator.size() > 0,
666            ExcMessage("The separator must have a non-zero length."));
667   }
668 
669 
670 
List(const List & other)671   List::List(const List &other)
672     : pattern(other.pattern->clone())
673     , min_elements(other.min_elements)
674     , max_elements(other.max_elements)
675     , separator(other.separator)
676   {}
677 
678 
679   const std::string &
get_separator() const680   List::get_separator() const
681   {
682     return separator;
683   }
684 
685 
686 
687   const PatternBase &
get_base_pattern() const688   List::get_base_pattern() const
689   {
690     return *pattern;
691   }
692 
693 
694 
695   bool
match(const std::string & test_string_list) const696   List::match(const std::string &test_string_list) const
697   {
698     const std::vector<std::string> split_list =
699       Utilities::split_string_list(test_string_list, separator);
700 
701     if ((split_list.size() < min_elements) ||
702         (split_list.size() > max_elements))
703       return false;
704 
705     // check the different possibilities
706     for (const std::string &string : split_list)
707       if (pattern->match(string) == false)
708         return false;
709 
710     return true;
711   }
712 
713 
714 
715   std::string
description(const OutputStyle style) const716   List::description(const OutputStyle style) const
717   {
718     switch (style)
719       {
720         case Machine:
721           {
722             std::ostringstream description;
723 
724             description << description_init << " of <"
725                         << pattern->description(style) << ">"
726                         << " of length " << min_elements << "..."
727                         << max_elements << " (inclusive)";
728             if (separator != ",")
729               description << " separated by <" << separator << ">";
730             description << "]";
731 
732             return description.str();
733           }
734         case Text:
735         case LaTeX:
736           {
737             std::ostringstream description;
738 
739             description << "A list of " << min_elements << " to "
740                         << max_elements << " elements ";
741             if (separator != ",")
742               description << "separated by <"
743                           << internal::escape(separator, style) << "> ";
744             description << "where each element is ["
745                         << pattern->description(style) << "]";
746 
747             return description.str();
748           }
749         default:
750           AssertThrow(false, ExcNotImplemented());
751       }
752     // Should never occur without an exception, but prevent compiler from
753     // complaining
754     return "";
755   }
756 
757 
758 
759   std::unique_ptr<PatternBase>
clone() const760   List::clone() const
761   {
762     return std::unique_ptr<PatternBase>(
763       new List(*pattern, min_elements, max_elements, separator));
764   }
765 
766 
767   std::size_t
memory_consumption() const768   List::memory_consumption() const
769   {
770     return (sizeof(*this) + MemoryConsumption::memory_consumption(*pattern) +
771             MemoryConsumption::memory_consumption(separator));
772   }
773 
774 
775 
776   std::unique_ptr<List>
create(const std::string & description)777   List::create(const std::string &description)
778   {
779     if (description.compare(0,
780                             std::strlen(description_init),
781                             description_init) == 0)
782       {
783         unsigned int min_elements = 0, max_elements = 0;
784 
785         std::istringstream is(description);
786         is.ignore(strlen(description_init) + strlen(" of <"));
787 
788         std::string str;
789         std::getline(is, str, '>');
790 
791         std::unique_ptr<PatternBase> base_pattern(pattern_factory(str));
792 
793         is.ignore(strlen(" of length "));
794         if (!(is >> min_elements))
795           return std::make_unique<List>(*base_pattern);
796 
797         is.ignore(strlen("..."));
798         if (!(is >> max_elements))
799           return std::make_unique<List>(*base_pattern, min_elements);
800 
801         is.ignore(strlen(" (inclusive) separated by <"));
802         std::string separator;
803         if (!is.eof())
804           std::getline(is, separator, '>');
805         else
806           separator = ",";
807 
808         return std::make_unique<List>(*base_pattern,
809                                       min_elements,
810                                       max_elements,
811                                       separator);
812       }
813     else
814       return std::unique_ptr<List>();
815   }
816 
817 
818 
819   const unsigned int Map::max_int_value =
820     std::numeric_limits<unsigned int>::max();
821 
822   const char *Map::description_init = "[Map";
823 
824 
Map(const PatternBase & p_key,const PatternBase & p_value,const unsigned int min_elements,const unsigned int max_elements,const std::string & separator,const std::string & key_value_separator)825   Map::Map(const PatternBase &p_key,
826            const PatternBase &p_value,
827            const unsigned int min_elements,
828            const unsigned int max_elements,
829            const std::string &separator,
830            const std::string &key_value_separator)
831     : key_pattern(p_key.clone())
832     , value_pattern(p_value.clone())
833     , min_elements(min_elements)
834     , max_elements(max_elements)
835     , separator(separator)
836     , key_value_separator(key_value_separator)
837   {
838     Assert(min_elements <= max_elements,
839            ExcInvalidRange(min_elements, max_elements));
840     Assert(separator.size() > 0,
841            ExcMessage("The separator must have a non-zero length."));
842     Assert(key_value_separator.size() > 0,
843            ExcMessage("The key_value_separator must have a non-zero length."));
844     Assert(separator != key_value_separator,
845            ExcMessage(
846              "The separator can not be the same of the key_value_separator "
847              "since that is used as the separator between the two elements "
848              "of <key:value> pairs"));
849   }
850 
851 
852 
Map(const Map & other)853   Map::Map(const Map &other)
854     : key_pattern(other.key_pattern->clone())
855     , value_pattern(other.value_pattern->clone())
856     , min_elements(other.min_elements)
857     , max_elements(other.max_elements)
858     , separator(other.separator)
859     , key_value_separator(other.key_value_separator)
860   {}
861 
862 
863 
864   bool
match(const std::string & test_string_list) const865   Map::match(const std::string &test_string_list) const
866   {
867     std::vector<std::string> split_list =
868       Utilities::split_string_list(test_string_list, separator);
869     if ((split_list.size() < min_elements) ||
870         (split_list.size() > max_elements))
871       return false;
872 
873     for (const auto &key_value_pair : split_list)
874       {
875         std::vector<std::string> pair =
876           Utilities::split_string_list(key_value_pair, key_value_separator);
877 
878         // Check that we have in fact two matches
879         if (pair.size() != 2)
880           return false;
881 
882         // then verify that the patterns are satisfied
883         if (key_pattern->match(pair[0]) == false)
884           return false;
885         if (value_pattern->match(pair[1]) == false)
886           return false;
887       }
888 
889     return true;
890   }
891 
892 
893 
894   std::string
description(const OutputStyle style) const895   Map::description(const OutputStyle style) const
896   {
897     switch (style)
898       {
899         case Machine:
900           {
901             std::ostringstream description;
902 
903             description << description_init << " of <"
904                         << key_pattern->description(style) << ">"
905                         << key_value_separator << "<"
906                         << value_pattern->description(style) << ">"
907                         << " of length " << min_elements << "..."
908                         << max_elements << " (inclusive)";
909             if (separator != ",")
910               description << " separated by <" << separator << ">";
911             description << "]";
912 
913             return description.str();
914           }
915         case Text:
916         case LaTeX:
917           {
918             std::ostringstream description;
919 
920             description << "A key"
921                         << internal::escape(key_value_separator, style)
922                         << "value map of " << min_elements << " to "
923                         << max_elements << " elements ";
924             if (separator != ",")
925               description << " separated by <"
926                           << internal::escape(separator, style) << "> ";
927             description << " where each key is ["
928                         << key_pattern->description(style) << "]"
929                         << " and each value is ["
930                         << value_pattern->description(style) << "]";
931 
932             return description.str();
933           }
934         default:
935           AssertThrow(false, ExcNotImplemented());
936       }
937     // Should never occur without an exception, but prevent compiler from
938     // complaining
939     return "";
940   }
941 
942 
943 
944   std::unique_ptr<PatternBase>
clone() const945   Map::clone() const
946   {
947     return std::unique_ptr<PatternBase>(new Map(*key_pattern,
948                                                 *value_pattern,
949                                                 min_elements,
950                                                 max_elements,
951                                                 separator,
952                                                 key_value_separator));
953   }
954 
955 
956   std::size_t
memory_consumption() const957   Map::memory_consumption() const
958   {
959     return (sizeof(*this) +
960             MemoryConsumption::memory_consumption(*key_pattern) +
961             MemoryConsumption::memory_consumption(*value_pattern) +
962             MemoryConsumption::memory_consumption(separator) +
963             MemoryConsumption::memory_consumption(key_value_separator));
964   }
965 
966 
967 
968   std::unique_ptr<Map>
create(const std::string & description)969   Map::create(const std::string &description)
970   {
971     if (description.compare(0,
972                             std::strlen(description_init),
973                             description_init) == 0)
974       {
975         unsigned int min_elements = 0, max_elements = 0;
976 
977         std::istringstream is(description);
978         is.ignore(strlen(description_init) + strlen(" of <"));
979 
980         std::string key;
981         std::getline(is, key, '>');
982 
983         std::string key_value_separator;
984         std::getline(is, key_value_separator, '<');
985 
986         // split 'str' into key and value
987         std::string value;
988         std::getline(is, value, '>');
989 
990         std::unique_ptr<PatternBase> key_pattern(pattern_factory(key));
991         std::unique_ptr<PatternBase> value_pattern(pattern_factory(value));
992 
993         is.ignore(strlen(" of length "));
994         if (!(is >> min_elements))
995           return std::make_unique<Map>(*key_pattern, *value_pattern);
996 
997         is.ignore(strlen("..."));
998         if (!(is >> max_elements))
999           return std::make_unique<Map>(*key_pattern,
1000                                        *value_pattern,
1001                                        min_elements);
1002 
1003         is.ignore(strlen(" (inclusive) separated by <"));
1004         std::string separator;
1005         if (!is.eof())
1006           std::getline(is, separator, '>');
1007         else
1008           separator = ",";
1009 
1010         return std::make_unique<Map>(*key_pattern,
1011                                      *value_pattern,
1012                                      min_elements,
1013                                      max_elements,
1014                                      separator,
1015                                      key_value_separator);
1016       }
1017     else
1018       return std::unique_ptr<Map>();
1019   }
1020 
1021 
1022 
1023   const PatternBase &
get_key_pattern() const1024   Map::get_key_pattern() const
1025   {
1026     return *key_pattern;
1027   }
1028 
1029 
1030 
1031   const PatternBase &
get_value_pattern() const1032   Map::get_value_pattern() const
1033   {
1034     return *value_pattern;
1035   }
1036 
1037 
1038 
1039   const std::string &
get_separator() const1040   Map::get_separator() const
1041   {
1042     return separator;
1043   }
1044 
1045 
1046   const std::string &
get_key_value_separator() const1047   Map::get_key_value_separator() const
1048   {
1049     return key_value_separator;
1050   }
1051 
1052 
1053 
1054   const char *Tuple::description_init = "[Tuple";
1055 
1056 
Tuple(const std::vector<std::unique_ptr<PatternBase>> & ps,const std::string & separator)1057   Tuple::Tuple(const std::vector<std::unique_ptr<PatternBase>> &ps,
1058                const std::string &                              separator)
1059     : separator(separator)
1060   {
1061     Assert(ps.size() > 0,
1062            ExcMessage("The Patterns list must have a non-zero length."));
1063     Assert(separator.size() > 0,
1064            ExcMessage("The separator must have a non-zero length."));
1065     patterns.resize(ps.size());
1066     for (unsigned int i = 0; i < ps.size(); ++i)
1067       patterns[i] = ps[i]->clone();
1068   }
1069 
1070 
1071 
Tuple(const std::vector<std::unique_ptr<PatternBase>> & ps,const char * separator)1072   Tuple::Tuple(const std::vector<std::unique_ptr<PatternBase>> &ps,
1073                const char *                                     separator)
1074     : Tuple(ps, std::string(separator))
1075   {}
1076 
1077 
1078 
Tuple(const Tuple & other)1079   Tuple::Tuple(const Tuple &other)
1080     : separator(other.separator)
1081   {
1082     patterns.resize(other.patterns.size());
1083     for (unsigned int i = 0; i < other.patterns.size(); ++i)
1084       patterns[i] = other.patterns[i]->clone();
1085   }
1086 
1087 
1088 
1089   bool
match(const std::string & test_string_list) const1090   Tuple::match(const std::string &test_string_list) const
1091   {
1092     std::vector<std::string> split_list =
1093       Utilities::split_string_list(test_string_list, separator);
1094     if (split_list.size() != patterns.size())
1095       return false;
1096 
1097     for (unsigned int i = 0; i < patterns.size(); ++i)
1098       {
1099         if (patterns[i]->match(split_list[i]) == false)
1100           return false;
1101       }
1102 
1103     return true;
1104   }
1105 
1106 
1107 
1108   std::string
description(const OutputStyle style) const1109   Tuple::description(const OutputStyle style) const
1110   {
1111     switch (style)
1112       {
1113         case Machine:
1114           {
1115             std::ostringstream description;
1116 
1117             description << description_init << " of <" << patterns.size()
1118                         << "> elements <" << patterns[0]->description(style)
1119                         << ">";
1120             for (unsigned int i = 1; i < patterns.size(); ++i)
1121               description << ", <" << patterns[i]->description(style) << ">";
1122 
1123             if (separator != ":")
1124               description << " separated by <" << separator << ">";
1125             description << "]";
1126 
1127             return description.str();
1128           }
1129         case Text:
1130         case LaTeX:
1131           {
1132             std::ostringstream description;
1133 
1134             description << "A Tuple of " << patterns.size() << " elements ";
1135             if (separator != ":")
1136               description << " separated by <"
1137                           << internal::escape(separator, style) << "> ";
1138             description << " where each element is ["
1139                         << patterns[0]->description(style) << "]";
1140             for (unsigned int i = 1; i < patterns.size(); ++i)
1141               {
1142                 description << internal::escape(separator, style) << "["
1143                             << patterns[i]->description(style) << "]";
1144               }
1145             return description.str();
1146           }
1147 
1148         default:
1149           AssertThrow(false, ExcNotImplemented());
1150       }
1151     // Should never occur without an exception, but prevent compiler from
1152     // complaining
1153     return "";
1154   }
1155 
1156 
1157 
1158   std::unique_ptr<PatternBase>
clone() const1159   Tuple::clone() const
1160   {
1161     return std::unique_ptr<PatternBase>(new Tuple(patterns, separator));
1162   }
1163 
1164 
1165   std::size_t
memory_consumption() const1166   Tuple::memory_consumption() const
1167   {
1168     return (sizeof(*this) + MemoryConsumption::memory_consumption(patterns) +
1169             MemoryConsumption::memory_consumption(separator));
1170   }
1171 
1172 
1173 
1174   std::unique_ptr<Tuple>
create(const std::string & description)1175   Tuple::create(const std::string &description)
1176   {
1177     if (description.compare(0,
1178                             std::strlen(description_init),
1179                             description_init) == 0)
1180       {
1181         std::vector<std::unique_ptr<PatternBase>> patterns;
1182 
1183         std::istringstream is(description);
1184         is.ignore(strlen(description_init) + strlen(" of <"));
1185 
1186         std::string len;
1187         std::getline(is, len, '>');
1188         const unsigned int n_elements = Utilities::string_to_int(len);
1189         Assert(n_elements > 0,
1190                ExcMessage("Provide at least 1 element in the tuple."));
1191         patterns.resize(n_elements);
1192 
1193         is.ignore(strlen(" elements <"));
1194 
1195         std::string element;
1196         std::getline(is, element, '>');
1197         patterns[0] = pattern_factory(element);
1198 
1199         for (unsigned int i = 1; i < n_elements; ++i)
1200           {
1201             is.ignore(strlen(", <"));
1202             std::getline(is, element, '>');
1203             patterns[i] = pattern_factory(element);
1204           }
1205 
1206         is.ignore(strlen(" separated by <"));
1207 
1208         std::string separator;
1209         if (!is.eof())
1210           std::getline(is, separator, '>');
1211         else
1212           separator = ":";
1213 
1214         return std::make_unique<Tuple>(patterns, separator);
1215       }
1216     else
1217       return std::unique_ptr<Tuple>();
1218   }
1219 
1220 
1221 
1222   const PatternBase &
get_pattern(const unsigned int i) const1223   Tuple::get_pattern(const unsigned int i) const
1224   {
1225     return *patterns[i];
1226   }
1227 
1228 
1229 
1230   const std::string &
get_separator() const1231   Tuple::get_separator() const
1232   {
1233     return separator;
1234   }
1235 
1236 
1237 
1238   const char *MultipleSelection::description_init = "[MultipleSelection";
1239 
1240 
MultipleSelection(const std::string & seq)1241   MultipleSelection::MultipleSelection(const std::string &seq)
1242   {
1243     Assert(seq.find(',') == std::string::npos,
1244            ExcCommasNotAllowed(seq.find(',')));
1245 
1246     sequence = seq;
1247     while (sequence.find(" |") != std::string::npos)
1248       sequence.replace(sequence.find(" |"), 2, "|");
1249     while (sequence.find("| ") != std::string::npos)
1250       sequence.replace(sequence.find("| "), 2, "|");
1251   }
1252 
1253 
1254 
1255   bool
match(const std::string & test_string_list) const1256   MultipleSelection::match(const std::string &test_string_list) const
1257   {
1258     std::string              tmp = test_string_list;
1259     std::vector<std::string> split_names;
1260 
1261     // first split the input list
1262     while (tmp.length() != 0)
1263       {
1264         std::string name;
1265         name = tmp;
1266 
1267         if (name.find(',') != std::string::npos)
1268           {
1269             name.erase(name.find(','), std::string::npos);
1270             tmp.erase(0, tmp.find(',') + 1);
1271           }
1272         else
1273           tmp = "";
1274 
1275         while ((name.length() != 0) && (std::isspace(name[0])))
1276           name.erase(0, 1);
1277         while (std::isspace(name[name.length() - 1]))
1278           name.erase(name.length() - 1, 1);
1279 
1280         split_names.push_back(name);
1281       }
1282 
1283 
1284     // check the different possibilities
1285     for (const auto &test_string : split_names)
1286       {
1287         bool string_found = false;
1288 
1289         tmp = sequence;
1290         while (tmp.find('|') != std::string::npos)
1291           {
1292             if (test_string == std::string(tmp, 0, tmp.find('|')))
1293               {
1294                 // string found, quit
1295                 // loop. don't change
1296                 // tmp, since we don't
1297                 // need it anymore.
1298                 string_found = true;
1299                 break;
1300               }
1301 
1302             tmp.erase(0, tmp.find('|') + 1);
1303           }
1304         // check last choice, not finished by |
1305         if (!string_found)
1306           if (test_string == tmp)
1307             string_found = true;
1308 
1309         if (!string_found)
1310           return false;
1311       }
1312 
1313     return true;
1314   }
1315 
1316 
1317 
1318   std::string
description(const OutputStyle style) const1319   MultipleSelection::description(const OutputStyle style) const
1320   {
1321     switch (style)
1322       {
1323         case Machine:
1324           {
1325             std::ostringstream description;
1326 
1327             description << description_init << " " << sequence << " ]";
1328 
1329             return description.str();
1330           }
1331         case Text:
1332         case LaTeX:
1333           {
1334             std::ostringstream description;
1335 
1336             description << "A comma-separated list of any of "
1337                         << internal::escape(
1338                              Utilities::replace_in_string(sequence, "|", ", "),
1339                              style);
1340 
1341             return description.str();
1342           }
1343         default:
1344           AssertThrow(false, ExcNotImplemented());
1345       }
1346     // Should never occur without an exception, but prevent compiler from
1347     // complaining
1348     return "";
1349   }
1350 
1351 
1352 
1353   std::unique_ptr<PatternBase>
clone() const1354   MultipleSelection::clone() const
1355   {
1356     return std::unique_ptr<PatternBase>(new MultipleSelection(sequence));
1357   }
1358 
1359 
1360   std::size_t
memory_consumption() const1361   MultipleSelection::memory_consumption() const
1362   {
1363     return (sizeof(PatternBase) +
1364             MemoryConsumption::memory_consumption(sequence));
1365   }
1366 
1367 
1368 
1369   std::unique_ptr<MultipleSelection>
create(const std::string & description)1370   MultipleSelection::create(const std::string &description)
1371   {
1372     if (description.compare(0,
1373                             std::strlen(description_init),
1374                             description_init) == 0)
1375       {
1376         std::string sequence(description);
1377 
1378         sequence.erase(0, std::strlen(description_init) + 1);
1379         sequence.erase(sequence.length() - 2, 2);
1380 
1381         return std::make_unique<MultipleSelection>(sequence);
1382       }
1383     else
1384       return std::unique_ptr<MultipleSelection>();
1385   }
1386 
1387 
1388 
1389   const char *Bool::description_init = "[Bool";
1390 
1391 
Bool()1392   Bool::Bool()
1393     : Selection("true|false")
1394   {}
1395 
1396 
1397 
1398   std::string
description(const OutputStyle style) const1399   Bool::description(const OutputStyle style) const
1400   {
1401     switch (style)
1402       {
1403         case Machine:
1404           {
1405             std::ostringstream description;
1406 
1407             description << description_init << "]";
1408 
1409             return description.str();
1410           }
1411         case Text:
1412         case LaTeX:
1413           {
1414             return "A boolean value (true or false)";
1415           }
1416         default:
1417           AssertThrow(false, ExcNotImplemented());
1418       }
1419     // Should never occur without an exception, but prevent compiler from
1420     // complaining
1421     return "";
1422   }
1423 
1424 
1425 
1426   std::unique_ptr<PatternBase>
clone() const1427   Bool::clone() const
1428   {
1429     return std::unique_ptr<PatternBase>(new Bool());
1430   }
1431 
1432 
1433 
1434   std::unique_ptr<Bool>
create(const std::string & description)1435   Bool::create(const std::string &description)
1436   {
1437     if (description.compare(0,
1438                             std::strlen(description_init),
1439                             description_init) == 0)
1440       return std::make_unique<Bool>();
1441     else
1442       return std::unique_ptr<Bool>();
1443   }
1444 
1445 
1446 
1447   const char *Anything::description_init = "[Anything";
1448 
1449 
1450 
1451   bool
match(const std::string &) const1452   Anything::match(const std::string &) const
1453   {
1454     return true;
1455   }
1456 
1457 
1458 
1459   std::string
description(const OutputStyle style) const1460   Anything::description(const OutputStyle style) const
1461   {
1462     switch (style)
1463       {
1464         case Machine:
1465           {
1466             std::ostringstream description;
1467 
1468             description << description_init << "]";
1469 
1470             return description.str();
1471           }
1472         case Text:
1473         case LaTeX:
1474           {
1475             return "Any string";
1476           }
1477         default:
1478           AssertThrow(false, ExcNotImplemented());
1479       }
1480     // Should never occur without an exception, but prevent compiler from
1481     // complaining
1482     return "";
1483   }
1484 
1485 
1486 
1487   std::unique_ptr<PatternBase>
clone() const1488   Anything::clone() const
1489   {
1490     return std::unique_ptr<PatternBase>(new Anything());
1491   }
1492 
1493 
1494 
1495   std::unique_ptr<Anything>
create(const std::string & description)1496   Anything::create(const std::string &description)
1497   {
1498     if (description.compare(0,
1499                             std::strlen(description_init),
1500                             description_init) == 0)
1501       return std::make_unique<Anything>();
1502     else
1503       return std::unique_ptr<Anything>();
1504   }
1505 
1506 
1507 
1508   const char *FileName::description_init = "[FileName";
1509 
1510 
FileName(const FileType type)1511   FileName::FileName(const FileType type)
1512     : file_type(type)
1513   {}
1514 
1515 
1516 
1517   bool
match(const std::string &) const1518   FileName::match(const std::string &) const
1519   {
1520     return true;
1521   }
1522 
1523 
1524 
1525   std::string
description(const OutputStyle style) const1526   FileName::description(const OutputStyle style) const
1527   {
1528     switch (style)
1529       {
1530         case Machine:
1531           {
1532             std::ostringstream description;
1533 
1534             description << description_init;
1535 
1536             if (file_type == input)
1537               description << " (Type: input)]";
1538             else
1539               description << " (Type: output)]";
1540 
1541             return description.str();
1542           }
1543         case Text:
1544         case LaTeX:
1545           {
1546             if (file_type == input)
1547               return "an input filename";
1548             else
1549               return "an output filename";
1550           }
1551         default:
1552           AssertThrow(false, ExcNotImplemented());
1553       }
1554     // Should never occur without an exception, but prevent compiler from
1555     // complaining
1556     return "";
1557   }
1558 
1559 
1560 
1561   std::unique_ptr<PatternBase>
clone() const1562   FileName::clone() const
1563   {
1564     return std::unique_ptr<PatternBase>(new FileName(file_type));
1565   }
1566 
1567 
1568 
1569   std::unique_ptr<FileName>
create(const std::string & description)1570   FileName::create(const std::string &description)
1571   {
1572     if (description.compare(0,
1573                             std::strlen(description_init),
1574                             description_init) == 0)
1575       {
1576         std::istringstream is(description);
1577         std::string        file_type;
1578         FileType           type;
1579 
1580         is.ignore(strlen(description_init) + strlen(" (Type:"));
1581 
1582         is >> file_type;
1583 
1584         if (file_type == "input)]")
1585           type = input;
1586         else
1587           type = output;
1588 
1589         return std::make_unique<FileName>(type);
1590       }
1591     else
1592       return std::unique_ptr<FileName>();
1593   }
1594 
1595 
1596 
1597   const char *DirectoryName::description_init = "[DirectoryName";
1598 
1599 
1600 
1601   bool
match(const std::string &) const1602   DirectoryName::match(const std::string &) const
1603   {
1604     return true;
1605   }
1606 
1607 
1608 
1609   std::string
description(const OutputStyle style) const1610   DirectoryName::description(const OutputStyle style) const
1611   {
1612     switch (style)
1613       {
1614         case Machine:
1615           {
1616             std::ostringstream description;
1617 
1618             description << description_init << "]";
1619 
1620             return description.str();
1621           }
1622         case Text:
1623         case LaTeX:
1624           {
1625             return "A directory name";
1626           }
1627         default:
1628           AssertThrow(false, ExcNotImplemented());
1629       }
1630     // Should never occur without an exception, but prevent compiler from
1631     // complaining
1632     return "";
1633   }
1634 
1635 
1636 
1637   std::unique_ptr<PatternBase>
clone() const1638   DirectoryName::clone() const
1639   {
1640     return std::unique_ptr<PatternBase>(new DirectoryName());
1641   }
1642 
1643 
1644 
1645   std::unique_ptr<DirectoryName>
create(const std::string & description)1646   DirectoryName::create(const std::string &description)
1647   {
1648     if (description.compare(0,
1649                             std::strlen(description_init),
1650                             description_init) == 0)
1651       return std::make_unique<DirectoryName>();
1652     else
1653       return std::unique_ptr<DirectoryName>();
1654   }
1655 
1656 } // end namespace Patterns
1657 
1658 DEAL_II_NAMESPACE_CLOSE
1659