// bslim_printer.h                                                    -*-C++-*-
#ifndef INCLUDED_BSLIM_PRINTER
#define INCLUDED_BSLIM_PRINTER

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a mechanism to implement standard `print` methods.
//
//@CLASSES:
//  bslim::Printer: mechanism to implement standard `print` methods
//
//@DESCRIPTION: This component provides a mechanism class, `bslim::Printer`,
// that, in many cases, simplifies the implementation of types providing a
// `print` method with the signature:
// ```
// bsl::ostream& print(bsl::ostream& stream,
//                     int           level          = 0,
//                     int           spacesPerLevel = 4) const;
//     // Format this object to the specified output 'stream' at the (absolute
//     // value of) the optionally specified indentation 'level' and return a
//     // reference to 'stream'.  If 'level' is specified, optionally specify
//     // 'spacesPerLevel', the number of spaces per indentation level for
//     // this and all of its nested objects.  If 'level' is negative,
//     // suppress indentation of the first line.  If 'spacesPerLevel' is
//     // negative format the entire output on one line, suppressing all but
//     // the initial indentation (as governed by 'level').  If 'stream' is
//     // not valid on entry, this operation has no effect.
// ```
// Note that all value-semantic types are expected to provide this method.
// `bslim::Printer` also supports generic containers, including those in BSL's
// standard library implementation, through use of standard conforming
// iterators and the `bslalg::HasStlIterators` trait.  Use of the `Printer`
// mechanism provides a uniform style of output formatting:
//
// * Enclose the object's salient attributes with square brackets.
// * Prefix each attribute with the attribute's name, separated by an "equal"
//   sign surrounded by space characters (" = ").
// * If the attributes are to be printed on multiple lines, then print them
//   with one more level of indentation than that of the enclosing brackets.
//   If any of the attributes are compositions, then the composite values
//   must be printed with an additional level of indentation.
// * If the attributes are to be printed on a single line, then separate each
//   value with a single space character.
// * For small, common types, such as `bdlt::Date`, the names of attributes,
//   equal sign, and brackets may be omitted, with the entire value
//   represented on a single line in a custom format.  For example, the
//   `bdlt::Date::print` method emits the date value in the format: 01JAN2001.
//
// For example, consider a class having two attributes, `ticker`, represented
// by a `bsl::string`, and `price`, represented by a `double`.  The output for
// a `print` method that produces standardized output for
// `print(bsl::cout, 0, -4)` (single-line output) is shown below:
// ```
// [ ticker = "ABC" price = 65.89 ]
// ```
// Output for `print(bsl::cout, 0, 4)` (multi-line output) is shown below:
// ```
// [
//     ticker = "ABC"
//     price = 65.89
// ]
// ```
// The `Printer` mechanism provides methods and method templates to format data
// as described above.  `Printer` objects are instantiated with the target
// stream to be written to, and the values of the indentation level of the
// data, `level`, and the spaces per level, `spacesPerLevel`.  The methods
// provided by `Printer`, `printAttribute`, `printValue`, `printOrNull`,
// `printHexAddr` and `printForeign`, use these values for formatting.  The
// `start` and `end` methods print the enclosing brackets of the output.  In
// order to generate the standard output format, `start` should be called
// before any of the other methods, and `end` should be called after all the
// other methods have been called.
//
///Usage
///-----
// In the following examples, we examine the implementation of the `print`
// method of different types of classes using `Printer`.
//
///Example 1: `print` Method for a Value-Semantic Class
/// - - - - - - - - - - - - - - - - - - - - - - - - - -
// In this example, we demonstrate how to use `Printer` to implement the
// standard `print` function of a value-semantic class having multiple
// attributes.  Suppose we have a class, `StockTrade`, that provides a
// container for a fixed set of attributes.  A `StockTrade` object has four
// attributes, `ticker`, `price`, `quantity`, and optional `notes`:
// ```
// class StockTrade {
//     // This class represents the properties of a stock trace.
//
//     // DATA
//     bsl::string                d_ticker;    // ticker symbol
//     double                     d_price;     // stock price
//     double                     d_quantity;  // quanity traded
//     bsl::optional<bsl::string> d_notes;     // optional trade notes
//
//   public:
//     ...
//
//     // ACCESSORS
//     bsl::ostream& print(bsl::ostream& stream,
//                         int           level = 0,
//                         int           spacesPerLevel = 4) const
//     {
//         if (stream.bad()) {
//             return stream;                                        // RETURN
//         }
//
//         bslim::Printer printer(&stream, level, spacesPerLevel);
//         printer.start();
//         printer.printAttribute("ticker",   d_ticker);
//         printer.printAttribute("price",    d_price);
//         printer.printAttribute("quantity", d_quantity);
//         printer.printAttribute("notes",    d_notes);
//         printer.end();
//
//         return stream;
//     }
// };
// ```
// Sample output for `StockTrade::print(bsl::cout, 0, -4)`:
// ```
// [ ticker = "IBM" price = 107.3 quantity = 200 notes = "XYZ" ]
// ```
// Sample output for `StockTrade::print(bsl::cout, 0, 4)`:
// ```
// [
//     ticker = "IBM"
//     price = 107.3
//     quantity = 200
//     notes = "XYZ"
// ]
// ```
//
///Example 2: `print` Method for a Mechanism Class
///- - - - - - - - - - - - - - - - - - - - - - - -
// In this example, we discuss the implementation of `print` for a mechanism
// class.  A mechanism class does not have any salient attributes that define
// its value (as a mechanism does not have a "value").  However, the `print`
// method may be implemented to output the internal state of an object of such
// a type, e.g., for debugging purposes.
//
// For example, consider a memory manager class, `BlockList`, that maintains a
// linked list of memory blocks:
// ```
// class BlockList {
//     // This class implements a low-level memory manager that allocates and
//     // manages a sequence of memory blocks.
//
//     // TYPES
//     struct Block {
//         // This 'struct' overlays the beginning of each managed block of
//         // allocated memory, implementing a doubly-linked list of managed
//         // blocks, and thereby enabling constant-time deletions from, as
//         // well as additions to, the list of blocks.
//
//         Block                                *d_next_p;       // next
//                                                               // pointer
//
//         Block                               **d_addrPrevNext; // enable
//                                                               // delete
//
//         bsls::AlignmentUtil::MaxAlignedType   d_memory;       // force
//                                                               // alignment
//     };
//
//     // DATA
//     Block            *d_head_p;      // address of first block of memory
//                                      // (or 0)
//
//     bslma::Allocator *d_allocator_p; // memory allocator; held, but not
//                                      // owned
//
//   public:
//     // ...
//     // ACCESSORS
//     // ...
//     bsl::ostream& print(bsl::ostream& stream,
//                         int           level          = 0,
//                         int           spacesPerLevel = 4) const;
// };
// ```
// For the purposes of debugging, it may be useful to print the starting
// address of every memory block in a `BlockList`, which can be done using the
// `printHexAddr` method of the `Printer` class:
// ```
// bsl::ostream& BlockList::print(bsl::ostream& stream,
//                                int           level,
//                                int           spacesPerLevel) const
// {
//     if (stream.bad()) {
//         return stream;                                            // RETURN
//     }
//
//     bslim::Printer printer(&stream, level, spacesPerLevel);
//     printer.start();
//     for (Block *it = d_head_p; it; it = it->d_next_p) {
//         printer.printHexAddr(it, 0);
//     }
//     printer.end();
//
//     return stream;
// }
// ```
// Sample output for 'BlockList::print(bsl::cout, 0, -4):
// ```
// [ 0x0012fab4 0x0012fab8 ]
// ```
// Sample output for 'BlockList::print(bsl::cout, 0, 4):
// ```
// [
//     0x0012fab4
//     0x0012fab8
// ]
// ```
//
///Example 3: Foreign (Third-Party) Classes, and Printing STL Containers
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In this example, we use a `Printer` object to help format the properties of
// a class supplied by a third-party that does not implement the standard
// `print` method.  Consider a struct, `ThirdPartyStruct`, defined in
// `/usr/include/thirdparty.h` that has no standard `print` method.  We will be
// using this struct within another class `Customer`, storing some `Customer`
// objects in a map, and printing the map.
// ```
// struct ThirdPartyStruct {
//     // Suppose this struct is defined somewhere in
//     // '/usr/include/thirdparty.h', we have no control over it and hence
//     // cannot add a .print method to it.
//
//     enum { PRIVATE  = 1,
//            WRITABLE = 2 };
//
//     short pid;              // process id
//     short access_flags;     // options
//     char  user_id[20];      // userid
// };
// ```
// We create a struct `MyThirdPartyStructPrintUtil`:
// ```
// struct MyThirdPartyStructPrintUtil {
//     static
//     bsl::ostream& print(bsl::ostream&           stream,
//                         const ThirdPartyStruct& data,
//                         int                     level = 0,
//                         int                     spacesPerLevel = 4);
//         // You write this function in your own code to accommodate
//         // 'ThirdPartyStruct'.
// };
//
// bsl::ostream& MyThirdPartyStructPrintUtil::print(
//                                     bsl::ostream&           stream,
//                                     const ThirdPartyStruct& data,
//                                     int                     level,
//                                     int                     spacesPerLevel)
// {
//     bslim::Printer printer(&stream, level, spacesPerLevel);
//     printer.start();
//     printer.printAttribute("pid",          data.pid);
//     printer.printAttribute("access_flags", data.access_flags);
//     printer.printAttribute("user_id",      data.user_id);
//     printer.end();
//
//     return stream;
// }
// ```
//  We create a class `Customer` that has a `ThirdPartyStruct` in it:
// ```
// class Customer {
//     // DATA
//     bsl::string      d_companyName;
//     ThirdPartyStruct d_thirdPartyStruct;
//     bool             d_loyalCustomer;
//
//   public:
//     // CREATORS
//     Customer() {}
//
//     Customer(const bsl::string& companyName,
//              short              pid,
//              short              accessFlags,
//              const bsl::string& userId,
//              bool               loyalCustomer)
//     : d_companyName(companyName)
//     , d_loyalCustomer(loyalCustomer)
//     {
//         d_thirdPartyStruct.pid = pid;
//         d_thirdPartyStruct.access_flags = accessFlags;
//         bsl::strcpy(d_thirdPartyStruct.user_id, userId.c_str());
//     }
//
//     // ACCESSORS
//     void print(bsl::ostream& stream,
//                int           level = 0,
//                int           spacesPerLevel = 4) const
//     {
//         bslim::Printer printer(&stream, level, spacesPerLevel);
//         printer.start();
//         printer.printAttribute("CompanyName", d_companyName);
//         printer.printForeign(d_thirdPartyStruct,
//                              &MyThirdPartyStructPrintUtil::print,
//                              "ThirdPartyStruct");
//         printer.printAttribute("LoyalCustomer", d_loyalCustomer);
//         printer.end();
//     }
// };
// ```
//  We then create some `Customer` objects and put them in a map:
// ```
// void myFunc()
// {
//     bsl::map<int, Customer> myMap;
//     myMap[7] = Customer("Honeywell",
//                         27,
//                         ThirdPartyStruct::PRIVATE,
//                         "hw",
//                         true);
//     myMap[5] = Customer("IBM",
//                         32,
//                         ThirdPartyStruct::WRITABLE,
//                         "ibm",
//                         false);
//     myMap[8] = Customer("Burroughs",
//                         45,
//                         0,
//                         "burr",
//                         true);
// ```
//  Now we print the map
// ```
//     bslim::Printer printer(&cout, 0, 4);
//     printer.start();
//     printer.printValue(myMap);
//     printer.end();
// }
// ```
// The following is written to `stdout`:
// ```
// [
//     [
//         [
//             5
//             [
//                 CompanyName = "IBM"
//                 ThirdPartyStruct = [
//                     pid = 32
//                     access_flags = 2
//                     user_id = "ibm"
//                 ]
//                 LoyalCustomer = false
//             ]
//         ]
//         [
//             7
//             [
//                 CompanyName = "Honeywell"
//                 ThirdPartyStruct = [
//                     pid = 27
//                     access_flags = 1
//                     user_id = "hw"
//                 ]
//                 LoyalCustomer = true
//             ]
//         ]
//         [
//             8
//             [
//                 CompanyName = "Burroughs"
//                 ThirdPartyStruct = [
//                     pid = 45
//                     access_flags = 0
//                     user_id = "burr"
//                 ]
//                 LoyalCustomer = true
//             ]
//         ]
//     ]
// ]
// ```
//
///Example 4: Printing Ranges, and Typed Pointers
/// - - - - - - - - - - - - - - - - - - - - - - -
// In this examples we demonstrate two capabilities of a `bslim::Printer`
// object: printing a range of elements using iterators and printing a pointer
// type.
//
// The `printValue` or `printAttribute` methods of `bslim::Printer` will print
// out all of the elements in the range specified by a pair of iterator
// arguments, which can be of any type that provides appropriately behaving
// operators `++`, `*`, and `==` (a non-void pointer would qualify).
//
// When `bslim` encounters a single pointer of type `TYPE *`, where `TYPE` is
// neither `void` nor `char`, the pointer value is printed out in hex followed
// by printing out the value of `TYPE`.  A compile error will occur if bslim is
// unable to print out `TYPE`.
//
// As an example, we print out a range of pointers to sets.
//
// First we create 3 sets and populate them with different values.
// ```
// typedef bsl::set<int> Set;
//
// Set s0, s1, s2;
//
// s0.insert(0);
// s0.insert(1);
// s0.insert(2);
//
// s1.insert(4);
// s1.insert(5);
//
// s2.insert(8);
// ```
// Then, we store the addresses to those 3 sets into a fixed-length array:
// ```
// const Set *setArray[] = { &s0, &s1, &s2 };
// const int NUM_SET_ARRAY = sizeof setArray / sizeof *setArray;
// ```
// Next we use `printValue` to print a range of values by supplying an iterator
// to the beginning and end of the range, in the address of `setArray` and the
// address one past the end of `setArray`:
// ```
// bslim::Printer printer(&cout, 0, -1);
// printer.printValue(setArray + 0, setArray + NUM_SET_ARRAY);
// ```
//  The expected output is:
// ```
// [ 0xffbfd688 [ 0 1 2 ] 0xffbfd678 [ 4 5 ] 0xffbfd668 [ 8 ] ]
// ```
//
///Example 5: `print` Method for a Low-Level Value-Semantic Class
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// For very simple classes, it may be desirable always to format the attributes
// on a single line.  In this example, we discuss the `print` method formatting
// for such a low-level value-semantic class.
//
// Usually, single-line or multi-line formatting options are specified by the
// value of the `spacesPerLevel` argument, but for a simple class that always
// prints on a single line, the only difference between the single- and
// multi-line cases is that a newline character is printed at the end of the
// output for the multi-line case.  For such classes, the "name" of the
// attribute and the enclosing brackets may be omitted as well.
//
// For example, consider a class, `DateTz`, having as attributes a local date
// and a time offset:
// ```
// class DateTz {
//     // This 'class' represents a date value explicitly in a local time
//     // zone.  The offset of that time (in minutes) from UTC is also part of
//     // the value of this class.
//
//   private:
//     // DATA
//     int d_localDate;  // date in YYYYMMDD format, local to the timezone
//                       // indicated by 'd_offset'
//
//     int d_offset;     // offset from UTC (in minutes)
//
//   public:
//     // ...
//     // ACCESSORS
//     bsl::ostream& print(bsl::ostream& stream,
//                         int           level          = 0,
//                         int           spacesPerLevel = 4) const;
//     // ...
// };
// ```
// The `Printer` class may be used in this case to print the start and end
// indentation by passing a `suppressBracket` flag to the `start` and `end`
// methods.  The value itself can be written to the stream directly without
// using `Printer`.  Note that to ensure correct formatting of the value in the
// presence of a call to `setw` on the stream, the output must be written to a
// `bsl::ostringstream` first; the string containing the output can then be
// written to the specified `stream`:
// ```
// bsl::ostream& DateTz::print(bsl::ostream& stream,
//                             int           level,
//                             int           spacesPerLevel) const
// {
//    if (stream.bad()) {
//        return stream;                                             // RETURN
//    }
//
//    bsl::ostringstream tmp;
//    tmp << d_localDate;
//
//    const char sign    = d_offset < 0 ? '-' : '+';
//    const int  minutes = '-' == sign ? -d_offset : d_offset;
//    const int  hours   = minutes / 60;
//
//    // space usage: +-  hh  mm  nil
//    const int SIZE = 1 + 2 + 2 + 1;
//    char buf[SIZE];
//
//    // Use at most 2 digits for 'hours'
//    if (hours < 100) {
//        snprintf(buf, sizeof buf, "%c%02d%02d", sign, hours, minutes % 60);
//    }
//    else {
//        snprintf(buf, sizeof buf, "%cXX%02d", sign, minutes % 60);
//    }
//
//    tmp << buf;
//
//    bslim::Printer printer(&stream, level, spacesPerLevel);
//    printer.start(true);
//    stream << tmp.str();
//    printer.end(true);
//
//    return stream;
// }
// ```
// Sample output for 'DateTz::print(bsl::cout, 0, -4):
// ```
// 01JAN2011-0500
// ```
// Sample output for 'DateTz::print(bsl::cout, 0, 4):
// ```
// 01JAN2011-0500<\n>
// ```

