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-06-15 15:46:12 Functions: 18.2 % 88 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 <algorithm>
      25              : #include "OutputFormatter.h"
      26              : #include <utils/common/ToString.h>
      27              : #include <utils/common/Translation.h>
      28              : 
      29              : 
      30              : // ===========================================================================
      31              : // class definitions
      32              : // ===========================================================================
      33              : /**
      34              :  * @class CSVFormatter
      35              :  * @brief Output formatter for CSV output
      36              :  */
      37              : class CSVFormatter : public OutputFormatter {
      38              : public:
      39              :     /// @brief Constructor
      40              :     CSVFormatter(const std::string& columnNames, const char separator = ';');
      41              : 
      42              :     /// @brief Destructor
      43           56 :     virtual ~CSVFormatter() { }
      44              : 
      45              :     /** @brief Keeps track of an open XML tag by adding a new element to the stack
      46              :      *
      47              :      * @param[in] into The output stream to use (unused)
      48              :      * @param[in] xmlElement Name of element to open (unused)
      49              :      * @return The OutputDevice for further processing
      50              :      */
      51              :     void openTag(std::ostream& into, const std::string& xmlElement);
      52              : 
      53              :     /** @brief Keeps track of an open XML tag by adding a new element to the stack
      54              :      *
      55              :      * @param[in] into The output stream to use (unused)
      56              :      * @param[in] xmlElement Name of element to open (unused)
      57              :      */
      58              :     void openTag(std::ostream& into, const SumoXMLTag& xmlElement);
      59              : 
      60              :     /** @brief Closes the most recently opened tag
      61              :      *
      62              :      * @param[in] into The output stream to use
      63              :      * @return Whether a further element existed in the stack and could be closed
      64              :      * @todo it is not verified that the topmost element was closed
      65              :      */
      66              :     bool closeTag(std::ostream& into, const std::string& comment = "");
      67              : 
      68              :     /** @brief writes a named attribute
      69              :      *
      70              :      * @param[in] into The output stream to use
      71              :      * @param[in] attr The attribute (name)
      72              :      * @param[in] val The attribute value
      73              :      */
      74              :     template <class T>
      75        17606 :     void writeAttr(std::ostream& into, const SumoXMLAttr attr, const T& val, const bool isNull) {
      76        17606 :         checkAttr(attr);
      77        24344 :         myValues.emplace_back(isNull ? "" : toString(val, into.precision()));
      78        17606 :     }
      79              : 
      80              :     template <class T>
      81        53176 :     void writeAttr(std::ostream& into, const std::string& attr, const T& val, const bool isNull) {
      82              :         assert(!myCheckColumns);
      83        53176 :         checkHeader(attr);
      84        73724 :         myValues.emplace_back(isNull ? "" : toString(val, into.precision()));
      85        53176 :     }
      86              : 
      87         1120 :     void writeTime(std::ostream& /* into */, const SumoXMLAttr attr, const SUMOTime val) {
      88         1120 :         checkAttr(attr);
      89         1120 :         myValues.emplace_back(time2string(val));
      90         1120 :     }
      91              : 
      92            0 :     bool wroteHeader() const {
      93            0 :         return myWroteHeader;
      94              :     }
      95              : 
      96           28 :     void setExpectedAttributes(const SumoXMLAttrMask& expected, const int depth = 2) {
      97           28 :         myExpectedAttrs = expected;
      98           28 :         myMaxDepth = depth;
      99           28 :         myCheckColumns = expected.any();
     100           28 :     }
     101              : 
     102              : private:
     103              :     /** @brief Helper function to keep track of the written attributes and accumulate the header.
     104              :      * It checks whether the written attribute is expected in the column based format.
     105              :      * 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.
     106              :      *
     107              :      * @param[in] attr The attribute (name)
     108              :      */
     109        18726 :     inline void checkAttr(const SumoXMLAttr attr) {
     110        18726 :         if (myCheckColumns && myMaxDepth == (int)myXMLStack.size()) {
     111         8100 :             mySeenAttrs.set(attr);
     112         8100 :             if (!myExpectedAttrs.test(attr)) {
     113            0 :                 throw ProcessError(TLF("Unexpected attribute '%', this file format does not support CSV output yet.", toString(attr)));
     114              :             }
     115              :         }
     116        18726 :         checkHeader(attr);
     117        18726 :     }
     118              : 
     119              :     template <class ATTR_TYPE>
     120        71902 :     inline void checkHeader(const ATTR_TYPE& attr) {
     121        71902 :         myNeedsWrite = true;
     122        71902 :         if (!myWroteHeader) {
     123         3933 :             std::string headerName = toString(attr);
     124        54091 :             if (myHeaderFormat != "plain" && !(myHeaderFormat == "auto" && std::find(myHeader.begin(), myHeader.end(), headerName) == myHeader.end())) {
     125       162273 :                 headerName = myCurrentTag + "_" + headerName;
     126              :             }
     127        54091 :             if (std::find(myHeader.begin(), myHeader.end(), headerName) == myHeader.end()) {
     128        12737 :                 for (std::string& row : myBufferedRows) {
     129        12347 :                     row += mySeparator;
     130              :                 }
     131          478 :                 while (myValues.size() < myHeader.size()) {
     132           88 :                     myValues.emplace_back("");
     133              :                 }
     134          390 :                 myHeader.emplace_back(headerName);
     135              :             }
     136              :         }
     137        71902 :     }
     138              : 
     139              :     /// @brief the format to use for the column names
     140              :     const std::string myHeaderFormat;
     141              : 
     142              :     /// @brief The value separator
     143              :     const char mySeparator;
     144              : 
     145              :     /// @brief the CSV header
     146              :     std::vector<std::string> myHeader;
     147              : 
     148              :     /// @brief the currently read tag (only valid when generating the header)
     149              :     std::string myCurrentTag;
     150              : 
     151              :     /// @brief The number of attributes in the currently open XML elements
     152              :     std::vector<int> myXMLStack;
     153              : 
     154              :     /// @brief the current attribute / column values
     155              :     std::vector<std::string> myValues;
     156              : 
     157              :     /// @brief the maximum depth of the XML hierarchy (excluding the root element)
     158              :     int myMaxDepth = 2;
     159              : 
     160              :     /// @brief whether the CSV header line has been written
     161              :     bool myWroteHeader = false;
     162              : 
     163              :     /// @brief whether any attribute has been written since the last row was emitted
     164              :     bool myNeedsWrite = false;
     165              : 
     166              :     /// @brief partial rows buffered before the schema is known (depth < myMaxDepth)
     167              :     std::vector<std::string> myBufferedRows;
     168              : 
     169              :     /// @brief whether the columns should be checked for completeness
     170              :     bool myCheckColumns = false;
     171              : 
     172              :     /// @brief which CSV columns are expected (just for checking completeness)
     173              :     SumoXMLAttrMask myExpectedAttrs;
     174              : 
     175              :     /// @brief which CSV columns have been set (just for checking completeness)
     176              :     SumoXMLAttrMask mySeenAttrs;
     177              : };
        

Generated by: LCOV version 2.0-1