Browse Source

[3436] Added position info to top level parser creation in D2

Added element position argument to DCfgMgrBase::createConfigParser(),
so derivations have access to print position info unsupported top level
element errors.

Removed two log messages DCT_ORDER_ERROR and DCT_ORDER_NO_ELEMENT. These
conditions are well explained in exceptions thrown and these logs just
cluttered the log output.

Removed extra text from DCTL_CONFIG_LOAD_FAIL and DCTL_PARSER_FAIL log
messages. The log ID is self-explanatory and the underlying exceptions
provide ample explanation of the error.  Makes the log output much easier
to understand.

Revised items-not-in-parse-order detection in DCfgMgrBase::parseConfig().
Rather than complicated counting logic, objects are removed from the list
as they are parsed.  Any left over were not in the parsing-order.

Removed try-catch-throw from DCfgMgrBase::buildAndCommit. This method
already throws its own exception. Catching, logging, and re-throwing
exceptions from underneath it really just server to clutter the log.
Thomas Markwalder 11 years ago
parent
commit
e10603ef67

+ 3 - 2
src/bin/d2/d2_cfg_mgr.cc

@@ -234,7 +234,8 @@ D2CfgMgr::buildParams(isc::data::ConstElementPtr params_config) {
 }
 }
 
 
 isc::dhcp::ParserPtr
 isc::dhcp::ParserPtr