#include <bslscm_version.h>

#include <bslalg_typetraithasstliterators.h>

#include <bslmf_functionpointertraits.h>
#include <bslmf_isarray.h>
#include <bslmf_isfundamental.h>
#include <bslmf_ispointer.h>
#include <bslmf_selecttrait.h>

#include <bsls_assert.h>
#include <bsls_types.h>

#include <bsl_optional.h>
#include <bsl_ostream.h>
#include <bsl_memory.h>
#include <bsl_string.h>
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_TUPLE
#include <bsl_tuple.h>
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_TUPLE
#include <bsl_utility.h>

namespace BloombergLP {

namespace bslim {

                                // =============
                                // class Printer
                                // =============

/// This class implements a *mechanism* used to format data as required by
/// the standard BDE `print` method contract.
class Printer {

    // DATA
    bsl::ostream *d_stream_p;                  // output stream (held, not
                                               // owned)

    int           d_level;                     // level used in formatting

    int           d_levelPlusOne;              // 'd_level + 1'; useful in
                                               // implementation

    bool          d_suppressInitialIndentFlag; // formatting flag

    int           d_spacesPerLevel;            // spaces per level used in
                                               // formatting

  private:
    // NOT IMPLEMENTED
    Printer& operator=(const Printer&);

  public:
    // CREATORS

