// balxml_encoder.h                                                   -*-C++-*-
#ifndef INCLUDED_BALXML_ENCODER
#define INCLUDED_BALXML_ENCODER

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

//@PURPOSE: Provide an XML encoder utility.
//
//@CLASSES:
//  balxml::Encoder: XML encoder utility class
//
//@SEE_ALSO: balxml_decoder, balber_berencoder
//
//@DESCRIPTION: This component provides a class for encoding value-semantic
// objects in XML format.  In particular, the `balxml::Encoder` `class`
// contains a parameterized `encode` function that encodes a specified
// value-semantic object into a specified stream.  There are three overloaded
// versions of this function:
//
// * writes to an `bsl::streambuf`
// * writes to an `bsl::ostream`
// * writes to an `balxml::Formatter`
//
// The `encode` function encodes objects in XML format, which is a very useful
// format for debugging.  For more efficient performance, a binary encoding
// (such as BER) should be used.
//
// This component can be used with types supported by the `bdlat` framework.
// In particular, types generated by the `bas_codegen.pl` tool can be used.
//
// Note that encoding top-level `array` objects (a.k.a. `sequence-of` types, in
// the X.690 spec) is not allowed.
//
///Usage
///-----
// The following snippets of code illustrate the usage of this component.
// Suppose we have an XML schema inside a file named `employee.xsd`:
// ```
// <?xml version='1.0' encoding='UTF-8'?>
// <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
//            xmlns:test='http://bloomberg.com/schemas/test'
//            targetNamespace='http://bloomberg.com/schemas/test'
//            elementFormDefault='unqualified'>
//
//     <xs:complexType name='Address'>
//         <xs:sequence>
//             <xs:element name='street' type='xs:string'/>
//             <xs:element name='city'   type='xs:string'/>
//             <xs:element name='state'  type='xs:string'/>
//         </xs:sequence>
//     </xs:complexType>
//
//     <xs:complexType name='Employee'>
//         <xs:sequence>
//             <xs:element name='name'        type='xs:string'/>
//             <xs:element name='homeAddress' type='test:Address'/>
//             <xs:element name='age'         type='xs:int'/>
//         </xs:sequence>
//     </xs:complexType>
// </xs:schema>
// ```
// Using the `bas_codegen.pl` tool, we generate C++ classes for this schema as
// follows:
// ```
// $ bas_codegen.pl -m msg -p test employee.xsd
// ```
// This tool will generate the header and implementation files for the
// `test_messages` components in the current directory.
//
// Now suppose we wanted to encode information about a particular employee
// using XML encoding to the standard output, and using the `PRETTY` option for
// formatting the output.  The following function will do this:
// ```
// #include <test_messages.h>
//
// #include <balxml_encoder.h>
// #include <balxml_encodingstyle.h>
//
// #include <bsl_iostream.h>
// #include <bsl_sstream.h>
//
// using namespace BloombergLP;
//
// void usageExample()
// {
//     test::Employee bob;
//
//     bob.name()                 = "Bob";
//     bob.homeAddress().street() = "Some Street";
//     bob.homeAddress().city()   = "Some City";
//     bob.homeAddress().state()  = "Some State";
//     bob.age()                  = 21;
//
//     balxml::EncoderOptions options;
//     options.setEncodingStyle(balxml::EncodingStyle::BAEXML_PRETTY);
//
//     balxml::Encoder encoder(&options, &bsl::cerr, &bsl::cerr);
//
//     const bsl::string EXPECTED_OUTPUT =
//      "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
//      "<Employee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
//      "    <name>Bob</name>\n"
//      "    <homeAddress>\n"
//      "        <street>Some Street</street>\n"
//      "        <city>Some City</city>\n"
//      "        <state>Some State</state>\n"
//      "    </homeAddress>\n"
//      "    <age>21</age>\n"
//      "</Employee>\n";
//
//     bsl::ostringstream os;
//     const int rc = encoder.encodeToStream(os, bob);
//
//     assert(0 == rc);
//     assert(EXPECTED_OUTPUT == os.str());
// }
// ```

#include <balscm_version.h>

#include <balxml_encoderoptions.h>
#include <balxml_encodingstyle.h>
#include <balxml_errorinfo.h>      // for Severity
#include <balxml_formatter.h>
#include <balxml_typesprintutil.h>

#include <bdlar_refutil.h>

#include <bdlat_arrayfunctions.h>
#include <bdlat_choicefunctions.h>
#include <bdlat_nullablevaluefunctions.h>
#include <bdlat_sequencefunctions.h>
#include <bdlat_typecategory.h>
#include <bdlat_typename.h>

#include <bdlsb_memoutstreambuf.h>

#include <bslma_allocator.h>
#include <bslma_default.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>
#include <bsls_keyword.h>
#include <bsls_objectbuffer.h>
#include <bsls_review.h>

#include <bsl_ostream.h>
#include <bsl_string.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace balxml {

class Encoder_Context;

                               // =============
                               // class Encoder
                               // =============

/// This `class` contains the parameterized `encode` functions that encode
/// `bdlat` types in XML format.
class Encoder {

    // FRIENDS
    friend class Encoder_Context;

  private:
    // PRIVATE TYPES

    /// This class provides stream for logging using
    /// `bdlsb::MemOutStreamBuf` as a streambuf.  The logging stream is
    /// created on demand, i.e., during the first attempt to log message.
    class MemOutStream : public bsl::ostream
    {
        bdlsb::MemOutStreamBuf d_sb;

        // Not implemented:
        MemOutStream(const MemOutStream&);
        MemOutStream& operator=(const MemOutStream&);

      public:
        // CREATORS

        /// Create a new stream using the optionally specified
        /// `basicAllocator`.
        MemOutStream(bslma::Allocator *basicAllocator = 0);

        /// Destroy this stream and release memory back to the allocator.
        ///
        /// Although the compiler should generate this destructor
        /// implicitly, xlC 8 breaks when the destructor is called by name
        /// unless it is explicitly declared.
        ~MemOutStream() BSLS_KEYWORD_OVERRIDE;

        // MANIPULATORS

        /// Reset the internal streambuf to empty.
        void reset();

        // ACCESSORS

        /// Return a pointer to the memory containing the formatted values
        /// formatted to this stream.  The data is not null-terminated
        /// unless a null character was appended onto this stream.
        const char *data() const;

        /// Return the length of the formatted data, including null
        /// characters appended to the stream, if any.
        int length() const;
    };

