LCOV - code coverage report
Current view: top level - src/utils/iodevices - CSVFormatter.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.5 % 40 37
Test Date: 2026-03-26 16:31:35 Functions: 18.8 % 85 16

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2012-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    CSVFormatter.h
      15              : /// @author  Michael Behrisch
      16              : /// @date    2025-06-12
      17              : ///
      18              : // Output formatter for CSV output
      19              : /****************************************************************************/
      20              : #pragma once
      21              : #include <config.h>
      22              : 
      23              : #include <memory>
      24              : #include "OutputFormatter.h"
      25              : 
      26              : 
      27              : // ===========================================================================
      28              : // class definitions
      29              : // ===========================================================================
      30              : /**
      31              :  * @class CSVFormatter
      32              :  * @brief Output formatter for CSV output
      33              :  */
      34              : class CSVFormatter : public OutputFormatter {
      35              : public:
      36              :     /// @brief Constructor
      37              :     CSVFormatter(const std::string& columnNames, const char separator = ';');
      38              : 
      39              :     /// @brief Destructor
      40           56 :     virtual ~CSVFormatter() { }
      41              : 
      42              :     /** @brief Keeps track of an open XML tag by adding a new element to the stack
      43              :      *
      44              :      * @param[in] into The output stream to use (unused)
      45              :      * @param[in] xmlElement Name of element to open (unused)
      46              :      * @return The OutputDevice for further processing
      47              :      */
      48              :     void openTag(std::ostream& into, const std::string& xmlElement);
      49              : 
      50              :     /** @brief Keeps track of an open XML tag by adding a new element to the stack
      51              :      *
      52              :      * @param[in] into The output stream to use (unused)
      53              :      * @param[in] xmlElement Name of element to open (unused)
      54              :      */
      55              :     void openTag(std::ostream& into, const SumoXMLTag& xmlElement);
      56              : 
      57              :     /** @brief Closes the most recently opened tag
      58              :      *
      59              :      * @param[in] into The output stream to use
      60              :      * @return Whether a further element existed in the stack and could be closed
      61              :      * @todo it is not verified that the topmost element was closed
      62              :      */
      63              :     bool closeTag(std::ostream& into, const std::string& comment = "");
      64              : 
      65              :     /** @brief writes a named attribute
      66              :      *
      67              :      * @param[in] into The output stream to use
      68              :      * @param[in] attr The attribute (name)
      69              :      * @param[in] val The attribute value
      70              :      */
      71              :     template <class T>
      72        17606 :     void writeAttr(std::ostream& into, const SumoXMLAttr attr, const T& val, const bool isNull) {
      73        17606 :         checkAttr(attr);
      74        24344 :         myValues.emplace_back(isNull ? "" : toString(val, into.precision()));
      75        17606 :     }
      76              : 
      77              :     template <class T>
      78        53176 :     void writeAttr(std::ostream& into, const std::string& attr, const T& val, const bool isNull) {
      79              :         assert(!myCheckColumns);
      80        53176 :         checkHeader(attr);
      81        73724 :         myValues.emplace_back(isNull ? "" : toString(val, into.precision()));
      82        53176 :     }
      83              : 
      84         1120 :     void writeTime(std::ostream& /* into */, const SumoXMLAttr attr, const SUMOTime val) {
      85         1120 :         checkAttr(attr);
      86         1120 :         myValues.emplace_back(time2string(val));
      87         1120 :     }
      88              : 
      89            0 :     bool wroteHeader() const {
      90            0 :         return myWroteHeader;
      91              :     }
      92              : 
      93           28 :     void setExpectedAttributes(const SumoXMLAttrMask& expected, const int depth = 2) {
      94           28 :         myExpectedAttrs = expected;
      95           28 :         myMaxDepth = depth;
      96           28 :         myCheckColumns = expected.any();
      97           28 :     }
      98              : 
      99              : private:
     100              :     /** @brief Helper function to keep track of the written attributes and accumulate the header.
     101              :      * It checks whether the written attribute is expected in the column based format.
     102              :      * The check does only apply to the deepest level of the XML hierarchy and not to the order of the columns just to the presence.
     103              :      *
     104              :      * @param[in] attr The attribute (name)
     105              :      */
     106        18726 :     inline void checkAttr(const SumoXMLAttr attr) {
     107        18726 :         if (myCheckColumns && myMaxDepth == (int)myXMLStack.size()) {
     108         8100 :             mySeenAttrs.set(attr);
     109         8100 :             if (!myExpectedAttrs.test(attr)) {
     110            0 :                 throw ProcessError(TLF("Unexpected attribute '%', this file format does not support CSV output yet.", toString(attr)));
     111              :             }
     112              :         }
     113        18726 :         checkHeader(attr);
     114        18726 :     }
     115              : 
     116              :     template <class ATTR_TYPE>
     117        71902 :     inline void checkHeader(const ATTR_TYPE& attr) {
     118        71902 :         myNeedsWrite = true;
     119        71902 :         if (!myWroteHeader) {
     120         3933 :             std::string headerName = toString(attr);
     121        54091 :             if (myHeaderFormat != "plain" && !(myHeaderFormat == "auto" && std::find(myHeader.begin(), myHeader.end(), headerName) == myHeader.end())) {
     122       162273 :                 headerName = myCurrentTag + "_" + headerName;
     123              :             }
     124        54091 :             if (std::find(myHeader.begin(), myHeader.end(), headerName) == myHeader.end()) {
     125        12737 :                 for (std::string& row : myBufferedRows) {
     126        12347 :                     row += mySeparator;
     127              :                 }
     128          478 :                 while (myValues.size() < myHeader.size()) {
     129           88 :                     myValues.emplace_back("");
     130              :                 }
     131          390 :                 myHeader.emplace_back(headerName);
     132              :             }
     133              :         }
     134        71902 :     }
     135              : 
     136              :     /// @brief the format to use for the column names
     137              :     const std::string myHeaderFormat;
     138              : 
     139              :     /// @brief The value separator
     140              :     const char mySeparator;
     141              : 
     142              :     /// @brief the CSV header
     143              :     std::vector<std::string> myHeader;
     144              : 
     145              :     /// @brief the currently read tag (only valid when generating the header)
     146              :     std::string myCurrentTag;
     147              : 
     148              :     /// @brief The number of attributes in the currently open XML elements
     149              :     std::vector<int> myXMLStack;
     150              : 
     151              :     /// @brief the current attribute / column values
     152              :     std::vector<std::string> myValues;
     153              : 
     154              :     /// @brief the maximum depth of the XML hierarchy (excluding the root element)
     155              :     int myMaxDepth = 2;
     156              : 
     157              :     /// @brief whether the CSV header line has been written
     158              :     bool myWroteHeader = false;
     159              : 
     160              :     /// @brief whether any attribute has been written since the last row was emitted
     161              :     bool myNeedsWrite = false;
     162              : 
     163              :     /// @brief partial rows buffered before the schema is known (depth < myMaxDepth)
     164              :     std::vector<std::string> myBufferedRows;
     165              : 
     166              :     /// @brief whether the columns should be checked for completeness
     167              :     bool myCheckColumns = false;
     168              : 
     169              :     /// @brief which CSV columns are expected (just for checking completeness)
     170              :     SumoXMLAttrMask myExpectedAttrs;
     171              : 
     172              :     /// @brief which CSV columns have been set (just for checking completeness)
     173              :     SumoXMLAttrMask mySeenAttrs;
     174              : };
        

Generated by: LCOV version 2.0-1