    /// Create a `Printer` object that will print to the specified `stream`
    /// in a format dictated by the values of the specified `level` and
    /// `spacesPerLevel`, as per the contract of the standard BDE `print`
    /// method.  The behavior is undefined unless `stream` is valid.
    Printer(bsl::ostream *stream, int level, int spacesPerLevel);

    /// Destroy this `Printer` object.
    ~Printer();

    // ACCESSORS

    /// Return the absolute value of the formatting level supplied at
    /// construction.
    int absLevel() const;

    /// If `spacesPerLevel() >= 0`, print a newline character to the output
    /// stream supplied at construction.  If the optionally specified
    /// `suppressBracket` is false, print a closing square bracket, indented
    /// by `absLevel() * spacesPerLevel()` blank spaces.
    void end(bool suppressBracket = false) const;

#ifndef BDE_OPENSOURCE_PUBLICATION  // DEPRECATED

    /// Format to the output stream supplied at construction the specified
    /// `data`, prefixed by the specified `name` if `name` is not 0.  Format
    /// `data` based on the parameterized `TYPE`:
    ///
    /// * If `TYPE` is a fundamental type, output `data` to the stream.
    /// * If `TYPE` is `char *` or `const char *`, print `data` to the
    ///   stream as a null-terminated C-style string enclosed in quotes if
    ///   `data` is not 0, and print the string "NULL" otherwise.
    /// * If `TYPE` is `void *` or `const void *`, print the address value
    ///   of `data` in hexadecimal format if it is not 0, and print the
    ///   string "NULL" otherwise.
    /// * If `TYPE` is a pointer type (other than the, potentially
    ///   const-qualified,  `char *` or `void *`), print the address
    ///   value of `data` in hexadecimal format, then format the object at
    ///   that address if `data` is not 0, and print the string "NULL"
    ///   otherwise.  There will be a compile-time error if `data` is a
    ///   pointer to a user-defined type that does not provide a standard
    ///   `print` method.
    /// * If `TYPE` is any other type, call the standard `print` method on
    ///   `data`, specifying one additional level of indentation than the
    ///   current one.  There will be a compile-time error if `TYPE` does
    ///   not provide a standard `print` method.
    ///
    /// If `spacesPerLevel() < 0`, format `data` on a single line.  Otherwise,
    /// indent `data` by `(absLevel() + 1) * spacesPerLevel()` blank spaces.
    /// The behavior is undefined if `TYPE` is a `char *`, but not a
    /// null-terminated string.
    ///
    /// @DEPRECATED: Use `printAttribute` instead, or `printValue` if no name
    /// is wanted.
    template <class TYPE>
    void print(const TYPE& data, const char *name) const;
#endif  // BDE_OPENSOURCE_PUBLICATION