  private:
    // DATA
    const EncoderOptions            *d_options;        // held, not owned
    bslma::Allocator                *d_allocator;      // held, not owned

    // placeholder for MemOutStream
    bsls::ObjectBuffer<MemOutStream> d_logArea;

    // if not zero, log stream was created at the moment of first logging
    // and must be destroyed
    MemOutStream                    *d_logStream;

    ErrorInfo::Severity              d_severity;       // error severity

    bsl::ostream                    *d_errorStream;    // held, not owned
    bsl::ostream                    *d_warningStream;  // held, not owned

    // PRIVATE MANIPULATORS
    ErrorInfo::Severity logError(const char               *text,
                                 const bsl::string_view&   tag,
                                 int                       formattingMode,
                                 int                       index = -1);

    /// Return the stream for logging.  Note the if stream has not been
    /// created yet, it will be created during this call.
    bsl::ostream& logStream();

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(Encoder, bslma::UsesBslmaAllocator);

    // CREATORS
    Encoder(const EncoderOptions *options, bslma::Allocator *basicAllocator);

    /// Construct a encoder object using the specified `options`.  Errors
    /// and warnings will be rendered to the optionally specified
    /// `errorStream` and `warningStream` respectively.
    Encoder(const EncoderOptions *options,
            bsl::ostream         *errorStream   = 0,
            bsl::ostream         *warningStream = 0,
            bslma::Allocator     *basicAllocator = 0);

    /// Destroy this object.  This destruction has no effect on objects
    /// pointed-to by the pointers provided at construction.
    ~Encoder();

    /// Encode the specified non-modifiable `object` to the specified
    /// `buffer`.  Return 0 on success, and a non-zero value otherwise.
    /// Note that the encoder will use encoder options, error and warning
    /// streams specified at the construction time.
    template <class TYPE>
    int encode(bsl::streambuf *buffer, const TYPE& object);

    /// Encode the specified non-modifiable `object` to the specified
    /// `stream`.  Return 0 on success, and a non-zero value otherwise.
    /// Note that the encoder will use encoder options, error and warning
    /// streams specified at the construction time.
    template <class TYPE>
    int encodeToStream(bsl::ostream& stream, const TYPE& object);

    /// Encode the specified non-modifiable `object` to the specified
    /// `stream`.  Return a reference to `stream`.  If an encoding error is
    /// detected, `stream.fail()` will be true on return.  Note that the
    /// encoder will use encoder options, error and warning streams
    /// specified at the construction time.  IMPORTANT: The use of
    /// `stream.fail()` to communicate errors to the caller has two
    /// consequences: 1) if `stream` is the same as the `errorStream`
    /// passed to the constructor, then the error message may be suppressed
    /// (because of the output/error stream becoming invalidated) and 2) it
    /// is important to call `stream.clear()` after testing the stream
    /// state.  To avoid these issues, we recommend that you use use
    /// `encodeToStream`, above, instead of this version of `encode`.
    template <class TYPE>
    bsl::ostream& encode(bsl::ostream& stream, const TYPE& object);

    /// Encode the specified non-modifiable `object` to the specified
    /// `formatter`.  Return 0 on success, and a non-zero value otherwise.
    /// Note that encoder will use encoder options, error and warning
    /// streams specified at the construction time.
    template <class TYPE>
    int encode(Formatter& formatter, const TYPE& object);

    /// Encode the specified non-modifiable `object` to the specified `buffer`.
    /// Return 0 on success, and a non-zero value otherwise.  Note that the
    /// encoder will use encoder options, error and warning streams specified
    /// at the construction time.  Also note that this function behaves
    /// identically to `encode`, but does not instantiate any templates at
    /// compile time at the expense of being slightly slower at runtime; see
    /// the `balxml` package documentation for more details.
    template <class TYPE>
    int encodeAny(bsl::streambuf *buffer, const TYPE& object);
    int encodeAny(bsl::streambuf *buffer, const bdlar::AnyConstRef& object);

    /// Encode the specified non-modifiable `object` to the specified `stream`.
    /// Return 0 on success, and a non-zero value otherwise.  Note that the
    /// encoder will use encoder options, error and warning streams specified
    /// at the construction time.  Also note that this function behaves
    /// identically to `encodeToStream`, but does not instantiate any templates
    /// at compile time at the expense of being slightly slower at runtime; see
    /// the `balxml` package documentation for more details.
    template <class TYPE>
    int encodeAnyToStream(bsl::ostream& stream, const TYPE& object);
    int encodeAnyToStream(bsl::ostream&             stream,
                          const bdlar::AnyConstRef& object);

    /// Encode the specified non-modifiable `object` to the specified `stream`.
    /// Return a reference to `stream`.  If an encoding error is detected,
    /// `stream.fail()` will be true on return.  Note that the encoder will use
    /// encoder options, error and warning streams specified at the
    /// construction time.  Also note that this function behaves identically to
    /// `encode`, but does not instantiate any templates at compile time at the
    /// expense of being slightly slower at runtime; see the `balxml` package
    /// documentation for more details.
    template <class TYPE>
    bsl::ostream& encodeAny(bsl::ostream& stream, const TYPE& object);
    bsl::ostream& encodeAny(bsl::ostream&             stream,
                            const bdlar::AnyConstRef& object);

    /// Encode the specified non-modifiable `object` to the specified
    /// `formatter`.  Return 0 on success, and a non-zero value otherwise.
    /// Note that encoder will use encoder options, error and warning streams
    /// specified at the construction time.  Also note that this function
    /// behaves identically to `encode`, but does not instantiate any templates
    /// at compile time at the expense of being slightly slower at runtime; see
    /// the `balxml` package documentation for more details.
    template <class TYPE>
    int encodeAny(Formatter& formatter, const TYPE& object);
    int encodeAny(Formatter& formatter, const bdlar::AnyConstRef& object);

    //ACCESSORS

    /// Return the encoder options.
    const EncoderOptions *options() const;

    /// Return `true` if the encoding style in the encoder options is
    /// defined as `EncodingStyle::BAEXML_COMPACT`, and `false` otherwise.
    bool isCompact() const;

    /// Return pointer to the error stream.
    bsl::ostream *errorStream() const;

    /// Return pointer to the warning stream.
    bsl::ostream *warningStream() const;

    /// Return the severity of the most severe warning or error encountered
    /// during the last call to the `encode` method.  The severity is reset
    /// each time `encode` is called.
    ErrorInfo::Severity  errorSeverity() const;

    /// Return a string containing any error, warning, or trace messages
    /// that were logged during the last call to the `encode` method.  The
    /// log is reset each time `encode` is called.
    bslstl::StringRef loggedMessages() const;
};

