Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/iodevices/CSVFormatter.h
169678 views
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
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
void writeAttr(std::ostream& into, const SumoXMLAttr attr, const T& val) {
77
checkAttr(attr);
78
*myXMLStack[myCurrentDepth - 1] << toString(val, into.precision()) << mySeparator;
79
}
80
81
template <class T>
82
void writeAttr(std::ostream& into, const std::string& attr, const T& val) {
83
assert(!myCheckColumns);
84
if (!myWroteHeader) {
85
if (std::find(myHeader.begin(), myHeader.end(), attr) != myHeader.end()) {
86
myHeader.push_back(myCurrentTag + "_" + attr);
87
} else {
88
myHeader.push_back(attr);
89
}
90
}
91
*myXMLStack[myCurrentDepth - 1] << toString(val, into.precision()) << mySeparator;
92
}
93
94
void writeNull(std::ostream& /* into */, const SumoXMLAttr attr) {
95
checkAttr(attr);
96
*myXMLStack[myCurrentDepth - 1] << mySeparator;
97
}
98
99
void writeTime(std::ostream& /* into */, const SumoXMLAttr attr, const SUMOTime val) {
100
checkAttr(attr);
101
*myXMLStack[myCurrentDepth - 1] << time2string(val) << mySeparator;
102
}
103
104
bool wroteHeader() const {
105
return myWroteHeader;
106
}
107
108
void setExpectedAttributes(const SumoXMLAttrMask& expected, const int depth = 2) {
109
myExpectedAttrs = expected;
110
myMaxDepth = depth;
111
myCheckColumns = expected.any();
112
}
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
inline void checkAttr(const SumoXMLAttr attr) {
122
if (myCheckColumns && myMaxDepth == myCurrentDepth) {
123
mySeenAttrs.set(attr);
124
if (!myExpectedAttrs.test(attr)) {
125
throw ProcessError(TLF("Unexpected attribute '%', this file format does not support CSV output yet.", toString(attr)));
126
}
127
}
128
if (!myWroteHeader) {
129
const std::string attrString = toString(attr);
130
if (myHeaderFormat == "plain" || (myHeaderFormat == "auto" && std::find(myHeader.begin(), myHeader.end(), attrString) == myHeader.end())) {
131
myHeader.push_back(attrString);
132
} else {
133
myHeader.push_back(myCurrentTag + "_" + attrString);
134
}
135
}
136
}
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
inline void CSVFormatter::writeAttr(std::ostream& into, const SumoXMLAttr attr, const double& val) {
178
checkAttr(attr);
179
#ifdef HAVE_FMT
180
fmt::print(*myXMLStack[myCurrentDepth - 1], "{:.{}f}{}", val, into.precision(), mySeparator);
181
#else
182
*myXMLStack[myCurrentDepth - 1] << toString(val, into.precision()) << mySeparator;
183
#endif
184
}
185
186
187
template <>
188
inline void CSVFormatter::writeAttr(std::ostream& /* into */, const SumoXMLAttr attr, const std::string& val) {
189
checkAttr(attr);
190
*myXMLStack[myCurrentDepth - 1] << val << mySeparator;
191
}
192
193