    /// Format to the output stream supplied at construction the specified
    /// `data`, prefixed by the specified `name`.  Format `data` based on
    /// the parameterized `TYPE`:
    ///
    /// * If `TYPE` is a fundamental type, output `data` to the stream.
    /// * If `TYPE` is a fixed length array (`Element[NUM]`) and not a char
    ///   array, print out all the elements of the array.
    /// * If `TYPE` is `void * or `const void *', or function pointer,
    ///   print the address value of `data` in hexadecimal format if it is
    ///   not 0, and print the string "NULL" otherwise.
    /// * If `TYPE` is `char *`, `const char *`, `char [*]`, or 'const char
    ///   `[*]` or `bsl::string` print `data` to the stream as a
    ///   null-terminated C-style string enclosed in quotes if `data` is
    ///   not 0, and print the string "NULL" otherwise.
    /// * If `TYPE` is a pointer type (other than the, potentially
    ///   const-qualified,  `char *` or `void *`), print the address
    ///   value of `data` in hexadecimal format, then format the object at
    ///   that address if `data` is not 0, and print the string "NULL"
    ///   otherwise.  There will be a compile-time error if `data` is a
    ///   pointer to a user-defined type that does not provide a standard
    ///   `print` method.
    /// * If `TYPE` is a `bsl::pair` object, print out the two elements of
    ///   the pair.
    /// * If `TYPE` is a `bslstl::StringRef` object, print the referenced
    ///   string enclosed in quotes (possibly including embedded 0s).
    /// * If `TYPE` has STL iterators (this includes all STL sequence and
    ///   associative containers: vector, deque, list, set, map, multiset,
    ///   multimap, unordered_set, unordered_map, unordered_multiset, and
    ///   unordered_multimap), print all the objects in the container.
    /// * If `TYPE` is any other type, call the standard `print` method on
    ///   `data`, specifying one additional level of indentation than the
    ///   current one.  There will be a compile-time error if `TYPE` does
    ///   not provide a standard `print` method.
    ///
    /// If `spacesPerLevel() < 0`, format `data` on a single line.
    /// Otherwise, indent `data` by `(absLevel() + 1) * spacesPerLevel()`
    /// blank spaces.  The behavior is undefined if `TYPE` is a `char *`,
    /// but not a null-terminated string.
    template <class TYPE>
    void printAttribute(const bslstl::StringRef& name, const TYPE& data) const;

    /// Format to the output stream supplied at construction, the specified
    /// `name` followed by the range of values starting at the specified
    /// `begin` position and ending immediately before the specified `end`
    /// position.  The parameterized `ITERATOR` type must support
    /// `operator++`, `operator*`, and `operator==`.  This function will
    /// call `printValue` on each element in the range `[begin, end)`.
    template <class ITERATOR>
    void printAttribute(const bslstl::StringRef& name,
                        const ITERATOR&          begin,
                        const ITERATOR&          end) const;