// ---- Anything below this line is implementation specific.  Do not use.  ----

                           // ======================
                           // struct Encoder_Context
                           // ======================

/// This `struct` contains state that is maintained during encoding.  It
/// also contains methods for switching between pretty formatting and
/// compact formatting, based on the encoding options.
class Encoder_Context {

    // DATA
    Formatter  *d_formatter;
    Encoder    *d_encoder;

    // NOT IMPLEMENTED
    Encoder_Context(const Encoder_Context& other);
    Encoder_Context& operator=(const Encoder_Context& other);

  public:
    // CREATORS
    Encoder_Context(Formatter *formatter, Encoder *encoder);

    // MANIPULATORS
    template <class NAME_TYPE, class VALUE_TYPE>
    void addAttribute(const NAME_TYPE& name, const VALUE_TYPE& value);

    template<class NAME_TYPE, class VALUE_TYPE>
    void addAttribute(const NAME_TYPE&  name,
                      const VALUE_TYPE& value,
                      int               formattingMode);

    template <class NAME_TYPE>
    void closeElement(const NAME_TYPE& name);

    void invalidate();

    ErrorInfo::Severity logError(const char               *text,
                                 const bsl::string_view&   tag,
                                 int                       formattingMode,
                                 int                       index = -1);

    template <class NAME_TYPE>
    void openElement(const NAME_TYPE& name);

    bsl::ostream& rawOutputStream();

    // ACCESSORS
    const EncoderOptions& encoderOptions() const;

    int status() const;
};

                  // =======================================
                  // struct Encoder_OptionsCompatibilityUtil
                  // =======================================

/// Component-private `struct`.  Do not use.
///
/// This struct provides a namespace for a suite of functions used to
/// compute the options for the underlying XML formatter used by the
/// encoder, given the encoder's options.
struct Encoder_OptionsCompatibilityUtil {

  private:
    // PRIVATE CLASS METHODS

    /// Return the value of the `InitialIndentLevel` field for a
    /// `FormatterOptions` object that corresponds to the specified
    /// `encoderOptions`, which is 0 if the `EncodingStyle` of
    /// `encoderOptions` is `EncodingStyle::e_COMPACT`, and is the value of
    /// `InitialIndentLevel` of `encoderOptions` otherwise.
    static int getFormatterInitialIndentLevel(
                                         const EncoderOptions& encoderOptions);

    /// Return the value of the `SpacesPerLevel` field for a
    /// `FormatterOptions` object that corresponds to the specified
    /// `encoderOptions`, which is 0 if the `EncodingStyle` of
    /// `encoderOptions` is `EncodingStyle::e_COMPACT`, and is the value of
    /// `SpacesPerLevel` of `encoderOptions` otherwise.
    static int getFormatterSpacesPerLevel(
                                         const EncoderOptions& encoderOptions);

    /// Return the value of the `WrapColumn` field for a
    /// `FormatterOptions` object that corresponds to the specified
    /// `encoderOptions`, which is -1 if the `EncodingStyle` of
    /// `encoderOptions` is `EncodingStyle::e_COMPACT`, and is the value of
    /// `WrapColumn` of `encoderOptions` otherwise.
    static int getFormatterWrapColumn(const EncoderOptions& encoderOptions);

  public:
    // CLASS METHODS

    /// Load to the specified `formatterIndentLevel`,
    /// `formatterSpacesPerLevel`, and `formatterWrapColumn`, the number of
    /// spaces to indent the first element in the XML document, the number
    /// of spaces to use for indenting each level of nesting in the
    /// document, and the maximum horizontal column number after which the
    /// encoder should insert a line break, respectively, based on the
    /// specified `encoderOptions`.  Load to the specified
    /// `formatterOptions` the options that the formatter should use to emit
    /// XML, based on the `encoderOptions`.  The behavior is undefined
    /// unless `formatterOptions` has the default value.
    static void getFormatterOptions(
                                int                   *formatterIndentLevel,
                                int                   *formatterSpacesPerLevel,
                                int                   *formatterWrapColumn,
                                EncoderOptions        *formatterOptions,
                                const EncoderOptions&  encoderOptions);
};

                         // ==========================
                         // class Encoder_EncodeObject
                         // ==========================

/// Component-private class.  Do not use.
///
/// This struct encodes an object *with* enclosing tags.  Compared to the
/// `EncoderUtil_EncodeValue` class below, this class prefixes the value
/// with an opening tag, and suffixes the value with a closing tag.  In
/// pseudocode, this is equivalent to:
/// ```
/// openTag()
/// Encoder_EncodeValue()
/// closeTag()
/// ```
/// There is an overloaded version of `bsl::vector<char>` because, based on
/// the formatting mode, this class needs to switch between encoding the
/// value in a single tag (i.e., when using BASE64, TEXT, IS_LIST or HEX)
/// and encoding the value in multiple tags (i.e., when repetition is used).
class Encoder_EncodeObject {

    // PRIVATE TYPES
    struct CanBeListOrRepetition { };
    struct CanBeRepetitionOnly   { };

    // PRIVATE DATA MEMBERS
    Encoder_Context *d_context_p;

  public:
    // IMPLEMENTATION MANIPULATORS
    template <class TYPE>
    int executeImp(const TYPE&               object,
                   const bsl::string_view&   tag,
                   int                       formattingMode,
                   bool                      isMultiple,
                   bdlat_TypeCategory::Array);

    template <class TYPE>
    int executeImp(const TYPE&                       object,
                   const bsl::string_view&           tag,
                   int                               formattingMode,
                   bool                              isMultiple,
                   bdlat_TypeCategory::NullableValue);

    template <class TYPE>
    int executeImp(const TYPE&                     object,
                   const bsl::string_view&         tag,
                   int                             formattingMode,
                   bool                            isMultiple,
                   bdlat_TypeCategory::DynamicType);

    template <class TYPE, class ANY_CATEGORY>
    int executeImp(const TYPE&              object,
                   const bsl::string_view&  tag,
                   int                      formattingMode,
                   bool                     isMultiple,
                   ANY_CATEGORY);

    int executeImp(const bsl::vector<char>&  object,
                   const bsl::string_view&   tag,
                   int                       formattingMode,
                   bool                      isMultiple,
                   bdlat_TypeCategory::Array);

    template <class TYPE>
    int executeArrayListImp(const TYPE& object, const bsl::string_view& tag);