-D2CfgMgr::createConfigParser(const std::string& config_id) {
+D2CfgMgr::createConfigParser(const std::string& config_id,
+                             const isc::data::Element::Position& pos) {
     // Get D2 specific context.
     // Get D2 specific context.
     D2CfgContextPtr context = getD2CfgContext();
     D2CfgContextPtr context = getD2CfgContext();
 
 
@@ -263,7 +264,7 @@ D2CfgMgr::createConfigParser(const std::string& config_id) {
     } else {
     } else {
         isc_throw(NotImplemented,
         isc_throw(NotImplemented,
                   "parser error: D2CfgMgr parameter not supported: "
                   "parser error: D2CfgMgr parameter not supported: "
-                  << config_id);
+                  << config_id << pos);
     }
     }
 
 
     return (parser);
     return (parser);

+ 5 - 1
src/bin/d2/d2_cfg_mgr.h

@@ -264,11 +264,15 @@ protected:
     ///
     ///
     /// @param element_id is the string name of the element as it will appear
     /// @param element_id is the string name of the element as it will appear
     /// in the configuration set.
     /// in the configuration set.
+    /// @param pos position within the configuration text (or file) of element
+    /// to be parsed.  This is passed for error messaging.
     ///
     ///
     /// @return returns a ParserPtr to the parser instance.
     /// @return returns a ParserPtr to the parser instance.
     /// @throw throws DCfgMgrBaseError if an error occurs.
     /// @throw throws DCfgMgrBaseError if an error occurs.
     virtual isc::dhcp::ParserPtr
     virtual isc::dhcp::ParserPtr
-    createConfigParser(const std::string& element_id);
+    createConfigParser(const std::string& element_id,
+                       const isc::data::Element::Position& pos =
+                       isc::data::Element::Position());
 
 
     /// @brief Creates an new, blank D2CfgContext context
     /// @brief Creates an new, blank D2CfgContext context
     ///
     ///

+ 6 - 17
src/bin/d2/d2_messages.mes

@@ -32,7 +32,7 @@ new configuration. It is output during server startup, and when an updated
 configuration is committed by the administrator.  Additional information
 configuration is committed by the administrator.  Additional information
 may be provided.
 may be provided.
 
 
-% DCTL_CONFIG_FILE_LOAD_FAIL %1 configuration could not be loaded from file: %2
+% DCTL_CONFIG_FILE_LOAD_FAIL %1 : %2
 This fatal error message indicates that the application attempted to load its
 This fatal error message indicates that the application attempted to load its
 initial configuration from file and has failed. The service will exit.
 initial configuration from file and has failed. The service will exit.
 
 
@@ -72,22 +72,11 @@ application and will exit.
 A warning message is issued when an attempt is made to shut down the
 A warning message is issued when an attempt is made to shut down the
 application when it is not running.
 application when it is not running.
 
 
-% DCTL_ORDER_ERROR configuration contains more elements than the parsing order
-An error message which indicates that configuration being parsed includes
-element ids not specified the configuration manager's parse order list. This
-is a programmatic error.
-
-% DCTL_ORDER_NO_ELEMENT element: %1 is in the parsing order but is missing from the configuration
-An error message output during a configuration update.  The program is
-expecting an item but has not found it in the new configuration.  This may
-mean that the BIND 10 configuration database is corrupt.
-
-% DCTL_PARSER_FAIL configuration parsing failed for configuration element: %1, reason: %2
-On receipt of message containing details to a change of its configuration,
-the server failed to create a parser to decode the contents of the named
-configuration element, or the creation succeeded but the parsing actions
-and committal of changes failed.  The reason for the failure is given in
-the message.
+% DCTL_PARSER_FAIL %1
+On receipt of a new configuration, the server failed to create a parser to
+decode the contents of the named configuration element, or the creation
+succeeded but the parsing actions and committal of changes failed.
+The reason for the failure is given in the message.
 
 
 % DCTL_PROCESS_FAILED %1 application execution failed: %2
 % DCTL_PROCESS_FAILED %1 application execution failed: %2
 The controller has encountered a fatal error while running the
 The controller has encountered a fatal error while running the

+ 33 - 35
src/bin/d2/d_cfg_mgr.cc

@@ -189,17 +189,15 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
             // thrown.  Note, that elements tagged as "optional" from the user
             // thrown.  Note, that elements tagged as "optional" from the user
             // perspective must still have default or empty entries in the
             // perspective must still have default or empty entries in the
             // configuration set to be parsed.
             // configuration set to be parsed.
-            int parsed_count = 0;
             std::map<std::string, ConstElementPtr>::const_iterator it;
             std::map<std::string, ConstElementPtr>::const_iterator it;
             BOOST_FOREACH(element_id, parse_order_) {
             BOOST_FOREACH(element_id, parse_order_) {
                 it = objects_map.find(element_id);
                 it = objects_map.find(element_id);
                 if (it != objects_map.end()) {
                 if (it != objects_map.end()) {
-                    ++parsed_count;
                     buildAndCommit(element_id, it->second);
                     buildAndCommit(element_id, it->second);
+                    // We parsed it, take it out of the list.
+                    objects_map.erase(it);
                 }
                 }
                 else {
                 else {
-                    LOG_ERROR(dctl_logger, DCTL_ORDER_NO_ELEMENT)
-                              .arg(element_id);
                     isc_throw(DCfgMgrBaseError, "Element:" << element_id <<
                     isc_throw(DCfgMgrBaseError, "Element:" << element_id <<
                               " is listed in the parse order but is not "
                               " is listed in the parse order but is not "
                               " present in the configuration");
                               " present in the configuration");
@@ -208,18 +206,25 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
 
 
             // NOTE: When using ordered parsing, the parse order list MUST
             // NOTE: When using ordered parsing, the parse order list MUST
             // include every possible element id that the value_map may contain.
             // include every possible element id that the value_map may contain.
-            // Entries in the map that are not in the parse order, would not be
+            // Entries in the map that are not in the parse order, will not be
             // parsed. For now we will flag this as a programmatic error.  One
             // parsed. For now we will flag this as a programmatic error.  One
             // could attempt to adjust for this, by identifying such entries
             // could attempt to adjust for this, by identifying such entries
             // and parsing them either first or last but which would be correct?
             // and parsing them either first or last but which would be correct?
-            // Better to hold the engineer accountable.  So, if we parsed none
-            // or we parsed fewer than are in the map; then either the parse i
-            // order is incomplete OR the map has unsupported values.
-            if (!parsed_count ||
-                (parsed_count && ((parsed_count + 1) < objects_map.size()))) {
-                LOG_ERROR(dctl_logger, DCTL_ORDER_ERROR);
+            // Better to hold the engineer accountable.  So, if there are any
+            // left in the objects_map then they were not in the parse order.
+            if (objects_map.size() > 0) {
+                std::ostringstream stream;
+                bool add_comma = false;
+                ConfigPair config_pair;
+                BOOST_FOREACH(config_pair, objects_map) {
+                    stream << ( add_comma ? ", " : "") << config_pair.first
+                           << " at: " << config_pair.second->getPosition();
+                    add_comma = true;
+                }
+
                 isc_throw(DCfgMgrBaseError,
                 isc_throw(DCfgMgrBaseError,
-                        "Configuration contains elements not in parse order");
+                        "Configuration contains elements not in parse order: "
+                        << stream.str());
             }
             }
         } else {
         } else {
             // Order doesn't matter so iterate over the value map directly.
             // Order doesn't matter so iterate over the value map directly.
@@ -235,10 +240,9 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
         LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg("");
         LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg("");
         answer = isc::config::createAnswer(0, "Configuration committed.");
         answer = isc::config::createAnswer(0, "Configuration committed.");
 
 
-    } catch (const isc::Exception& ex) {
-        LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(element_id).arg(ex.what());
-        answer = isc::config::createAnswer(1,
-                     string("Configuration parsing failed: ") + ex.what());
+    } catch (const std::exception& ex) {
+        LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(ex.what());
+        answer = isc::config::createAnswer(1, + ex.what());
 
 
         // An error occurred, so make sure that we restore original context.
         // An error occurred, so make sure that we restore original context.
         context_ = original_context;
         context_ = original_context;
@@ -253,7 +257,8 @@ DCfgMgrBase::buildParams(isc::data::ConstElementPtr params_config) {
     // Loop through scalars parsing them and committing them to storage.
     // Loop through scalars parsing them and committing them to storage.
     BOOST_FOREACH(dhcp::ConfigPair param, params_config->mapValue()) {
     BOOST_FOREACH(dhcp::ConfigPair param, params_config->mapValue()) {
         // Call derivation's method to create the proper parser.
         // Call derivation's method to create the proper parser.
-        dhcp::ParserPtr parser(createConfigParser(param.first));
+        dhcp::ParserPtr parser(createConfigParser(param.first,
+                                                  param.second->getPosition()));
         parser->build(param.second);
         parser->build(param.second);
         parser->commit();
         parser->commit();
     }
     }
@@ -263,28 +268,21 @@ void DCfgMgrBase::buildAndCommit(std::string& element_id,
                                  isc::data::ConstElementPtr value) {
                                  isc::data::ConstElementPtr value) {
     // Call derivation's implementation to create the appropriate parser
     // Call derivation's implementation to create the appropriate parser
     // based on the element id.
     // based on the element id.
-    ParserPtr parser = createConfigParser(element_id);
+    ParserPtr parser = createConfigParser(element_id, value->getPosition());
     if (!parser) {
     if (!parser) {
         isc_throw(DCfgMgrBaseError, "Could not create parser");
         isc_throw(DCfgMgrBaseError, "Could not create parser");
     }
     }
 
 
-    try {
-        // Invoke the parser's build method passing in the value. This will
-        // "convert" the Element form of value into the actual data item(s)
-        // and store them in parser's local storage.
-        parser->build(value);
-
-        // Invoke the parser's commit method. This "writes" the the data
-        // item(s) stored locally by the parser into the context.  (Note that
-        // parsers are free to do more than update the context, but that is an
-        // nothing something we are concerned with here.)
-        parser->commit();
-    } catch (const isc::Exception& ex) {
-        isc_throw(DCfgMgrBaseError,
-                  "Could not build and commit: " << ex.what());
-    } catch (...) {
-        isc_throw(DCfgMgrBaseError, "Non-ISC exception occurred");
-    }
+    // Invoke the parser's build method passing in the value. This will
+    // "convert" the Element form of value into the actual data item(s)
+    // and store them in parser's local storage.
+    parser->build(value);
+
+    // Invoke the parser's commit method. This "writes" the the data
+    // item(s) stored locally by the parser into the context.  (Note that
+    // parsers are free to do more than update the context, but that is an
+    // nothing something we are concerned with here.)
+    parser->commit();
 }
 }
 
 
 }; // end of isc::dhcp namespace
 }; // end of isc::dhcp namespace

+ 5 - 1
src/bin/d2/d_cfg_mgr.h

@@ -316,11 +316,15 @@ protected:
     ///
     ///
     /// @param element_id is the string name of the element as it will appear
     /// @param element_id is the string name of the element as it will appear
     /// in the configuration set.
     /// in the configuration set.
+    /// @param pos position within the configuration text (or file) of element
+    /// to be parsed.  This is passed for error messaging.
     ///
     ///
     /// @return returns a ParserPtr to the parser instance.
     /// @return returns a ParserPtr to the parser instance.
     /// @throw throws DCfgMgrBaseError if an error occurs.
     /// @throw throws DCfgMgrBaseError if an error occurs.
     virtual isc::dhcp::ParserPtr
     virtual isc::dhcp::ParserPtr
-    createConfigParser(const std::string& element_id) = 0;
+    createConfigParser(const std::string& element_id,
+                       const isc::data::Element::Position& pos
+                       = isc::data::Element::Position()) = 0;
 
 
     /// @brief Abstract factory which creates a context instance.
     /// @brief Abstract factory which creates a context instance.
     ///
     ///

+ 55 - 10
src/bin/d2/tests/d2_cfg_mgr_unittests.cc

@@ -83,6 +83,12 @@ public:
         return (config.str());
         return (config.str());
     }
     }
 
 
+    /// @brief Enumeration to select between expected configuration outcomes
+    enum RunConfigMode {
+       SHOULD_PASS,
+       SHOULD_FAIL
+    };
+
     /// @brief Parses a configuration string and tests against a given outcome
     /// @brief Parses a configuration string and tests against a given outcome
     ///
     ///
     /// Convenience method which accepts JSON text and an expected pass or fail
     /// Convenience method which accepts JSON text and an expected pass or fail
@@ -93,15 +99,16 @@ public:
     /// the D2Params pointer with the newly parsed instance.
     /// the D2Params pointer with the newly parsed instance.
     ///
     ///
     /// @param config_str the JSON configuration text to parse
     /// @param config_str the JSON configuration text to parse
-    /// @param should_fail boolean indicator if the parsing should fail or not.
-    /// It defaults to false.
-    void runConfig(std::string config_str, bool should_fail=false) {
+    /// @param mode indicator if the parsing should fail or not.  It defaults
+    /// defaults to SHOULD_PASS.
+    ///
+    void runConfig(std::string config_str, RunConfigMode mode=SHOULD_PASS) {
         // We assume the config string is valid JSON.
         // We assume the config string is valid JSON.
         ASSERT_TRUE(fromJSON(config_str));
         ASSERT_TRUE(fromJSON(config_str));
 
 
         // Parse the configuration and verify we got the expected outcome.
         // Parse the configuration and verify we got the expected outcome.
         answer_ = cfg_mgr_->parseConfig(config_set_);
         answer_ = cfg_mgr_->parseConfig(config_set_);
-        ASSERT_TRUE(checkAnswer(should_fail));
+        ASSERT_TRUE(checkAnswer(mode == SHOULD_FAIL));
 
 
         // Verify that the D2 context can be retrieved and is not null.
         // Verify that the D2 context can be retrieved and is not null.
         D2CfgContextPtr context;
         D2CfgContextPtr context;
@@ -397,6 +404,44 @@ TEST_F(D2CfgMgrTest, defaultValues) {
               d2_params_->getNcrFormat());
               d2_params_->getNcrFormat());
 }
 }
 
 