    /// Print to the output stream supplied at construction the specified
    /// `name` and then call the specified `printFunctionObject` with the
    /// range of values starting at the specified `begin` position and
    /// ending immediately before the specified `end` position, the stream
    /// supplied at construction, `absLevel() + 1`, and `spacesPerLevel()`.
    /// The parameterized `PRINT_FUNCTOR` must be an invocable type whose
    /// arguments match the following function signature:
    /// ```
    /// bsl::ostream& (*)(bsl::ostream& stream,
    ///                   const TYPE&   data,
    ///                   int           level,
    ///                   int           spacesPerLevel)
    /// ```
    template <class ITERATOR, class PRINT_FUNCTOR>
    void printAttribute(const bslstl::StringRef&    name,
                        const ITERATOR&             begin,
                        const ITERATOR&             end,
                        const PRINT_FUNCTOR&        printFunctionObject) const;

    /// Print to the output stream supplied at construction
    /// `absLevel() * spacesPerLevel()` blank spaces if
    /// `spacesPerLevel() >= 0`, and print a single blank space otherwise.
    void printEndIndentation() const;

    /// Print to the output stream supplied at construction the specified
    /// `name`, if name is not 0, and then call the specified
    /// `printFunctionObject` with the specified `data`, the `stream`
    /// supplied at construction, `absLevel() + 1`, and `spacesPerLevel()`.
    /// The parameterized `PRINT_FUNCTOR` must be an invocable type whose
    /// arguments match the following function signature:
    /// ```
    /// bsl::ostream& (*)(bsl::ostream& stream,
    ///                   const TYPE&   data,
    ///                   int           level,
    ///                   int           spacesPerLevel)
    /// ```
    template <class TYPE, class PRINT_FUNCTOR>
    void printForeign(const TYPE&           data,
                      const PRINT_FUNCTOR&  printFunctionObject,
                      const char           *name) const;

    /// Write to the output stream supplied at construction the specified
    /// `address` in a hexadecimal format, if `address` is not 0, and print
    /// the string "NULL" otherwise, prefixed by the specified `name` if
    /// `name` is not 0.  If `spacesPerLevel() < 0`, print on a single line.
    /// If `spacesPerLevel() >= 0`, indent by
    /// `(absLevel() + 1) * spacesPerLevel()` blank spaces.
    void printHexAddr(const void *address, const char *name) const;

    /// Print to the output stream supplied at construction
    /// `(absLevel() + 1) * spacesPerLevel()` blank spaces if
    /// `spacesPerLevel() >= 0`, and print a single blank space otherwise.
    void printIndentation() const;

    /// Format to the output stream supplied at construction the object at
    /// the specified `address`, if `address` is not 0, and print the string
    /// "NULL" otherwise, prefixed by the specified `name` if `name` is not
    /// 0.  If `spacesPerLevel() < 0`, print on a single line.  If
    /// `spacesPerLevel() >= 0`, indent by
    /// `(absLevel() + 1) * spacesPerLevel()` blank spaces.  The behavior is
    /// undefined unless `TYPE` is a pointer type.
    template <class TYPE>
    void printOrNull(const TYPE& address, const char *name) const;

    /// Format to the output stream supplied at construction the specified
    /// `data`.  Format `data` based on the parameterized `TYPE`:
    ///
    /// * If `TYPE` is a fundamental type, output `data` to the stream.
    /// * If `TYPE` is a fixed length array (`Element[NUM]`) and not a char
    ///   array, print out all the elements of the array.
    /// * If `TYPE` is `void * or `const void *', or function pointer,
    ///   print the address value of `data` in hexadecimal format if it is
    ///   not 0, and print the string "NULL" otherwise.
    /// * If `TYPE` is `char *`, `const char *`, `char [*]`, or 'const char
    ///   `[*]` or `bsl::string` print `data` to the stream as a
    ///   null-terminated C-style string enclosed in quotes if `data` is
    ///   not 0, and print the string "NULL" otherwise.
    /// * If `TYPE` is a pointer type (other than the, potentially
    ///   const-qualified,  `char *` or `void *`), print the address
    ///   value of `data` in hexadecimal format, then format the object at
    ///   that address if `data` is not 0, and print the string "NULL"
    ///   otherwise.  There will be a compile-time error if `data` is a
    ///   pointer to a user-defined type that does not provide a standard
    ///   `print` method.
    /// * If `TYPE` is a `bsl::pair` object, print out the two elements of
    ///   the pair.
    /// * If `TYPE` is a `bslstl::StringRef` object, print the referenced
    ///   string enclosed in quotes (possibly including embedded 0s).
    /// * If `TYPE` has STL iterators (this includes all STL sequence and
    ///   associative containers: vector, deque, list, set, map, multiset,
    ///   multimap, unordered_set, unordered_map, unordered_multiset, and
    ///   unordered_multimap), print all the objects in the container.
    /// * If `TYPE` is any other type, call the standard `print` method on
    ///   `data`, specifying one additional level of indentation than the
    ///   current one.  There will be a compile-time error if `TYPE` does
    ///   not provide a standard `print` method.
    ///
    /// If `spacesPerLevel() < 0`, format `data` on a single line.
    /// Otherwise, indent `data` by `(absLevel() + 1) * spacesPerLevel()`
    /// blank spaces.  The behavior is undefined if `TYPE` is a `char *`,
    /// but not a null-terminated string.
    template <class TYPE>
    void printValue(const TYPE& data) const;

    /// Format to the output stream supplied at construction, the range of
    /// values starting at the specified `begin` position and ending
    /// immediately before the specified `end` position.  The parameterized
    /// `ITERATOR` type must support `operator++`, `operator*`, and
    /// `operator==`.  This function will call `printValue` on each element
    /// in the range `[begin, end)`.
    template <class ITERATOR>
    void printValue(const ITERATOR& begin,
                    const ITERATOR& end) const;

    /// Print to the output stream supplied at construction the specified
    /// `name`, if name is not 0, and then call the specified
    /// `printFunctionObject` with the range of values starting at the
    /// specified `begin` position and ending immediately before the
    /// specified `end` position, the `stream` supplied at construction,
    /// `absLevel() + 1`, and `spacesPerLevel()`. The parameterized
    /// `PRINT_FUNCTOR` must be an invocable type whose arguments match the
    /// following function signature:
    /// ```
    /// bsl::ostream& (*)(bsl::ostream& stream,
    ///                   const TYPE&   data,
    ///                   int           level,
    ///                   int           spacesPerLevel)
    /// ```
    template <class ITERATOR, class PRINT_FUNCTOR>
    void printValue(const ITERATOR&       begin,
                    const ITERATOR&       end,
                    const PRINT_FUNCTOR&  printFunctionObject) const;