    template <class TYPE>
    int executeArrayRepetitionImp(const TYPE&              object,
                                  const bsl::string_view&  tag,
                                  int                      formattingMode);

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

  public:
    // CREATORS
    explicit Encoder_EncodeObject(Encoder_Context *context);

    // Using compiler generated destructor:
    //  ~Encoder_EncodeObject();

    // MANIPULATORS
    template <class TYPE, class INFO_TYPE>
    int operator()(const TYPE& object, const INFO_TYPE& info);

    template <class TYPE>
    int execute(const TYPE&              object,
                const bsl::string_view&  tag,
                int                      formattingMode,
                bool                     isMultiple);
};

                         // =========================
                         // class Encoder_EncodeValue
                         // =========================

/// Component-private class.  Do not use.
///
/// This class just encodes a value *without* any enclosing tags.
class Encoder_EncodeValue {

    // PRIVATE DATA MEMBERS
    Encoder_Context *d_context_p;

  public:
    // IMPLEMENTATION MANIPULATORS
    template <class TYPE>
    int executeImp(const TYPE&                  object,
                   int                          formattingMode,
                   bdlat_TypeCategory::Sequence);

    template <class TYPE>
    int executeImp(const TYPE&                object,
                   int                        formattingMode,
                   bdlat_TypeCategory::Choice);

    template <class TYPE>
    int executeImp(const TYPE&                     object,
                   int                             formattingMode,
                   bdlat_TypeCategory::DynamicType);

    template <class TYPE, class ANY_CATEGORY>
    int executeImp(const TYPE& object, int formattingMode, ANY_CATEGORY);

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

  public:
    // CREATORS
    explicit Encoder_EncodeValue(Encoder_Context *context);

    // Using compiler generated destructor:
    //  ~Encoder_EncodeValue();

    // MANIPULATORS
    template <class TYPE, class INFO_TYPE>
    int operator()(const TYPE& object, const INFO_TYPE& info);

    template <class TYPE>
    int execute(const TYPE& object, int formattingMode);
};

                      // ===============================
                      // class Encoder_SequenceFirstPass
                      // ===============================

/// Component private class.  Do not use.
///
/// This class is used as the first pass when encoding elements of a
/// sequence.  It basically does two things:
///     o encode elements with the
///       `bdlat_FormattingMode::e_IS_ATTRIBUTE` flag using the
///       `Formatter::addAttribute` method.
///     o looks for an element with the
///       `bdlat_FormattingMode::e_IS_SIMPLE_CONTENT` flag and, if
///       found, provides accessors to obtain the `id` of the element.
///       Note that the behavior is undefined unless there is only one
///       element with `IS_SIMPLE_CONTENT` flag and, if this element exist,
///       all other elements must have `IS_ATTRIBUTE` flag.
class Encoder_SequenceFirstPass {

    // PRIVATE DATA MEMBERS
    Encoder_Context          *d_context_p;        // held, not owned
    bool                      d_hasSubElements;   // true if an element with
                                                  // neither 'IS_ATTRIBUTE' nor
                                                  // 'IS_SIMPLE_CONTENT' is
                                                  // found
    bdlb::NullableValue<int>  d_simpleContentId;  // the 'id' of the element
                                                  // with 'IS_SIMPLE_CONTENT'
                                                  // flag, if found

  public:
    // IMPLEMENTATION MANIPULATORS

    /// Add an attribute with the specified `name`, the value of the
    /// specified `object`, using the specified `formattingMode`.  Note that
    /// the last argument is used for overloading purposes only.
    template <class TYPE>
    int addAttributeImp(const TYPE&                       object,
                        const bsl::string_view&           name,
                        int                               formattingMode,
                        bdlat_TypeCategory::NullableValue);
    template <class TYPE>
    int addAttributeImp(const TYPE&                     object,
                        const bsl::string_view&         name,
                        int                             formattingMode,
                        bdlat_TypeCategory::DynamicType);
    template <class TYPE, class ANY_CATEGORY>
    int addAttributeImp(const TYPE&              object,
                        const bsl::string_view&  name,
                        int                      formattingMode,
                        ANY_CATEGORY);

    /// Add an attribute with the specified `name`, the value of the
    /// specified `object`, using the specified `formattingMode`.
    template <class TYPE>
    int addAttribute(const TYPE&              object,
                     const bsl::string_view&  name,
                     int                      formattingMode);

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

  public:
    // CREATORS

    /// Create a visitor for first pass for sequences.
    explicit Encoder_SequenceFirstPass(Encoder_Context *context);

    // Generated by compiler:
    //  ~Encoder_SequenceFirstPass();

    // MANIPULATORS

    /// Called back when an element is visited.
    template <class TYPE, class INFO_TYPE>
    int operator()(const TYPE& object, const INFO_TYPE& info);

    // ACCESSORS

    /// Return true if a sub-element is found, and false otherwise.
    const bool& hasSubElements() const;

    /// Return a null value if there is no element with `IS_SIMPLE_CONTENT`
    /// flag, or a non-null value with the integer `id` of the element
    /// otherwise.
    const bdlb::NullableValue<int>& simpleContentId() const;
};

                      // ================================
                      // class Encoder_SequenceSecondPass
                      // ================================

/// Component-private class.  Do not use.
///
/// This class is used as the second pass when encoding elements of a
/// sequence.  It basically calls `EncoderUtil_EncodeObject` for elements
/// that do not have `IS_ATTRIBUTE` flag.  Note that the behavior is
/// undefined if there is an element with the `IS_SIMPLE_CONTENT` flag.
class Encoder_SequenceSecondPass {

    // DATA

    // functor used to encode sub-elements
    Encoder_EncodeObject d_encodeObjectFunctor;

    // NOT IMPLEMENTED
    Encoder_SequenceSecondPass(const Encoder_SequenceSecondPass&);
    Encoder_SequenceSecondPass& operator=(const Encoder_SequenceSecondPass&);

  public:
    // CREATORS

    /// Create a visitor for the second pass for sequences.
    explicit
    Encoder_SequenceSecondPass(Encoder_Context *context);

    // Generated by compiler:
    //  ~Encoder_SequenceSecondPass();

    // MANIPULATORS

    /// Called back when an element is visited.
    template <class TYPE, class INFO_TYPE>
    int operator()(const TYPE& object, const INFO_TYPE& info);
};

// ============================================================================
//                               PROXY CLASSES
// ============================================================================

                  // ========================================
                  // struct Encoder_EncodeObject_executeProxy
                  // ========================================

/// Component-private struct.  Do not use.
struct Encoder_EncodeObject_executeProxy {

