LCOV - code coverage report
Current view: top level - src/utils/iodevices - CSVFormatter.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 67.4 % 46 31
Test Date: 2025-12-06 15:35:27 Functions: 9.5 % 84 8

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2012-2025 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              : #ifdef HAVE_FMT
      25              : #include <fmt/ostream.h>
      26              : #endif
      27              : 
      28              : #include "OutputFormatter.h"
      29              : 
      30              : 
      31              : // ===========================================================================
      32              : // class definitions
      33              : // ===========================================================================
      34              : /**
      35              :  * @class CSVFormatter
      36              :  * @brief Output formatter for CSV output
      37              :  */
      38              : class CSVFormatter : public OutputFormatter {
      39              : public:
      40              :     /// @brief Constructor
      41              :     CSVFormatter(const std::string& columnNames, const char separator = ';');
      42              : 
      43              :     /// @brief Destructor
      44           12 :     virtual ~CSVFormatter() { }
      45              : 
      46              :     /** @brief Keeps track of an open XML tag by adding a new element to the stack
      47              :      *
      48              :      * @param[in] into The output stream to use (unused)
      49              :      * @param[in] xmlElement Name of element to open (unused)
      50              :      * @return The OutputDevice for further processing
      51              :      */
      52              :     void openTag(std::ostream& into, const std::string& xmlElement);
      53              : 
      54              :     /** @brief Keeps track of an open XML tag by adding a new element to the stack
      55              :      *
      56              :      * @param[in] into The output stream to use (unused)
      57              :      * @param[in] xmlElement Name of element to open (unused)
      58              :      */
      59              :     void openTag(std::ostream& into, const SumoXMLTag& xmlElement);
      60              : 
      61              :     /** @brief Closes the most recently opened tag
      62              :      *
      63              :      * @param[in] into The output stream to use
      64              :      * @return Whether a further element existed in the stack and could be closed
      65              :      * @todo it is not verified that the topmost element was closed
      66              :      */
      67              :     bool closeTag(std::ostream& into, const std::string& comment = "");
      68              : 
      69              :     /** @brief writes a named attribute
      70              :      *
      71              :      * @param[in] into The output stream to use
      72              :      * @param[in] attr The attribute (name)
      73              :      * @param[in] val The attribute value
      74              :      */
      75              :     template <class T>
      76            0 :     void writeAttr(std::ostream& into, const SumoXMLAttr attr, const T& val) {
      77            0 :         checkAttr(attr);
      78            0 :         *myXMLStack[myCurrentDepth - 1] << toString(val, into.precision()) << mySeparator;
      79            0 :     }
      80              : 
      81              :     template <class T>
      82            0 :     void writeAttr(std::ostream& into, const std::string& attr, const T& val) {
      83              :         assert(!myCheckColumns);
      84            0 :         if (!myWroteHeader) {
      85            0 :             if (std::find(myHeader.begin(), myHeader.end(), attr) != myHeader.end()) {
      86            0 :                 myHeader.push_back(myCurrentTag + "_" + attr);
      87              :             } else {
      88            0 :                 myHeader.push_back(attr);
      89              :             }
      90              :         }
      91            0 :         *myXMLStack[myCurrentDepth - 1] << toString(val, into.precision()) << mySeparator;
      92            0 :     }
      93              : 
      94          696 :     void writeNull(std::ostream& /* into */, const SumoXMLAttr attr) {
      95          696 :         checkAttr(attr);
      96          696 :         *myXMLStack[myCurrentDepth - 1] << mySeparator;
      97          696 :     }
      98              : 
      99          720 :     void writeTime(std::ostream& /* into */, const SumoXMLAttr attr, const SUMOTime val) {
     100          720 :         checkAttr(attr);
     101         1440 :         *myXMLStack[myCurrentDepth - 1] << time2string(val) << mySeparator;
     102          720 :     }
     103              : 
     104            0 :     bool wroteHeader() const {
     105            0 :         return myWroteHeader;
     106              :     }
     107              : 
     108            6 :     void setExpectedAttributes(const SumoXMLAttrMask& expected, const int depth = 2) {
     109            6 :         myExpectedAttrs = expected;
     110            6 :         myMaxDepth = depth;
     111            6 :         myCheckColumns = expected.any();
     112            6 :     }
     113              : 
     114              : private:
     115              :     /** @brief Helper function to keep track of the written attributes and accumulate the header.
     116              :      * It checks whether the written attribute is expected in the column based format.
     117              :      * 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.
     118              :      *
     119              :      * @param[in] attr The attribute (name)
     120              :      */
     121         7680 :     inline void checkAttr(const SumoXMLAttr attr) {
     122         7680 :         if (myCheckColumns && myMaxDepth == myCurrentDepth) {
     123         6960 :             mySeenAttrs.set(attr);
     124         6960 :             if (!myExpectedAttrs.test(attr)) {
     125            0 :                 throw ProcessError(TLF("Unexpected attribute '%', this file format does not support CSV output yet.", toString(attr)));
     126              :             }
     127              :         }
     128         7680 :         if (!myWroteHeader) {
     129          666 :             const std::string attrString = toString(attr);
     130          666 :             if (myHeaderFormat == "plain" || (myHeaderFormat == "auto" && std::find(myHeader.begin(), myHeader.end(), attrString) == myHeader.end())) {
     131            0 :                 myHeader.push_back(attrString);
     132              :             } else {
     133         1332 :                 myHeader.push_back(myCurrentTag + "_" + attrString);
     134              :             }
     135              :         }
     136         7680 :     }
     137              : 
     138              :     /// @brief the format to use for the column names
     139              :     const std::string myHeaderFormat;
     140              : 
     141              :     /// @brief The value separator
     142              :     const char mySeparator;
     143              : 
     144              :     /// @brief the CSV header
     145              :     std::vector<std::string> myHeader;
     146              : 
     147              :     /// @brief the currently read tag (only valid when generating the header)
     148              :     std::string myCurrentTag;
     149              : 
     150              :     /// @brief The attributes to write for each begun xml element (excluding the root element)
     151              :     std::vector<std::unique_ptr<std::ostringstream>> myXMLStack;
     152              : 
     153              :     /// @brief the maximum depth of the XML hierarchy (excluding the root element)
     154              :     int myMaxDepth = 0;
     155              : 
     156              :     /// @brief the current depth of the XML hierarchy (excluding the root element)
     157              :     int myCurrentDepth = 0;
     158              : 
     159              :     /// @brief whether the CSV header line has been written
     160              :     bool myWroteHeader = false;
     161              : 
     162              :     /// @brief whether the columns should be checked for completeness
     163              :     bool myCheckColumns = false;
     164              : 
     165              :     /// @brief which CSV columns are expected (just for checking completeness)
     166              :     SumoXMLAttrMask myExpectedAttrs;
     167              : 
     168              :     /// @brief which CSV columns have been set (just for checking completeness)
     169              :     SumoXMLAttrMask mySeenAttrs;
     170              : };
     171              : 
     172              : 
     173              : // ===========================================================================
     174              : // specialized template implementations (for speedup)
     175              : // ===========================================================================
     176              : template <>
     177         4176 : inline void CSVFormatter::writeAttr(std::ostream& into, const SumoXMLAttr attr, const double& val) {
     178         4176 :     checkAttr(attr);
     179              : #ifdef HAVE_FMT
     180              :     fmt::print(*myXMLStack[myCurrentDepth - 1], "{:.{}f}{}", val, into.precision(), mySeparator);
     181              : #else
     182         8352 :     *myXMLStack[myCurrentDepth - 1] << toString(val, into.precision()) << mySeparator;
     183              : #endif
     184         4176 : }
     185              : 
     186              : 
     187              : template <>
     188         2088 : inline void CSVFormatter::writeAttr(std::ostream& /* into */, const SumoXMLAttr attr, const std::string& val) {
     189         2088 :     checkAttr(attr);
     190         2088 :     *myXMLStack[myCurrentDepth - 1] << val << mySeparator;
     191         2088 : }
        

Generated by: LCOV version 2.0-1