+/// @brief Tests the unsupported scalar parameters and objects are detected.
+TEST_F(D2CfgMgrTest, unsupportedTopLevelItems) {
+    // Check that an unsupported top level parameter fails.
+    std::string config =
+            "{"
+            " \"ip_address\": \"127.0.0.1\", "
+            " \"port\": 777 , "
+            " \"dns_server_timeout\": 333 , "
+            " \"ncr_protocol\": \"UDP\" , "
+            " \"ncr_format\": \"JSON\", "
+            "\"tsig_keys\": [], "
+            "\"forward_ddns\" : {}, "
+            "\"reverse_ddns\" : {}, "
+            "\"bogus_param\" : true "
+            "}";
+
+    runConfig(config, SHOULD_FAIL);
+
+    // Check that unsupported top level objects fails.  For
+    // D2 these fail as they are not in the parse order.
+    config =
+            "{"
+            " \"ip_address\": \"127.0.0.1\", "
+            " \"port\": 777 , "
+            " \"dns_server_timeout\": 333 , "
+            " \"ncr_protocol\": \"UDP\" , "
+            " \"ncr_format\": \"JSON\", "
+            "\"tsig_keys\": [], "
+            "\"bogus_object_one\" : {}, "
+            "\"forward_ddns\" : {}, "
+            "\"reverse_ddns\" : {}, "
+            "\"bogus_object_two\" : {} "
+            "}";
+
+    runConfig(config, SHOULD_FAIL);
+}
+
+
 /// @brief Tests the enforcement of data validation when parsing D2Params.
 /// @brief Tests the enforcement of data validation when parsing D2Params.
 /// It verifies that:
 /// It verifies that:
 /// -# ip_address cannot be "0.0.0.0"
 /// -# ip_address cannot be "0.0.0.0"