    // DATA MEMBERS
    Encoder_EncodeObject    *d_instance_p;
    const bsl::string_view  *d_tag_p;
    int                      d_formattingMode;
    bool                     d_isMultiple; // = false

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // FUNCTIONS
    template <class TYPE>
    inline
    int operator()(const TYPE& object)
    {
        return d_instance_p->execute(object,
                                     *d_tag_p,
                                     d_formattingMode,
                                     d_isMultiple);
    }
};

                // ===========================================
                // struct Encoder_EncodeObject_executeImpProxy
                // ===========================================

/// Component-private struct.  Do not use.
struct Encoder_EncodeObject_executeImpProxy {

    // DATA
    Encoder_EncodeObject    *d_instance_p;
    const bsl::string_view  *d_tag_p;
    int                      d_formattingMode;
    bool                     d_isMultiple;

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // FUNCTIONS
    template <class TYPE>
    inline
    int operator()(const TYPE&, bslmf::Nil)
    {
        BSLS_ASSERT_SAFE(0);
        return -1;
    }

    template <class TYPE, class ANY_CATEGORY>
    inline
    int operator()(const TYPE& object, ANY_CATEGORY category)
    {
        return d_instance_p->executeImp(object,
                                        *d_tag_p,
                                        d_formattingMode,
                                        d_isMultiple,
                                        category);
    }
};

                 // ==========================================
                 // struct Encoder_EncodeValue_executeImpProxy
                 // ==========================================

/// Component-private struct.  Do not use.
struct Encoder_EncodeValue_executeImpProxy {

    // DATA MEMBERS
    Encoder_EncodeValue *d_instance_p;
    int                         d_formattingMode;

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // FUNCTIONS
    template <class TYPE>
    inline
    int operator()(const TYPE&, bslmf::Nil)
    {
        BSLS_ASSERT_SAFE(0);
        return -1;
    }

    template <class TYPE, class ANY_CATEGORY>
    inline
    int operator()(const TYPE& object, ANY_CATEGORY category)
    {
        return d_instance_p->executeImp(object, d_formattingMode, category);
    }
};

             // ==================================================
             // struct Encoder_SequenceFirstPass_addAttributeProxy
             // ==================================================

/// Component-private struct.  Do not use.
struct Encoder_SequenceFirstPass_addAttributeProxy {

    // DATA MEMBERS
    Encoder_SequenceFirstPass *d_instance_p;
    const bsl::string_view    *d_name_p;
    int                        d_formattingMode;

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // FUNCTIONS
    template <class TYPE>
    inline
    int operator()(const TYPE& object)
    {
        return d_instance_p->addAttribute(object, *d_name_p, d_formattingMode);
    }
};

           // =====================================================
           // struct Encoder_SequenceFirstPass_addAttributeImpProxy
           // =====================================================

/// Component-private struct.  Do not use.
struct Encoder_SequenceFirstPass_addAttributeImpProxy {

    // DATA MEMBERS
    Encoder_SequenceFirstPass *d_instance_p;
    const bsl::string_view    *d_name_p;
    int                        d_formattingMode;

    // CREATORS

    // Creators have been omitted to allow simple static initialization of this
    // struct.

    // FUNCTIONS
    template <class TYPE>
    inline
    int operator()(const TYPE&, bslmf::Nil)
    {
        BSLS_ASSERT_SAFE(0);
        return -1;
    }

    template <class TYPE, class ANY_CATEGORY>
    inline
    int operator()(const TYPE& object, ANY_CATEGORY category)
    {
        return d_instance_p->addAttributeImp(object,
                                             *d_name_p,
                                             d_formattingMode,
                                             category);
    }
};
}  // close package namespace

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

namespace balxml {

                       // ------------------------------
                       // class BerEncoder::MemOutStream
                       // ------------------------------

inline
Encoder::MemOutStream::MemOutStream(bslma::Allocator *basicAllocator)
: bsl::ostream(0)
, d_sb(bslma::Default::allocator(basicAllocator))
{
    rdbuf(&d_sb);
}

// MANIPULATORS
inline
void Encoder::MemOutStream::reset()
{
    d_sb.reset();
}

// ACCESSORS
inline
const char *Encoder::MemOutStream::data() const
{
    return d_sb.data();
}

inline
int Encoder::MemOutStream::length() const
{
    return (int)d_sb.length();
}

