Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2004-2024 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 OutputDevice.h
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Mario Krumnow
19 : /// @date 2004
20 : ///
21 : // Static storage of an output device and its base (abstract) implementation
22 : /****************************************************************************/
23 : #pragma once
24 : #include <config.h>
25 :
26 : #include <string>
27 : #include <map>
28 : #include <cassert>
29 : #include <utils/common/ToString.h>
30 : #include <utils/xml/SUMOXMLDefinitions.h>
31 : #include "PlainXMLFormatter.h"
32 :
33 :
34 : // ===========================================================================
35 : // class definitions
36 : // ===========================================================================
37 : /**
38 : * @class OutputDevice
39 : * @brief Static storage of an output device and its base (abstract) implementation
40 : *
41 : * OutputDevices are basically a capsule around an std::ostream, which give a
42 : * unified access to sockets, files and stdout.
43 : *
44 : * Usually, an application builds as many output devices as needed. Each
45 : * output device may also be used to save outputs from several sources
46 : * (several detectors, for example). Building is done using OutputDevice::getDevice()
47 : * what also parses the given output description in order to decide
48 : * what kind of an OutputDevice shall be built. OutputDevices are
49 : * closed via OutputDevice::closeAll(), normally called at the application's
50 : * end.
51 : *
52 : * Although everything that can be written to a stream can also be written
53 : * to an OutputDevice, there is special support for XML tags (remembering
54 : * all open tags to close them at the end). OutputDevices are still lacking
55 : * support for function pointers with the '<<' operator (no endl, use '\n').
56 : * The most important method to implement in subclasses is getOStream,
57 : * the most used part of the interface is the '<<' operator.
58 : *
59 : * The Boolean markers are used rarely and might get removed in future versions.
60 : */
61 : class OutputDevice {
62 : public:
63 : /// @name static access methods to OutputDevices
64 : /// @{
65 :
66 : /** @brief Returns the described OutputDevice
67 : *
68 : * Creates and returns the named device. "stdout" and "stderr" refer to the relevant console streams,
69 : * "hostname:port" initiates socket connection. Otherwise a filename
70 : * is assumed (where "nul" and "/dev/null" do what you would expect on both platforms).
71 : * If there already is a device with the same name this one is returned.
72 : *
73 : * @param[in] name The description of the output name/port/whatever
74 : * @return The corresponding (built or existing) device
75 : * @exception IOError If the output could not be built for any reason (error message is supplied)
76 : */
77 : static OutputDevice& getDevice(const std::string& name, bool usePrefix = true);
78 :
79 :
80 : /** @brief Creates the device using the output definition stored in the named option
81 : *
82 : * Creates and returns the device named by the option. Asks whether the option
83 : * and retrieves the name from the option if so. Optionally the XML header
84 : * gets written as well. Returns whether a device was created (option was set).
85 : *
86 : * Please note, that we do not have to consider the "application base" herein,
87 : * because this call is only used to get file names of files referenced
88 : * within XML-declarations of structures which paths already is aware of the
89 : * cwd.
90 : *
91 : * @param[in] optionName The name of the option to use for retrieving the output definition
92 : * @param[in] rootElement The root element to use (XML-output)
93 : * @param[in] schemaFile The basename of the schema file to use (XML-output)
94 : * @return Whether a device was built (the option was set)
95 : * @exception IOError If the output could not be built for any reason (error message is supplied)
96 : */
97 : static bool createDeviceByOption(const std::string& optionName,
98 : const std::string& rootElement = "",
99 : const std::string& schemaFile = "");
100 :
101 :
102 : /** @brief Returns the device described by the option
103 : *
104 : * Returns the device named by the option. If the option is unknown, unset
105 : * or the device was not created before, InvalidArgument is thrown.
106 : *
107 : * Please note, that we do not have to consider the "application base" herein.
108 : *
109 : * @param[in] name The name of the option to use for retrieving the output definition
110 : * @return The corresponding (built or existing) device
111 : * @exception IOError If the output could not be built for any reason (error message is supplied)
112 : * @exception InvalidArgument If the option with the given name does not exist
113 : */
114 : static OutputDevice& getDeviceByOption(const std::string& name);
115 :
116 : /** Flushes all registered devices
117 : */
118 : static void flushAll();
119 :
120 : /** Closes all registered devices
121 : */
122 : static void closeAll(bool keepErrorRetrievers = false);
123 : /// @}
124 :
125 :
126 : /** @brief Helper method for string formatting
127 : *
128 : * @param[in] v The floating point value to be formatted
129 : * @param[in] precision the precision to achieve
130 : * @return The formatted string
131 : */
132 : static std::string realString(const double v, const int precision = gPrecision);
133 :
134 :
135 : public:
136 : /// @name OutputDevice member methods
137 : /// @{
138 :
139 : /// @brief Constructor
140 : OutputDevice(const int defaultIndentation = 0, const std::string& filename = "");
141 :
142 :
143 : /// @brief Destructor
144 : virtual ~OutputDevice();
145 :
146 :
147 : /** @brief returns the information whether one can write into the device
148 : * @return Whether the device can be used (stream is good)
149 : */
150 : virtual bool ok();
151 :
152 : /** @brief returns the information whether the device will discard all output
153 : * @return Whether the device redirects to /dev/null
154 : */
155 0 : virtual bool isNull() {
156 0 : return false;
157 : }
158 :
159 : /// @brief get filename or suitable description of this device
160 : const std::string& getFilename();
161 :
162 : /** @brief Closes the device and removes it from the dictionary
163 : */
164 : void close();
165 :
166 :
167 : /** @brief Sets the precision or resets it to default
168 : * @param[in] precision The accuracy (number of digits behind '.') to set
169 : */
170 : void setPrecision(int precision = gPrecision);
171 :
172 : /// @brief return precision set on the device
173 : int precision();
174 :
175 : /** @brief Returns the precision of the underlying stream
176 : */
177 : int getPrecision() {
178 0 : return (int)getOStream().precision();
179 : }
180 :
181 : /** @brief Writes an XML header with optional configuration
182 : *
183 : * If something has been written (myXMLStack is not empty), nothing
184 : * is written and false returned.
185 : *
186 : * @param[in] rootElement The root element to use
187 : * @param[in] schemaFile The basename of the schema file to use
188 : * @param[in] attrs Additional attributes to save within the rootElement
189 : * @return Whether the header could be written (stack was empty)
190 : * @todo Describe what is saved
191 : */
192 : bool writeXMLHeader(const std::string& rootElement,
193 : const std::string& schemaFile,
194 : std::map<SumoXMLAttr, std::string> attrs = std::map<SumoXMLAttr, std::string>(),
195 : bool includeConfig = true);
196 :
197 :
198 : template <typename E>
199 12466 : bool writeHeader(const SumoXMLTag& rootElement) {
200 12466 : return static_cast<PlainXMLFormatter*>(myFormatter)->writeHeader(getOStream(), rootElement);
201 : }
202 :
203 :
204 : /** @brief Opens an XML tag
205 : *
206 : * An indentation, depending on the current xml-element-stack size, is written followed
207 : * by the given xml element ("<" + xmlElement)
208 : * The xml element is added to the stack, then.
209 : *
210 : * @param[in] xmlElement Name of element to open
211 : * @return The OutputDevice for further processing
212 : */
213 : OutputDevice& openTag(const std::string& xmlElement);
214 :
215 :
216 : /** @brief Opens an XML tag
217 : *
218 : * Helper method which finds the correct string before calling openTag.
219 : *
220 : * @param[in] xmlElement Id of the element to open
221 : * @return The OutputDevice for further processing
222 : */
223 : OutputDevice& openTag(const SumoXMLTag& xmlElement);
224 :
225 :
226 : /** @brief Closes the most recently opened tag and optionally adds a comment
227 : *
228 : * The topmost xml-element from the stack is written into the stream
229 : * as a closing element. Depending on the formatter used
230 : * this may be something like "</" + element + ">" or "/>" or
231 : * nothing at all.
232 : *
233 : * @return Whether a further element existed in the stack and could be closed
234 : * @todo it is not verified that the topmost element was closed
235 : */
236 : bool closeTag(const std::string& comment = "");
237 :
238 :
239 :
240 : /** @brief writes a line feed if applicable
241 : */
242 125874 : void lf() {
243 125874 : getOStream() << "\n";
244 125874 : }
245 :
246 :
247 : /** @brief writes a named attribute
248 : *
249 : * @param[in] attr The attribute (name)
250 : * @param[in] val The attribute value
251 : * @return The OutputDevice for further processing
252 : */
253 : template <typename T>
254 : OutputDevice& writeAttr(const SumoXMLAttr attr, const T& val) {
255 33157933 : PlainXMLFormatter::writeAttr(getOStream(), attr, val);
256 12862829 : return *this;
257 : }
258 :
259 : inline bool useAttribute(const SumoXMLAttr attr, SumoXMLAttrMask attributeMask) const {
260 14866500 : return attributeMask.test(attr);
261 : }
262 :
263 : /** @brief writes a named attribute unless filtered
264 : *
265 : * @param[in] attr The attribute (name)
266 : * @param[in] val The attribute value
267 : * @param[in] attributeMask The filter that specifies whether the attribute shall be written
268 : * @return The OutputDevice for further processing
269 : */
270 : template <typename T>
271 10461606 : OutputDevice& writeOptionalAttr(const SumoXMLAttr attr, const T& val, long long int attributeMask) {
272 : assert((int)attr <= 63);
273 10461606 : if (attributeMask == 0 || useAttribute(attr, attributeMask)) {
274 10455184 : PlainXMLFormatter::writeAttr(getOStream(), attr, val);
275 : }
276 10461606 : return *this;
277 : }
278 : template <typename T>
279 13644802 : OutputDevice& writeOptionalAttr(const SumoXMLAttr attr, const T& val, SumoXMLAttrMask attributeMask) {
280 : assert((int)attr <= (int)attributeMask.size());
281 27289604 : if (attributeMask.none() || useAttribute(attr, attributeMask)) {
282 9055890 : PlainXMLFormatter::writeAttr(getOStream(), attr, val);
283 : }
284 13644802 : return *this;
285 : }
286 :
287 :
288 : /** @brief writes an arbitrary attribute
289 : *
290 : * @param[in] attr The attribute (name)
291 : * @param[in] val The attribute value
292 : * @return The OutputDevice for further processing
293 : */
294 : template <typename T>
295 : OutputDevice& writeAttr(const std::string& attr, const T& val) {
296 19010631 : PlainXMLFormatter::writeAttr(getOStream(), attr, val);
297 15627917 : return *this;
298 : }
299 :
300 : /** @brief writes a string attribute only if it is not the empty string and not the string "default"
301 : *
302 : * @param[in] attr The attribute (name)
303 : * @param[in] val The attribute value
304 : * @return The OutputDevice for further processing
305 : */
306 284122 : OutputDevice& writeNonEmptyAttr(const SumoXMLAttr attr, const std::string& val) {
307 284122 : if (val != "" && val != "default") {
308 : writeAttr(attr, val);
309 : }
310 284122 : return *this;
311 : }
312 :
313 :
314 : /** @brief writes a preformatted tag to the device but ensures that any
315 : * pending tags are closed
316 : * @param[in] val The preformatted data
317 : * @return The OutputDevice for further processing
318 : */
319 : OutputDevice& writePreformattedTag(const std::string& val) {
320 1106 : myFormatter->writePreformattedTag(getOStream(), val);
321 1058 : return *this;
322 : }
323 :
324 : /// @brief writes padding (ignored for binary output)
325 : OutputDevice& writePadding(const std::string& val) {
326 53719 : myFormatter->writePadding(getOStream(), val);
327 53719 : return *this;
328 : }
329 :
330 : /** @brief Retrieves a message to this device.
331 : *
332 : * Implementation of the MessageRetriever interface. Writes the given message to the output device.
333 : *
334 : * @param[in] msg The msg to write to the device
335 : */
336 : void inform(const std::string& msg, const bool progress = false);
337 :
338 :
339 : /** @brief Abstract output operator
340 : * @return The OutputDevice for further processing
341 : */
342 : template <class T>
343 37244714 : OutputDevice& operator<<(const T& t) {
344 37244714 : getOStream() << t;
345 37244714 : postWriteHook();
346 37244714 : return *this;
347 : }
348 :
349 : void flush() {
350 15626939 : getOStream().flush();
351 15626939 : }
352 :
353 : bool wroteHeader() const {
354 240252 : return myFormatter->wroteHeader();
355 : }
356 :
357 : protected:
358 : /// @brief Returns the associated ostream
359 : virtual std::ostream& getOStream() = 0;
360 :
361 :
362 : /** @brief Called after every write access.
363 : *
364 : * Default implementation does nothing.
365 : */
366 : virtual void postWriteHook();
367 :
368 :
369 : private:
370 : /// @brief map from names to output devices
371 : static std::map<std::string, OutputDevice*> myOutputDevices;
372 :
373 : /// @brief old console code page to restore after ending
374 : static int myPrevConsoleCP;
375 :
376 : protected:
377 : const std::string myFilename;
378 :
379 : private:
380 : /// @brief The formatter for XML
381 : OutputFormatter* const myFormatter;
382 :
383 : private:
384 : /// @brief Invalidated copy constructor.
385 : OutputDevice(const OutputDevice&) = delete;
386 :
387 : /// @brief Invalidated assignment operator.
388 : OutputDevice& operator=(const OutputDevice&) = delete;
389 :
390 : };
|