@@ -409,27 +454,27 @@ TEST_F(D2CfgMgrTest, invalidEntry) {
     // Cannot use IPv4 ANY address
     // Cannot use IPv4 ANY address
     std::string config = makeParamsConfigString ("0.0.0.0", 777, 333,
     std::string config = makeParamsConfigString ("0.0.0.0", 777, 333,
                                            "UDP", "JSON");
                                            "UDP", "JSON");
-    runConfig(config, 1);
+    runConfig(config, SHOULD_FAIL);
 
 
     // Cannot use IPv6 ANY address
     // Cannot use IPv6 ANY address
     config = makeParamsConfigString ("::", 777, 333, "UDP", "JSON");
     config = makeParamsConfigString ("::", 777, 333, "UDP", "JSON");
-    runConfig(config, 1);
+    runConfig(config, SHOULD_FAIL);
 
 
     // Cannot use port  0
     // Cannot use port  0
     config = makeParamsConfigString ("127.0.0.1", 0, 333, "UDP", "JSON");
     config = makeParamsConfigString ("127.0.0.1", 0, 333, "UDP", "JSON");
-    runConfig(config, 1);
+    runConfig(config, SHOULD_FAIL);
 
 
     // Cannot use dns server timeout of 0
     // Cannot use dns server timeout of 0
     config = makeParamsConfigString ("127.0.0.1", 777, 0, "UDP", "JSON");
     config = makeParamsConfigString ("127.0.0.1", 777, 0, "UDP", "JSON");
-    runConfig(config, 1);
+    runConfig(config, SHOULD_FAIL);
 
 
     // Invalid protocol
     // Invalid protocol
     config = makeParamsConfigString ("127.0.0.1", 777, 333, "BOGUS", "JSON");
     config = makeParamsConfigString ("127.0.0.1", 777, 333, "BOGUS", "JSON");
-    runConfig(config, 1);
+    runConfig(config, SHOULD_FAIL);
 
 
     // Invalid format
     // Invalid format
     config = makeParamsConfigString ("127.0.0.1", 777, 333, "UDP", "BOGUS");
     config = makeParamsConfigString ("127.0.0.1", 777, 333, "UDP", "BOGUS");
-    runConfig(config, 1);
+    runConfig(config, SHOULD_FAIL);
 }
 }
 
 
 /// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos.
 /// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos.