                               // -------------
                               // class Encoder
                               // -------------

inline
bool Encoder::isCompact() const
{
    return EncodingStyle::COMPACT == d_options->encodingStyle();
}

inline
const EncoderOptions *Encoder::options() const
{
    return d_options;
}

inline
bsl::ostream *Encoder::errorStream() const
{
    return d_errorStream;
}

inline
bsl::ostream *Encoder::warningStream() const
{
    return d_warningStream;
}

inline
ErrorInfo::Severity Encoder::errorSeverity() const
{
    return d_severity;
}

inline
bslstl::StringRef Encoder::loggedMessages() const
{
    if (d_logStream) {
        return bslstl::StringRef(d_logStream->data(), d_logStream->length());
                                                                      // RETURN
    }
    return bslstl::StringRef();
}

inline
bsl::ostream& Encoder::logStream()
{
    if (0 == d_logStream) {
        d_logStream = new(d_logArea.buffer()) MemOutStream(d_allocator);
    }
    return *d_logStream;
}

template <class TYPE>
inline
int Encoder::encode(bsl::streambuf *buffer, const TYPE& object)
{
    int indentLevel    = 0;
    int spacesPerLevel = 0;
    int wrapColumn     = 0;

    EncoderOptions formatterEncoderOptions;
    Encoder_OptionsCompatibilityUtil::getFormatterOptions(
        &indentLevel,
        &spacesPerLevel,
        &wrapColumn,
        &formatterEncoderOptions,
        *d_options);

    Formatter formatter(buffer,
                        formatterEncoderOptions,
                        indentLevel,
                        spacesPerLevel,
                        wrapColumn);

    const int rc = encode(formatter, object);

    buffer->pubsync();

    return rc;
}

template <class TYPE>
inline
int Encoder::encodeToStream(bsl::ostream& stream, const TYPE& object)
{
    return encode(stream.rdbuf(), object);
}

template <class TYPE>
inline
bsl::ostream& Encoder::encode(bsl::ostream& stream, const TYPE& object)
{
    int indentLevel    = 0;
    int spacesPerLevel = 0;
    int wrapColumn     = 0;

    EncoderOptions formatterEncoderOptions;
    Encoder_OptionsCompatibilityUtil::getFormatterOptions(
        &indentLevel,
        &spacesPerLevel,
        &wrapColumn,
        &formatterEncoderOptions,
        *d_options);

    Formatter formatter(stream,
                        formatterEncoderOptions,
                        indentLevel,
                        spacesPerLevel,
                        wrapColumn);

    encode(formatter, object);

    stream.flush();

    return stream;
}

template <class TYPE>
int Encoder::encode(Formatter& formatter, const TYPE& object)
{
    d_severity = ErrorInfo::e_NO_ERROR;
    if (d_logStream != 0) {
        d_logStream->reset();
    }

    Encoder_Context context(&formatter,this);

    if (d_options->outputXMLHeader()) {
        formatter.addHeader();
    }

    const char *tag = d_options->tag().empty()
                    ? bdlat_TypeName::xsdName(object,
                                              d_options->formattingMode())
                    : d_options->tag().c_str();

    context.openElement(tag);

    if (!d_options->objectNamespace().empty()) {

        context.addAttribute("xmlns", d_options->objectNamespace());

        if (d_options->outputXSIAlias()) {
            // Only declare the "xsi" namespace and schema location if an
            // object namespace was provided because only then can validation
            // happen.
            context.addAttribute("xmlns:xsi",
                                 "http://www.w3.org/2001/XMLSchema-instance");

            if (!d_options->schemaLocation().empty()) {
                context.addAttribute("xsi:schemaLocation",
                                     d_options->objectNamespace()
                                     + " "
                                     + d_options->schemaLocation());
            }
        }
    }
    else if (d_options->outputXSIAlias()) {
        context.addAttribute("xmlns:xsi",
                             "http://www.w3.org/2001/XMLSchema-instance");
    }

    Encoder_EncodeValue encodeValue(&context);

    int rc = 0;
    if (0 != encodeValue.execute(object,d_options->formattingMode())) {

        logError("Failed to encode", tag, d_options->formattingMode());

        context.invalidate();
        rc = -1;
    }
    else {
        context.closeElement(tag);
    }

    switch (d_severity) {
      case ErrorInfo::e_NO_ERROR: {
      } break;
      case ErrorInfo::e_WARNING: {
        if (d_warningStream) {
            *d_warningStream << loggedMessages();
        }
      } break;
      default: {
        if (d_errorStream) {
            *d_errorStream << loggedMessages();
        }
      } break;
    }
    return rc;
}

template <class TYPE>
inline
int Encoder::encodeAny(bsl::streambuf *streamBuf, const TYPE& object)
{
    return encodeAny(streamBuf, bdlar::RefUtil::makeAnyConstRef(object));
}

template <class TYPE>
inline
int Encoder::encodeAnyToStream(bsl::ostream& stream, const TYPE& object)
{
    return encodeAnyToStream(stream, bdlar::RefUtil::makeAnyConstRef(object));
}

inline
int Encoder::encodeAnyToStream(bsl::ostream&             stream,
                               const bdlar::AnyConstRef& object)
{
    return encodeAny(stream.rdbuf(), object);
}

template <class TYPE>
inline
bsl::ostream& Encoder::encodeAny(bsl::ostream& stream, const TYPE& object)
{
    return encodeAny(stream, bdlar::RefUtil::makeAnyConstRef(object));
}

template <class TYPE>
inline
int Encoder::encodeAny(Formatter& formatter, const TYPE& object)
{
    return encode(formatter, bdlar::RefUtil::makeAnyConstRef(object));
}

                           // ---------------------
                           // class Encoder_Context
                           // ---------------------

// MANIPULATORS
template <class NAME_TYPE, class VALUE_TYPE>
inline
void Encoder_Context::addAttribute(const NAME_TYPE&  name,
                                   const VALUE_TYPE& value)
{
    d_formatter->addAttribute(name,
                              value,
                              bdlat_FormattingMode::e_DEFAULT);
}

template <class NAME_TYPE, class VALUE_TYPE>
inline
void Encoder_Context::addAttribute(const NAME_TYPE&  name,
                                   const VALUE_TYPE& value,
                                   int               formattingMode)
{
    d_formatter->addAttribute(name, value, formattingMode);
}

template <class NAME_TYPE>
inline
void Encoder_Context::closeElement(const NAME_TYPE& name)
{
    d_formatter->closeElement(name);
}

inline
void Encoder_Context::invalidate()
{
    rawOutputStream().setstate(bsl::ios_base::failbit);
}

inline
ErrorInfo::Severity Encoder_Context::logError(
                                      const char               *text,
                                      const bsl::string_view&   tag,
                                      int                       formattingMode,
                                      int                       index)
{
    return d_encoder->logError(text, tag, formattingMode, index);
}

template <class NAME_TYPE>
inline
void Encoder_Context::openElement(const NAME_TYPE& name)
{
    d_formatter->openElement(name);
}

inline
bsl::ostream& Encoder_Context::rawOutputStream()
{
    return d_formatter->rawOutputStream();
}

// ACCESSORS
inline
const EncoderOptions& Encoder_Context::encoderOptions() const
{
    return *d_encoder->options();
}

inline
int Encoder_Context::status() const
{
    return d_formatter->status();
}

