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