From e26a8d7c6db75feefe4874878bc27637bb72725e Mon Sep 17 00:00:00 2001 From: David Wells Date: Thu, 15 Sep 2016 19:05:33 -0400 Subject: [PATCH] Use exceptions when reading XML parameter files. --- include/deal.II/base/parameter_handler.h | 36 +++- source/base/parameter_handler.cc | 195 +++++++----------- .../parameter_handler_read_xml.cc | 5 +- .../parameter_handler_read_xml_error_01.cc | 15 +- ...parameter_handler_read_xml_error_01.output | 3 +- .../parameter_handler_read_xml_error_02.cc | 15 +- ...parameter_handler_read_xml_error_02.output | 8 +- .../parameter_handler_read_xml_error_03.cc | 13 +- ...parameter_handler_read_xml_error_03.output | 3 +- .../parameter_handler_read_xml_error_04.cc | 15 +- ...parameter_handler_read_xml_error_04.output | 8 +- 11 files changed, 167 insertions(+), 149 deletions(-) diff --git a/include/deal.II/base/parameter_handler.h b/include/deal.II/base/parameter_handler.h index a4347b2f32..659ed2c77f 100644 --- a/include/deal.II/base/parameter_handler.h +++ b/include/deal.II/base/parameter_handler.h @@ -1674,9 +1674,20 @@ public: * method and then modified by the graphical parameter GUI (see the general * documentation of this class). * - * Return whether the read was successful. + * @deprecated This function has been deprecated in favor of the replacement + * ParameterHandler::parse_input_from_xml, which raises exceptions to indicate + * errors instead of returning an error code. + */ + virtual bool read_input_from_xml (std::istream &input) DEAL_II_DEPRECATED; + + /** + * Parse input from an XML stream to populate known parameter fields. This + * could be from a file originally written by the print_parameters() function + * using the XML output style and then modified by hand as necessary, or from + * a file written using this method and then modified by the graphical + * parameter GUI (see the general documentation of this class). */ - virtual bool read_input_from_xml (std::istream &input); + virtual void parse_input_from_xml (std::istream &input); /** * Clear all contents. @@ -2038,6 +2049,27 @@ public: " does not match the given pattern:\n" << " " << arg5); + /** + * Exception for when an XML file cannot be read at all. This happens when + * there is no top-level XML element called "ParameterHandler" or when there + * are multiple top level elements. + */ + DeclExceptionMsg (ExcInvalidXMLParameterFile, + "The provided file could not be parsed as a " + "ParameterHandler description."); + + /** + * Exception for when an entry in an XML parameter file does not match the + * provided pattern. The arguments are, in order, the entry value, entry + * name, and a description of the pattern. + */ + DeclException3 (ExcInvalidEntryForPatternXML, + std::string, std::string, std::string, + << " The entry value \n" << " " << arg1 << '\n' << + " for the entry named\n" << " " << arg2 << '\n' << + " does not match the given pattern:\n" << " " << + arg3); + /** * Exception for when the file given in an include statement cannot be * open. The arguments are, in order, the line number of the include diff --git a/source/base/parameter_handler.cc b/source/base/parameter_handler.cc index 54f719518b..fe611210cc 100644 --- a/source/base/parameter_handler.cc +++ b/source/base/parameter_handler.cc @@ -1746,15 +1746,12 @@ bool ParameterHandler::read_input_from_string (const char *s, namespace { - // Recursively go through the 'source' tree - // and see if we can find corresponding - // entries in the 'destination' tree. If - // not, error out (i.e. we have just read - // an XML file that has entries that - // weren't declared in the ParameterHandler - // object); if so, copy the value of these + // Recursively go through the 'source' tree and see if we can find + // corresponding entries in the 'destination' tree. If not, error out + // (i.e. we have just read an XML file that has entries that weren't + // declared in the ParameterHandler object); if so, copy the value of these // nodes into the destination object - bool + void read_xml_recursively (const boost::property_tree::ptree &source, const std::string ¤t_path, const char path_separator, @@ -1765,107 +1762,81 @@ namespace for (boost::property_tree::ptree::const_iterator p = source.begin(); p != source.end(); ++p) { - // a sub-tree must either be a - // parameter node or a subsection + // a sub-tree must either be a parameter node or a subsection if (p->second.get_optional("value")) { - // make sure we have a - // corresponding entry in the - // destination object as well + // make sure we have a corresponding entry in the destination + // object as well const std::string full_path = (current_path == "" ? p->first : current_path + path_separator + p->first); - if (destination.get_optional (full_path) - && - destination.get_optional (full_path + - path_separator + - "value")) - { - // first make sure that the - // new entry actually - // satisfies its constraints - const std::string new_value - = p->second.get("value"); - - const unsigned int pattern_index - = destination.get (full_path + - path_separator + - "pattern"); - if (patterns[pattern_index]->match(new_value) == false) - { - std::cerr << " The entry value" << std::endl - << " " << new_value << std::endl - << " for the entry named" << std::endl - << " " << full_path << std::endl - << " does not match the given pattern" << std::endl - << " " << patterns[pattern_index]->description() - << std::endl; - return false; - } - - // set the found parameter in - // the destination argument - destination.put (full_path + path_separator + "value", - new_value); - - // this node might have - // sub-nodes in addition to - // "value", such as - // "default_value", - // "documentation", etc. we - // might at some point in the - // future want to make sure - // that if they exist that - // they match the ones in the - // 'destination' tree - } - else - { - std::cerr << "The entry <" << full_path - << "> with value <" - << p->second.get("value") - << "> has not been declared." - << std::endl; - return false; - } + + const std::string new_value + = p->second.get("value"); + AssertThrow (destination.get_optional (full_path) + && + destination.get_optional + (full_path + path_separator + "value"), + ParameterHandler::ExcEntryUndeclared (p->first)); + + // first make sure that the new entry actually satisfies its + // constraints + const unsigned int pattern_index + = destination.get (full_path + + path_separator + + "pattern"); + AssertThrow (patterns[pattern_index]->match(new_value), + ParameterHandler::ExcInvalidEntryForPatternXML + // XML entries sometimes have extra surrounding + // newlines + (Utilities::trim(new_value), + p->first, + patterns[pattern_index]->description())); + + // set the found parameter in the destination argument + destination.put (full_path + path_separator + "value", + new_value); + + // this node might have sub-nodes in addition to "value", such as + // "default_value", "documentation", etc. we might at some point + // in the future want to make sure that if they exist that they + // match the ones in the 'destination' tree } else if (p->second.get_optional("alias")) { - // it is an alias node. alias nodes are static and - // there is nothing to do here (but the same applies as - // mentioned in the comment above about the static - // nodes inside parameter nodes + // it is an alias node. alias nodes are static and there is + // nothing to do here (but the same applies as mentioned in the + // comment above about the static nodes inside parameter nodes } else { // it must be a subsection - const bool result - = read_xml_recursively (p->second, - (current_path == "" ? - p->first : - current_path + path_separator + p->first), - path_separator, - patterns, - destination); - - // see if the recursive read - // succeeded. if yes, continue, - // otherwise exit now - if (result == false) - return false; + read_xml_recursively (p->second, + (current_path == "" ? + p->first : + current_path + path_separator + p->first), + path_separator, + patterns, + destination); } } - - return true; } } -bool ParameterHandler::read_input_from_xml (std::istream &in) +bool ParameterHandler::read_input_from_xml(std::istream &in) +{ + parse_input_from_xml(in); + return true; +} + + + +void ParameterHandler::parse_input_from_xml (std::istream &in) { AssertThrow(in, ExcIO()); // read the XML tree assuming that (as we @@ -1873,47 +1844,29 @@ bool ParameterHandler::read_input_from_xml (std::istream &in) // a single top-level node called // "ParameterHandler" boost::property_tree::ptree single_node_tree; - try - { - read_xml (in, single_node_tree); - } - catch (...) - { - std::cerr << "This input stream appears not to be valid XML" - << std::endl; - return false; - } + // This boost function will raise an exception if this is not a valid XML + // file. + read_xml (in, single_node_tree); - // make sure there is a top-level element + // make sure there is a single top-level element // called "ParameterHandler" - if (!single_node_tree.get_optional("ParameterHandler")) - { - std::cerr << "There is no top-level XML element called \"ParameterHandler\"." - << std::endl; - return false; - } - - // ensure that there is only a single - // top-level element - if (std::distance (single_node_tree.begin(), single_node_tree.end()) != 1) - { - std::cerr << "The top-level XML element \"ParameterHandler\" is " - << "not the only one." - << std::endl; - std::cerr << "(There are " - << std::distance (single_node_tree.begin(), - single_node_tree.end()) - << " top-level elements.)" - << std::endl; - return false; - } + AssertThrow (single_node_tree.get_optional("ParameterHandler"), + ExcInvalidXMLParameterFile ("There is no top-level XML element " + "called \"ParameterHandler\".")); + + const std::size_t n_top_level_elements = std::distance (single_node_tree.begin(), + single_node_tree.end()); + AssertThrow (n_top_level_elements == 1, + ExcInvalidXMLParameterFile ("There are " + + Utilities::to_string(n_top_level_elements) + + " top-level elements, but there " + "should only be one.")); // read the child elements recursively const boost::property_tree::ptree &my_entries = single_node_tree.get_child("ParameterHandler"); - return read_xml_recursively (my_entries, "", path_separator, patterns, - *entries); + read_xml_recursively (my_entries, "", path_separator, patterns, *entries); } diff --git a/tests/parameter_handler/parameter_handler_read_xml.cc b/tests/parameter_handler/parameter_handler_read_xml.cc index 1e71ed9f15..6e49201552 100644 --- a/tests/parameter_handler/parameter_handler_read_xml.cc +++ b/tests/parameter_handler/parameter_handler_read_xml.cc @@ -15,7 +15,7 @@ -// check ParameterHandler::read_input_from_xml +// check ParameterHandler::parse_input_from_xml #include "../tests.h" #include @@ -75,8 +75,7 @@ int main () // read from XML std::ifstream in (SOURCE_DIR "/prm/parameter_handler_read_xml.prm"); - bool result = prm.read_input_from_xml (in); - AssertThrow (result == true, ExcInternalError()); + prm.parse_input_from_xml (in); // write it out again prm.print_parameters (deallog.get_file_stream(), ParameterHandler::XML); diff --git a/tests/parameter_handler/parameter_handler_read_xml_error_01.cc b/tests/parameter_handler/parameter_handler_read_xml_error_01.cc index db4199be77..cedb86b839 100644 --- a/tests/parameter_handler/parameter_handler_read_xml_error_01.cc +++ b/tests/parameter_handler/parameter_handler_read_xml_error_01.cc @@ -15,7 +15,7 @@ -// check ParameterHandler::read_input_from_xml. try to read a file with a +// check ParameterHandler::parse_input_from_xml. try to read a file with a // parameter that has not been declared #include "../tests.h" @@ -76,10 +76,15 @@ int main () // read from XML std::ifstream in (SOURCE_DIR "/prm/parameter_handler_read_xml_error_01.prm"); - bool result = prm.read_input_from_xml (in); - Assert (result == false, ExcInternalError()); - - deallog << "OK" << std::endl; + try + { + prm.parse_input_from_xml (in); + } + catch (ParameterHandler::ExcEntryUndeclared &exc) + { + deallog << exc.get_exc_name() << std::endl; + exc.print_info(deallog.get_file_stream()); + } return 0; } diff --git a/tests/parameter_handler/parameter_handler_read_xml_error_01.output b/tests/parameter_handler/parameter_handler_read_xml_error_01.output index 0fd8fc12f0..5e12ed85b0 100644 --- a/tests/parameter_handler/parameter_handler_read_xml_error_01.output +++ b/tests/parameter_handler/parameter_handler_read_xml_error_01.output @@ -1,2 +1,3 @@ -DEAL::OK +DEAL::ParameterHandler::ExcEntryUndeclared (p->first) +You can't ask for entry you have not yet declared. diff --git a/tests/parameter_handler/parameter_handler_read_xml_error_02.cc b/tests/parameter_handler/parameter_handler_read_xml_error_02.cc index 69c3b96844..5547725b0d 100644 --- a/tests/parameter_handler/parameter_handler_read_xml_error_02.cc +++ b/tests/parameter_handler/parameter_handler_read_xml_error_02.cc @@ -15,7 +15,7 @@ -// check ParameterHandler::read_input_from_xml. try to read a file that is not +// check ParameterHandler::parse_input_from_xml. try to read a file that is not // valid XML (the last end-tag is missing) #include "../tests.h" @@ -76,10 +76,15 @@ int main () // read from XML std::ifstream in (SOURCE_DIR "/prm/parameter_handler_read_xml_error_02.prm"); - bool result = prm.read_input_from_xml (in); - AssertThrow (result == false, ExcInternalError()); - - deallog << "OK" << std::endl; + try + { + prm.parse_input_from_xml (in); + } + catch (const ParameterHandler:: ExcInvalidEntryForPatternXML &exc) + { + deallog << exc.get_exc_name() << std::endl; + exc.print_info(deallog.get_file_stream()); + } return 0; } diff --git a/tests/parameter_handler/parameter_handler_read_xml_error_02.output b/tests/parameter_handler/parameter_handler_read_xml_error_02.output index 0fd8fc12f0..4589349dd1 100644 --- a/tests/parameter_handler/parameter_handler_read_xml_error_02.output +++ b/tests/parameter_handler/parameter_handler_read_xml_error_02.output @@ -1,2 +1,8 @@ -DEAL::OK +DEAL::ParameterHandler::ExcInvalidEntryForPatternXML (Utilities::trim(new_value), p->first, patterns[pattern_index]->description()) + The entry value + __2 + for the entry named + int_2aint + does not match the given pattern: + [Integer range -2147483648...2147483647 (inclusive)] diff --git a/tests/parameter_handler/parameter_handler_read_xml_error_03.cc b/tests/parameter_handler/parameter_handler_read_xml_error_03.cc index 065d372fbc..bca1ed568e 100644 --- a/tests/parameter_handler/parameter_handler_read_xml_error_03.cc +++ b/tests/parameter_handler/parameter_handler_read_xml_error_03.cc @@ -76,10 +76,15 @@ int main () // read from XML std::ifstream in (SOURCE_DIR "/prm/parameter_handler_read_xml_error_03.prm"); - bool result = prm.read_input_from_xml (in); - AssertThrow (result == false, ExcInternalError()); - - deallog << "OK" << std::endl; + try + { + prm.parse_input_from_xml (in); + } + catch (const ParameterHandler::ExcInvalidXMLParameterFile &exc) + { + deallog << exc.get_exc_name() << std::endl; + exc.print_info(deallog.get_file_stream()); + } return 0; } diff --git a/tests/parameter_handler/parameter_handler_read_xml_error_03.output b/tests/parameter_handler/parameter_handler_read_xml_error_03.output index 0fd8fc12f0..71bf9d0049 100644 --- a/tests/parameter_handler/parameter_handler_read_xml_error_03.output +++ b/tests/parameter_handler/parameter_handler_read_xml_error_03.output @@ -1,2 +1,3 @@ -DEAL::OK +DEAL::ExcInvalidXMLParameterFile ("There are " + Utilities::to_string(n_top_level_elements) + " top-level elements, but there " "should only be one.") +There are 2 top-level elements, but there should only be one. diff --git a/tests/parameter_handler/parameter_handler_read_xml_error_04.cc b/tests/parameter_handler/parameter_handler_read_xml_error_04.cc index 013643deec..604ceb30e8 100644 --- a/tests/parameter_handler/parameter_handler_read_xml_error_04.cc +++ b/tests/parameter_handler/parameter_handler_read_xml_error_04.cc @@ -15,7 +15,7 @@ -// check ParameterHandler::read_input_from_xml. try to read a file with a +// check ParameterHandler::parse_input_from_xml. try to read a file with a // parameter that does not satisfy its pattern #include "../tests.h" @@ -76,10 +76,15 @@ int main () // read from XML std::ifstream in (SOURCE_DIR "/prm/parameter_handler_read_xml_error_04.prm"); - bool result = prm.read_input_from_xml (in); - AssertThrow (result == false, ExcInternalError()); - - deallog << "OK" << std::endl; + try + { + prm.parse_input_from_xml (in); + } + catch (const ParameterHandler::ExcInvalidEntryForPatternXML &exc) + { + deallog << exc.get_exc_name() << std::endl; + exc.print_info(deallog.get_file_stream()); + } return 0; } diff --git a/tests/parameter_handler/parameter_handler_read_xml_error_04.output b/tests/parameter_handler/parameter_handler_read_xml_error_04.output index 0fd8fc12f0..d93a0cbaf5 100644 --- a/tests/parameter_handler/parameter_handler_read_xml_error_04.output +++ b/tests/parameter_handler/parameter_handler_read_xml_error_04.output @@ -1,2 +1,8 @@ -DEAL::OK +DEAL::ParameterHandler::ExcInvalidEntryForPatternXML (Utilities::trim(new_value), p->first, patterns[pattern_index]->description()) + The entry value + 2x + for the entry named + int1 + does not match the given pattern: + [Integer range -2147483648...2147483647 (inclusive)] -- 2.39.5