// 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:
//
//: o Enclose the object's salient attributes with square brackets.
//:
//: o Prefix each attribute with the attribute's name, separated by an "equal"
//:   sign surrounded by space characters (" = ").
//:
//: o 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.
//:
//: o If the attributes are to be printed on a single line, then separate each
//:   value with a single space character.
//:
//: o 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) {
//         bsl::sprintf(buf, "%c%02d%02d", sign, hours, minutes % 60);
//     }
//     else {
//         bsl::sprintf(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 << bsl::flush;
//  }
//..
// 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>
#include <bsl_utility.h>

namespace BloombergLP {

namespace bslim {

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

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

    // 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
    Printer(bsl::ostream *stream, int level, int spacesPerLevel);
        // 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();
        // Destroy this 'Printer' object.

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

    void end(bool suppressBracket = false) 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.

#ifndef BDE_OPENSOURCE_PUBLICATION  // DEPRECATED
    template <class TYPE>
    void print(const TYPE& data, const char *name) const;
        // [!DEPRECATED!  -- use 'printAttribute' instead, or 'printValue' if
        // no name is wanted.]
        //
        // 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':
        //
        //: o If 'TYPE' is a fundamental type, output 'data' to the stream.
        //:
        //: o 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.
        //:
        //: o 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.
        //:
        //: o 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.
        //:
        //: o 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.
#endif  // BDE_OPENSOURCE_PUBLICATION

    template <class TYPE>
    void printAttribute(const bslstl::StringRef& name, const TYPE& data) const;
        // Format to the output stream supplied at construction the specified
        // 'data', prefixed by the specified 'name'.  Format 'data' based on
        // the parameterized 'TYPE':
        //
        //: o If 'TYPE' is a fundamental type, output 'data' to the stream.
        //:
        //: o If 'TYPE' is a fixed length array ('Element[NUM]') and not a char
        //:   array, print out all the elements of the array.
        //:
        //: o 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.
        //:
        //: o 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.
        //:
        //: o 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.
        //:
        //: o If 'TYPE' is a 'bsl::pair' object, print out the two elements of
        //:   the pair.
        //:
        //: o If 'TYPE' is a 'bslstl::StringRef' object, print the referenced
        //:   string enclosed in quotes (possibly including embedded 0s).
        //:
        //: o 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.
        //:
        //: o 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 ITERATOR>
    void printAttribute(const bslstl::StringRef& name,
                        const ITERATOR&          begin,
                        const ITERATOR&          end) 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, 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 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)
        //..

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

    template <class TYPE, class PRINT_FUNCTOR>
    void printForeign(const TYPE&           data,
                      const PRINT_FUNCTOR&  printFunctionObject,
                      const char           *name) 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)
        //..

    void printHexAddr(const void *address, 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 printIndentation() const;
        // Print to the output stream supplied at construction
        // '(absLevel() + 1) * spacesPerLevel()' blank spaces if
        // 'spacesPerLevel() >= 0', and print a single blank space otherwise.

    template <class TYPE>
    void printOrNull(const TYPE& address, const char *name) 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 printValue(const TYPE& data) const;
        // Format to the output stream supplied at construction the specified
        // 'data'.  Format 'data' based on the parameterized 'TYPE':
        //
        //: o If 'TYPE' is a fundamental type, output 'data' to the stream.
        //:
        //: o If 'TYPE' is a fixed length array ('Element[NUM]') and not a char
        //:   array, print out all the elements of the array.
        //:
        //: o 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.
        //:
        //: o 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.
        //:
        //: o 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.
        //:
        //: o If 'TYPE' is a 'bsl::pair' object, print out the two elements of
        //:   the pair.
        //:
        //: o If 'TYPE' is a 'bslstl::StringRef' object, print the referenced
        //:   string enclosed in quotes (possibly including embedded 0s).
        //:
        //: o 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.
        //:
        //: o 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 ITERATOR>
    void printValue(const ITERATOR& begin,
                    const ITERATOR& end) 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, class PRINT_FUNCTOR>
    void printValue(const ITERATOR&       begin,
                    const ITERATOR&       end,
                    const PRINT_FUNCTOR&  printFunctionObject) 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)
        //..

    int spacesPerLevel() 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.

    void start(bool suppressBracket = false) 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.

    bool suppressInitialIndentFlag() 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.
};

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

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'.

    // CLASS METHODS
    template <class TYPE>
    static void print(bsl::ostream& stream,
                      const TYPE&   data,
                      int           level,
                      int           spacesPerLevel);
        // 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 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.  Individual objects are printed with
        // 'printValue'.

    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);
        // 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'.

                      // 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<>);

    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<>);

    template <class TYPE>
    static void printRaw(bsl::ostream&              stream,
                         const 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'.
};

// ============================================================================
//                              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();
}

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 ----------------------------------