                         // --------------------------
                         // class Encoder_EncodeObject
                         // --------------------------

// IMPLEMENTATION MANIPULATORS
template <class TYPE>
inline
int Encoder_EncodeObject::executeImp(const TYPE&               object,
                                     const bsl::string_view&   tag,
                                     int                       formattingMode,
                                     bool                      ,
                                     bdlat_TypeCategory::Array)
{
    if (formattingMode & bdlat_FormattingMode::e_LIST) {
        return executeArrayListImp(object, tag);                      // RETURN
    }
    // else { return ... } removed, to prevent warning with gcc-4.1.1 (reach
    // end of non-void function), instead, have unconditional:

    return executeArrayRepetitionImp(object, tag, formattingMode);
}

template <class TYPE>
inline
int Encoder_EncodeObject::executeImp(
                              const TYPE&                       object,
                              const bsl::string_view&           tag,
                              int                               formattingMode,
                              bool                              isMultiple,
                              bdlat_TypeCategory::NullableValue)
{
    enum { k_SUCCESS = 0 };

    if (bdlat_NullableValueFunctions::isNull(object)) {
        if (formattingMode & bdlat_FormattingMode::e_NILLABLE) {
            if ((!d_context_p->encoderOptions().objectNamespace().empty() &&
                 d_context_p->encoderOptions().outputXSIAlias()) ||
                isMultiple) {
                // Add the "xsi:nil" attribute for array elements even when
                // `outputXSIAlias()` is false; otherwise the number of the
                // encoded array elements wiil be different.
                d_context_p->openElement(tag);
                d_context_p->addAttribute("xsi:nil", "true");
                d_context_p->closeElement(tag);
            }
        }

        return d_context_p->status();                                 // RETURN
    }

    Encoder_EncodeObject_executeProxy proxy = {
        this,
        &tag,
        formattingMode,
        false
    };

    return bdlat_NullableValueFunctions::accessValue(object, proxy);
}

template <class TYPE>
inline
int Encoder_EncodeObject::executeImp(
                                const TYPE&                     object,
                                const bsl::string_view&         tag,
                                int                             formattingMode,
                                bool                            isMultiple,
                                bdlat_TypeCategory::DynamicType)
{
    Encoder_EncodeObject_executeImpProxy proxy = {
        this,
        &tag,
        formattingMode,
        isMultiple
    };

    return bdlat_TypeCategoryUtil::accessByCategory(object, proxy);
}

template <class TYPE, class ANY_CATEGORY>
int Encoder_EncodeObject::executeImp(const TYPE&              object,
                                     const bsl::string_view&  tag,
                                     int                      formattingMode,
                                     bool                     ,
                                     ANY_CATEGORY)
{
    enum { k_FAILURE = -1 };

    bool isUntagged = formattingMode & bdlat_FormattingMode::e_UNTAGGED;

    if (!isUntagged) {
        d_context_p->openElement(tag);
    }

    Encoder_EncodeValue encodeValue(d_context_p);

    if (0 != encodeValue.execute(object, formattingMode)) {
        d_context_p->logError("Unable to encode value", tag, formattingMode);
        return k_FAILURE;                                             // RETURN
    }

    if (!isUntagged) {
        d_context_p->closeElement(tag);
    }

    int ret = d_context_p->status();

    if (ret) {
        d_context_p->logError("Formatter was invalidated for",
                              tag,
                              formattingMode);
    }

    return ret;
}

template <class TYPE>
int Encoder_EncodeObject::executeArrayListImp(const TYPE&              object,
                                              const bsl::string_view&  tag)
{
    d_context_p->openElement(tag);

    TypesPrintUtil::printList(d_context_p->rawOutputStream(),
                              object,
                              &d_context_p->encoderOptions());

    d_context_p->closeElement(tag);

    int ret = d_context_p->status();

    if (ret) {

        d_context_p->logError(
            "Error while encoding list for",
            tag,
            EncoderOptions::DEFAULT_INITIALIZER_FORMATTING_MODE);
    }

    return ret;
}

template <class TYPE>
int Encoder_EncodeObject::executeArrayRepetitionImp(
                                       const TYPE&              object,
                                       const bsl::string_view&  tag,
                                       int                      formattingMode)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    const int size = (int)bdlat_ArrayFunctions::size(object);

    Encoder_EncodeObject_executeProxy proxy =
                                          { this, &tag, formattingMode, true };

    for (int i = 0; i < size; ++i) {
        if (0 != bdlat_ArrayFunctions::accessElement(object, proxy, i)) {

            d_context_p->logError(
                "Error while encoding array element",
                tag,
                formattingMode,
                i);

            return k_FAILURE;                                         // RETURN
        }
    }

    return k_SUCCESS;
}

// CREATORS
inline
Encoder_EncodeObject::Encoder_EncodeObject(Encoder_Context *context)
: d_context_p(context)
{
    BSLS_ASSERT(d_context_p);
}

// MANIPULATORS
template <class TYPE, class INFO_TYPE>
inline
int Encoder_EncodeObject::operator()(const TYPE& object, const INFO_TYPE& info)
{
    bsl::string_view name(info.name(), info.nameLength());

    return execute(object, name, info.formattingMode(), false);
}

template <class TYPE>
inline
int Encoder_EncodeObject::execute(const TYPE&              object,
                                  const bsl::string_view&  tag,
                                  int                      formattingMode,
                                  bool                     isMultiple)
{
    typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;

    return executeImp(object, tag, formattingMode, isMultiple, TypeCategory());
}

                         // -------------------------
                         // class Encoder_EncodeValue
                         // -------------------------

// IMPLEMENTATION MANIPULATORS
template <class TYPE>
inline
int Encoder_EncodeValue::executeImp(
                                   const TYPE&                  object,
                                   int                          formattingMode,
                                   bdlat_TypeCategory::Sequence)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

#if defined(BSLS_ASSERT_SAFE_IS_ACTIVE)
    int type = formattingMode & bdlat_FormattingMode::e_TYPE_MASK;

    BSLS_ASSERT_SAFE(bdlat_FormattingMode::e_DEFAULT == type);
#else
    (void) formattingMode;
#endif

    Encoder_SequenceFirstPass firstPass(d_context_p);

    if (0 != bdlat_SequenceFunctions::accessAttributes(object, firstPass)) {
        return k_FAILURE;                                             // RETURN
    }

    if (!firstPass.simpleContentId().isNull()) {
        Encoder_EncodeValue encodeValue(d_context_p);

        return bdlat_SequenceFunctions::accessAttribute(
                                          object,
                                          encodeValue,
                                          firstPass.simpleContentId().value());
                                                                      // RETURN
    }

    if (firstPass.hasSubElements()) {
        Encoder_SequenceSecondPass secondPass(d_context_p);

        return bdlat_SequenceFunctions::accessAttributes(object, secondPass);
                                                                      // RETURN
    }

    return k_SUCCESS;
}

template <class TYPE>
inline
int Encoder_EncodeValue::executeImp(const TYPE&                object,
                                    int                        formattingMode,
                                    bdlat_TypeCategory::Choice)
{
    enum { k_FAILURE = -1 };

#if defined(BSLS_ASSERT_SAFE_IS_ACTIVE)
    int type = formattingMode & bdlat_FormattingMode::e_TYPE_MASK;

    BSLS_ASSERT_SAFE(bdlat_FormattingMode::e_DEFAULT == type);
#endif

    if (bdlat_ChoiceFunctions::k_UNDEFINED_SELECTION_ID
                               == bdlat_ChoiceFunctions::selectionId(object)) {

        d_context_p->logError("Undefined selection is not allowed ",
                              "???",
                              formattingMode);
        return k_FAILURE;                                             // RETURN
    }

    Encoder_EncodeObject encodeObject(d_context_p);

    return bdlat_ChoiceFunctions::accessSelection(object, encodeObject);
}

