From c46554551191a7a67439cd156cdfe563f1de3d6c Mon Sep 17 00:00:00 2001
From: wgodoy <wgodoy@wgodoy-desktop>
Date: Mon, 19 Jun 2017 00:23:28 -0400
Subject: [PATCH] Testing XML implementation with MPI

Initial implementation of XML running with helloBPWriter example
Corrected bug in ADIOS.cpp when detecting IO in ConfigFile
---
 .../runtimeconfig/grandSchema.xml             |   6 +-
 examples/hello/bpWriter/CMakeLists.txt        |   6 +
 examples/hello/bpWriter/helloBPWriter.xml     |  17 +-
 examples/hello/bpWriter/helloBPWriterXML.cpp  |  82 ++++
 source/adios2/core/ADIOS.cpp                  |  14 +-
 source/adios2/helper/adiosString.cpp          |   4 +-
 source/adios2/helper/adiosSystem.cpp          |  21 +-
 source/adios2/helper/adiosSystem.h            |   3 +-
 source/adios2/helper/adiosXML.cpp             | 372 +++++++++---------
 source/adios2/helper/adiosXML.h               |  59 +--
 10 files changed, 336 insertions(+), 248 deletions(-)
 create mode 100644 examples/hello/bpWriter/helloBPWriterXML.cpp

diff --git a/examples/experimental/runtimeconfig/grandSchema.xml b/examples/experimental/runtimeconfig/grandSchema.xml
index e069a7764..71d80bd30 100644
--- a/examples/experimental/runtimeconfig/grandSchema.xml
+++ b/examples/experimental/runtimeconfig/grandSchema.xml
@@ -4,9 +4,9 @@
     <io name="Output">
         
         <var name="myArray"> <!-- provide a sequence of transforms -->
-            <transform type="BZip2"> <!-- not implemented yet -->
+            <transform type='BZip2'> <!-- not implemented yet -->
                 BlockSize100K=3; <!-- must be a number between 0 and 9-->
-            </transform> 
+            </transform>
             <transform type="Zfp"> <!-- not implemented yet, mutually exclusive options -->
                 Tolerance=1e-3; <!-- must be a number between 0 and 9-->
                 <!-- Rate=1; --> <!-- must be a number between 0 and 9-->
@@ -58,7 +58,7 @@
             CacheSize=8192; <!-- not to be confused with ADIOS heap buffer, this is transport library cache, always in bytes -->    
         </transport>
         
-        <transport type="RDMA"> <!-- Staging gets more complex depending on library requirements-->
+        <transport type="RDMA"> <!-- Staging gets more complex  depending on library requirements-->
         	ProfileUnits=Microseconds; <!-- Microseconds, Milliseconds, Seconds, Minutes, Hours --> 
             Library=ibverbs; <!-- SST (default), Flexpath, Dataspaces, ibverbs -->
             Port=18515; <!-- ibverbs -->
diff --git a/examples/hello/bpWriter/CMakeLists.txt b/examples/hello/bpWriter/CMakeLists.txt
index 56c647c17..17fc96c85 100644
--- a/examples/hello/bpWriter/CMakeLists.txt
+++ b/examples/hello/bpWriter/CMakeLists.txt
@@ -9,6 +9,12 @@ if(ADIOS_HAVE_MPI)
   add_executable(hello_bpWriter helloBPWriter.cpp)
   target_include_directories(hello_bpWriter PRIVATE ${MPI_C_INCLUDE_PATH})
   target_link_libraries(hello_bpWriter ${MPI_C_LIBRARIES})
+  
+  add_executable(hello_bpWriterXML helloBPWriterXML.cpp)
+  target_include_directories(hello_bpWriterXML PRIVATE ${MPI_C_INCLUDE_PATH})
+  target_link_libraries(hello_bpWriterXML ${MPI_C_LIBRARIES})
+  target_link_libraries(hello_bpWriterXML adios2)
+  
 else()
   add_executable(hello_bpWriter helloBPWriter_nompi.cpp)
 endif()
diff --git a/examples/hello/bpWriter/helloBPWriter.xml b/examples/hello/bpWriter/helloBPWriter.xml
index 00afc9126..bda56e4c1 100644
--- a/examples/hello/bpWriter/helloBPWriter.xml
+++ b/examples/hello/bpWriter/helloBPWriter.xml
@@ -4,12 +4,17 @@
     <io name="BPFile_N2N">
         <engine type="BPFileWriter">
             Threads=1; <!-- for vectorized memory operations and asynchronous tasks --> 
