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.cpp
15 : /// @author Michael Behrisch
16 : /// @date 2025-06-12
17 : ///
18 : // An output formatter for CSV files
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #ifdef HAVE_FMT
23 : #include <fmt/ostream.h>
24 : #include <fmt/ranges.h>
25 : #endif
26 : #include <utils/common/MsgHandler.h>
27 : #include <utils/common/ToString.h>
28 : #include "CSVFormatter.h"
29 :
30 :
31 : // ===========================================================================
32 : // member method definitions
33 : // ===========================================================================
34 28 : CSVFormatter::CSVFormatter(const std::string& columnNames, const char separator)
35 28 : : OutputFormatter(OutputFormatterType::CSV), myHeaderFormat(columnNames), mySeparator(separator) {
36 28 : if (myHeaderFormat == "none") {
37 0 : myWroteHeader = true;
38 : }
39 28 : }
40 :
41 :
42 : void
43 6567 : CSVFormatter::openTag(std::ostream& /* into */, const std::string& xmlElement) {
44 6567 : myXMLStack.push_back((int)myValues.size());
45 6567 : if (!myWroteHeader) {
46 5501 : myCurrentTag = xmlElement;
47 : }
48 6567 : if (myMaxDepth == (int)myXMLStack.size() && myWroteHeader && myCurrentTag != xmlElement) {
49 699 : WRITE_WARNINGF("Encountered mismatch in XML tags (expected % but got %). Column names may be incorrect.", myCurrentTag, xmlElement);
50 : }
51 6567 : }
52 :
53 :
54 : void
55 1612 : CSVFormatter::openTag(std::ostream& /* into */, const SumoXMLTag& xmlElement) {
56 1612 : myXMLStack.push_back((int)myValues.size());
57 1612 : if (!myWroteHeader) {
58 516 : myCurrentTag = toString(xmlElement);
59 : }
60 2746 : if (myMaxDepth == (int)myXMLStack.size() && myWroteHeader && myCurrentTag != toString(xmlElement)) {
61 33 : WRITE_WARNINGF("Encountered mismatch in XML tags (expected % but got %). Column names may be incorrect.", myCurrentTag, toString(xmlElement));
62 : }
63 1612 : }
64 :
65 :
66 : bool
67 8207 : CSVFormatter::closeTag(std::ostream& into, const std::string& /* comment */) {
68 8207 : if (myMaxDepth == 0) {
69 : // the auto detection case: the first closed tag determines the depth
70 0 : myMaxDepth = (int)myXMLStack.size();
71 : }
72 8207 : if ((myMaxDepth == (int)myXMLStack.size() || myXMLStack.empty()) && !myWroteHeader) {
73 : // First complete row or EOF: write the header
74 28 : if (!myCheckColumns) {
75 42 : WRITE_WARNING("Column based formats are still experimental. Autodetection only works for homogeneous output.");
76 : }
77 : #ifdef HAVE_FMT
78 : fmt::print(into, "{}\n", fmt::join(myHeader, std::string_view(&mySeparator, 1)));
79 : #else
80 56 : into << joinToString(myHeader, mySeparator) << "\n";
81 : #endif
82 28 : myWroteHeader = true;
83 : }
84 8207 : if (myNeedsWrite) {
85 : #ifdef HAVE_FMT
86 : const std::string row = fmt::format("{}", fmt::join(myValues, std::string_view(&mySeparator, 1)));
87 : #else
88 7285 : const std::string row = joinToString(myValues, mySeparator);
89 : #endif
90 7285 : myBufferedRows.emplace_back(row);
91 : mySeenAttrs.reset();
92 7285 : myNeedsWrite = false;
93 : }
94 8207 : if (myWroteHeader && !myBufferedRows.empty()) {
95 9002 : for (const std::string& row : myBufferedRows) {
96 7285 : into << row << '\n';
97 : }
98 : myBufferedRows.clear();
99 : }
100 8207 : if (!myXMLStack.empty()) {
101 8179 : if ((int)myValues.size() > myXMLStack.back()) {
102 7979 : myValues.resize(myXMLStack.back());
103 : }
104 : myXMLStack.pop_back();
105 : }
106 8207 : return false;
107 : }
108 :
109 :
110 : /****************************************************************************/
|