BDE 4.39.x Production Release
Loading...
Searching...
No Matches
balxml_formatter.h
Go to the documentation of this file.
1/// @file balxml_formatter.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// balxml_formatter.h -*-C++-*-
8#ifndef INCLUDED_BALXML_FORMATTER
9#define INCLUDED_BALXML_FORMATTER
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup balxml_formatter balxml_formatter
15/// @brief Provide a simple interface for writing formatted XML.
16/// @addtogroup bal
17/// @{
18/// @addtogroup balxml
19/// @{
20/// @addtogroup balxml_formatter
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#balxml_formatter-purpose"> Purpose</a>
25/// * <a href="#balxml_formatter-classes"> Classes </a>
26/// * <a href="#balxml_formatter-description"> Description </a>
27/// * <a href="#balxml_formatter-special-characters"> Special Characters </a>
28/// * <a href="#balxml_formatter-valid-strings"> Valid Strings </a>
29/// * <a href="#balxml_formatter-usage"> Usage </a>
30/// * <a href="#balxml_formatter-example-1-basic-usage"> Example 1: Basic Usage </a>
31///
32/// # Purpose {#balxml_formatter-purpose}
33/// Provide a simple interface for writing formatted XML.
34///
35/// # Classes {#balxml_formatter-classes}
36///
37/// - balxml::Formatter: provides formatted XML
38///
39/// @see
40///
41/// # Description {#balxml_formatter-description}
42/// The `balxml::Formatter` class provides methods to write a
43/// formatted XML to an underlining output stream. These methods generate
44/// header, tags, data, attributes, comments in a human-readable, indented
45/// format.
46///
47/// XML documents use a self-describing and simple syntax that consists of
48/// nested XML elements. Each element is bounded by a pair of opening and
49/// closing tags. Within the pair of tags, there can be more nested elements,
50/// or just plain text or numeric data in text format. The opening tag of an
51/// element can also contain attributes in the form of name="value" pairs.
52/// This component provides methods to generate these XML ingredients and takes
53/// care of proper indentation and line wrapping. Visit
54/// http://www.w3schools.com/xml/xml_syntax.asp for a complete tutorial.
55///
56/// ## Special Characters {#balxml_formatter-special-characters}
57///
58///
59/// XML defines five special characters that must not appear text; instead
60/// these characters are must be represented by multi-byte escape sequences.
61/// @code
62/// Special (Hex) XML Escape
63/// Character Value Description Sequence
64/// --------- ----- ----------- ----------
65/// " x22 quote &quot;
66/// & x26 ampersand &amp;
67/// ' x27 apostrophe &apos;
68/// < x3C less than &lt;
69/// > x3E greater than &gt;
70/// @endcode
71/// The following methods:
72/// * `addAttribute`,
73/// * `addData`, and
74/// * `addListData`
75/// implicitly convert each special character found in string input to the
76/// appropriate escape sequence in the resulting XML document.
77///
78/// ## Valid Strings {#balxml_formatter-valid-strings}
79///
80///
81/// Strings used to set element attributes and element data (see `addAttribute`,
82/// `addData`, and `addListData`) must consist of (valid) UTF-8 byte sequences
83/// excepting certain control characters.
84/// @code
85/// Control
86/// Characters Description Allowed
87/// ----------- -------------------------- -------
88/// x09 HT '\t' (horizontal tab) true
89/// x0A LF '\n' (new line) true
90/// x0D CR '\r' (carriage return) true
91/// x7F DEL (delete) false
92///
93/// x01 .. 0x1F Other than HT, LF, and CR. false
94/// @endcode
95/// Notice that range of 31 control characters between `0x01` and `0x1F`
96/// (inclusive) consist of three that are allowed and 28 that are not.
97///
98/// The detection of an invalid character in an input stream stops the transfer
99/// of data to the output stream. The output stream is put into a failed state.
100///
101/// ## Usage {#balxml_formatter-usage}
102///
103///
104/// This section illustrates intended use of this component.
105///
106/// ### Example 1: Basic Usage {#balxml_formatter-example-1-basic-usage}
107///
108///
109/// This example shows ten steps of how to create an XML document using this
110/// component's major manipulators:
111/// @code
112/// // 1. Create a formatter:
113/// balxml::Formatter formatter(bsl::cout);
114///
115/// // 2. Add a header:
116/// formatter.addHeader("UTF-8");
117///
118/// // 3. Open the root element,
119/// // Add attributes if there are any:
120/// formatter.openElement("Fruits");
121///
122/// // 4. Open an element,
123/// // Add attributes if there are any:
124/// formatter.openElement("Oranges");
125/// formatter.addAttribute("farm", "Francis' Orchard"); // ' is escaped
126/// formatter.addAttribute("size", 3.5);
127///
128/// // 5. If there are nested elements, recursively do steps 4 - 8:
129/// // 6. Else, there are no more nested elements, add data:
130/// formatter.openElement("pickDate"); // step 4
131/// formatter.addData(bdlt::Date(2004, 8, 31)); // step 6
132/// formatter.closeElement("pickDate"); // step 7
133/// formatter.addElementAndData("Quantity", 12); // step 8
134/// // element "Quantity" has no attributes, can use
135/// // shortcut 'addElementAndData' to complete steps
136/// // 4, 6 and 7 in one shot.
137///
138/// // 7. Close the element:
139/// formatter.closeElement("Oranges");
140///
141/// // 8. If there are more elements, repeat steps 4 - 8
142/// formatter.openElement("Apples"); // step 4
143/// formatter.addAttribute("farm", "Fuji & Sons"); // '&' is escaped
144/// formatter.addAttribute("size", 3);
145/// formatter.closeElement("Apples"); // step 7
146///
147/// // 9. Close the root element:
148/// formatter.closeElement("Fruits");
149/// @endcode
150/// Indentation is correctly taken care of and the user need only concern
151/// themselves with the correct ordering of the XML elements they're trying to
152/// write. The output of the above example is:
153/// @code
154/// +--bsl::cout--------------------------------------------------------------+
155/// |<?xml version="1.0" encoding="UTF-8" ?> |
156/// |<Fruits> |
157/// | <Oranges farm="Francis&apos; Orchard" size="3.5"> |
158/// | <pickDate>2004-08-31</pickDate> |
159/// | <Quantity>12</Quantity> |
160/// | </Oranges> |
161/// | <Apples farm="Fuji &amp; Sons" size="3"/> |
162/// |</Fruits> |
163/// +-------------------------------------------------------------------------+
164/// @endcode
165/// Following is a more complete usage example that uses most of the
166/// manipulators provided by balxml::Formatter:
167/// @code
168/// balxml::Formatter formatter(bsl::cout, 0, 4, 40);
169///
170/// formatter.addHeader("UTF-8");
171///
172/// formatter.openElement("Fruits");
173/// formatter.openElement("Oranges");
174/// formatter.addAttribute("farm", "Francis' Orchard");
175/// // notice that the apostrophe in the string will be escaped
176/// formatter.addAttribute("size", 3.5);
177///
178/// formatter.addElementAndData("Quantity", 12);
179///
180/// formatter.openElement("pickDate");
181/// formatter.addData(bdlt::Date(2004, 8, 31));
182/// formatter.closeElement("pickDate");
183///
184/// formatter.openElement("Feature");
185/// formatter.addAttribute("shape", "round");
186/// formatter.closeElement("Feature");
187///
188/// formatter.addComment("No wrapping for long comments");
189///
190/// formatter.closeElement("Oranges");
191///
192/// formatter.addBlankLine();
193///
194/// formatter.openElement("Apples");
195/// formatter.addAttribute("farm", "Fuji & Sons");
196/// formatter.addAttribute("size", 3);
197///
198/// formatter.openElement("pickDates",
199/// balxml::Formatter::BAEXML_NEWLINE_INDENT);
200/// formatter.addListData(bdlt::Date(2005, 1, 17));
201/// formatter.addListData(bdlt::Date(2005, 2, 21));
202/// formatter.addListData(bdlt::Date(2005, 3, 25));
203/// formatter.addListData(bdlt::Date(2005, 5, 30));
204/// formatter.addListData(bdlt::Date(2005, 7, 4));
205/// formatter.addListData(bdlt::Date(2005, 9, 5));
206/// formatter.addListData(bdlt::Date(2005, 11, 24));
207/// formatter.addListData(bdlt::Date(2005, 12, 25));
208///
209/// formatter.closeElement("pickDates");
210///
211/// formatter.openElement("Feature");
212/// formatter.addAttribute("color", "red");
213/// formatter.addAttribute("taste", "juicy");
214/// formatter.closeElement("Feature");
215///
216/// formatter.closeElement("Apples");
217///
218/// formatter.closeElement("Fruits");
219///
220/// formatter.reset();
221/// // reset the formatter for a new document in the same stream
222///
223/// formatter.addHeader();
224/// formatter.openElement("Grains");
225///
226/// bsl::ostream& os = formatter.rawOutputStream();
227/// os << "<free>anything that can mess up the XML doc</free>";
228/// // Now coming back to the formatter, but can't do the following:
229/// // formatter.addAttribute("country", "USA");
230/// formatter.addData("Corn, Wheat, Oat");
231/// formatter.closeElement("Grains");
232/// @endcode
233/// Following are the two resulting documents, as separated by the call to
234/// reset(),
235/// @code
236/// +--bsl::cout-----------------------------+
237/// |<?xml version="1.0" encoding="UTF-8" ?> |
238/// |<Fruits> |
239/// | <Oranges |
240/// | farm="Francis&apos; Orchard" |
241/// | size="3.5"> |
242/// | <Quantity>12</Quantity> |
243/// | <pickDate>2004-08-31</pickDate> |
244/// | <Feature shape="round"/> |
245/// | <!-- No wrapping for long comments --> |
246/// | </Oranges> |
247/// | |
248/// | <Apples farm="Fuji &amp; Sons" |
249/// | size="3"> |
250/// | <pickDates> |
251/// | 2005-01-17 2005-02-21 |
252/// | 2005-03-25 2005-05-30 |
253/// | 2005-07-04 2005-09-05 |
254/// | 2005-11-24 2005-12-25 |
255/// | </pickDates> |
256/// | <Feature color="red" |
257/// | taste="juicy"/> |
258/// | </Apples> |
259/// |</Fruits> |
260/// +----------------------------------------+
261/// +--bsl::cout-----------------------------+
262/// |<?xml version="1.0" encoding="UTF-8" ?> |
263/// |<Grains><free>anything that can mess up the XML doc</free>
264/// | Corn, Wheat, Oat</Grains> |
265/// +----------------------------------------+
266/// @endcode
267/// @}
268/** @} */
269/** @} */
270
271/** @addtogroup bal
272 * @{
273 */
274/** @addtogroup balxml
275 * @{
276 */
277/** @addtogroup balxml_formatter
278 * @{
279 */
280
281#include <balscm_version.h>
282
287
288#include <bslma_allocator.h>
289#include <bslma_bslallocator.h>
291
293
294#include <bsls_assert.h>
295#include <bsls_keyword.h>
296#include <bsls_objectbuffer.h>
297
298#include <bslstl_inplace.h>
299
300#include <bsl_optional.h>
301#include <bsl_ostream.h>
302#include <bsl_streambuf.h>
303#include <bsl_string.h>
304
305
306namespace balxml {
307
308class Formatter;
309
310 // ============================
311 // class Formatter_StreamHolder
312 // ============================
313
314/// This component-private class provides a mechanism for holding the
315/// `bsl::ostream` used by the `Formatter` to emit XML documents. Objects
316/// of `Formatter_StreamHolder` type can be constructed with either a
317/// `bsl::streambuf *` or a `bsl::ostream *`. If a stream holder is
318/// constructed with a `bsl::streambuf *`, then it owns its held
319/// `bsl::ostream`, which is constructed with the supplied stream buffer.
320/// If a stream holder is constructed with a `bsl::ostream *`, its held
321/// `bsl::ostream` is the supplied stream.
322///
323/// See @ref balxml_formatter
325
326 // DATA
327
328 // `bsl::ostream` if constructed with a `bsl::streambuf *`, and
329 // disengaged otherwise
330 bsl::optional<bsl::ostream> d_ownStream;
331
332 // the held `bsl::ostream`, which is equal to `&d_ownStream.value()` if
333 // `d_ownStream` is engaged, and the `bsl::ostream *` supplied on
334 // construction otherwise
335 bsl::ostream *d_stream_p;
336
337 private:
338 // NOT IMPLEMENTED
340 Formatter_StreamHolder& operator=(
342
343 public:
344 // CREATORS
345
346 /// Create a `Formatter_StreamHolder` object that holds a `bsl::ostream`
347 /// constructed from the specified `streamBuffer`.
348 explicit Formatter_StreamHolder(bsl::streambuf *streamBuffer);
349
350 /// Create a `Formatter_StreamHolder` that holds the specified `stream`.
351 explicit Formatter_StreamHolder(bsl::ostream *stream);
352
353 // MANIPULATORS
354
355 /// Return the address that provides modifiable access to this object's
356 /// held `bsl::ostream`.
357 bsl::ostream *stream();
358
359 // ACCESSORS
360
361 /// Return the address that provides non-modifiable access to this
362 /// object's held `bsl::ostream`.
363 const bsl::ostream *stream() const;
364};
365
366 // =====================
367 // struct Formatter_Mode
368 // =====================
369
370/// This component-private utility `struct` provides a namespace for
371/// enumerating the set of formatting modes that the `Formatter` can take.
372///
373/// See @ref balxml_formatter
375
376 // TYPES
381};
382
383 // =====================
384 // class Formatter_State
385 // =====================
386
387/// This component-private, in-core, value-semantic class provides a variant
388/// that can be inhabited by an object of either the component-private
389/// `Formatter_CompactImplState` type or the component-private
390/// `Formatter_PrettyImplState` type.
391///
392/// See @ref balxml_formatter
394
395 public:
396 // TYPES
399
400 private:
401 // PRIVATE TYPES
405
406 enum { k_COMPACT_MODE_WRAP_COLUMN = -1 };
407
408 // DATA
409 Mode::Enum d_mode;
410 union {
413 };
414 allocator_type d_allocator;
415
416 // PRIVATE CREATORS
417
418 /// If the specified `wrapColumn` is -1, create a `Formatter_State`
419 /// object having a `compact` selection, which is a
420 /// `Formatter_CompactImplState` constructed with the specified
421 /// `indentLevel` and `spacesPerLevel`. Otherwise, create a
422 /// `Formatter_State` object having a `pretty` selection, which is a
423 /// `Formatter_PrettyImplState` constructed with the `indentLevel`,
424 /// `spacesPerLevel`, and `wrapColumn`. Optionally specify an
425 /// `allocator` (e.g., the address of a `bslma::Allocator` object) to
426 /// supply memory; otherwise, the default allocator is used.
427 Formatter_State(int indentLevel,
428 int spacesPerLevel,
429 int wrapColumn,
430 const allocator_type& allocator = allocator_type());
431
432 // PRIVATE MANIPULATORS
433
434 /// Return a reference providing modifiable access to the `compact` selection of this object.
435 ///
436 /// \pre The behavior is undefined unless the
437 /// current selection of this object is `compact`.
438 Compact& compact();
439
440 /// Return a reference providing modifiable access to the `pretty` selection of this object.
441 ///
442 /// \pre The behavior is undefined unless the
443 /// current selection of this object is `pretty`.
444 Pretty& pretty();
445
446 // PRIVATE ACCESSORS
447
448 /// Return a reference providing non-modifiable access to the `compact` selection of this object.
449 ///
450 /// \pre The behavior is undefined unless the
451 /// current selection of this object is `compact`.
452 const Compact& compact() const;
453
454 /// Return `Mode::e_COMPACT` if the current selection of this object is
455 /// `compact`, and return `Mode::e_PRETTY` otherwise.
456 Mode::Enum mode() const;
457
458 /// Return a reference providing non-modifiable access to the `pretty` selection of this object.
459 ///
460 /// \pre The behavior is undefined unless the
461 /// current selection of this object is `pretty`.
462 const Pretty& pretty() const;
463
464 // FRIENDS
465 friend class balxml::Formatter;
466
467 public:
468 // TRAITS
470
471 // CREATORS
472
473 /// Create a `Formatter_State` with a `compact` selection having the
474 /// default value. Optionally specify an `allocator` (e.g., the address
475 /// of a `bslma::Allocator` object) to supply memory; otherwise, the
476 /// default allocator is used.
478 explicit Formatter_State(const allocator_type& allocator);
479
480 /// Create a `Formatter_State` object having the same value as the
481 /// specified `original` object. Optionally specify an `allocator`
482 /// (e.g., the address of a `bslma::Allocator` object) to supply memory;
483 /// otherwise, the default allocator is used.
485 const allocator_type& allocator = allocator_type());
486
487 /// Destroy this object.
489
490 // MANIPULATORS
491
492 /// Assign to this object the value of the specified `rhs` and return a
493 /// reference to this object.
495
496 // ACCESSORS
497
498 /// Return the allocator associated with this object.
500};
501
502 // ===============
503 // class Formatter
504 // ===============
505
506/// This class provides a set of XML-style formatting utilities that enable
507/// transparent indentation and wrapping for users attempting to format data
508/// with XML tags and attributes. A formatter object is instantiated with a
509/// pointer to an output stream or streambuf. Users can then use the
510/// provided utilities to write element tags, attributes, data in a valid
511/// XML sequence into the underlying stream.
512///
513/// This class has no features that would impair thread safety. However, it
514/// does not mediate between two threads attempting to access the same
515/// stream.
516///
517/// See @ref balxml_formatter
519
520 public:
521 // TYPES
522
523 /// `WhitespaceType` describes options available when outputting textual
524 /// data of an element between its pair of opening and closing tags.
526
527 // PUBLIC CLASS DATA
528#ifdef BDE_VERIFY
529#pragma bde_verify push
530#pragma bde_verify -MN03
531#pragma bde_verify -UC01
532#endif
533
536 // data is output as is
537
540 // data may be wrapped if output otherwise exceeds the wrap column
541
544 // in addition to allowing wrapping, indent properly before continuing to
545 // output on the next line after wrapping
546
549 // in addition to allowing wrapping and indentation, the tags do not share
550 // their respective lines with data
551
554 // @deprecated Use @ref e_NEWLINE_INDENT instead.
555
556#ifdef BDE_VERIFY
557#pragma bde_verify pop
558#endif
559
560 private:
561 // PRIVATE TYPES
565 typedef Formatter_Mode Mode;
566 typedef Formatter_State State;
567
568 // DATA
569 StreamHolder d_streamHolder;
570 State d_state;
571 EncoderOptions d_encoderOptions;
572
573 private:
574 // NOT IMPLEMENTED
575 Formatter(const Formatter&);
576 Formatter& operator=(const Formatter&);
577
578 public:
579 // CREATORS
580
581 /// Construct an object to format XML data into the specified `output`
582 /// stream or streambuf. Optionally specify `encoderOptions`, initial
583 /// `indentLevel`, `spacesPerLevel`, and `wrapColumn` for formatting.
584 /// An `indentLevel` of 0 (the default) indicates the root element will
585 /// have no indentation. A `wrapColumn` of 0 will cause the formatter
586 /// to behave as though the line length were infinite, but will still
587 /// insert newlines and indent when starting a new element. A
588 /// `wrapColumn` of -1 will cause output to be formatted in "compact"
589 /// mode -- with no added newlines or indentation. Optionally specify a
590 /// `basicAllocator` used to supply memory. If `basicAllocator` is 0,
591 /// the currently install default allocator is used.
592 ///
593 /// \pre The behavior is undefined if the `output` stream or streambuf is destroyed before
594 Formatter(bsl::streambuf *output,
595 int indentLevel = 0,
596 int spacesPerLevel = 4,
597 int wrapColumn = 80,
598 bslma::Allocator *basicAllocator = 0); // IMPLICIT
599 Formatter(bsl::ostream& output,
600 int indentLevel = 0,
601 int spacesPerLevel = 4,
602 int wrapColumn = 80,
603 bslma::Allocator *basicAllocator = 0); // IMPLICIT
604 Formatter(bsl::streambuf *output,
606 int indentLevel = 0,
607 int spacesPerLevel = 4,
608 int wrapColumn = 80,
609 bslma::Allocator *basicAllocator = 0);
610 Formatter(bsl::ostream& output,
612 int indentLevel = 0,
613 int spacesPerLevel = 4,
614 int wrapColumn = 80,
615 bslma::Allocator *basicAllocator = 0);
616
617 // MANIPULATORS
618
619 /// Add an attribute of the specified `name` and specified `value` to
620 /// the currently open element. `value` can be of the following types:
621 /// `char`, `short`, `int`, `bsls::Types::Int64`, `float`, `double`,
622 /// `bsl::string`, `bdlt::Datetime`, `bdlt::Date`, and `bdlt::Time`.
623 /// Precede this name="value" pair with a single space. Wrap line
624 /// (write the attribute on next line with proper indentation), if the
625 /// length of name="value" is too long. Optionally specify a
626 /// `formattingMode` used to control the formatting of the `value`. If
627 /// `value` is of type `bsl::string` or convertible to
628 /// `bsl::string_view`, the presence of invalid input stops the transfer
629 /// of data to the output specified on construction (see {Valid
630 /// Strings}). If `value` is of type `bsl::string` or convertible to
631 /// `bsl::string_view`, any special characters in `value` are escaped
632 /// (see {Special Characters}). If `value` is of type `char`, it is
633 /// cast to a signed byte value with a range `[ -128 .. 127 ]`.
634 ///
635 /// \pre The behavior is undefined unless the last manipulator was `openElement`
636 /// or `addAttribute`.
637 template <class TYPE>
638 void addAttribute(const bsl::string_view& name,
639 const TYPE& value,
640 int formattingMode = 0);
641
642 /// Insert one or two newline characters into the output stream such
643 /// that a blank line results. If the last output was a newline, then
644 /// only one newline is added, otherwise two newlines are added. If
645 /// following a call to `openElement`, or `addAttribute`, add a closing
646 /// `>` to the opened tag.
647 void addBlankLine();
648
649 /// Write the specified `comment` into the stream. The optionally
650 /// specified `forceNewline`, if true, forces to start a new line solely
651 /// for the comment if it's not on a new line already. Otherwise,
652 /// comments continue on current line. If an element-opening tag is not
653 /// completed with a `>`, `addComment` will add `>`.
654 ///
655 /// @deprecated Use @ref addValidComment instead.
656 void addComment(const bsl::string_view& comment, bool forceNewline = true);
657
658 /// Add the specified `value` as the data content, where `value` can be
659 /// of the following types: `char`, `short`, `int`,
660 /// `bsls::Types::Int64`, `float`, `double`, `bsl::string`,
661 /// `bdlt::Datetime`, `bdlt::Date`, and `bdlt::Time`. Perform no
662 /// line-wrapping or indentation as if the whitespace constraint were
663 /// always `BAEXML_PRESERVE_WHITESPACE` in `openElement`, with the only
664 /// exception that an initial newline and an initial indent is added
665 /// when `openElement` specifies `BAEXML_NEWLINE_INDENT` option. If
666 /// `value` is of type `bsl::string` or convertible to
667 /// `bsl::string_view`, the presence of invalid input stops the transfer
668 /// of data to the output specified on construction (see {Valid
669 /// Strings}). If `value` is of type `bsl::string` or convertible to
670 /// `bsl::string_view`, characters in `value` are escaped (see {Special
671 /// Characters}). If `value` is of type `char`, it is cast to a signed
672 /// byte value with a range of `[ -128 .. 127 ]`. Optionally specify
673 /// the `formattingMode` to specify the format used to encode `value`.
674 ///
675 /// \pre The behavior is undefined if the call is made when there are no
676 /// opened elements.
677 template <class TYPE>
678 void addData(const TYPE& value, int formattingMode = 0);
679
680 /// Add element of the specified `name` and the specified `value` as the
681 /// data content. This has the same effect as calling the following
682 /// sequence: `openElement(name); addData(value), closeElement(name);`.
683 /// Optionally specify the `formattingMode`.
684 template <class TYPE>
685 void addElementAndData(const bsl::string_view& name,
686 const TYPE& value,
687 int formattingMode = 0);
688
689 /// Add XML header with optionally specified `encoding`. Version is always "1.0".
690 ///
691 /// \pre The behavior is undefined unless `addHeader` is the
692 /// first manipulator (with the exception of `rawOutputStream`) after
693 /// construction or `reset`.
694 void addHeader(const bsl::string_view& encoding = "UTF-8");
695
696 /// Add the specified `value` as the data content, where `value` can be
697 /// of the following types: `char`, `short`, `int`,
698 /// `bsls::Types::Int64`, `float`, `double`, `bsl::string`,
699 /// `bdlt::Datetime`, `bdlt::Date`, and `bdlt::Time`. Prefix the
700 /// `value` with a space(`0x20`) unless the data being added is the
701 /// first data on a line. When adding the data makes the line too long,
702 /// perform line-wrapping and indentation as determined by the
703 /// whitespace constraint used when the current element is opened with
704 /// `openElement`. If `value` is of type `bsl::string` or convertible
705 /// to `bsl::string_view`, the presence of invalid input stops the
706 /// transfer of data to the output specified on construction (see {Valid
707 /// Strings}). If `value` is of type `bsl::string` or convertible to
708 /// `bsl::string_view`, any special characters in `value` are escaped
709 /// (see {Special Characters}). If `value` is of type `char`, it is
710 /// cast to a signed byte value with a range of `[ -128 .. 127 ]`.
711 /// Optionally specify the `formattingMode` to specify the format used to encode `value`.
712 ///
713 /// \pre The behavior is undefined if the call is made
714 /// when there are no opened elements.
715 template <class TYPE>
716 void addListData(const TYPE& value, int formattingMode = 0);
717
718 /// Insert a literal newline into the XML output. If following a call
719 /// to `openElement`, or `addAttribute`, add a closing `>` to the opened
720 /// tag.
721 void addNewline();
722
723 /// Write the specified `comment` into the stream. Optionally specify
724 /// `forceNewline` that specifies if a new line should be added before
725 /// the comment if it is not already on a new line. If `forceNewline`
726 /// is not specified then a new line is inserted for comments not
727 /// already on a new line. Also optionally specify an
728 /// `omitEnclosingWhitespace` that specifies if a space character should
729 /// be omitted before and after `comment`. If `omitEnclosingWhitespace`
730 /// is not specified then a space character is inserted before and after
731 /// `comment`. Return 0 on success, and non-zero value otherwise.
732 ///
733 /// \note Note that a non-zero return value is returned if either `comment`
734 /// contains `--` or if `omitEnclosingWhitespace` is `true` and
735 /// `comment` ends with `-`. Also note that if an element-opening tag
736 /// is not completed with a `>`, `addValidComment` will add `>`.
738 const bsl::string_view& comment,
739 bool forceNewline = true,
740 bool omitEnclosingWhitespace = false);
741
742 /// Decrement the indent level and add the closing tag for the element
743 /// of the specified `name`. If the element does not have content,
744 /// write `/>` and a newline into stream. Otherwise, write `</name>`
745 /// and a newline. If this `</name>` does not share the same line with
746 /// data, or it follows another element's closing tag, indent properly
747 /// before writing `</name>` and the newline. If `name` is root element, flush the output stream.
748 ///
749 /// \pre The behavior is undefined if
750 /// `name` is not the most recently opened element that's yet to be
751 /// closed.
753
754 /// Insert the closing `>` if there is an incomplete tag, and flush the
755 /// output stream.
756 void flush();
757
758 /// Open an element of the specified `name` at current indent level with
759 /// the optionally specified whitespace constraint `whitespaceMode` for
760 /// its textual data and increment indent level. `whitespaceMode`
761 /// constrains how textual data is written with `addListData` for the
762 /// current element, but not its nested elements.
763 ///
764 /// \pre The behavior is undefined if `openElement` is called after the root element is
765 /// closed and there is no subsequent call to `reset`.
767 const bsl::string_view& name,
768 WhitespaceType whitespaceMode = e_PRESERVE_WHITESPACE);
769
770 /// Return a reference to the underlining output stream. This method is
771 /// provided in order to enable user to temporarily jump out of the
772 /// formatter and write user's own free-lance content directly to the
773 /// stream.
774 bsl::ostream& rawOutputStream();
775
776 /// Reset the formatter such that it can be used to format a new XML
777 /// document as if the formatter were just constructed
778 void reset();
779
780 // ACCESSORS
781
782 /// Return the encoder options being used.
783 const EncoderOptions& encoderOptions() const;
784
785 /// Return the current level of indentation.
786 int indentLevel() const;
787
788 /// Return the current column position at a line where next output
789 /// starts. This is unreliable if called after free-lance information
790 /// is written onto the stream returned by `rawOutputStream`
791 int outputColumn() const;
792
793 /// Return the number of spaces per indentation level.
794 int spacesPerLevel() const;
795
796 /// Return 0 if no errors have been detected since construction or
797 /// since the last call to `reset`, otherwise return a negative value.
798 int status() const;
799
800 /// Return the line width where line-wrapping takes place.
801 int wrapColumn() const;
802};
803
804// ============================================================================
805// INLINE DEFINITIONS
806// ============================================================================
807
808 // ----------------------------
809 // class Formatter_StreamHolder
810 // ----------------------------
811
812// CREATORS
813inline
814Formatter_StreamHolder::Formatter_StreamHolder(bsl::streambuf *streamBuffer)
815: d_ownStream(bsl::in_place_t(), streamBuffer)
816, d_stream_p(&d_ownStream.value())
817{
818}
819
820inline
821Formatter_StreamHolder::Formatter_StreamHolder(bsl::ostream *stream)
822: d_ownStream()
823, d_stream_p(stream)
824{
825}
826
827// MANIPULATORS
828inline
830{
831 return d_stream_p;
832}
833
834// ACCESSORS
835inline
836const bsl::ostream *Formatter_StreamHolder::stream() const
837{
838 return d_stream_p;
839}
840
841 // ---------------------
842 // class Formatter_State
843 // ---------------------
844
845// PRIVATE MANIPULATORS
846inline
847Formatter_CompactImplState& Formatter_State::compact()
848{
849 BSLS_ASSERT(Mode::e_COMPACT == d_mode);
850
851 return d_compact.object();
852}
853
854inline
855Formatter_PrettyImplState& Formatter_State::pretty()
856{
857 BSLS_ASSERT(Mode::e_PRETTY == d_mode);
858
859 return d_pretty.object();
860}
861
862// PRIVATE ACCESSORS
863inline
864const Formatter_CompactImplState& Formatter_State::compact() const
865{
866 BSLS_ASSERT(Mode::e_COMPACT == d_mode);
867
868 return d_compact.object();
869}
870
871inline
872Formatter_Mode::Enum Formatter_State::mode() const
873{
874 return d_mode;
875}
876
877inline
878const Formatter_PrettyImplState& Formatter_State::pretty() const
879{
880 BSLS_ASSERT(Mode::e_PRETTY == d_mode);
881
882 return d_pretty.object();
883}
884
885// CREATORS
886inline
888: d_mode(Mode::e_COMPACT)
889, d_allocator()
890{
891 new (d_compact.buffer()) Compact();
892}
893
894inline
896: d_mode(Mode::e_COMPACT)
897, d_allocator(allocator)
898{
899 new (d_compact.buffer()) Compact();
900}
901
902// ACCESSORS
903inline
905{
906 return d_allocator;
907}
908
909 // ---------------
910 // class Formatter
911 // ---------------
912
913// MANIPULATORS
914template <class TYPE>
916 const TYPE& value,
917 int formattingMode)
918{
919 switch (d_state.mode()) {
920 case Mode::e_COMPACT: {
921 CompactUtil::addAttribute(*d_streamHolder.stream(),
922 &d_state.compact(),
923 name,
924 value,
925 formattingMode,
926 d_encoderOptions);
927 } break;
928 case Mode::e_PRETTY: {
929 PrettyUtil::addAttribute(*d_streamHolder.stream(),
930 &d_state.pretty(),
931 name,
932 value,
933 formattingMode,
934 d_encoderOptions);
935 } break;
936 }
937}
938
939inline
941{
942 switch (d_state.mode()) {
943 case Mode::e_COMPACT: {
944 CompactUtil::addBlankLine(*d_streamHolder.stream(),
945 &d_state.compact());
946 } break;
947 case Mode::e_PRETTY: {
948 PrettyUtil::addBlankLine(*d_streamHolder.stream(), &d_state.pretty());
949 } break;
950 }
951}
952
953template <class TYPE>
954void Formatter::addData(const TYPE& value, int formattingMode)
955{
956 switch (d_state.mode()) {
957 case Mode::e_COMPACT: {
958 CompactUtil::addData(*d_streamHolder.stream(),
959 &d_state.compact(),
960 value,
961 formattingMode,
962 d_encoderOptions);
963 } break;
964 case Mode::e_PRETTY: {
965 PrettyUtil::addData(*d_streamHolder.stream(),
966 &d_state.pretty(),
967 value,
968 formattingMode,
969 d_encoderOptions);
970 } break;
971 }
972}
973
974template <class TYPE>
976 const TYPE& value,
977 int formattingMode)
978{
979 switch (d_state.mode()) {
980 case Mode::e_COMPACT: {
981 CompactUtil::addElementAndData(*d_streamHolder.stream(),
982 &d_state.compact(),
983 name,
984 value,
985 formattingMode,
986 d_encoderOptions);
987 } break;
988 case Mode::e_PRETTY: {
989 PrettyUtil::addElementAndData(*d_streamHolder.stream(),
990 &d_state.pretty(),
991 name,
992 value,
993 formattingMode,
994 d_encoderOptions);
995 } break;
996 }
997}
998
999template <class TYPE>
1000void Formatter::addListData(const TYPE& value, int formattingMode)
1001{
1002 switch (d_state.mode()) {
1003 case Mode::e_COMPACT: {
1004 CompactUtil::addListData(*d_streamHolder.stream(),
1005 &d_state.compact(),
1006 value,
1007 formattingMode,
1008 d_encoderOptions);
1009 } break;
1010 case Mode::e_PRETTY: {
1011 PrettyUtil::addListData(*d_streamHolder.stream(),
1012 &d_state.pretty(),
1013 value,
1014 formattingMode,
1015 d_encoderOptions);
1016 } break;
1017 }
1018}
1019
1020inline
1022{
1023 switch (d_state.mode()) {
1024 case Mode::e_COMPACT: {
1025 CompactUtil::addNewline(*d_streamHolder.stream(), &d_state.compact());
1026 } break;
1027 case Mode::e_PRETTY: {
1028 PrettyUtil::addNewline(*d_streamHolder.stream(), &d_state.pretty());
1029 } break;
1030 }
1031}
1032
1033inline
1035{
1036 switch (d_state.mode()) {
1037 case Mode::e_COMPACT: {
1038 CompactUtil::flush(*d_streamHolder.stream(), &d_state.compact());
1039 } break;
1040 case Mode::e_PRETTY: {
1041 PrettyUtil::flush(*d_streamHolder.stream(), &d_state.pretty());
1042 } break;
1043 }
1044}
1045
1046inline
1048{
1049 switch (d_state.mode()) {
1050 case Mode::e_COMPACT: {
1051 CompactUtil::flush(*d_streamHolder.stream(), &d_state.compact());
1052 } break;
1053 case Mode::e_PRETTY: {
1054 PrettyUtil::flush(*d_streamHolder.stream(), &d_state.pretty());
1055 } break;
1056 }
1057
1058 return *d_streamHolder.stream();
1059}
1060
1061// ACCESSORS
1062inline
1064{
1065 return d_encoderOptions;
1066}
1067
1068inline
1070{
1071 int result = 0;
1072
1073 switch (d_state.mode()) {
1074 case Mode::e_COMPACT: {
1075 result = d_state.compact().indentLevel();
1076 } break;
1077 case Mode::e_PRETTY: {
1078 result = d_state.pretty().indentLevel();
1079 } break;
1080 }
1081
1082 return result;
1083}
1084
1085inline
1087{
1088 int result = 0;
1089
1090 switch (d_state.mode()) {
1091 case Mode::e_COMPACT: {
1092 result = d_state.compact().column();
1093 } break;
1094 case Mode::e_PRETTY: {
1095 result = d_state.pretty().column();
1096 } break;
1097 }
1098
1099 return result;
1100}
1101
1102inline
1104{
1105 int result = 0;
1106
1107 switch (d_state.mode()) {
1108 case Mode::e_COMPACT: {
1109 result = d_state.compact().spacesPerLevel();
1110 } break;
1111 case Mode::e_PRETTY: {
1112 result = d_state.pretty().spacesPerLevel();
1113 } break;
1114 }
1115
1116 return result;
1117}
1118
1119inline
1121{
1122 return d_streamHolder.stream()->good() ? 0 : -1;
1123}
1124
1125inline
1127{
1128 int result = 0;
1129
1130 switch (d_state.mode()) {
1131 case Mode::e_COMPACT: {
1132 result = State::k_COMPACT_MODE_WRAP_COLUMN;
1133 } break;
1134 case Mode::e_PRETTY: {
1135 result = d_state.pretty().wrapColumn();
1136 } break;
1137 }
1138
1139 return result;
1140}
1141
1142} // close package namespace
1143
1144
1145#endif
1146
1147// ----------------------------------------------------------------------------
1148// Copyright 2015 Bloomberg Finance L.P.
1149//
1150// Licensed under the Apache License, Version 2.0 (the "License");
1151// you may not use this file except in compliance with the License.
1152// You may obtain a copy of the License at
1153//
1154// http://www.apache.org/licenses/LICENSE-2.0
1155//
1156// Unless required by applicable law or agreed to in writing, software
1157// distributed under the License is distributed on an "AS IS" BASIS,
1158// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1159// See the License for the specific language governing permissions and
1160// limitations under the License.
1161// ----------------------------- END-OF-FILE ----------------------------------
1162
1163/** @} */
1164/** @} */
1165/** @} */
Definition balxml_encoderoptions.h:89
Definition balxml_formatter_compactimpl.h:164
int & indentLevel()
Definition balxml_formatter_compactimpl.h:569
int & column()
Definition balxml_formatter_compactimpl.h:557
int & spacesPerLevel()
Definition balxml_formatter_compactimpl.h:581
Definition balxml_formatter_prettyimpl.h:227
int & column()
Definition balxml_formatter_prettyimpl.h:761
int & wrapColumn()
Definition balxml_formatter_prettyimpl.h:792
int & spacesPerLevel()
Definition balxml_formatter_prettyimpl.h:786
int & indentLevel()
Definition balxml_formatter_prettyimpl.h:780
Definition balxml_formatter.h:393
BSLMF_NESTED_TRAIT_DECLARATION(Formatter_State, bslma::UsesBslmaAllocator)
~Formatter_State()
Destroy this object.
Formatter_State(const Formatter_State &original, const allocator_type &allocator=allocator_type())
bsls::ObjectBuffer< Pretty > d_pretty
Definition balxml_formatter.h:412
Formatter_State & operator=(const Formatter_State &rhs)
bsl::allocator< char > allocator_type
Definition balxml_formatter.h:397
Formatter_Mode Mode
Definition balxml_formatter.h:398
Formatter_State()
Definition balxml_formatter.h:887
bsls::ObjectBuffer< Compact > d_compact
Definition balxml_formatter.h:411
allocator_type get_allocator() const
Return the allocator associated with this object.
Definition balxml_formatter.h:904
Definition balxml_formatter.h:324
bsl::ostream * stream()
Definition balxml_formatter.h:829
Definition balxml_formatter.h:518
int addValidComment(const bsl::string_view &comment, bool forceNewline=true, bool omitEnclosingWhitespace=false)
static const WhitespaceType e_WORDWRAP
Definition balxml_formatter.h:538
Formatter(bsl::ostream &output, const EncoderOptions &encoderOptions, int indentLevel=0, int spacesPerLevel=4, int wrapColumn=80, bslma::Allocator *basicAllocator=0)
int indentLevel() const
Return the current level of indentation.
Definition balxml_formatter.h:1069
void openElement(const bsl::string_view &name, WhitespaceType whitespaceMode=e_PRESERVE_WHITESPACE)
static const WhitespaceType BAEXML_NEWLINE_INDENT
Definition balxml_formatter.h:552
FormatterWhitespaceType::Enum WhitespaceType
Definition balxml_formatter.h:525
int outputColumn() const
Definition balxml_formatter.h:1086
int status() const
Definition balxml_formatter.h:1120
const EncoderOptions & encoderOptions() const
Return the encoder options being used.
Definition balxml_formatter.h:1063
Formatter(bsl::ostream &output, int indentLevel=0, int spacesPerLevel=4, int wrapColumn=80, bslma::Allocator *basicAllocator=0)
Formatter(bsl::streambuf *output, const EncoderOptions &encoderOptions, int indentLevel=0, int spacesPerLevel=4, int wrapColumn=80, bslma::Allocator *basicAllocator=0)
void addHeader(const bsl::string_view &encoding="UTF-8")
void addElementAndData(const bsl::string_view &name, const TYPE &value, int formattingMode=0)
Definition balxml_formatter.h:975
void addNewline()
Definition balxml_formatter.h:1021
bsl::ostream & rawOutputStream()
Definition balxml_formatter.h:1047
void addListData(const TYPE &value, int formattingMode=0)
Definition balxml_formatter.h:1000
void closeElement(const bsl::string_view &name)
void addData(const TYPE &value, int formattingMode=0)
Definition balxml_formatter.h:954
void addBlankLine()
Definition balxml_formatter.h:940
int wrapColumn() const
Return the line width where line-wrapping takes place.
Definition balxml_formatter.h:1126
static const WhitespaceType e_WORDWRAP_INDENT
Definition balxml_formatter.h:542
static const WhitespaceType e_PRESERVE_WHITESPACE
Definition balxml_formatter.h:534
void addAttribute(const bsl::string_view &name, const TYPE &value, int formattingMode=0)
Definition balxml_formatter.h:915
void flush()
Definition balxml_formatter.h:1034
int spacesPerLevel() const
Return the number of spaces per indentation level.
Definition balxml_formatter.h:1103
static const WhitespaceType e_NEWLINE_INDENT
Definition balxml_formatter.h:547
Formatter(bsl::streambuf *output, int indentLevel=0, int spacesPerLevel=4, int wrapColumn=80, bslma::Allocator *basicAllocator=0)
void addComment(const bsl::string_view &comment, bool forceNewline=true)
Definition bslma_bslallocator.h:588
Definition bslstl_stringview.h:471
Definition bslstl_optional.h:2043
Definition bslma_allocator.h:545
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
#define BSLS_KEYWORD_DELETED
Definition bsls_keyword.h:651
Definition balxml_base64parser.h:150
Definition bdlat_valuetypefunctions.h:939
Definition balxml_formatterwhitespacetype.h:70
Enum
Definition balxml_formatterwhitespacetype.h:73
@ e_NEWLINE_INDENT
Definition balxml_formatterwhitespacetype.h:83
@ e_WORDWRAP_INDENT
Definition balxml_formatterwhitespacetype.h:79
@ e_WORDWRAP
Definition balxml_formatterwhitespacetype.h:76
@ e_PRESERVE_WHITESPACE
Definition balxml_formatterwhitespacetype.h:74
Definition balxml_formatter_compactimpl.h:273
static bsl::ostream & addElementAndData(bsl::ostream &stream, State *state, const bsl::string_view &name, const TYPE &value, int formattingMode=0, const EncoderOptions &encoderOptions=EncoderOptions())
Definition balxml_formatter_compactimpl.h:671
static bsl::ostream & addAttribute(bsl::ostream &stream, State *state, const bsl::string_view &name, const VALUE_TYPE &value, int valueFormattingMode=0, const EncoderOptions &encoderOptions=EncoderOptions())
Definition balxml_formatter_compactimpl.h:624
static bsl::ostream & addListData(bsl::ostream &stream, State *state, const VALUE_TYPE &value, int formattingMode=0, const EncoderOptions &encoderOptions=EncoderOptions())
Definition balxml_formatter_compactimpl.h:687
static bsl::ostream & addNewline(bsl::ostream &stream, State *state)
static bsl::ostream & flush(bsl::ostream &stream, State *state)
static bsl::ostream & addData(bsl::ostream &stream, State *state, const VALUE_TYPE &value, int formattingMode=0, const EncoderOptions &encoderOptions=EncoderOptions())
Definition balxml_formatter_compactimpl.h:645
static bsl::ostream & addBlankLine(bsl::ostream &stream, State *state)
Definition balxml_formatter.h:374
Enum
Definition balxml_formatter.h:377
@ e_PRETTY
Definition balxml_formatter.h:379
@ e_COMPACT
Definition balxml_formatter.h:378
Definition balxml_formatter_prettyimpl.h:377
static bsl::ostream & addListData(bsl::ostream &stream, State *state, const VALUE_TYPE &value, int formattingMode=0, const EncoderOptions &encoderOptions=EncoderOptions())
Definition balxml_formatter_prettyimpl.h:913
static bsl::ostream & addNewline(bsl::ostream &stream, State *state)
static bsl::ostream & addAttribute(bsl::ostream &stream, State *state, const bsl::string_view &name, const VALUE_TYPE &value, int formattingMode=0, const EncoderOptions &encoderOptions=EncoderOptions())
Definition balxml_formatter_prettyimpl.h:848
static bsl::ostream & flush(bsl::ostream &stream, State *state)
static bsl::ostream & addData(bsl::ostream &stream, State *state, const VALUE_TYPE &value, int formattingMode=0, const EncoderOptions &encoderOptions=EncoderOptions())
Definition balxml_formatter_prettyimpl.h:875
static bsl::ostream & addElementAndData(bsl::ostream &stream, State *state, const bsl::string_view &name, const TYPE &value, int formattingMode=0, const EncoderOptions &encoderOptions=EncoderOptions())
Definition balxml_formatter_prettyimpl.h:898
static bsl::ostream & addBlankLine(bsl::ostream &stream, State *state)
Definition bslma_usesbslmaallocator.h:344
Definition bsls_objectbuffer.h:277