-        	ProfileUnits=Microseconds; <!-- Microseconds (default), Milliseconds, Seconds, Minutes, Hours -->
-        	MaxBufferSize=20Mb;  <!-- XXKb, XXMb, or XXXGb supported, 16Mb (default should depend on system) -->
-        	InitialBufferSize=1Mb; <!-- XXKb, XXMb, or XXXGb supported, 16Kb (default shoudl depende on system) -->
-        	BufferGrowthFactor=2;  <!-- exponential growth factor > 1,  1.5 (default), for this case: 1, 2, 4, 8, 16, 20 Mb-->
+            ProfileUnits=Microseconds; <!-- Microseconds (default), Milliseconds, Seconds, Minutes, Hours -->
+            MaxBufferSize=20Mb;  <!-- XXKb, XXMb, or XXXGb supported, 16Mb (default should depend on system) -->
+            InitialBufferSize=1Mb; <!-- XXKb, XXMb, or XXXGb supported, 16Kb (default should depend on system) -->
+            BufferGrowthFactor=2;  <!-- exponential growth factor > 1,  1.5 (default, e.g. STL default=2), for this case: 1, 2, 4, 8, 16, 20 Mb-->
         </engine>
-    </io>
 
-</adios-config>
+        <transport type="File">
+            Library=POSIX;
+            ProfileUnits=Milliseconds;
+        </transport>
+
+    </io>
 
+</adios-config>
\ No newline at end of file
diff --git a/examples/hello/bpWriter/helloBPWriterXML.cpp b/examples/hello/bpWriter/helloBPWriterXML.cpp
new file mode 100644
index 000000000..807b0c9c3
--- /dev/null
+++ b/examples/hello/bpWriter/helloBPWriterXML.cpp
@@ -0,0 +1,82 @@
+/*
+ * Distributed under the OSI-approved Apache License, Version 2.0.  See
+ * accompanying file Copyright.txt for details.
+ *
+ * helloBPWriter.cpp: Simple self-descriptive example of how to write a variable
+ * to a BP File that lives in several MPI processes.
+ *
+ *  Created on: Feb 16, 2017
+ *      Author: William F Godoy godoywf@ornl.gov
+ */
+
+#include <ios>      //std::ios_base::failure
+#include <iostream> //std::cout
+#include <mpi.h>
+#include <stdexcept> //std::invalid_argument std::exception
+#include <vector>
+
+#include <adios2.h>
+
+int main(int argc, char *argv[])
+{
+    MPI_Init(&argc, &argv);
+    int rank, size;
+    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
+    MPI_Comm_size(MPI_COMM_WORLD, &size);
+
+    /** Application variable */
+    std::vector<float> myFloats = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+    const std::size_t Nx = myFloats.size();
+
+    try
+    {
+        /** ADIOS class factory of IO class objects, DebugON is recommended */
+        adios::ADIOS adios("helloBPWriter.xml", MPI_COMM_WORLD, adios::DebugON);
+
+        /*** IO class object: settings and factory of Settings: Variables,
+         * Parameters, Transports, and Execution: Engines */
+        adios::IO &bpIO = adios.DeclareIO("BPFile_N2N");
+
+        /** global array : name, { shape (total) }, { start (local) }, { count
+         * (local) }, all are constant dimensions */
+        adios::Variable<float> &bpFloats = bpIO.DefineVariable<float>(
+            "bpFloats", {size * Nx}, {rank * Nx}, {Nx}, adios::ConstantDims);
+
+        /** Engine derived class, spawned to start IO operations */
+        auto bpWriter = bpIO.Open("myVector.bp", adios::OpenMode::Write);
+
+        if (!bpWriter)
+        {
+            throw std::ios_base::failure(
+                "ERROR: bpWriter not created at Open\n");
+        }
+
+        /** Write variable for buffering */
+        bpWriter->Write<float>(bpFloats, myFloats.data());
+
+        /** Create bp file, engine becomes unreachable after this*/
+        bpWriter->Close();
+    }
+    catch (std::invalid_argument &e)
+    {
+        std::cout << "Invalid argument exception, STOPPING PROGRAM from rank "
+                  << rank << "\n";
+        std::cout << e.what() << "\n";
+    }
+    catch (std::ios_base::failure &e)
+    {
+        std::cout
+            << "IO System base failure exception, STOPPING PROGRAM from rank "
+            << rank << "\n";
+        std::cout << e.what() << "\n";
+    }
+    catch (std::exception &e)
+    {
+        std::cout << "Exception, STOPPING PROGRAM from rank " << rank << "\n";
+        std::cout << e.what() << "\n";
+    }
+
+    MPI_Finalize();
+
+    return 0;
+}
diff --git a/source/adios2/core/ADIOS.cpp b/source/adios2/core/ADIOS.cpp
index ee373fb86..194dbdddb 100644
--- a/source/adios2/core/ADIOS.cpp
+++ b/source/adios2/core/ADIOS.cpp
@@ -19,6 +19,7 @@
 /// \endcond
 
 #include "adios2/ADIOSMPI.h"