    /// Return the number of whitespace characters to output for each level
    /// of indentation.  The number of whitespace characters for each level
    /// of indentation is configured using the `spacesPerLevel` supplied at
    /// construction.
    int spacesPerLevel() const;

    /// Print to the output stream supplied at construction
    /// `absLevel() * spacesPerLevel()` blank spaces if the
    /// `suppressInitialIndentFlag` is `false`, and suppress the initial
    /// indentation otherwise.  If the optionally specified
    /// `suppressBracket` is `false`, print an opening square bracket.
    void start(bool suppressBracket = false) const;

    /// Return `true` if the initial output indentation will be suppressed,
    /// and `false` otherwise.  The initial indentation will be suppressed
    /// if the `level` supplied at construction is negative.
    bool suppressInitialIndentFlag() const;
};

                        // =====================
                        // struct Printer_Helper
                        // =====================

/// This struct is an aid to the implementation of the accessors of the
/// `Printer` mechanism.  It provides a method template, `print`, that
/// adheres to the BDE `print` method contract.  It is not to be accessed
/// directly by clients of `bslim`.
struct Printer_Helper {

    // CLASS METHODS

    /// Format the specified `data` to the specified output `stream` at the
    /// (absolute value of) the specified indentation `level`, using the
    /// specified `spacesPerLevel`, the number of spaces per indentation
    /// level for this and all of its nested objects.  Note that this
    /// function dispatches to `printRaw` based on the type traits of the
    /// deduced (template parameter) `TYPE`.
    template <class TYPE>
    static void print(bsl::ostream& stream,
                      const TYPE&   data,
                      int           level,
                      int           spacesPerLevel);

    /// Format the range of objects specified by `[ begin, end )` to the
    /// specified output `stream` at the (absolute value of) the specified
    /// indentation `level`, using the specified `spacesPerLevel`, the
    /// number of spaces per indentation level for the objects and their
    /// nested objects, where `ITERATOR` supports the operators `++` and `*`
    /// to access the objects.  Individual objects are printed with
    /// `printValue`.
    template <class ITERATOR>
    static void print(bsl::ostream&   stream,
                      const ITERATOR& begin,
                      const ITERATOR& end,
                      int             level,
                      int             spacesPerLevel);

    /// Format the range of objects specified by `[ begin, end )` to the
    /// specified output `stream` at the (absolute value of) the specified
    /// indentation `level`, using the specified `spacesPerLevel`, the
    /// number of spaces per indentation level for the objects and their
    /// nested objects, where `ITERATOR` supports the operators `++` and `*`
    /// to access the objects, printing the individual objects with the
    /// specified `printFunctionObject`.
    template <class ITERATOR, class PRINT_FUNCTOR>
    static void print(bsl::ostream&        stream,
                      const ITERATOR&      begin,
                      const ITERATOR&      end,
                      const PRINT_FUNCTOR& printFunctionObject,
                      const int            level,
                      const int            spacesPerLevel);

                      // Fundamental types

    static void printRaw(bsl::ostream&                  stream,
                         char                           data,
                         int                            level,
                         int                            spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_fundamental>);
    static void printRaw(bsl::ostream&                  stream,
                         unsigned char                  data,
                         int                            level,
                         int                            spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_fundamental>);
    static void printRaw(bsl::ostream&                  stream,
                         bool                           data,
                         int                            level,
                         int                            spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_fundamental>);
    template <class TYPE>
    static void printRaw(bsl::ostream&                  stream,
                         TYPE                           data,
                         int                            level,
                         int                            spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_fundamental>);
    template <class TYPE>
    static void printRaw(bsl::ostream&                  stream,
                         TYPE                           data,
                         int                            level,
                         int                            spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_enum>);

                      // Function pointer types

    template <class TYPE>
    static void printRaw(bsl::ostream&                      stream,
                         const TYPE&                        data,
                         int                                level,
                         int                                spacesPerLevel,
                         bslmf::SelectTraitCase<bslmf::IsFunctionPointer>);

                      // Pointer types

    static void printRaw(bsl::ostream&              stream,
                         const char                *data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_pointer>);
    static void printRaw(bsl::ostream&              stream,
                         const void                *data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_pointer>);
    template <class TYPE>
    static void printRaw(bsl::ostream&              stream,
                         const TYPE                *data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_pointer>);
    template <class TYPE>
    static void printRaw(bsl::ostream&              stream,
                         const TYPE                *data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<bsl::is_array>);

                      // Types with STL iterators

    static void printRaw(bsl::ostream&                      stream,
                         const bsl::string&                 data,
                         int                                level,
                         int                                spacesPerLevel,
                         bslmf::SelectTraitCase<bslalg::HasStlIterators>);
    template <class TYPE>
    static void printRaw(bsl::ostream&                      stream,
                         const TYPE&                        data,
                         int                                level,
                         int                                spacesPerLevel,
                         bslmf::SelectTraitCase<bslalg::HasStlIterators>);

                      // Default types

    template <class T1, class T2>
    static void printRaw(bsl::ostream&              stream,
                         const bsl::pair<T1, T2>&   data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<>);

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_TUPLE
    /// No op.
    template <class t_TUPLE>
    static void printTupleElements(Printer&,
                                   const t_TUPLE&,
                                   bsl::integral_constant<int, 0>);

    /// Print the first `t_SIZE` elements of the specified tuple, `data`, using the
    /// specified `printer`.
    template <int t_SIZE, class t_TUPLE>
    static void printTupleElements(Printer&       printer,
                                   const t_TUPLE& data,
                                   bsl::integral_constant<int, t_SIZE>);

    template <class... t_TYPES>
    static void printRaw(bsl::ostream&                 stream,
                         const bsl::tuple<t_TYPES...>& data,
                         int                           level,
                         int                           spacesPerLevel,
                         bslmf::SelectTraitCase<>);
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_TUPLE