template <class TYPE>
inline
int Encoder_EncodeValue::executeImp(
                                const TYPE&                     object,
                                int                             formattingMode,
                                bdlat_TypeCategory::DynamicType)
{
    Encoder_EncodeValue_executeImpProxy proxy = { this, formattingMode };

    return bdlat_TypeCategoryUtil::accessByCategory(object, proxy);
}

template <class TYPE, class ANY_CATEGORY>
inline
int Encoder_EncodeValue::executeImp(const TYPE&  object,
                                    int          formattingMode,
                                    ANY_CATEGORY)
{
    TypesPrintUtil::print(d_context_p->rawOutputStream(),
                          object,
                          formattingMode,
                          &d_context_p->encoderOptions());

    return d_context_p->status();
}

// CREATORS
inline
Encoder_EncodeValue::Encoder_EncodeValue(Encoder_Context *context)
: d_context_p(context)
{
    BSLS_ASSERT(d_context_p);
}

// MANIPULATORS
template <class TYPE, class INFO_TYPE>
inline
int Encoder_EncodeValue::operator()(const TYPE& object, const INFO_TYPE& info)
{
    typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;

    return executeImp(object, info.formattingMode(), TypeCategory());
}

template <class TYPE>
inline
int Encoder_EncodeValue::execute(const TYPE& object, int formattingMode)
{
    typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;

    return executeImp(object, formattingMode, TypeCategory());
}

                      // -------------------------------
                      // class Encoder_SequenceFirstPass
                      // -------------------------------

// IMPLEMENTATION MANIPULATORS
template <class TYPE>
inline
int Encoder_SequenceFirstPass::addAttributeImp(
                              const TYPE&                       object,
                              const bsl::string_view&           name,
                              int                               formattingMode,
                              bdlat_TypeCategory::NullableValue)
{
    enum { k_SUCCESS = 0 };

    if (bdlat_NullableValueFunctions::isNull(object)) {
        return k_SUCCESS;                                             // RETURN
    }

    Encoder_SequenceFirstPass_addAttributeProxy proxy = {
        this,
        &name,
        formattingMode
    };

    return bdlat_NullableValueFunctions::accessValue(object, proxy);
}

template <class TYPE>
inline
int Encoder_SequenceFirstPass::addAttributeImp(
                                const TYPE&                     object,
                                const bsl::string_view&         name,
                                int                             formattingMode,
                                bdlat_TypeCategory::DynamicType)
{
    Encoder_SequenceFirstPass_addAttributeImpProxy proxy = {
        this,
        &name,
        formattingMode
    };

    return bdlat_TypeCategoryUtil::accessByCategory(object, proxy);

}

template <class TYPE, class ANY_CATEGORY>
inline
int Encoder_SequenceFirstPass::addAttributeImp(
                                       const TYPE&              object,
                                       const bsl::string_view&  name,
                                       int                      formattingMode,
                                       ANY_CATEGORY)
{
    d_context_p->addAttribute(name, object, formattingMode);

    int ret = d_context_p->status();

    if (ret) {
        d_context_p->logError("Failed to encode attribute",
                              name,
                              formattingMode);
    }

    return ret;
}

template <class TYPE>
inline
int Encoder_SequenceFirstPass::addAttribute(
                                       const TYPE&              object,
                                       const bsl::string_view&  name,
                                       int                      formattingMode)
{
    typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;

    return addAttributeImp(object, name, formattingMode, TypeCategory());
}

// CREATORS
inline
Encoder_SequenceFirstPass::Encoder_SequenceFirstPass(Encoder_Context *context)
: d_context_p(context)
, d_hasSubElements(false)
{
    BSLS_ASSERT(d_context_p);
    BSLS_ASSERT(d_simpleContentId.isNull());

    // {DRQS 153551134<GO>}: gcc can occasionally mis-diagnose
    // 'd_simpleContentId' as uninitialized.  This workaround avoids that
    // problem (which can cause build failures if '-Wmaybe-uninitialized' and
    // '-Werror' are set).  See also {DRQS 75130685<GO>} and {DRQS
    // 115347303<GO>}.
    d_simpleContentId.makeValue(0);
    d_simpleContentId.reset();
}

// MANIPULATORS
template <class TYPE, class INFO_TYPE>
int Encoder_SequenceFirstPass::operator()(const TYPE&      object,
                                          const INFO_TYPE& info)
{
    enum { k_SUCCESS = 0 };

    int  formattingMode  = info.formattingMode();
    bool isSimpleContent = formattingMode
                         & bdlat_FormattingMode::e_SIMPLE_CONTENT;
    bool isAttribute     = formattingMode & bdlat_FormattingMode::e_ATTRIBUTE;

    if (isSimpleContent) {
        BSLS_ASSERT(!isAttribute);
        BSLS_ASSERT(!d_hasSubElements);
        BSLS_ASSERT(d_simpleContentId.isNull());

        d_simpleContentId.makeValue(info.id());
    }
    else if (isAttribute) {
        bsl::string_view name(info.name(), info.nameLength());

        return addAttribute(object, name, formattingMode);            // RETURN
    }
    else {
        BSLS_ASSERT(d_simpleContentId.isNull());

        d_hasSubElements = true;
    }

    return k_SUCCESS;
}

// ACCESSORS
inline
const bool& Encoder_SequenceFirstPass::hasSubElements() const
{
    return d_hasSubElements;
}

inline
const bdlb::NullableValue<int>&
Encoder_SequenceFirstPass::simpleContentId() const
{
    return d_simpleContentId;
}

                      // --------------------------------
                      // class Encoder_SequenceSecondPass
                      // --------------------------------

// CREATORS
inline
Encoder_SequenceSecondPass::Encoder_SequenceSecondPass(
                                                      Encoder_Context* context)
: d_encodeObjectFunctor(context)
{
}

// MANIPULATORS
template <class TYPE, class INFO_TYPE>
int Encoder_SequenceSecondPass::operator()(const TYPE&      object,
                                           const INFO_TYPE& info)
{
    enum { k_SUCCESS = 0 };

    int formattingMode = info.formattingMode();

    BSLS_ASSERT(
               !(formattingMode & bdlat_FormattingMode::e_SIMPLE_CONTENT));

    if (!(formattingMode & bdlat_FormattingMode::e_ATTRIBUTE)) {
        return d_encodeObjectFunctor(object, info);                   // RETURN
    }

    return k_SUCCESS;
}

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

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 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 ----------------------------------