+#include "adios2/helper/adiosFunctions.h"
 
 namespace adios
 {
@@ -31,9 +32,14 @@ ADIOS::ADIOS(const std::string configFile, MPI_Comm mpiComm,
     {
         CheckMPI();
     }
-    // XML to be implemented later
-    // InitXML( m_XMLConfigFile, m_MPIComm, m_DebugMode, m_HostLanguage,
-    // m_Transforms, m_Groups );
+    if (!configFile.empty())
+    {
+        if (configFile.substr(configFile.size() - 3) == "xml")
+        {
+            InitXML(configFile, m_MPIComm, m_DebugMode, m_Transforms, m_IOs);
+        }
+        // TODO expand for other formats
+    }
 }
 
 ADIOS::ADIOS(const std::string configFile, const bool debugMode)
@@ -56,7 +62,7 @@ IO &ADIOS::DeclareIO(const std::string ioName)
     {
         if (m_DebugMode)
         {
-            if (itIO->second.InConfigFile())
+            if (!itIO->second.InConfigFile())
             {
                 throw std::invalid_argument(
                     "ERROR: IO class object with name " + ioName +
diff --git a/source/adios2/helper/adiosString.cpp b/source/adios2/helper/adiosString.cpp
index 90b6399b1..bc7841276 100644
--- a/source/adios2/helper/adiosString.cpp
+++ b/source/adios2/helper/adiosString.cpp
@@ -24,9 +24,9 @@ std::string FileToString(const std::string &fileName)
 {
     std::ifstream fileStream(fileName);
 
-    if (!fileStream.good())
+    if (!fileStream)
     {
-        throw std::ios_base::failure(
+        throw std::invalid_argument(
             "ERROR: file " + fileName +
             " could not be opened. Check permissions or existence\n");
     }
diff --git a/source/adios2/helper/adiosSystem.cpp b/source/adios2/helper/adiosSystem.cpp
index 0dd5896d8..bc8acdcfb 100644
--- a/source/adios2/helper/adiosSystem.cpp
+++ b/source/adios2/helper/adiosSystem.cpp
@@ -79,10 +79,9 @@ std::string LocalTimeDate() noexcept
     return std::string(std::ctime(&now));
 }
 
-std::string BroadcastFileContents(const std::string &fileName,
-                                  MPI_Comm mpiComm) noexcept
+std::string BroadcastString(const std::string &input, MPI_Comm mpiComm)
 {
-    std::string fileContents;
+    std::string receivedInput;
     size_t characterCount = 0;
 
     int rank;
@@ -90,15 +89,16 @@ std::string BroadcastFileContents(const std::string &fileName,
 
     if (rank == 0) // sender
     {
-        fileContents = FileToString(fileName);
-        characterCount = fileContents.size();
+        characterCount = input.size();
 
         // broadcast size for allocation
         MPI_Bcast(&characterCount, 1, ADIOS2_MPI_SIZE_T, 0, mpiComm);
 
         // broadcast contents
-        MPI_Bcast(const_cast<char *>(fileContents.c_str()),
+        MPI_Bcast(const_cast<char *>(input.c_str()),
                   static_cast<int>(characterCount), MPI_CHAR, 0, mpiComm);
+
+        return input;
     }
     else // receivers
     {
@@ -106,15 +106,14 @@ std::string BroadcastFileContents(const std::string &fileName,
         MPI_Bcast(&characterCount, 1, ADIOS2_MPI_SIZE_T, 0, mpiComm);
 
         // allocate receiver
-        std::vector<char> fileContentsReceiver(characterCount);
-        MPI_Bcast(fileContentsReceiver.data(), static_cast<int>(characterCount),
+        std::vector<char> stringReceiver(characterCount);
+        MPI_Bcast(stringReceiver.data(), static_cast<int>(characterCount),
                   MPI_CHAR, 0, mpiComm);
 
-        fileContents.assign(fileContentsReceiver.begin(),
-                            fileContentsReceiver.end());
+        receivedInput.assign(stringReceiver.begin(), stringReceiver.end());
     }
 
-    return fileContents;
+    return receivedInput;
 }
 
 } // end namespace adios
diff --git a/source/adios2/helper/adiosSystem.h b/source/adios2/helper/adiosSystem.h
index acbae391d..389e095dd 100644
--- a/source/adios2/helper/adiosSystem.h
+++ b/source/adios2/helper/adiosSystem.h
@@ -50,8 +50,7 @@ std::string LocalTimeDate() noexcept;
  * @param mpiComm
  * @return fileContents as a single string
  */
-std::string BroadcastFileContents(const std::string &fileName,
-                                  MPI_Comm mpiComm) noexcept;
+std::string BroadcastString(const std::string &input, MPI_Comm mpiComm);
 
 } // end namespace adios
 
diff --git a/source/adios2/helper/adiosXML.cpp b/source/adios2/helper/adiosXML.cpp
index 8107a4104..d52968e1e 100644
--- a/source/adios2/helper/adiosXML.cpp
+++ b/source/adios2/helper/adiosXML.cpp
@@ -16,145 +16,163 @@
 /// \endcond
 
 #include "adios2/ADIOSTypes.h"
-#include "adios2/helper/adiosSystem.h"
+#include "adios2/helper/adiosString.h"
 
 namespace adios
 {
 
-std::string GetSubString(const std::string initialTag,
-                         const std::string finalTag, const std::string &content,
-                         std::string::size_type &currentPosition)
+void RemoveCommentsXML(std::string &currentContent) noexcept
 {
-    auto lf_Wipe = [](std::string &subString,
-                      std::string::size_type &currentPosition) {
-        subString.clear();
-        currentPosition = std::string::npos;
-    };
+    std::string::size_type startComment(currentContent.find("<!--"));
+
+    while (startComment != currentContent.npos)
+    {
+        std::string::size_type endComment(currentContent.find("-->"));
+        currentContent.erase(startComment, endComment - startComment + 3);
+        startComment = currentContent.find("<!--");
+    }
+}
 
+TagXML GetTagXML(const std::string tagName, const std::string &content,
+                 std::string::size_type &position)
+{
     auto lf_SetPositions =
-        [](const char quote, const std::string::size_type quotePosition,
-           const std::string &content, std::string::size_type &currentPosition,
-           std::string::size_type &closingQuotePosition) {
-            currentPosition = quotePosition;
-            closingQuotePosition = content.find(quote, currentPosition + 1);
-        };
-
-    // BODY OF FUNCTION STARTS HERE
-    std::string subString;
-
-    std::string::size_type start(content.find(initialTag, currentPosition));
-    if (start == content.npos)
+        [](const std::string input, const std::string &content,
+           std::string::size_type &position) -> std::string::size_type {
+
+        const std::string::size_type inputPosition =
+            GetStringPositionXML(input, content, position);
+
+        if (inputPosition != std::string::npos)
+        {
+            position = inputPosition + input.size();
+        }
+        return inputPosition;
+    };
+
+    TagXML tagXML;
+
+    std::string name(tagName);
+    if (name.back() == ' ')
+    {
+        name.pop_back();
+    }
+
+    auto openingStart = lf_SetPositions("<" + name, content, position);
+    auto openingEnd = lf_SetPositions(">", content, position);
+
+    if (openingStart == std::string::npos || openingEnd == std::string::npos)
+    {
+        tagXML.IsFull = false;
+        return tagXML;
+    }
+
+    tagXML.Header = content.substr(openingStart, openingEnd + 1 - openingStart);
+
+    auto closingStart =
+        GetStringPositionXML("</" + name + ">", content, position);
+
+    if (closingStart == std::string::npos)
     {
-        lf_Wipe(subString, currentPosition);
-        return subString;
+        throw std::invalid_argument(
+            "ERROR: could not find closing tag </" + name +
+            "> in XML config file, in call to ADIOS constructor\n");
     }
-    currentPosition = start;
+    tagXML.IsFull = true;
+    tagXML.Elements =
+        content.substr(openingEnd + 1, closingStart - (openingEnd + 1));
+
+    //    std::cout << "START..." << tagXML.Header << "...";
+    //    std::cout << tagXML.Elements << "...END\n";
+
+    return tagXML;
+}
 
-    std::string::size_type end(content.find(finalTag, currentPosition));
-    if (end == content.npos)
+std::string::size_type
+GetStringPositionXML(const std::string input, const std::string &content,
+                     const std::string::size_type &startPosition) noexcept
+{
+    std::string::size_type foundPosition(content.find(input, startPosition));
+    if (foundPosition == content.npos)
     {
-        lf_Wipe(subString, currentPosition);
-        return subString;
+        return foundPosition;
     }
 
-    // make sure the finalTag is not a value surrounded by " " or ' ',
-    // if so find next
-    bool isValue = true;
+    // check if it is not inside " " or ' '
+    std::string::size_type currentPosition(startPosition);
 
-    while (isValue)
+    while (foundPosition != content.npos)
     {
-        std::string::size_type singleQuotePosition =
-            content.find('\'', currentPosition);
-        std::string::size_type doubleQuotePosition =
-            content.find('\"', currentPosition);
+        const std::string::size_type singleQuotePosition(
+            content.find('\'', currentPosition));
+        const std::string::size_type doubleQuotePosition(
+            content.find('\"', currentPosition));
 
         if ((singleQuotePosition == content.npos &&
              doubleQuotePosition == content.npos) ||
             (singleQuotePosition == content.npos &&
-             end < doubleQuotePosition) ||
+             foundPosition < doubleQuotePosition) ||
             (doubleQuotePosition == content.npos &&
-             end < singleQuotePosition) ||
-            (end < singleQuotePosition && end < doubleQuotePosition))
+             foundPosition < singleQuotePosition) ||
+            (foundPosition < singleQuotePosition &&
+             foundPosition < doubleQuotePosition))
         {
             break;
         }
-
         // find the closing corresponding quote
         std::string::size_type closingQuotePosition;
 
-        if (singleQuotePosition == content.npos)
-        { // no ' anywhere
-            lf_SetPositions('\"', doubleQuotePosition, content, currentPosition,
-                            closingQuotePosition);
+        if (singleQuotePosition != content.npos &&
+            doubleQuotePosition == content.npos)
+        {
+            currentPosition = singleQuotePosition;
+            closingQuotePosition = content.find('\'', currentPosition + 1);
         }
-        else if (doubleQuotePosition == content.npos)
-        { // no " anywhere
-            lf_SetPositions('\'', singleQuotePosition, content, currentPosition,
-                            closingQuotePosition);
+        else if (singleQuotePosition == content.npos &&
+                 doubleQuotePosition != content.npos)
+        {
+            currentPosition = doubleQuotePosition;
+            closingQuotePosition = content.find('\"', currentPosition + 1);
         }
         else
         {
             if (singleQuotePosition < doubleQuotePosition)
             {
-                lf_SetPositions('\'', singleQuotePosition, content,
-                                currentPosition, closingQuotePosition);
+                currentPosition = singleQuotePosition;
+                closingQuotePosition = content.find('\'', currentPosition + 1);
             }
             else
-            { // find the closing "
-                lf_SetPositions('\"', doubleQuotePosition, content,
-                                currentPosition, closingQuotePosition);
+            {
+                currentPosition = doubleQuotePosition;
+                closingQuotePosition = content.find('\"', currentPosition + 1);
             }
         }
-
         // if can't find closing it's open until the end
         if (closingQuotePosition == content.npos)
         {
-            lf_Wipe(subString, currentPosition);
-            return subString;
+            currentPosition == content.npos;
+            break;
         }
 
         currentPosition = closingQuotePosition + 1;
 
-        if (closingQuotePosition < end)
+        if (closingQuotePosition < foundPosition)
         {
             continue;
         }
-
-        // if this point is reached it means it's a value inside " " or ' ',
-        // move to the next end
-        end = content.find(finalTag, currentPosition);
-    }
-
-    currentPosition = end;
-    subString = content.substr(start, end - start + finalTag.size());
-    return subString;
-}
-
-std::string GetOpeningTag(const std::string tagName,
-                          const std::string &tagContents,
-                          std::string::size_type &position,
-                          const bool debugMode)
-{
-    std::string openingTag(
-        GetSubString("<" + tagName + " ", ">", tagContents, position));
-
-    const std::string::size_type elementsStartPosition(position);
-
-    if (debugMode)
-    {
-        if (openingTag.size() < 2)
+        else
         {
-            throw std::invalid_argument("ERROR: wrong XML tag <" + tagName +
-                                        ", in call to ADIOS constructor\n");
+            // if this point is reached it means it's a value inside " " or ' ',
+            // iterate
+            foundPosition = content.find(input, currentPosition);
+            currentPosition = foundPosition;
         }
     }
 
-    // eliminate < >
-    openingTag = openingTag.substr(1, openingTag.size() - 2);
-    return openingTag;
+    return foundPosition;
 }
 
-Params GetTagAttributes(const std::string &fileContent, const std::string &tag)
+Params GetTagAttributesXML(const std::string &tagHeader)
 {
     auto lf_GetQuotedValue = [](const char quote,
                                 const std::string::size_type &quotePosition,
@@ -170,8 +188,9 @@ Params GetTagAttributes(const std::string &fileContent, const std::string &tag)
                 "...check XML file, in call to ADIOS constructor\n");
         }
 
+        const std::string value(currentTag.substr(0, nextQuotePosition));
         currentTag = currentTag.substr(nextQuotePosition + 1);
-        return currentTag.substr(0, nextQuotePosition);
+        return value;
     };
 
     auto lf_GetAttributes = [&](const std::string &tag) -> Params {
@@ -199,6 +218,10 @@ Params GetTagAttributes(const std::string &fileContent, const std::string &tag)
             {
                 value = lf_GetQuotedValue(quote, equalPosition + 1, currentTag);
             }
+            else
+            {
+                // throw exception here?
+            }
 
             attributes.emplace(key, value);
         }
@@ -207,105 +230,80 @@ Params GetTagAttributes(const std::string &fileContent, const std::string &tag)
 
     // BODY of function starts here
     Params attributes;
+    // eliminate < >
+    std::string openingTag = tagHeader.substr(1, tagHeader.size() - 2);
 
-    if (tag.back() == '/') // last char is / --> "XML empty tag"
+    if (tagHeader.back() == '/') // last char is / --> "XML empty tag"
     {
-        attributes = lf_GetAttributes(tag);
+        // attributes = lf_GetAttributes(openingTag);
+        // throw exception here, ADIOS2 doesn't allow XML empty tags
     }
-    else if (tag[0] == '/') // first char is / ---> closing tag
+    else if (tagHeader[0] == '/') // first char is / ---> closing tag
     {
-        attributes = lf_GetAttributes(tag);
+        attributes = lf_GetAttributes(openingTag);
         if (attributes.size() > 0)
         {
             throw std::invalid_argument(
-                "ERROR: closing tag " + tag +
+                "ERROR: closing tag " + tagHeader +
                 " can't have attributes, in call to ADIOS constructor\n");
         }
     }
     else // opening tag
     {
-        const std::string tagName(tag.substr(0, tag.find_first_of(" \t\n\r")));
-        // look for closing tagName
-        const std::string closingTagName("</" + tagName + ">");
-
-        if (fileContent.find(closingTagName) == fileContent.npos)
-        {
-            throw std::invalid_argument(
-                "ERROR: closing tag missing for " + closingTagName +
-                " check XML config file, in call to ADIOS constructor\n");
-        }
-
-        attributes = lf_GetAttributes(tag);
+        attributes = lf_GetAttributes(openingTag);
     }
     return attributes;
 }
 
-void RemoveXMLComments(std::string &currentContent) noexcept
-{
-    std::string::size_type startComment(currentContent.find("<!--"));
-
-    while (startComment != currentContent.npos)
-    {
-        std::string::size_type endComment(currentContent.find("-->"));
-        currentContent.erase(startComment, endComment - startComment + 3);
-        startComment = currentContent.find("<!--");
-    }
-}
-
 void InitXML(const std::string configXML, const MPI_Comm mpiComm,
              const bool debugMode,
              std::vector<std::shared_ptr<Transform>> &transforms,
              std::map<std::string, IO> &ios)
 {
-    // if using collective IO only?
-    std::string fileContents = BroadcastFileContents(configXML, mpiComm);
+    // independent IO
+    std::string fileContents(FileToString(configXML));
+    RemoveCommentsXML(fileContents);
 
     // adios-config
-    std::string::size_type currentPosition(0);
-    std::string currentContent(GetSubString("<adios-config ", "</adios-config>",
-                                            fileContents, currentPosition));
-    RemoveXMLComments(currentContent);
+    std::string::size_type position(0);
+    const TagXML adiosConfigXML(
+        GetTagXML("adios-config", fileContents, position));
 
     // process transforms, not yet implemented
-    while (currentPosition != std::string::npos)
+
+    while (position != std::string::npos)
     {
-        std::string transformTag(GetSubString("<transform ", "</transform>",
-                                              currentContent, currentPosition));
+        const TagXML transformXML(
+            GetTagXML("transform ", adiosConfigXML.Elements, position));
 
-        if (transformTag.empty()) // no more transforms
+        if (transformXML.Header.empty())
         {
             break;
         }
         // InitTransform(transformTag, debugMode, transforms);
     }
 
-    currentPosition = 0;
+    position = 0;
     // process IOs
-    while (currentPosition != std::string::npos)
+    while (position != std::string::npos)
     {
         // io
-        std::string ioTag(
-            GetSubString("<io ", "</io>", currentContent, currentPosition));
+        const TagXML ioXML(GetTagXML("io ", adiosConfigXML.Elements, position));
 
-        if (ioTag.empty()) // no more groups to find
+        if (ioXML.Header.empty()) // no more groups to find
         {
             break;
         }
-        InitIOXML(ioTag, mpiComm, debugMode, transforms, ios);
+        InitIOXML(ioXML, mpiComm, debugMode, transforms, ios);
     }
 }
 
-void InitIOXML(const std::string &ioTag, const MPI_Comm mpiComm,
+void InitIOXML(const TagXML &ioXML, const MPI_Comm mpiComm,
                const bool debugMode,
                std::vector<std::shared_ptr<Transform>> &transforms,
                std::map<std::string, IO> &ios)
 {
-    std::string::size_type position(0);
-    const std::string openingTag(
-        GetOpeningTag("io", ioTag, position, debugMode));
-    const std::string::size_type elementsStart(position);
-
-    Params ioAttributes = GetTagAttributes(ioTag, openingTag);
+    const Params ioAttributes(GetTagAttributesXML(ioXML.Header));
 
     std::string ioName;
     for (const auto &ioAttribute : ioAttributes)
@@ -323,7 +321,7 @@ void InitIOXML(const std::string &ioTag, const MPI_Comm mpiComm,
             throw std::invalid_argument(
                 "ERROR: io name=\"value\" attribute not found in opening XML "
                 "tag " +
-                openingTag +
+                ioXML.Header +
                 ", check XML config file, in call to ADIOS constructor\n");
         }
 
@@ -339,49 +337,45 @@ void InitIOXML(const std::string &ioTag, const MPI_Comm mpiComm,
     auto itIO = ios.emplace(ioName, IO(ioName, mpiComm, true, debugMode));
 
     // process engine
-    std::string engineTag(
-        GetSubString("<engine ", "</engine>", ioTag, position));
+    std::string::size_type position(0);
 
-    if (!engineTag.empty()) // no more groups to find
+    TagXML engineXML(GetTagXML("engine ", ioXML.Elements, position));
+    if (!engineXML.Header.empty()) // found first one
     {
-        InitEngineXML(engineTag, debugMode, itIO.first->second);
+        InitEngineXML(engineXML, debugMode, itIO.first->second);
     }
 
     if (debugMode)
     {
-        // try finding a 2nd one
-        std::string engineTag(
-            GetSubString("<engine ", "</engine>", ioTag, position));
-        if (!engineTag.empty())
+        // try finding a 2nd one from current position
+        TagXML engineXML(GetTagXML("engine ", ioXML.Elements, position));
+        if (!engineXML.Header.empty()) // found first one
         {
-            throw std::invalid_argument("ERROR: two engine tags found in IO " +
-                                        ioName + ", only one is allowed in XML "
-                                                 "config file, in call to "
-                                                 "ADIOS constructor\n");
+            throw std::invalid_argument(
+                "ERROR: more than one engine found in <io name=" + ioName +
+                "...>, only one per io tag is allowed in XML "
+                "config file, in call to "
+                "ADIOS constructor\n");
         }
     }
 
-    position = elementsStart;
+    position = 0;
     // process transports
     while (position != std::string::npos)
     {
-        std::string transportTag(
-            GetSubString("<transport ", "</transport>", ioTag, position));
+        TagXML transportXML(GetTagXML("transport", ioXML.Elements, position));
 
-        if (transportTag.empty()) // no more groups to find
+        if (transportXML.Header.empty()) // no more groups to find
         {
             break;
         }
+        InitTransportXML(transportXML, debugMode, itIO.first->second);
     }
 }
 
-void InitEngineXML(const std::string &engineTag, const bool debugMode, IO &io)
+void InitEngineXML(const TagXML &engineXML, const bool debugMode, IO &io)
 {
-    std::string::size_type position(0);
-    const std::string openingTag(
-        GetOpeningTag("engine", engineTag, position, debugMode));
-    const std::string::size_type elementsStartPosition(position);
-    Params attributes = GetTagAttributes(engineTag, openingTag);
+    const Params attributes = GetTagAttributesXML(engineXML.Header);
 
     std::string type;
     for (const auto &attribute : attributes)
@@ -389,22 +383,21 @@ void InitEngineXML(const std::string &engineTag, const bool debugMode, IO &io)
         if (attribute.first == "type")
         {
             type = attribute.second;
+            break;
         }
     }
 
-    io.SetEngine(type);
-    io.SetParameters(
-        ParseParamsXML(engineTag, elementsStartPosition, debugMode));
+    if (!type.empty())
+    {
+        io.SetEngine(type);
+    }
+
+    io.SetParameters(ParseParamsXML(engineXML.Elements, debugMode));
 }
 
-void InitTransportXML(const std::string &transportTag, const bool debugMode,
-                      IO &io)
+void InitTransportXML(const TagXML &transportXML, const bool debugMode, IO &io)
 {
-    std::string::size_type position(0);
-    const std::string openingTag(
-        GetOpeningTag("transport", transportTag, position, debugMode));
-    const std::string::size_type elementsStartPosition(position);
-    Params attributes = GetTagAttributes(transportTag, openingTag);
+    const Params attributes = GetTagAttributesXML(transportXML.Header);
 
     std::string type;
     for (const auto &attribute : attributes)
@@ -412,24 +405,44 @@ void InitTransportXML(const std::string &transportTag, const bool debugMode,
         if (attribute.first == "type")
         {
             type = attribute.second;
+            break;
         }
     }
 
-    io.AddTransport(
-        type, ParseParamsXML(transportTag, elementsStartPosition, debugMode));
+    if (type.empty())
+    {
+        throw std::invalid_argument(
+            "ERROR: missing transport type in " + transportXML.Header +
+            ", in XML config file, in call to ADIOS constructor\n");
+    }
+
+    io.AddTransport(type, ParseParamsXML(transportXML.Elements, debugMode));
 }
 
-Params ParseParamsXML(const std::string &tag,
-                      const std::string::size_type elementsStartPosition,
-                      const bool debugMode)
+Params ParseParamsXML(const std::string &tagElements, const bool debugMode)
 {
-    std::istringstream parametersSS(tag.substr(elementsStartPosition));
+    auto start = tagElements.find_first_not_of(" \t\n");
+    auto end = tagElements.find_last_not_of(" \t\n");
+
+    std::string parametersString(tagElements.substr(start, end - start + 1));
+    if (debugMode)
+    {
+        if (parametersString.back() != ';')
+        {
+            throw std::invalid_argument(
+                "ERROR: parameters in config XML file must end with a ; " +
+                tagElements + ", in call to ADIOS constructor\n");
+        }
+    }
+
+    std::istringstream parametersSS(parametersString);
     std::string pair;
 
     Params parameters;
 
     while (std::getline(parametersSS, pair, ';'))
     {
+        pair = pair.substr(pair.find_first_not_of(" \t\n"));
         auto equalPosition = pair.find("=");
 
         if (debugMode)
@@ -443,6 +456,7 @@ Params ParseParamsXML(const std::string &tag,
                                             "call to ADIOS constructor\n");
             }
         }
+
         const std::string key(pair.substr(0, equalPosition));
         const std::string value(pair.substr(equalPosition + 1));
         parameters.emplace(key, value);
diff --git a/source/adios2/helper/adiosXML.h b/source/adios2/helper/adiosXML.h
index 30ae76222..22d6abd89 100644
--- a/source/adios2/helper/adiosXML.h
+++ b/source/adios2/helper/adiosXML.h
@@ -25,43 +25,23 @@
 namespace adios
 {
 
-/**
- * Extracts a substring between two tags from content
- * @param initialTag
- * @param finalTag
- * @param content full string
- * @param subString if found return substring between initialTag and finalTag,
- * otherwise returns empty
- * @param currentPosition to start the search, moved forward to finalTag
- * position
- */
-std::string GetSubString(const std::string initialTag,
-                         const std::string finalTag, const std::string &content,
-                         std::string::size_type &currentPosition);
+struct TagXML
+{
+    std::string Header;
+    std::string Elements;
+    bool IsFull;
+};
 
-/**
- * Determine tag type ( opening, empty, closing ), populates and returns Params
- * (map<string,string>) object with xml tag attributes
- * @param fileContent to check for missing tag closing
- * @param tag single string: key0="value0" ... keyN="valueN"
- * @return std::map of attributes: { { "key0", "value0" } ,..., { "keyN",
- * "valueN" } }
- */
-Params GetTagAttributes(const std::string &fileContent, const std::string &tag);
+void RemoveCommentsXML(std::string &currentContent) noexcept;
 
-/**
- * Set members m_Groups and m_HostLanguage from XML file content, called within
- * Init functions
- * @param fileContent file Content in a single string
- * @param mpiComm MPI Communicator passed from application passed to Transport
- * method if required
- * @param hostLanguage return the host language from fileContent
- * @param transforms return the modified transforms vector if there are
- * variables with transformations
- * @param groups passed returns the map of groups defined in fileContent
- */
+TagXML GetTagXML(const std::string tagName, const std::string &content,
+                 std::string::size_type &position);
+
+std::string::size_type
+GetStringPositionXML(const std::string input, const std::string &content,
+                     const std::string::size_type &startPosition) noexcept;
 
-void RemoveXMLComments(std::string &currentContent) noexcept;
+Params GetTagAttributesXML(const std::string &tagHeader);
 
 /**
  * Called inside the ADIOS XML constructors to get contents from file,
@@ -77,19 +57,16 @@ void InitXML(const std::string configXML, const MPI_Comm mpiComm,
              std::vector<std::shared_ptr<Transform>> &transforms,
              std::map<std::string, IO> &ios);
 
-void InitIOXML(const std::string &ioTag, const MPI_Comm mpiComm,
+void InitIOXML(const TagXML &ioXML, const MPI_Comm mpiComm,
                const bool debugMode,
                std::vector<std::shared_ptr<Transform>> &transforms,
                std::map<std::string, IO> &ios);
 
-void InitEngineXML(const std::string &engineTag, const bool debugMode, IO &io);
+void InitEngineXML(const TagXML &engineXML, const bool debugMode, IO &io);
 
-void InitTransportXML(const std::string &transportTag, const bool debugMode,
-                      IO &io);
+void InitTransportXML(const TagXML &transportXML, const bool debugMode, IO &io);
 
-Params ParseParamsXML(const std::string &tag,
-                      const std::string::size_type elementsStartPosition,
-                      const bool debugMode);
+Params ParseParamsXML(const std::string &tagContents, const bool debugMode);
 }
 
 #endif /* ADIOS2_HELPER_ADIOSXML_H_ */
-- 
GitLab