    static void printRaw(bsl::ostream&              stream,
                         const bslstl::StringRef&   data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<>);

    static void printRaw(bsl::ostream&              stream,
                         const bsl::string_view&    data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<>);

    template <class TYPE>
    static void printRaw(bsl::ostream&                stream,
                         const bsl::shared_ptr<TYPE>& data,
                         int                          level,
                         int                          spacesPerLevel,
                         bslmf::SelectTraitCase<>);

    template <class TYPE>
    static void printRaw(bsl::ostream&              stream,
                         const bsl::optional<TYPE>& data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<>);

    /// The `print` method of this class dispatches based on `TYPE` and
    /// traits to a `printRaw` method to do the actual printing of the
    /// specified `data` to the specified `stream` with indentation based on
    /// the specified `level` and `spacesPerLevel`.
    template <class TYPE>
    static void printRaw(bsl::ostream&              stream,
                         const TYPE&                data,
                         int                        level,
                         int                        spacesPerLevel,
                         bslmf::SelectTraitCase<>);
};

// ============================================================================
//                              INLINE DEFINITIONS
// ============================================================================


                                // -------------
                                // class Printer
                                // -------------

// ACCESSORS
#ifndef BDE_OPENSOURCE_PUBLICATION  // DEPRECATED
template <class TYPE>
void Printer::print(const TYPE& data, const char *name) const
{
    printIndentation();

    if (name) {
        *d_stream_p << name << " = ";
    }

    Printer_Helper::print(*d_stream_p,
                          data,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}
#endif  // BDE_OPENSOURCE_PUBLICATION

template <class TYPE>
void Printer::printAttribute(const bslstl::StringRef& name,
                             const TYPE&              data) const
{
    printIndentation();

    *d_stream_p << name << " = ";

    Printer_Helper::print(*d_stream_p,
                          data,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

template <class ITERATOR>
void Printer::printAttribute(const bslstl::StringRef& name,
                             const ITERATOR&          begin,
                             const ITERATOR&          end) const
{
    printIndentation();

    *d_stream_p << name << " = ";

    Printer_Helper::print(*d_stream_p,
                          begin,
                          end,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

template <class ITERATOR, class PRINT_FUNCTOR>
void Printer::printAttribute(
                            const bslstl::StringRef& name,
                            const ITERATOR&          begin,
                            const ITERATOR&          end,
                            const PRINT_FUNCTOR&     printFunctionObject) const
{
    printIndentation();

    *d_stream_p << name << " = ";

    Printer_Helper::print(*d_stream_p,
                          begin,
                          end,
                          printFunctionObject,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

template <class TYPE, class PRINT_FUNCTOR>
void Printer::printForeign(const TYPE&           data,
                           const PRINT_FUNCTOR&  printFunctionObject,
                           const char           *name) const
{
    printIndentation();

    if (name) {
        *d_stream_p << name << " = ";
    }

    printFunctionObject(*d_stream_p,
                        data,
                        -d_levelPlusOne,
                        d_spacesPerLevel);
}

template <class TYPE>
void Printer::printOrNull(const TYPE& address, const char *name) const
{
    printIndentation();

    if (name) {
        *d_stream_p << name << " = ";
    }

    if (0 == address) {
        *d_stream_p << "NULL";
        if (d_spacesPerLevel >= 0) {
            *d_stream_p << '\n';
        }
    }
    else {
        Printer_Helper::print(*d_stream_p,
                              *address,
                              -d_levelPlusOne,
                              d_spacesPerLevel);
    }
}

template <>
inline
void Printer::printOrNull<const void *>(const void *const&  address,
                                        const char         *name) const
{
    printIndentation();

    if (name) {
        *d_stream_p << name << " = ";
    }
    const void *temp = address;

    Printer_Helper::print(*d_stream_p,
                          temp,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

template <>
inline
void Printer::printOrNull<void *>(void *const& address, const char *name) const
{
    const void *const& temp = address;
    printOrNull(temp, name);
}

template <class TYPE>
inline
void Printer::printValue(const TYPE& data) const
{
    printIndentation();

    Printer_Helper::print(*d_stream_p,
                          data,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

template <class ITERATOR>
void Printer::printValue(const ITERATOR& begin,
                         const ITERATOR& end) const
{
    printIndentation();

    Printer_Helper::print(*d_stream_p,
                          begin,
                          end,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

template <class ITERATOR, class PRINT_FUNCTOR>
void Printer::printValue(const ITERATOR&       begin,
                         const ITERATOR&       end,
                         const PRINT_FUNCTOR&  printFunctionObject) const
{

    printIndentation();

    Printer_Helper::print(*d_stream_p,
                          begin,
                          end,
                          printFunctionObject,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

                            // ---------------------
                            // struct Printer_Helper
                            // ---------------------

// CLASS METHODS

// 'Printer_Helper::print(stream, data, level, spacesPerLevel)', though defined
// first in the struct, is implemented last within this class so it can inline
// the calls to 'printRaw' that it makes.

template <class ITERATOR>
inline
void Printer_Helper::print(bsl::ostream&   stream,
                           const ITERATOR& begin,
                           const ITERATOR& end,
                           const int       level,
                           const int       spacesPerLevel)
{
    bslim::Printer printer(&stream, level, spacesPerLevel);
    printer.start();
    for (ITERATOR it = begin; end != it; ++it) {
        printer.printValue(*it);
    }
    printer.end();
}

template <class ITERATOR, class PRINT_FUNCTOR>
inline
void Printer_Helper::print(bsl::ostream&        stream,
                           const ITERATOR&      begin,
                           const ITERATOR&      end,
                           const PRINT_FUNCTOR& printFunctionObject,
                           const int            level,
                           const int            spacesPerLevel)
{
    bslim::Printer printer(&stream, level, spacesPerLevel);
    printer.start();
    for (ITERATOR it = begin; end != it; ++it) {
        printFunctionObject(stream,
                            *it,
                            printer.absLevel() + 1,
                            spacesPerLevel);
        if (spacesPerLevel >= 0) {
            stream << '\n';
        }
    }
    printer.end();
}

                      // Fundamental types

template <class TYPE>
inline
void Printer_Helper::printRaw(bsl::ostream&                  stream,
                              TYPE                           data,
                              int                            ,
                              int                            spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_fundamental>)
{
    stream << data;
    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

template <class TYPE>
inline
void Printer_Helper::printRaw(bsl::ostream&                  stream,
                              TYPE                           data,
                              int                            ,
                              int                            spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_enum>)
{
    Printer_Helper::printRaw(stream,
                             data,
                             0,
                             spacesPerLevel,
                             bslmf::SelectTraitCase<bsl::is_fundamental>());
}

                      // Function pointer types

template <class TYPE>
inline
void Printer_Helper::printRaw(
                             bsl::ostream&                      stream,
                             const TYPE&                        data,
                             int                                level,
                             int                                spacesPerLevel,
                             bslmf::SelectTraitCase<bslmf::IsFunctionPointer>)
{
    // GCC 3.4.6 does not allow a reinterpret-cast a function pointer directly
    // to 'void *', so first cast it to an integer data type.

    Printer_Helper::print(stream,
                          reinterpret_cast<const void *>(
                                 reinterpret_cast<bsls::Types::UintPtr>(data)),
                          level,
                          spacesPerLevel);
}

                      // Pointer types

template <class TYPE>
inline
void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const TYPE                *data,
                              int                        level,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_pointer>)
{
    Printer_Helper::printRaw(stream,
                             static_cast<const void *>(data),
                             level,
                             -1,
                             bslmf::SelectTraitCase<bsl::is_pointer>());
    if (0 == data) {
        if (spacesPerLevel >= 0) {
            stream << '\n';
        }
    }
    else {
        stream << ' ';
        Printer_Helper::print(stream, *data, level, spacesPerLevel);
    }
}

template <class TYPE>
inline
void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const TYPE                *data,
                              int                        level,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_array>)
{
    Printer_Helper::printRaw(stream,
                             data,
                             level,
                             spacesPerLevel,
                             bslmf::SelectTraitCase<bsl::is_pointer>());
}


                      // Types with STL iterators

inline
void Printer_Helper::printRaw(
                             bsl::ostream&                      stream,
                             const bsl::string&                 data,
                             int                                level,
                             int                                spacesPerLevel,
                             bslmf::SelectTraitCase<bslalg::HasStlIterators>)
{
    Printer_Helper::printRaw(stream,
                             data.c_str(),
                             level,
                             spacesPerLevel,
                             bslmf::SelectTraitCase<bsl::is_pointer>());
}

template <class TYPE>
inline
void Printer_Helper::printRaw(
                             bsl::ostream&                      stream,
                             const TYPE&                        data,
                             int                                level,
                             int                                spacesPerLevel,
                             bslmf::SelectTraitCase<bslalg::HasStlIterators>)
{
    Printer_Helper::print(stream,
                          data.begin(),
                          data.end(),
                          level,
                          spacesPerLevel);
}

                      // Default types

template <class T1, class T2>
inline
void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const bsl::pair<T1, T2>&   data,
                              int                        level,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<>)
{
    bslim::Printer printer(&stream, level, spacesPerLevel);
    printer.start();
    printer.printValue(data.first);
    printer.printValue(data.second);
    printer.end();
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_TUPLE
template <class t_TUPLE>
inline
void Printer_Helper::printTupleElements(Printer&,
                                        const t_TUPLE&,
                                        bsl::integral_constant<int, 0>)
{
}

template <int t_SIZE, class t_TUPLE>
inline
void Printer_Helper::printTupleElements(Printer&       printer,
                                        const t_TUPLE& data,
                                        bsl::integral_constant<int, t_SIZE>)
{
    printTupleElements(printer, data, bsl::integral_constant<int, t_SIZE-1>{});
    printer.printValue(bsl::get<t_SIZE-1>(data));
}

template <class... t_TYPES>
inline
void Printer_Helper::printRaw(bsl::ostream&                 stream,
                              const bsl::tuple<t_TYPES...>& data,
                              int                           level,
                              int                           spacesPerLevel,
                              bslmf::SelectTraitCase<>)
{
    bslim::Printer printer(&stream, level, spacesPerLevel);
    printer.start();
    printTupleElements(printer, data, bsl::integral_constant<int, sizeof...(t_TYPES)>{});
    printer.end();
}
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_TUPLE

template <class TYPE>
inline
void Printer_Helper::printRaw(bsl::ostream&                stream,
                              const bsl::shared_ptr<TYPE>& data,
                              int                          level,
                              int                          spacesPerLevel,
                              bslmf::SelectTraitCase<>)
{
    Printer_Helper::printRaw(stream,
                             static_cast<const void *>(data.get()),
                             level,
                             -1,
                             bslmf::SelectTraitCase<bsl::is_pointer>());
    if (data) {
        stream << ' ';
        Printer_Helper::print(stream, *data, level, spacesPerLevel);
    }
    else if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

template <class TYPE>
inline
void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const bsl::optional<TYPE>& data,
                              int                        level,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<>)
{
    if (data.has_value()) {
        Printer_Helper::print(stream, *data, level, spacesPerLevel);
    }
    else {
        if (spacesPerLevel >= 0) {
            stream << "NULL\n";
        }
        else {
            stream << "NULL";
        }
    }
}

template <class TYPE>
inline
void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const TYPE&                data,
                              int                        level,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<>)
{
    data.print(stream, level, spacesPerLevel);
}

// This method, though declared first in the struct, is placed last among the
// methods in 'Printer_Helper' so that it can inline the 'printRaw' methods it
// calls.

template <class TYPE>
inline
void Printer_Helper::print(bsl::ostream& stream,
                           const TYPE&   data,
                           int           level,
                           int           spacesPerLevel)
{
    typedef bslmf::SelectTrait<TYPE,
                               bsl::is_fundamental,
                               bsl::is_enum,
                               bslmf::IsFunctionPointer,
                               bsl::is_pointer,
                               bsl::is_array,
                               bslalg::HasStlIterators> Selection;

    Printer_Helper::printRaw(stream, data, level, spacesPerLevel, Selection());
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