+ 7 - 1
src/bin/d2/tests/d_cfg_mgr_unittests.cc

@@ -53,7 +53,8 @@ public:
 
 
     /// @brief Dummy implementation as this method is abstract.
     /// @brief Dummy implementation as this method is abstract.
     virtual isc::dhcp::ParserPtr
     virtual isc::dhcp::ParserPtr
-    createConfigParser(const std::string& /* element_id */) {
+    createConfigParser(const std::string& /* element_id */,
+                       const isc::data::Element::Position& /* pos */) {
         return (isc::dhcp::ParserPtr());
         return (isc::dhcp::ParserPtr());
     }
     }
 };
 };
@@ -125,18 +126,23 @@ TEST_F(DStubCfgMgrTest, basicParseTest) {
     answer_ = cfg_mgr_->parseConfig(config_set_);
     answer_ = cfg_mgr_->parseConfig(config_set_);
     EXPECT_TRUE(checkAnswer(0));
     EXPECT_TRUE(checkAnswer(0));
 
 
+
+std::cout << __FILE__ << ":" <<  __LINE__ << std::endl;
     // Verify that an error building the element is caught and returns a
     // Verify that an error building the element is caught and returns a
     // failed parse result.
     // failed parse result.
     SimFailure::set(SimFailure::ftElementBuild);
     SimFailure::set(SimFailure::ftElementBuild);
     answer_ = cfg_mgr_->parseConfig(config_set_);
     answer_ = cfg_mgr_->parseConfig(config_set_);
     EXPECT_TRUE(checkAnswer(1));
     EXPECT_TRUE(checkAnswer(1));
 
 
+std::cout << __FILE__ << ":" <<  __LINE__ << std::endl;
     // Verify that an error committing the element is caught and returns a
     // Verify that an error committing the element is caught and returns a
     // failed parse result.
     // failed parse result.
     SimFailure::set(SimFailure::ftElementCommit);
     SimFailure::set(SimFailure::ftElementCommit);
     answer_ = cfg_mgr_->parseConfig(config_set_);
     answer_ = cfg_mgr_->parseConfig(config_set_);
     EXPECT_TRUE(checkAnswer(1));
     EXPECT_TRUE(checkAnswer(1));
 
 
+std::cout << __FILE__ << ":" <<  __LINE__ << std::endl;
+
     // Verify that an unknown element error is caught and returns a failed
     // Verify that an unknown element error is caught and returns a failed
     // parse result.
     // parse result.
     SimFailure::set(SimFailure::ftElementUnknown);
     SimFailure::set(SimFailure::ftElementUnknown);

+ 4 - 2
src/bin/d2/tests/d_test_stubs.cc

@@ -396,7 +396,8 @@ DStubCfgMgr::createNewContext() {
 }
 }
 
 
 isc::dhcp::ParserPtr
 isc::dhcp::ParserPtr
-DStubCfgMgr::createConfigParser(const std::string& element_id) {
+DStubCfgMgr::createConfigParser(const std::string& element_id,
+                                const isc::data::Element::Position& pos) {
     isc::dhcp::ParserPtr parser;
     isc::dhcp::ParserPtr parser;
     DStubContextPtr context
     DStubContextPtr context
         = boost::dynamic_pointer_cast<DStubContext>(getContext());
         = boost::dynamic_pointer_cast<DStubContext>(getContext());
@@ -416,7 +417,8 @@ DStubCfgMgr::createConfigParser(const std::string& element_id) {
         // to "succeed" without specifically supporting them.
         // to "succeed" without specifically supporting them.
         if (SimFailure::shouldFailOn(SimFailure::ftElementUnknown)) {
         if (SimFailure::shouldFailOn(SimFailure::ftElementUnknown)) {
             isc_throw(DCfgMgrBaseError,
             isc_throw(DCfgMgrBaseError,
-                      "Configuration parameter not supported: " << element_id);
+                      "Configuration parameter not supported: " << element_id
+                      << pos);
         }
         }
 
 
         // Going to assume anything else is an object element.
         // Going to assume anything else is an object element.

+ 5 - 1
src/bin/d2/tests/d_test_stubs.h

@@ -691,11 +691,15 @@ public:
     ///
     ///
     /// @param element_id is the string name of the element as it will appear
     /// @param element_id is the string name of the element as it will appear
     /// in the configuration set.
     /// in the configuration set.
+    /// @param pos position within the configuration text (or file) of element
+    /// to be parsed.  This is passed for error messaging.
     ///
     ///
     /// @return returns a ParserPtr to the parser instance.
     /// @return returns a ParserPtr to the parser instance.
     /// @throw throws DCfgMgrBaseError if SimFailure is ftElementUnknown.
     /// @throw throws DCfgMgrBaseError if SimFailure is ftElementUnknown.
     virtual isc::dhcp::ParserPtr
     virtual isc::dhcp::ParserPtr
-    createConfigParser(const std::string& element_id);
+    createConfigParser(const std::string& element_id,
+                       const isc::data::Element::Position& pos
+                       = isc::data::Element::Position());
 
 
     /// @brief A list for remembering the element ids in the order they were
     /// @brief A list for remembering the element ids in the order they were
     /// parsed.
     /// parsed.