// balxml_decoder.h                                                   -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BALXML_DECODER
#define INCLUDED_BALXML_DECODER

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

//@PURPOSE: Provide a generic translation from XML into C++ objects.
//
//@CLASSES:
//   balxml::Decoder: an XML decoder
//
//@SEE_ALSO: balxml_decoderoptions, balxml_encoder, balber_berdecoder
//
//@DESCRIPTION: This component provides a class 'balxml::Decoder' for decoding
// value-semantic objects in XML format.  The 'decode' methods are function
// templates that will decode any object that meets the requirements of a
// sequence or choice object as defined in the 'bdlat_sequencefunctions' and
// 'bdlat_choicefunctions' components.  These generic frameworks provide a
// common compile-time interface for manipulating struct-like and union-like
// objects.
//
// There are two usage models for using 'balxml::Decoder'.  The common case,
// when the type of object being decoded is known in advance, involves calling
// one of a set of 'decode' method templates that decode a specified
// value-semantic object from a specified stream or other input source.  The
// caller may specify the input for 'decode' as a file, an 'bsl::istream', an
// 'bsl::streambuf', or a memory buffer.
//
// A less common but more flexible usage model involves calling the 'open' to
// open the XML document from the specified input, then calling 'decode' to
// decode to an object without specifying the input source, and finally
// calling 'close' to close the input source.  The 'open' method positions the
// internal reader to the root element node, so the caller can examine the
// root element, decide what type of object is contained in the input
// stream/source, and construct an object of the needed type before calling
// 'decode' to read from the already open input source.  Thus the input data
// is not constrained to a single root element type.
//
// Although the XML format is very useful for debugging and for conforming to
// external data-interchange specifications, it is relatively expensive to
// encode and decode and relatively bulky to transmit.  It is more efficient
// to use a binary encoding (such as BER) if the encoding format is under your
// control.  (See 'balber_berdecoder'.)
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Generating Code from a Schema
/// - - - - - - - - - - - - - - - - - - - -
// Suppose we have the following XML schema inside a file called
// '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:element name='Address' type='test:Address'/>
//      <xs:element name='Employee' type='test:Employee'/>
//
//  </xs:schema>
//..
// Using the 'bas_codegen.pl' tool, we can generate C++ classes for this
// schema:
//..
//  $ bas_codegen.pl -m msg -p test -E xsdfile.xsd
//..
// This tool will generate the header and implementation files for the
// 'test_address' and 'test_employee' components in the current directory.
//
// The following function decodes an XML string into a 'test::Employee' object
// and verifies the results:
//..
//  #include <test_employee.h>
//  #include <balxml_decoder.h>
//  #include <balxml_decoderoptions.h>
//  #include <balxml_errorinfo.h>
//  #include <balxml_minireader.h>
//  #include <bsl_sstream.h>
//
//  using namespace BloombergLP;
//
//  int main()
//  {
//      const char INPUT[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
//                           "<Employee>\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::stringstream ss(INPUT);
//
//      test::Employee bob;
//
//      balxml::DecoderOptions options;
//      balxml::MiniReader     reader;
//      balxml::ErrorInfo      errInfo;
//
//      balxml::Decoder decoder(&options, &reader, &errInfo);
//
//      decoder.decode(ss, &bob);
//
//      assert(ss);
//      assert("Bob"         == bob.name());
//      assert("Some Street" == bob.homeAddress().street());
//      assert("Some City"   == bob.homeAddress().city());
//      assert("Some State"  == bob.homeAddress().state());
//      assert(21            == bob.age());
//
//      return 0;
//  }
//..
//
///Example 2: Error and Warning Streams
/// - - - - - - - - - - - - - - - - - -
// The following snippets of code illustrate how to pass an error stream and
// warning stream to the 'decode' function.  We will use the same
// 'test_employee' component from the previous usage example.  Note that the
// input XML string contains an error.  (The 'homeAddress' object has an
// element called 'country', which does not exist in the schema.):
//..
//  #include <test_employee.h>
//  #include <balxml_decoder.h>
//  #include <balxml_decoderoptions.h>
//  #include <balxml_errorinfo.h>
//  #include <balxml_minireader.h>
//  #include <bsl_sstream.h>
//
//  using namespace BloombergLP;
//
//  int main()
//  {
//      const char INPUT[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
//                           "<Employee>\n"
//                           "    <name>Bob</name>\n"
//                           "    <homeAddress>\n"
//                           "        <street>Some Street</street>\n"
//                           "        <city>Some City</city>\n"
//                           "        <state>Some State</state>\n"
//                           "        <country>Some Country</country>\n"
//                           "    </homeAddress>\n"
//                           "    <age>21</age>\n"
//                           "</Employee>\n";
//
//      bsl::stringstream ss(INPUT);
//
//      test::Employee bob;
//
//      balxml::DecoderOptions options;
//      balxml::MiniReader     reader;
//      balxml::ErrorInfo      errInfo;
//
//      options.setSkipUnknownElements(false);
//      balxml::Decoder decoder(&options, &reader, &errInfo,
//                              &bsl::cerr, &bsl::cerr);
//      decoder.decode(ss, &bob);
//
//      assert(!ss);
//
//      return 0;
//  }
//..
// Note that the input stream is invalidated to indicate that an error
// occurred.  Also note that the following error message will be printed on
// 'bsl::cerr':
//..
//  employee.xml:8.18: Error: Unable to decode sub-element 'country'.\n"
//  employee.xml:8.18: Error: Unable to decode sub-element 'homeAddress'.\n";
//..
// The following snippets of code illustrate how to open decoder and read the
// first node before calling 'decode':
//..
//  int main()
//  {
//      const char INPUT[] =
//          "<?xml version='1.0' encoding='UTF-8' ?>\n"
//          "<Employee xmlns='http://www.bde.com/bdem_test'>\n"
//          "    <name>Bob</name>\n"
//          "    <homeAddress>\n"
//          "        <street>Some Street</street>\n"
//          "        <state>Some State</state>\n"
//          "        <city>Some City</city>\n"
//          "        <country>Some Country</country>\n"
//          "    </homeAddress>\n"
//          "    <age>21</age>\n"
//          "</Employee>\n";
//
//      balxml::MiniReader     reader;
//      balxml::ErrorInfo      errInfo;
//      balxml::DecoderOptions options;
//
//      balxml::Decoder decoder(&options, &reader, &errInfo,
//                              &bsl::cerr, &bsl::cerr);
//
//..
// Now we open the document, but we don't begin decoding yet:
//..
//      int rc = decoder.open(INPUT, sizeof(INPUT) - 1);
//      assert(0 == rc);
//..
// Depending on the value of the first node, we can now determine whether the
// document is an 'Address' object or an 'Employee' object, and construct the
// target object accordingly:
//..
//      if (0 == bsl::strcmp(reader.nodeLocalName(), "Address")) {
//          test::Address addr;
//          rc = decoder.decode(&addr);
//          bsl::cout << addr;
//      }
//      else {
//          test::Employee bob;
//          rc = decoder.decode(&bob);
//          bsl::cout << bob;
//      }
//
//      assert(0 == rc);
//..
// When decoding is complete, we must close the decoder object:
//..
//      decoder.close();
//      return 0;
//  }
//..

#include <balscm_version.h>

#include <balxml_base64parser.h>
#include <balxml_decoderoptions.h>
#include <balxml_hexparser.h>
#include <balxml_listparser.h>
#include <balxml_typesparserutil.h>
#include <balxml_errorinfo.h>
#include <balxml_reader.h>
#include <balxml_utf8readerwrapper.h>

#include <bdlat_arrayfunctions.h>
#include <bdlat_choicefunctions.h>
#include <bdlat_customizedtypefunctions.h>
#include <bdlat_formattingmode.h>
#include <bdlat_nullablevaluefunctions.h>
#include <bdlat_sequencefunctions.h>
#include <bdlat_typecategory.h>
#include <bdlat_valuetypefunctions.h>

#include <bdlb_string.h>

#include <bdlsb_memoutstreambuf.h>

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

#include <bslmf_conditional.h>

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

#include <bsl_algorithm.h>            // bsl::min
#include <bsl_istream.h>
#include <bsl_map.h>
#include <bsl_ostream.h>
#include <bsl_streambuf.h>
#include <bsl_string.h>
#include <bsl_vector.h>
#include <bsl_cstring.h>
#include <bsl_cstdlib.h>
#include <bsl_cerrno.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslmf_if.h>
#endif  // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace balxml {

class Reader;
class ErrorInfo;
class Decoder;

                        // ============================
                        // class Decoder_ElementContext
                        // ============================

class Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This protocol class contain functions related to parsing XML elements.
    // When the Decoder reads the XML document, it forwards the information
    // about the current node as events to this protocol.  There are several
    // implementations of this protocol, depending on the type of element.  The
    // correct implementation for each type is selected by the
    // 'Decoder_SelectContext' meta-function.  Each of the functions take a
    // 'context' parameter, which contains members related to the context of
    // the decoder.

  public:
    virtual ~Decoder_ElementContext();
        // For syntactic purposes only.

    // CALLBACKS
    virtual int startElement(Decoder *decoder) = 0;

    virtual int endElement(Decoder *decoder) = 0;

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder) = 0;

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder) = 0;

    virtual int parseSubElement(const char *elementName, Decoder *decoder) = 0;

    int beginParse(Decoder *decoder);
};

                               // =============
                               // class Decoder
                               // =============

class Decoder {
    // Engine for decoding value-semantic objects in XML format.  The 'decode'
    // methods are function templates that will decode any object that meets
    // the requirements of a sequence or choice object as defined in the
    // 'bdlat_sequencefunctions' and 'bdlat_choicefunctions' components.
    // These generic frameworks provide a common compile-time interface for
    // manipulating struct-like and union-like objects.

    friend class  Decoder_ElementContext;
    friend struct Decoder_decodeImpProxy;
    friend class  Decoder_ErrorLogger;

    // PRIVATE TYPES
    class MemOutStream : public bsl::ostream {
        // 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.
        bdlsb::MemOutStreamBuf d_sb;

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

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

        virtual ~MemOutStream();
            // Destroy this stream and release memory back to the allocator.

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

        // ACCESSORS
        const char *data() const;
            // 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.

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

    // DATA
    const DecoderOptions            *d_options;        // held, not owned
    Utf8ReaderWrapper                d_utf8ReaderWrapper;
    Reader                          *d_reader;         // held, not owned
    ErrorInfo                       *d_errorInfo;      // held, not owned

    bslma::Allocator                *d_allocator;      // held, not owned

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

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

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

    bsl::string                      d_sourceUri;      // URI of input document
    int                              d_errorCount;     // error count
    int                              d_warningCount;   // warning count

    int                              d_numUnknownElementsSkipped;
                                                       // number of unknown
                                                       // elements skipped

    bool                             d_fatalError;     // fatal error flag
    int                              d_remainingDepth;
        // remaining number of nesting levels allowed

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

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

    void resetErrors();
    int  checkForReaderErrors();
    int  checkForErrors(const ErrorInfo& errInfo);

    void setDecoderError(ErrorInfo::Severity severity, bsl::string_view msg);

    int  readTopElement();
    int  parse(Decoder_ElementContext *context);

    template <class TYPE>
    int decodeImp(TYPE *object, bdlat_TypeCategory::DynamicType);

    template <class TYPE, class ANY_CATEGORY>
    int decodeImp(TYPE *object, ANY_CATEGORY);

  public:
    // CREATORS
    Decoder(const DecoderOptions *options,
            Reader               *reader,
            ErrorInfo            *errInfo,
            bslma::Allocator     *basicAllocator);

    Decoder(const DecoderOptions *options,
            Reader               *reader,
            ErrorInfo            *errInfo = 0,
            bsl::ostream         *errorStream = 0,
            bsl::ostream         *warningStream = 0,
            bslma::Allocator     *basicAllocator = 0);
        // Construct a decoder object using the specified 'options' and the
        // specified 'reader' to perform the XML-level parsing.  If the
        // (optionally) specified 'errorInfo' is non-null, it is used to store
        // information about most serious error encountered during parsing.
        // During parsing, error and warning messages will be written to the
        // (optionally) specified 'errorStream' and 'warningStream'
        // respectively.  The behavior is undefined unless 'options' and
        // 'reader' are both non-zero.  The behavior becomes undefined if the
        // objects pointed to by any of the arguments is destroyed before this
        // object has completed parsing.

    ~Decoder();
        // Call 'close' and destroy this object.

    // MANIPULATORS
    void close();
        // Put the associated 'Reader' object (i.e., the 'reader' specified at
        // construction) into a closed state.

    int open(bsl::istream& stream, const char *uri = 0);
        // Open the associated 'Reader' object (see 'Reader::open') to read XML
        // data from the specified 'stream'.  The (optionally) specified 'uri'
        // is used for identifying the input document in error messages.
        // Return 0 on success and non-zero otherwise.

    int open(bsl::streambuf *buffer, const char *uri = 0);
        // Open the associated 'Reader' object (see 'Reader::open') to read XML
        // data from the specified stream 'buffer'.  The (optionally) specified
        // 'uri' is used for identifying the input document in error messages.
        // Return 0 on success and non-zero otherwise.

    int open(const char *buffer, bsl::size_t length, const char *uri = 0);
        // Open the associated 'Reader' object (see 'Reader::open') to read XML
        // data from memory at the specified 'buffer', with the specified
        // 'length'.  The (optionally) specified 'uri' is used for identifying
        // the input document in error messages.  Return 0 on success and
        // non-zero otherwise.

    int open(const char *filename);
        // Open the associated 'Reader' object (see 'Reader::open') to read XML
        // data from the file with the specified 'filename'.  Return 0 on
        // success and non-zero otherwise.

    template <class TYPE>
    bsl::istream& decode(bsl::istream&  stream,
                         TYPE          *object,
                         const char    *uri = 0);
        // Decode the specified 'object' of parameterized 'TYPE' from the
        // specified input 'stream'.  Return a reference to the modifiable
        // 'stream'.  If a decoding error is detected, 'stream.fail()' will be
        // 'true' after this method returns.  The (optionally) specified 'uri'
        // is used for identifying the input document in error messages.  A
        // compilation error will result unless 'TYPE' conforms to the
        // requirements of a 'bdlat' sequence or choice, as described in
        // 'bdlat_sequencefunctions' and 'bdlat_choicefunctions'.

    template <class TYPE>
    int decode(bsl::streambuf *buffer, TYPE *object, const char *uri = 0);
        // Decode the specified 'object' of parameterized 'TYPE' from the
        // specified stream 'buffer'.  The (optionally) specified 'uri' is
        // used for identifying the input document in error messages.  Return
        // 0 on success, and a non-zero value otherwise.  A compilation error
        // will result unless 'TYPE' conforms to the requirements of a bdlat
        // sequence or choice, as described in 'bdlat_sequencefunctions' and
        // 'bdlat_choicefunctions'.

    template <class TYPE>
    int decode(const char  *buffer,
               bsl::size_t  length,
               TYPE        *object,
               const char  *uri = 0);
        // Decode the specified 'object' of parameterized 'TYPE' from the
        // memory at the specified 'buffer' address, having the specified
        // 'length'.  The (optionally) specified 'uri' is used for identifying
        // the input document in error messages.  Return 0 on success, and a
        // non-zero value otherwise.  A compilation error will result unless
        // 'TYPE' conforms to the requirements of a bdlat sequence or choice,
        // as described in 'bdlat_sequencefunctions' and
        // 'bdlat_choicefunctions'.

    template <class TYPE>
    int decode(const char *filename, TYPE *object);
        // Decode the specified 'object' of parameterized 'TYPE' from the file
        // with the specified 'filename'.  Return 0 on success, and a non-zero
        // value otherwise.  A compilation error will result unless 'TYPE'
        // conforms to the requirements of a bdlat sequence or choice, as
        // described in 'bdlat_sequencefunctions' and 'bdlat_choicefunctions'.

    template <class TYPE>
    int decode(TYPE *object);
        // Decode the specified 'object' of parameterized 'TYPE' from the
        // input source specified by a previous call to 'open' and leave the
        // reader in an open state.  Return 0 on success, and a non-zero value
        // otherwise.  A compilation error will result unless 'TYPE' conforms
        // to the requirements of a bdlat sequence or choice, as described in
        // 'bdlat_sequencefunctions' and 'bdlat_choicefunctions'.  The
        // behavior is undefined unless this call was preceded by a prior
        // successful call to 'open'

    void setNumUnknownElementsSkipped(int value);
        // Set the number of unknown elements skipped by the decoder during
        // the current decoding operation to the specified 'value'.  The
        // behavior is undefined unless '0 <= value'.

    //ACCESSORS
    const DecoderOptions *options() const;
        // Return a pointer to the non-modifiable decoder options provided at
        // construction.

    Reader *reader() const;
        // Return the a pointer to the modifiable reader associated with this
        // decoder (i.e., the 'reader' pointer provided at construction).

    ErrorInfo *errorInfo() const;
        // Return a pointer to the modifiable error-reporting structure
        // associated with this decoder (i.e., the 'errInfo' pointer provided
        // at construction).  The value stored in the error structure is reset
        // to indicate no error on a successful call to 'open'.

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

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

    int numUnknownElementsSkipped() const;
        // Return the number of unknown elements that were skipped during the
        // previous decoding operation.  Note that unknown elements are skipped
        // only if 'true == options()->skipUnknownElements()'.

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

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

    int  errorCount() const;
        // Return the number of errors that occurred during decoding.  This
        // number is reset to zero on a call to 'open'.

    int  warningCount() const;
        // Return the number of warnings that occurred during decoding.  This
        // number is reset to zero on a call to 'open'.
};

                         // =========================
                         // class Decoder_ErrorLogger
                         // =========================

class Decoder_ErrorLogger {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This class is used for logging errors and warnings.  The usage of this
    // class is simplified with macros, which are defined below.

    // DATA
    Decoder::MemOutStream   d_stream;
    ErrorInfo::Severity     d_severity;    // severity
    Decoder                *d_decoder;     // context

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

  public:
    // CREATORS
    Decoder_ErrorLogger(ErrorInfo::Severity severity, Decoder *decoder)
        // Construct a logger for the specified 'decoder'.
    : d_stream(decoder->d_allocator)
    , d_severity(severity)
    , d_decoder(decoder)
    {
    }

    ~Decoder_ErrorLogger()
        // Set the decoder's error message to the contents of the message
        // stream.
    {
        d_decoder->setDecoderError(d_severity,
                                   bsl::string_view(d_stream.data(),
                                                    d_stream.length()));
    }

    bsl::ostream& stream()
    {
        return d_stream;
    }
};
}  // close package namespace

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

// LOGGING MACROS
#define BALXML_DECODER_LOG_ERROR(reporter)                     \
    do {                                                       \
        balxml::Decoder_ErrorLogger                             \
            logger(balxml::ErrorInfo::e_ERROR, reporter);  \
        logger.stream()
    // Usage: BAEXML_LOG_ERROR(myDecoder) << "Message"
    //                                    << value << BALXML_DECODER_LOG_END;

#define BALXML_DECODER_LOG_WARNING(reporter)                   \
    do {                                                       \
        balxml::Decoder_ErrorLogger                             \
           logger(balxml::ErrorInfo::e_WARNING, reporter); \
        logger.stream()
    // Usage: BAEXML_LOG_WARNING(myDecoder) << "Message"
    //                                      << value << BALXML_DECODER_LOG_END;

#define BALXML_DECODER_LOG_END     \
        bsl::flush;                \
    } while (false)
    // See usage of BALXML_DECODER_LOG_ERROR and BALXML_DECODER_LOG_WARNING,
    // above.

// FORWARD DECLARATIONS


namespace balxml {

class Decoder_ElementContext;

template <class TYPE>
class Decoder_ChoiceContext;
template <class TYPE>
class Decoder_CustomizedContext;
template <class TYPE, class PARSER>
class Decoder_PushParserContext;
template <class TYPE>
class Decoder_SequenceContext;
template <class TYPE>
class Decoder_SimpleContext;
template <class TYPE>
class Decoder_UTF8Context;

class Decoder_UnknownElementContext;

class Decoder_StdStringContext;      // proxy context
class Decoder_StdVectorCharContext;  // proxy context

                       // ==============================
                       // class Decoder_ListParser<TYPE>
                       // ==============================

template <class TYPE>
class Decoder_ListParser {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This is a wrapper around 'ListParser<TYPE>'.  The
    // 'Decoder_PushParserContext' class needs a default constructible push
    // parser.  However, 'ListParser<TYPE>' is not default constructible - it
    // requires an 'parseElementCallback'.  This wrapper provides a default
    // constructor that passes 'TypesParserUtil::parseDefault' as the callback.

    // PRIVATE TYPES
    typedef typename
    ListParser<TYPE>::ParseElementFunction ParseElementFunction;
    typedef typename
    ListParser<TYPE>::ParseElementCallback ParseElementCallback;

    // DATA
    ListParser<TYPE> d_imp;  // implementation object

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

    // COMPILER BUG WORKAROUNDS
    static ParseElementCallback convert(ParseElementFunction func)
        // This function is provided to work around a bug in the AIX compiler.
        // It incorrectly complains that the following constructor initializer
        // list for 'd_imp' is invalid:
        //..
        // : d_imp((ParseElementFunction)&TypesParserUtil::parseDefault)
        //..
        // The error message generated by the AIX compiler is:
        //..
        // An object of type "BloombergLP::balxml::ListParser<TYPE>" cannot be
        // constructed from an rvalue of type "ParseElementFunction".
        //..
        // To work around this, an explicit 'convert' function is used to aid
        // the conversion.
    {
        ParseElementCallback temp(func);
        return temp;
    }

  public:
    // CREATORS
    Decoder_ListParser()
    : d_imp(convert(&TypesParserUtil::parseDefault))
    {
    }

    // Using destructor generated by compiler:
    //  ~Decoder_ListParser();

    // MANIPULATORS
    int beginParse(TYPE *object)
    {
        return d_imp.beginParse(object);
    }

    int endParse()
    {
        return d_imp.endParse();
    }

    template <class INPUT_ITERATOR>
    int pushCharacters(INPUT_ITERATOR begin, INPUT_ITERATOR end)
    {
        return d_imp.pushCharacters(begin, end);
    }
};
}  // close package namespace

              // ===============================================
              // struct balxml::Decoder_InstantiateContext<TYPE>
              // ===============================================

namespace balxml {

template <class CATEGORY, class TYPE>
struct Decoder_InstantiateContext;
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This 'struct' instantiates a context for the parameterized 'TYPE' that
    // falls under the parameterized 'CATEGORY'.

template <class TYPE>
struct Decoder_InstantiateContext<bdlat_TypeCategory::Array, TYPE>
{
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    typedef Decoder_PushParserContext<TYPE, Decoder_ListParser<TYPE> > Type;
};

template <>
struct Decoder_InstantiateContext<bdlat_TypeCategory::Array,
                                      bsl::vector<char> >
{
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    typedef Decoder_StdVectorCharContext Type;
};

template <class TYPE>
struct Decoder_InstantiateContext<bdlat_TypeCategory::Choice, TYPE>
{
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    typedef Decoder_ChoiceContext<TYPE> Type;
};

template <class TYPE>
struct Decoder_InstantiateContext<bdlat_TypeCategory::Sequence, TYPE>
{
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    typedef Decoder_SequenceContext<TYPE> Type;
};

template <class TYPE>
struct Decoder_InstantiateContext<bdlat_TypeCategory::Simple, TYPE>
{
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    typedef Decoder_SimpleContext<TYPE> Type;
};

template <>
struct
Decoder_InstantiateContext<bdlat_TypeCategory::Simple, bsl::string>
{
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    typedef Decoder_StdStringContext Type;
};

template <class TYPE>
struct
Decoder_InstantiateContext<bdlat_TypeCategory::CustomizedType, TYPE>
{
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Note: Customized are treated as simple types (i.e., they are parsed by
    // 'TypesParserUtil').
    typedef Decoder_CustomizedContext<TYPE> Type;
};

template <class TYPE>
struct Decoder_InstantiateContext<bdlat_TypeCategory::Enumeration, TYPE>
{
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Note: Enums are treated as simple types (i.e., they are parsed by
    // 'TypesParserUtil').

    typedef Decoder_SimpleContext<TYPE> Type;
};

                     // ==================================
                     // struct Decoder_SelectContext<TYPE>
                     // ==================================

template <class TYPE>
struct Decoder_SelectContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This meta-function is used to select a context for the parameterized
    // 'TYPE'.

  private:
    typedef typename
    bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;

  public:
    typedef typename
    Decoder_InstantiateContext<TypeCategory, TYPE>::Type Type;
};

                     // =================================
                     // class Decoder_ChoiceContext<TYPE>
                     // =================================

template <class TYPE>
class Decoder_ChoiceContext :  public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This is the context for types that fall under
    // 'bdlat_TypeCategory::Choice'.

    // DATA
    bool         d_isSelectionNameKnown;
    TYPE        *d_object_p;
    bool         d_selectionIsRepeatable;
    bsl::string  d_selectionName;

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

  public:
    // CREATORS
    Decoder_ChoiceContext(TYPE *object, int formattingMode);

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

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                       // =============================
                       // class Decoder_NillableContext
                       // =============================

class Decoder_NillableContext : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Context for elements that have 'bdlat_FormattingMode::e_NILLABLE'.  It
    // acts as a proxy and forwards all callbacks to the held
    // 'd_elementContext_p'.  If 'endElement' is called directly after
    // 'startElement', then the 'isNil()' accessor will return true.

    // DATA
    Decoder_ElementContext *d_elementContext_p;
    bool                    d_isNil;

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

  public:
    // CREATORS
    Decoder_NillableContext();

    ~Decoder_NillableContext();

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);

    // MANIPULATORS
    void setElementContext(Decoder_ElementContext *elementContext);
        // Set the element context to the specified 'elementContext'.  The
        // behavior of all methods in this class are undefined if this method
        // has not been called.

    // ACCESSORS
    bool isNil() const;
        // Return 'true' if the element is nil.
};

             // ====================================================
             // class Decoder_PushParserContext<TYPE, PARSER>
             // ====================================================

template <class TYPE, class PARSER>
class Decoder_PushParserContext : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Context for types that use one of the following push parsers:
    //..
    //    o Base64Parser
    //    o HexParser
    //    o Decoder_ListParser
    //..

    // DATA
    int     d_formattingMode;
    TYPE   *d_object_p;
    PARSER  d_parser;

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

  public:
    // CREATORS
    Decoder_PushParserContext(TYPE *object, int formattingMode);

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

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                    // ===================================
                    // class Decoder_SequenceContext<TYPE>
                    // ===================================

template <class TYPE>
class Decoder_SequenceContext : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Context for types that fall under 'bdlat_TypeCategory::Sequence'.

    // DATA
    bdlb::NullableValue<int>  d_simpleContentId;
    TYPE                     *d_object_p;

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

  public:
    // CREATORS
    Decoder_SequenceContext(TYPE *object, int formattingMode);

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

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                     // =================================
                     // class Decoder_SimpleContext<TYPE>
                     // =================================

template <class TYPE>
class Decoder_SimpleContext : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Context for simple types (uses TypesParserUtil).

    // DATA
    //bsl::string  d_chars;
    int            d_formattingMode;
    TYPE          *d_object_p;

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

  public:
    // CREATORS
    Decoder_SimpleContext(TYPE *object, int formattingMode);

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

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                   // =====================================
                   // class Decoder_CustomizedContext<TYPE>
                   // =====================================

template <class TYPE>
class Decoder_CustomizedContext : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This is the context for types that fall under
    // 'bdlat_TypeCategory::Customized'.

    typedef typename
    bdlat_CustomizedTypeFunctions::BaseType<TYPE>::Type         BaseType;

    typedef typename Decoder_InstantiateContext<bdlat_TypeCategory::Simple,
                                                BaseType>::Type BaseContext;
    // DATA
    TYPE       *d_object;
    BaseType    d_baseObj;
    BaseContext d_baseContext;

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

  public:
    // CREATORS
    Decoder_CustomizedContext(TYPE *object, int formattingMode);

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

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                    // ===================================
                    // class Decoder_UnknownElementContext
                    // ===================================

class Decoder_UnknownElementContext : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Context for unknown elements.  This context is used when an unknown
    // element is found, and the user selected.

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

  public:
    // CREATORS
    Decoder_UnknownElementContext();

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

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                         // =========================
                         // class Decoder_UTF8Context
                         // =========================

template <class TYPE>
class Decoder_UTF8Context : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Context for UTF8 strings (i.e., 'bsl::string' and 'bsl::vector<char>').

    // DATA
    TYPE *d_object_p;

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

  public:
    // CREATORS
    Decoder_UTF8Context(TYPE *object, int formattingMode);

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

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                       // ==============================
                       // class Decoder_StdStringContext
                       // ==============================

class Decoder_StdStringContext : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Proxy context for 'bsl::string'.  This is just a proxy context.  It will
    // forward all callbacks to the appropriate context, based on the
    // formatting mode.

  public:
    // TYPES

    // Note that these typedefs need to be made public because Sun compiler
    // complains that they are inaccessible from the union (below).
    typedef Decoder_PushParserContext<bsl::string, Base64Parser<bsl::string> >
                                             Base64Context;
    typedef Decoder_PushParserContext<bsl::string, HexParser<bsl::string> >
                                             HexContext;
    typedef Decoder_UTF8Context<bsl::string> UTF8Context;

  private:
    // DATA
    union {
        bsls::ObjectBuffer<Base64Context> d_base64Context;
        bsls::ObjectBuffer<HexContext>    d_hexContext;
        bsls::ObjectBuffer<UTF8Context>   d_utf8Context;
    };

    Decoder_ElementContext *d_context_p;

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

  public:
    // CREATORS
    Decoder_StdStringContext(bsl::string *object, int formattingMode);

    virtual ~Decoder_StdStringContext();

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                     // ==================================
                     // class Decoder_StdVectorCharContext
                     // ==================================

class Decoder_StdVectorCharContext : public Decoder_ElementContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Proxy context for 'bsl::string'.  This is just a proxy context.  It will
    // forward all callbacks to the appropriate context, based on the
    // formatting mode.

  public:
    // TYPES

    // Note that these typedefs need to be made public because Sun compiler
    // complains that they are inaccessible from the union (below).
    typedef Decoder_PushParserContext<bsl::vector<char>,
                                      Base64Parser<bsl::vector<char> > >
                                                    Base64Context;
    typedef Decoder_PushParserContext<bsl::vector<char>,
                                      HexParser<bsl::vector<char> > >
                                                    HexContext;
    typedef Decoder_PushParserContext<bsl::vector<char>,
                                      Decoder_ListParser<bsl::vector<char> > >
                                                    ListContext;
    typedef Decoder_UTF8Context<bsl::vector<char> > UTF8Context;

  private:
    // DATA
    union {
        bsls::ObjectBuffer<Base64Context> d_base64Context;
        bsls::ObjectBuffer<HexContext>    d_hexContext;
        bsls::ObjectBuffer<ListContext>   d_listContext;
        bsls::ObjectBuffer<UTF8Context>   d_utf8Context;
    };

    Decoder_ElementContext *d_context_p;

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

  public:
    // CREATORS
    Decoder_StdVectorCharContext(bsl::vector<char> *object,
                                 int                formattingMode);

    virtual ~Decoder_StdVectorCharContext();

    // CALLBACKS
    virtual int startElement(Decoder *decoder);

    virtual int endElement(Decoder *decoder);

    virtual int addCharacters(const char   *chars,
                              bsl::size_t   length,
                              Decoder      *decoder);

    virtual int parseAttribute(const char  *name,
                               const char  *value,
                               bsl::size_t  lenValue,
                               Decoder     *decoder);

    virtual int parseSubElement(const char *elementName, Decoder *decoder);
};

                    // ====================================
                    // class Decoder_PrepareSequenceContext
                    // ====================================

class Decoder_PrepareSequenceContext {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This class does one thing:
    //..
    //  o finds an element that has the 'IS_SIMPLE_CONTENT' flag set
    //..

    // DATA
    bdlb::NullableValue<int> *d_simpleContentId_p; // held, not owned

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

  public:
    // CREATORS
    Decoder_PrepareSequenceContext(bdlb::NullableValue<int> *simpleContentId);

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

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

                  // ========================================
                  // class Decoder_ParseSequenceSimpleContent
                  // ========================================

class Decoder_ParseSequenceSimpleContent {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Parse simple content.

    // DATA
    //const bsl::string *d_chars_p;  // content characters
    const char          *d_chars_p;  // content characters
    bsl::size_t          d_len;
    Decoder             *d_decoder;  // error logger (held)

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

  public:
    // CREATORS
    Decoder_ParseSequenceSimpleContent(Decoder     *decoder,
                                       const char  *chars,
                                       bsl::size_t  len);

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

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

    template <class INFO_TYPE>
    int operator()(bsl::string *object, const INFO_TYPE& info);
};

                   // =====================================
                   // class Decoder_ParseSequenceSubElement
                   // =====================================

class Decoder_ParseSequenceSubElement {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // This is similar to 'Decoder_ParseObject'.

    // DATA
    Decoder     *d_decoder;        // held, not owned
    const char  *d_elementName_p;  // held, not owned
    bsl::size_t  d_lenName;

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

  public:
    // CREATORS
    Decoder_ParseSequenceSubElement(Decoder     *decoder,
                                    const char  *elementName,
                                    bsl::size_t  lenName);

    // Using compiler-generated destructor:
    //  ~Decoder_ParseSequenceSubElement();

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

    template <class TYPE>
    int execute(TYPE *object, int id, int formattingMode);
};

                        // ============================
                        // class Decoder_ParseAttribute
                        // ============================

class Decoder_ParseAttribute {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Parse an attribute.

    // DATA
    Decoder *d_decoder;     // error logger (held)
    bool            d_failed;      // set to true if parsing failed

    const char     *d_name_p;      // attribute name (held)
    const char     *d_value_p;     // attribute value (held)
    bsl::size_t     d_value_length;

  public:
    // IMPLEMENTATION MANIPULATORS
    template <class TYPE>
    int executeImp(TYPE                              *object,
                   int                                formattingMode,
                   bdlat_TypeCategory::NullableValue);
    template <class TYPE>
    int executeImp(TYPE                            *object,
                   int                              formattingMode,
                   bdlat_TypeCategory::DynamicType);
    template <class TYPE, class ANY_CATEGORY>
    int executeImp(TYPE *object, int formattingMode, ANY_CATEGORY);

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

  public:
    // CREATORS
    Decoder_ParseAttribute(Decoder     *decoder,
                           const char  *name,
                           const char  *value,
                           bsl::size_t  lengthValue);

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

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

    template <class TYPE>
    int execute(TYPE *object, int formattingMode);

    // ACCESSORS
    bool failed() const;
};

                         // =========================
                         // class Decoder_ParseObject
                         // =========================

class Decoder_ParseObject {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.
    //
    // Parse the visited object.

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

    // DATA
    Decoder *d_decoder;        // held, not owned
    const char     *d_elementName_p;  // held, not owned
    bsl::size_t     d_lenName;

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

  public:
    // IMPLEMENTATION MANIPULATORS
    int executeImp(bsl::vector<char>         *object,
                   int                        formattingMode,
                   bdlat_TypeCategory::Array);

    template <class TYPE>
    int executeImp(bsl::vector<TYPE>         *object,
                   int                        formattingMode,
                   bdlat_TypeCategory::Array);

    template <class TYPE>
    int executeImp(TYPE                      *object,
                   int                        formattingMode,
                   bdlat_TypeCategory::Array);

    template <class TYPE>
    int executeImp(TYPE                         *object,
                   int                           formattingMode,
                   bdlat_TypeCategory::Sequence);

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

    template <class TYPE>
    int executeImp(TYPE                              *object,
                   int                                formattingMode,
                   bdlat_TypeCategory::NullableValue);

    template <class TYPE>
    int executeImp(TYPE                               *object,
                   int                                 formattingMode,
                   bdlat_TypeCategory::CustomizedType);

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

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

    template <class TYPE>
    int executeArrayImp(TYPE                  *object,
                        int                    formattingMode,
                        CanBeListOrRepetition);

    template <class TYPE>
    int executeArrayImp(TYPE *object, int formattingMode, CanBeRepetitionOnly);

    template <class TYPE>
    int executeArrayRepetitionImp(TYPE *object, int formattingMode);

  public:
    // CREATORS
    Decoder_ParseObject(Decoder     *decoder,
                        const char  *elementName,
                        bsl::size_t  lenName);

    // Using compiler-generated destructor:
    //  ~Decoder_ParseObject();

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

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

                     // =================================
                     // class Decoder_ParseNillableObject
                     // =================================

class Decoder_ParseNillableObject {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.

    // DATA
    int                      d_formattingMode;
    Decoder_NillableContext  d_nillableContext;
    Decoder                 *d_decoder;

  public:
    // IMPLEMENTATION MANIPULATORS
    template <class TYPE>
    int executeImp(TYPE *object, bdlat_TypeCategory::DynamicType);

    template <class TYPE, class ANY_CATEGORY>
    int executeImp(TYPE *object, ANY_CATEGORY);

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

  public:
    Decoder_ParseNillableObject(Decoder *decoder, int formattingMode);
        // Construct a functor to parse nillable objects.

    // Using compiler-generated destructor:
    //  ~Decoder_ParseNillableObject();

    // MANIPULATORS
    template <class TYPE>
    int operator()(TYPE *object);
        // Visit the specified 'object'.

    // ACCESSORS
    bool isNil() const;
        // Return 'true' if the value was nil, and false otherwise.
};

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

                       // =============================
                       // struct Decoder_decodeImpProxy
                       // =============================

struct Decoder_decodeImpProxy {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.

    // DATA
    Decoder *d_decoder;

    // CREATORS

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

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

    template <class TYPE, class ANY_CATEGORY>
    inline
    int operator()(TYPE *object, ANY_CATEGORY category)
    {
        return d_decoder->decodeImp(object, category);
    }
};

                 // ==========================================
                 // struct Decoder_ParseAttribute_executeProxy
                 // ==========================================

struct Decoder_ParseAttribute_executeProxy {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.

    // DATA
    Decoder_ParseAttribute *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()(TYPE *object)
    {
        return d_instance_p->execute(object, d_formattingMode);
    }
};

               // =============================================
               // struct Decoder_ParseAttribute_executeImpProxy
               // =============================================

struct Decoder_ParseAttribute_executeImpProxy {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.

    // DATA
    Decoder_ParseAttribute *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()(TYPE *, bslmf::Nil)
    {
        BSLS_ASSERT_SAFE(0);
        return -1;
    }

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

                  // =======================================
                  // struct Decoder_ParseObject_executeProxy
                  // =======================================

struct Decoder_ParseObject_executeProxy {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.

    // DATA
    Decoder_ParseObject *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()(TYPE *object)
    {
        return d_instance_p->execute(object, d_formattingMode);
    }
};

                 // ==========================================
                 // struct Decoder_ParseObject_executeImpProxy
                 // ==========================================

struct Decoder_ParseObject_executeImpProxy {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.

    // DATA
    Decoder_ParseObject *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()(TYPE *, bslmf::Nil)
    {
        BSLS_ASSERT_SAFE(0);
        return -1;
    }

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

             // ==================================================
             // struct Decoder_ParseNillableObject_executeImpProxy
             // ==================================================

struct Decoder_ParseNillableObject_executeImpProxy {
    // COMPONENT-PRIVATE CLASS.  DO NOT USE OUTSIDE OF THIS COMPONENT.

    // DATA
    Decoder_ParseNillableObject *d_instance_p;

    // CREATORS

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

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

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

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

                    // -----------------------------------
                    // class balxml::Decoder::MemOutStream
                    // -----------------------------------

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

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

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

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

namespace balxml {
inline
void Decoder::setNumUnknownElementsSkipped(int value)
{
    BSLS_REVIEW(0 <= value);

    d_numUnknownElementsSkipped = value;
}

                               // -------------
                               // class Decoder
                               // -------------

inline
const DecoderOptions *Decoder::options() const
{
    return d_options;
}

inline
Reader *Decoder::reader() const
{
    return d_reader;
}

inline
ErrorInfo *Decoder::errorInfo() const
{
    return d_errorInfo;
}

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

inline
int Decoder::numUnknownElementsSkipped() const
{
    return d_numUnknownElementsSkipped;
}

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

inline
int Decoder::errorCount() const
{
    return d_errorCount;
}

inline
int Decoder::warningCount() const
{
    return d_warningCount;
}

inline
int Decoder::open(bsl::istream& stream, const char *uri)
{
    return open(stream.rdbuf(), uri);
}

template <class TYPE>
bsl::istream& Decoder::decode(bsl::istream&  stream,
                              TYPE          *object,
                              const char    *uri)
{
    if (!stream.good()) {

        BALXML_DECODER_LOG_ERROR(this)
                << "The input stream is invalid. "
                << "Unable to decode XML object. "
                << BALXML_DECODER_LOG_END;

        return stream;                                                // RETURN
    }

    if (0 != this->decode(stream.rdbuf(), object, uri)) {
        stream.setstate(bsl::ios_base::failbit);
    }

    return stream;
}

template <class TYPE>
int
Decoder::decode(bsl::streambuf *buffer, TYPE *object, const char *uri)
{
    if (this->open(buffer, uri) != 0) {

        return this->errorCount();                                    // RETURN

    }

    int ret = this->decode(object);

    switch(errorSeverity()) {
      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;
    }

    this->close();
    return ret;
}

template <class TYPE>
int Decoder::decode(const char  *buffer,
                    bsl::size_t  buflen,
                    TYPE        *object,
                    const char  *uri)
{
    if (this->open(buffer, buflen, uri) != 0) {

        return this->errorCount();                                    // RETURN
    }

    int ret = this->decode(object);
    this->close();
    return ret;
}

template <class TYPE>
int Decoder::decode(const char *filename, TYPE *object)
{
    if (this->open(filename) != 0) {

        return this->errorCount();                                    // RETURN
    }

    int ret = this->decode(object);
    this->close();
    return ret;
}

template <class TYPE>
inline
int Decoder::decode(TYPE *object)
{
    bdlat_ValueTypeFunctions::reset(object);

    typedef typename
    bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;

    this->decodeImp(object, TypeCategory());

    return this->errorCount();
}

template <class TYPE>
inline
int Decoder::decodeImp(TYPE *object, bdlat_TypeCategory::DynamicType)
{
    Decoder_decodeImpProxy proxy = { this };
    int ret = bdlat_TypeCategoryUtil::manipulateByCategory(object, proxy);
    if (0 != ret) {
        BALXML_DECODER_LOG_ERROR(this)
                << "The object being decoded is a 'DynamicType', and "
                   "attempting to manipulate the object by its dynamic "
                   "category returned a non-zero status."
                << BALXML_DECODER_LOG_END;
        return ret;
    }

    return 0;
}

template <class TYPE, class ANY_CATEGORY>
inline
int Decoder::decodeImp(TYPE *object, ANY_CATEGORY)
{
    typedef typename
    Decoder_InstantiateContext<ANY_CATEGORY, TYPE>::Type ContextType;

    ContextType elementContext(object, d_options->formattingMode());

    return elementContext.beginParse(this);
}

                     // ---------------------------------
                     // class Decoder_ChoiceContext<TYPE>
                     // ---------------------------------

template <class TYPE>
inline
Decoder_ChoiceContext<TYPE>::Decoder_ChoiceContext(TYPE *object,
                                                   int   formattingMode)
: d_isSelectionNameKnown(false)
, d_object_p(object)
, d_selectionIsRepeatable(false)
, d_selectionName()
{
    (void) formattingMode;
    BSLS_REVIEW(bdlat_FormattingMode::e_DEFAULT ==
                     (formattingMode & bdlat_FormattingMode::e_TYPE_MASK));
}

// CALLBACKS

template <class TYPE>
int Decoder_ChoiceContext<TYPE>::startElement(Decoder *)
{
    enum { k_SUCCESS = 0 };

    d_isSelectionNameKnown = false;  // no selection seen yet

    return k_SUCCESS;
}

template <class TYPE>
int Decoder_ChoiceContext<TYPE>::endElement(Decoder *decoder)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (!d_isSelectionNameKnown) {
        BALXML_DECODER_LOG_ERROR(decoder)
                 << "No elements selected in choice."
                 << BALXML_DECODER_LOG_END;

        return k_FAILURE;  // will trigger failure in parser          // RETURN
    }

    return k_SUCCESS;
}

template <class TYPE>
int Decoder_ChoiceContext<TYPE>::addCharacters(const char   *chars,
                                               bsl::size_t   length,
                                               Decoder      *decoder)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    BSLS_REVIEW(0 != length);

    const char *begin = chars;
    const char *end   = begin + length;

    bdlb::String::skipLeadingTrailing(&begin, &end);

    if (begin != end) {
        BALXML_DECODER_LOG_ERROR(decoder)
                         << "Invalid characters \""
                         << bsl::string(begin, end - begin)
                         << "\" when parsing choice."
                         << BALXML_DECODER_LOG_END;

        return k_FAILURE;  // will trigger failure in parser          // RETURN
    }

    return k_SUCCESS;
}

template <class TYPE>
inline
int Decoder_ChoiceContext<TYPE>::parseAttribute(const char *,
                                                const char *,
                                                bsl::size_t,
                                                Decoder *)
{
    enum { k_ATTRIBUTE_IGNORED = 0 };

    return k_ATTRIBUTE_IGNORED;
}

template <class TYPE>
int Decoder_ChoiceContext<TYPE>::parseSubElement(const char *elementName,
                                                 Decoder    *decoder)
{
    enum { k_FAILURE = -1 };

    const int lenName = static_cast<int>(bsl::strlen(elementName));

    if (d_isSelectionNameKnown
     && (!d_selectionIsRepeatable || d_selectionName != elementName))
    {
        BALXML_DECODER_LOG_ERROR(decoder)
                            << "Only one selection is permitted inside choice."
                            << BALXML_DECODER_LOG_END;

        return k_FAILURE;                                             // RETURN
    }

    bool wasSelectionNameKnown = d_isSelectionNameKnown;
    d_isSelectionNameKnown = true;

    if (decoder->options()->skipUnknownElements() &&
        false == bdlat_ChoiceFunctions::hasSelection(*d_object_p,
                                                     elementName,
                                                     lenName)) {
        decoder->setNumUnknownElementsSkipped(
                                     decoder->numUnknownElementsSkipped() + 1);
        d_selectionIsRepeatable = true;  // assume repeatable
        d_selectionName.assign(elementName, lenName);

        Decoder_UnknownElementContext unknownElement;
        return unknownElement.beginParse(decoder);                    // RETURN
    }

    if (!wasSelectionNameKnown) {
        if (0 != bdlat_ChoiceFunctions::makeSelection(d_object_p,
                                                      elementName,
                                                      lenName)) {
            BALXML_DECODER_LOG_ERROR(decoder)
                                              << "Unable to make selection: \""
                                              << elementName
                                              << "\"."
                                              << BALXML_DECODER_LOG_END;

            return k_FAILURE;                                         // RETURN
        }

        d_selectionIsRepeatable = true;  // TBD: check if repeatable
        d_selectionName.assign(elementName, lenName);
    }

    Decoder_ParseObject parseObject(decoder, elementName, lenName);
    return bdlat_ChoiceFunctions::manipulateSelection(d_object_p, parseObject);
}

             // ----------------------------------------------------
             // class Decoder_PushParserContext<TYPE, PARSER>
             // ----------------------------------------------------

// CREATORS
template <class TYPE, class PARSER>
inline
Decoder_PushParserContext<TYPE, PARSER>::Decoder_PushParserContext(
                                                          TYPE *object,
                                                          int   formattingMode)
: d_formattingMode(formattingMode), d_object_p(object)
{
}

// CALLBACKS

template <class TYPE, class PARSER>
int Decoder_PushParserContext<TYPE, PARSER>::startElement(Decoder *decoder)
{
    int result = d_parser.beginParse(d_object_p);

    if (0 != result) {
        BALXML_DECODER_LOG_ERROR(decoder)
                    << "Unable to begin parsing list or binary type"

                    << "\"."
                    << BALXML_DECODER_LOG_END;
    }

    return result;
}

template <class TYPE, class PARSER>
int Decoder_PushParserContext<TYPE, PARSER>::endElement(Decoder *decoder)
{
    int result = d_parser.endParse();

    if (0 != result) {
        BALXML_DECODER_LOG_ERROR(decoder)
                    << "Unable to end parsing list or binary type"
                    << "\"."
                    << BALXML_DECODER_LOG_END;
    }

    return result;
}

template <class TYPE, class PARSER>
int Decoder_PushParserContext<TYPE, PARSER>::addCharacters(
                                                         const char   *chars,
                                                         bsl::size_t   length,
                                                         Decoder      *decoder)
{
    const char *begin = chars;
    const char *end   = begin + length;

    int result = d_parser.pushCharacters(begin, end);

    if (0 != result) {
        BALXML_DECODER_LOG_ERROR(decoder)
              << "Unable to push \"" << chars
              << "\" when parsing list or binary type"

              << "\"."
              << BALXML_DECODER_LOG_END;
    }

    return result;
}

template <class TYPE, class PARSER>
inline
int Decoder_PushParserContext<TYPE, PARSER>::parseAttribute(const char *,
                                                            const char *,
                                                            bsl::size_t,
                                                            Decoder *)
{
    enum { k_ATTRIBUTE_IGNORED = 0 };

    return k_ATTRIBUTE_IGNORED;
}

template <class TYPE, class PARSER>
int Decoder_PushParserContext<TYPE, PARSER>::parseSubElement(
                                                       const char *elementName,
                                                       Decoder    *decoder)
{
    enum { k_FAILURE = -1 };

    BALXML_DECODER_LOG_ERROR(decoder)
                  << "Unexpected sub-element \"" << elementName
                  << "\" when parsing list or binary type"
                  << "\"."
                  << BALXML_DECODER_LOG_END;

    return k_FAILURE;
}

                    // -----------------------------------
                    // class Decoder_SequenceContext<TYPE>
                    // -----------------------------------

// CREATORS
template <class TYPE>
inline
Decoder_SequenceContext<TYPE>::Decoder_SequenceContext(TYPE *object,
                                                       int   formattingMode)
: d_object_p(object)
{
    (void) formattingMode;
    BSLS_REVIEW(bdlat_FormattingMode::e_DEFAULT ==
                     (formattingMode & bdlat_FormattingMode::e_TYPE_MASK));

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

// CALLBACKS

template <class TYPE>
int Decoder_SequenceContext<TYPE>::startElement(Decoder *decoder)
{
    //d_chars.clear();

    Decoder_PrepareSequenceContext prepareSequenceContext(&d_simpleContentId);

    int ret = bdlat_SequenceFunctions::manipulateAttributes(
                                                       d_object_p,
                                                       prepareSequenceContext);

    if (0 != ret) {
        BALXML_DECODER_LOG_ERROR(decoder)
                                       << "Unable to prepare sequence context!"
                                       << BALXML_DECODER_LOG_END;
    }

    return ret;
}

template <class TYPE>
int Decoder_SequenceContext<TYPE>::endElement(Decoder *)
{
    enum { k_SUCCESS = 0 };

    return k_SUCCESS;
}

template <class TYPE>
int Decoder_SequenceContext<TYPE>::addCharacters(const char   *chars,
                                                 bsl::size_t   length,
                                                 Decoder      *decoder)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    BSLS_REVIEW(0 != length);

    if (d_simpleContentId.isNull()) {

        const char *begin = chars;
        const char *end   = begin + length;

        bdlb::String::skipLeadingTrailing(&begin, &end);

        if (begin != end) {
            BALXML_DECODER_LOG_ERROR(decoder)
                                             << "Unexpected characters: \""
                                             << bsl::string(begin, end - begin)
                                             << "\"."
                                             << BALXML_DECODER_LOG_END;

            return k_FAILURE;                                         // RETURN
        }
        return k_SUCCESS;                                             // RETURN
    }

    Decoder_ParseSequenceSimpleContent parseSimpleContent(decoder,
                                                          chars,
                                                          length);

    return bdlat_SequenceFunctions::manipulateAttribute(
                                                    d_object_p,
                                                    parseSimpleContent,
                                                    d_simpleContentId.value());
}

template <class TYPE>
int Decoder_SequenceContext<TYPE>::parseAttribute(const char  *name,
                                                  const char  *value,
                                                  bsl::size_t  lenValue,
                                                  Decoder     *decoder)
{
    enum { k_SUCCESS = 0, k_ATTRIBUTE_IGNORED = 0, k_FAILURE = -1 };

    const int lenName = static_cast<int>(bsl::strlen(name));

    Decoder_ParseAttribute visitor(decoder, name, value, lenValue);

    if (0 != bdlat_SequenceFunctions::manipulateAttribute(d_object_p,
                                                          visitor,
                                                          name,
                                                          lenName)) {
        if (visitor.failed()) {
            return k_FAILURE;                                         // RETURN
        }
        return k_ATTRIBUTE_IGNORED;                                   // RETURN
    }

    return k_SUCCESS;
}

template <class TYPE>
int Decoder_SequenceContext<TYPE>::parseSubElement(const char *elementName,
                                                   Decoder    *decoder)
{
    enum { k_FAILURE = -1 };

    const int lenName = static_cast<int>(bsl::strlen(elementName));

    if (decoder->options()->skipUnknownElements()
     && false == bdlat_SequenceFunctions::hasAttribute(*d_object_p,
                                                       elementName,
                                                       lenName)) {
        decoder->setNumUnknownElementsSkipped(
                                     decoder->numUnknownElementsSkipped() + 1);
        Decoder_UnknownElementContext unknownElement;
        return unknownElement.beginParse(decoder);                    // RETURN
    }

    Decoder_ParseSequenceSubElement visitor(decoder, elementName, lenName);

    return bdlat_SequenceFunctions::manipulateAttribute(d_object_p,
                                                        visitor,
                                                        elementName,
                                                        lenName);
}

                     // ---------------------------------
                     // class Decoder_SimpleContext<TYPE>
                     // ---------------------------------

// CREATORS
template <class TYPE>
inline
Decoder_SimpleContext<TYPE>::Decoder_SimpleContext(TYPE *object,
                                                   int   formattingMode)
: d_formattingMode(formattingMode)
, d_object_p(object)
{
}

// CALLBACKS

template <class TYPE>
int Decoder_SimpleContext<TYPE>::startElement(Decoder *)
{
    enum { k_SUCCESS = 0 };

    //d_chars.clear();

    return k_SUCCESS;
}

template <class TYPE>
int Decoder_SimpleContext<TYPE>::endElement(Decoder *)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    return k_SUCCESS;
}

template <class TYPE>
int Decoder_SimpleContext<TYPE>::addCharacters(const char   *chars,
                                               bsl::size_t   length,
                                               Decoder      *decoder)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    const char *begin = chars;
    const char *end   = begin + length;

    bdlb::String::skipLeadingTrailing(&begin, &end);

    if (0 != TypesParserUtil::parse(d_object_p,
                                    begin,
                                    static_cast<int>(end - begin),
                                    d_formattingMode)) {
        BALXML_DECODER_LOG_ERROR(decoder)
                    << "Unable to parse \""
                    << bsl::string(begin, end)
                    << "\" when parsing list or binary type"
                    << "\".\n"
                    << BALXML_DECODER_LOG_END;

        return k_FAILURE;                                             // RETURN
    }

    return k_SUCCESS;
}

template <class TYPE>
inline
int Decoder_SimpleContext<TYPE>::parseAttribute(const char *,
                                                const char *,
                                                bsl::size_t,
                                                Decoder *)
{
    enum { k_ATTRIBUTE_IGNORED = 0 };

    return k_ATTRIBUTE_IGNORED;
}

template <class TYPE>
int Decoder_SimpleContext<TYPE>::parseSubElement(const char *elementName,
                                                 Decoder    *decoder)
{
    enum { k_FAILURE = -1 };

    BALXML_DECODER_LOG_ERROR(decoder)
               << "Attempted to create sub context for \""
               << elementName << "\" inside simple type"

               << "\"."
               << BALXML_DECODER_LOG_END;

    return k_FAILURE;  // will trigger failure in parser
}

                   // -------------------------------------
                   // class Decoder_CustomizedContext<TYPE>
                   // -------------------------------------

// CREATORS
template <class TYPE>
inline
Decoder_CustomizedContext<TYPE>::Decoder_CustomizedContext(
                                                          TYPE *object,
                                                          int   formattingMode)
: d_object (object)
, d_baseObj()
, d_baseContext(&d_baseObj, formattingMode)
{
}

// CALLBACKS

template <class TYPE>
int Decoder_CustomizedContext<TYPE>::startElement(Decoder *decoder)
{
    return d_baseContext.startElement (decoder);
}

template <class TYPE>
int Decoder_CustomizedContext<TYPE>::endElement(Decoder *decoder)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };
    int rc = d_baseContext.endElement(decoder);
    if (rc == k_SUCCESS
     &&  0 == bdlat_CustomizedTypeFunctions::convertFromBaseType(d_object,
                                                                 d_baseObj)) {
        return k_SUCCESS;                                             // RETURN
    }

    return k_FAILURE;
}

template <class TYPE>
int Decoder_CustomizedContext<TYPE>::addCharacters(const char   *chars,
                                                   bsl::size_t   length,
                                                   Decoder      *decoder)
{
    return d_baseContext.addCharacters(chars, length, decoder);
}

template <class TYPE>
int Decoder_CustomizedContext<TYPE>::parseAttribute(const char  *name,
                                                    const char  *value,
                                                    bsl::size_t  lenValue,
                                                    Decoder     *decoder)
{
    return d_baseContext.parseAttribute(name, value, lenValue, decoder);
}

template <class TYPE>
int Decoder_CustomizedContext<TYPE>::parseSubElement(const char *elementName,
                                                     Decoder    *decoder)
{
    return d_baseContext.parseSubElement(elementName, decoder);
}

                         // -------------------------
                         // class Decoder_UTF8Context
                         // -------------------------

// CREATORS
template <class TYPE>
inline
Decoder_UTF8Context<TYPE>::Decoder_UTF8Context(TYPE *object, int)
: d_object_p(object)
{
}

// CALLBACKS

template <class TYPE>
inline
int Decoder_UTF8Context<TYPE>::startElement(Decoder *)
{
    enum { k_SUCCESS = 0 };

    d_object_p->clear();

    return k_SUCCESS;
}

template <class TYPE>
inline
int Decoder_UTF8Context<TYPE>::endElement(Decoder *)
{
    enum { k_SUCCESS = 0 };

    return k_SUCCESS;
}

template <class TYPE>
inline int
Decoder_UTF8Context<TYPE>::addCharacters(const char   *chars,
                                         bsl::size_t   length,
                                         Decoder      *)
{
    enum { k_SUCCESS = 0 };

    d_object_p->insert(d_object_p->end(), chars, chars + length);

    return k_SUCCESS;
}

template <class TYPE>
inline
int Decoder_UTF8Context<TYPE>::parseAttribute(const char *,
                                              const char *,
                                              bsl::size_t,
                                              Decoder *)
{
    enum { k_ATTRIBUTE_IGNORED = 0 };

    return k_ATTRIBUTE_IGNORED;
}

template <class TYPE>
int Decoder_UTF8Context<TYPE>::parseSubElement(const char *elementName,
                                               Decoder    *decoder)
{
    enum { k_FAILURE = -1 };

    BALXML_DECODER_LOG_ERROR(decoder)
                                    << "Attempted to create sub context for \""
                                    << elementName << "\" inside UTF8 type."
                                    << BALXML_DECODER_LOG_END;

    return k_FAILURE;  // will trigger failure in parser
}

                    // ------------------------------------
                    // class Decoder_PrepareSequenceContext
                    // ------------------------------------

// CREATORS
inline
Decoder_PrepareSequenceContext::Decoder_PrepareSequenceContext(
                                     bdlb::NullableValue<int> *simpleContentId)
: d_simpleContentId_p(simpleContentId)
{
    d_simpleContentId_p->reset();
}

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

    if (info.formattingMode() & bdlat_FormattingMode::e_SIMPLE_CONTENT) {
        BSLS_ASSERT_SAFE(d_simpleContentId_p->isNull());
        d_simpleContentId_p->makeValue(info.id());
    }

    return k_SUCCESS;
}

                  // ----------------------------------------
                  // class Decoder_ParseSequenceSimpleContent
                  // ----------------------------------------

// CREATORS
inline
Decoder_ParseSequenceSimpleContent::Decoder_ParseSequenceSimpleContent(
                                                          Decoder     *decoder,
                                                          const char  *chars,
                                                          bsl::size_t  len)
: d_chars_p(chars), d_len(len), d_decoder(decoder)
{
    BSLS_REVIEW(d_chars_p);
    BSLS_REVIEW(d_decoder);
}

// MANIPULATORS
template <class TYPE, class INFO_TYPE>
int Decoder_ParseSequenceSimpleContent::operator()(TYPE             *object,
                                                   const INFO_TYPE&  info)
{
    BSLS_ASSERT_SAFE(info.formattingMode()
                                 & bdlat_FormattingMode::e_SIMPLE_CONTENT);

    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    const char *begin = d_chars_p;
    const char *end   = begin + d_len;

    bdlb::String::skipLeadingTrailing(&begin, &end);

    if (0 != TypesParserUtil::parse(object,
                                    begin,
                                    static_cast<int>(end - begin),
                                    info.formattingMode())) {
        BALXML_DECODER_LOG_ERROR(d_decoder)
               << "Unable to parse \""
               << bsl::string(begin, end)
               << "\" within simple content"

               << "\"."
               << BALXML_DECODER_LOG_END;

        return k_FAILURE;                                             // RETURN
    }

    return k_SUCCESS;
}

template <class INFO_TYPE>
inline
int Decoder_ParseSequenceSimpleContent::operator()(bsl::string      *object,
                                                   const INFO_TYPE&  info)
{
    enum { k_SUCCESS = 0 };

    BSLS_ASSERT_SAFE(info.formattingMode()
                   & bdlat_FormattingMode::e_SIMPLE_CONTENT);

    (void) info;

    object->assign(d_chars_p, d_len);

    return k_SUCCESS;
}

                   // -------------------------------------
                   // class Decoder_ParseSequenceSubElement
                   // -------------------------------------

// CREATORS
inline
Decoder_ParseSequenceSubElement::Decoder_ParseSequenceSubElement(
                                                      Decoder     *decoder,
                                                      const char  *elementName,
                                                      bsl::size_t  lenName)
: d_decoder(decoder), d_elementName_p(elementName), d_lenName(lenName)
{
}

// MANIPULATORS
template <class TYPE, class INFO_TYPE>
inline
int Decoder_ParseSequenceSubElement::operator()(TYPE             *object,
                                                const INFO_TYPE&  info)
{
    return execute(object, info.id(), info.formattingMode());
}

template <class TYPE>
int Decoder_ParseSequenceSubElement::execute(TYPE *object,
                                             int,
                                             int   formattingMode)
{
    enum { k_FAILURE = -1 };
    Decoder_ParseObject parseObject(d_decoder, d_elementName_p, d_lenName);

    return parseObject.execute(object, formattingMode);
}

                        // ----------------------------
                        // class Decoder_ParseAttribute
                        // ----------------------------

// PRIVATE MANIPULATORS
template <class TYPE>
int Decoder_ParseAttribute::executeImp(
                             TYPE                              *object,
                             int                                formattingMode,
                             bdlat_TypeCategory::NullableValue)
{
    if (bdlat_NullableValueFunctions::isNull(*object)) {
        bdlat_NullableValueFunctions::makeValue(object);
    }

    Decoder_ParseAttribute_executeProxy proxy = {
        this, formattingMode
    };

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

template <class TYPE>
inline
int Decoder_ParseAttribute::executeImp(
                               TYPE                            *object,
                               int                              formattingMode,
                               bdlat_TypeCategory::DynamicType)
{
    Decoder_ParseAttribute_executeImpProxy proxy = { this,
                                                         formattingMode };
    return bdlat_TypeCategoryUtil::manipulateByCategory(object, proxy);
}

template <class TYPE, class ANY_CATEGORY>
int Decoder_ParseAttribute::executeImp(TYPE         *object,
                                       int           formattingMode,
                                       ANY_CATEGORY)
{
    enum { k_SUCCESS = 0, k_FAILURE = - 1 };

    bool isAttribute = formattingMode
                     & bdlat_FormattingMode::e_ATTRIBUTE;

    if (!isAttribute) {
        BALXML_DECODER_LOG_WARNING(d_decoder)
                                          << "Object '" << d_name_p << "' is "
                                          << "being parsed as an attribute, "
                                          << "but it does not have the "
                                          << "'IS_ATTRIBUTE' flag set."
                                          << BALXML_DECODER_LOG_END;
    }

    if (0 != TypesParserUtil::parse(object,
                                    d_value_p,
                                    static_cast<int>(d_value_length),
                                    formattingMode)) {
        BALXML_DECODER_LOG_ERROR(d_decoder)
                   << "Unable to parse \""
                   << bsl::string(d_value_p, d_value_length)
                   << "\" (for '" << d_name_p << "' attribute)"

                   << "\".\n"
                   << BALXML_DECODER_LOG_END;

        d_failed = true;

        return k_FAILURE;                                             // RETURN
    }

    return k_SUCCESS;
}

// CREATORS
inline
Decoder_ParseAttribute::Decoder_ParseAttribute(Decoder     *decoder,
                                               const char  *name,
                                               const char  *value,
                                               bsl::size_t  lengthValue)
: d_decoder(decoder)
, d_failed(false)
, d_name_p(name)
, d_value_p(value)
, d_value_length(lengthValue)
{
    BSLS_REVIEW(d_decoder);
    BSLS_REVIEW(d_name_p);
    BSLS_REVIEW(d_value_p);
}

// MANIPULATORS
template <class TYPE, class INFO_TYPE>
inline
int Decoder_ParseAttribute::operator()(TYPE *object, const INFO_TYPE& info)
{
    return execute(object, info.formattingMode());
}

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

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

// ACCESSORS
inline
bool Decoder_ParseAttribute::failed() const
{
    return d_failed;
}

                         // -------------------------
                         // class Decoder_ParseObject
                         // -------------------------

// PRIVATE MANIPULATORS
template <class TYPE>
inline
int Decoder_ParseObject::executeImp(bsl::vector<TYPE>         *object,
                                    int                        formattingMode,
                                    bdlat_TypeCategory::Array)
{
    typedef bdlat_TypeCategory::Select<TYPE> Selector;

    enum {
        CAN_BE_REPETITION_ONLY
            = (  (int)Selector::e_SELECTION
                           == (int)bdlat_TypeCategory::e_SEQUENCE_CATEGORY
              || (int)Selector::e_SELECTION
                           == (int)bdlat_TypeCategory::e_CHOICE_CATEGORY)
    };

    typedef typename bsl::conditional<CAN_BE_REPETITION_ONLY,
                                      CanBeRepetitionOnly,
                                      CanBeListOrRepetition>::type Toggle;

    return executeArrayImp(object, formattingMode, Toggle());
}

template <class TYPE>
inline
int Decoder_ParseObject::executeImp(TYPE                      *object,
                                    int                        formattingMode,
                                    bdlat_TypeCategory::Array)
{
    return executeArrayImp(object, formattingMode, CanBeListOrRepetition());
}

template <class TYPE>
int Decoder_ParseObject::executeImp(
                                  TYPE                         *object,
                                  int                           formattingMode,
                                  bdlat_TypeCategory::Sequence)
{
    enum { k_FAILURE = -1 };

    if (formattingMode & bdlat_FormattingMode::e_UNTAGGED) {
        if (d_decoder->options()->skipUnknownElements()
         && false == bdlat_SequenceFunctions::hasAttribute(
                                                *object,
                                                d_elementName_p,
                                                static_cast<int>(d_lenName))) {
            d_decoder->setNumUnknownElementsSkipped(
                                   d_decoder->numUnknownElementsSkipped() + 1);
            Decoder_UnknownElementContext unknownElement;
            return unknownElement.beginParse(d_decoder);              // RETURN
        }

        return bdlat_SequenceFunctions::manipulateAttribute(
                                                  object,
                                                  *this,
                                                  d_elementName_p,
                                                  static_cast<int>(d_lenName));
                                                                      // RETURN
    }

    typedef typename
    Decoder_InstantiateContext<
                             bdlat_TypeCategory::Sequence, TYPE>::Type Context;

    Context context(object, formattingMode);

    return context.beginParse(d_decoder);
}

template <class TYPE>
int Decoder_ParseObject::executeImp(TYPE                       *object,
                                    int                         formattingMode,
                                    bdlat_TypeCategory::Choice)
{
    enum { k_FAILURE = -1 };

    bool isUntagged = formattingMode & bdlat_FormattingMode::e_UNTAGGED;

    if (isUntagged) {
        if (d_decoder->options()->skipUnknownElements()
         && false == bdlat_ChoiceFunctions::hasSelection(
                                                *object,
                                                d_elementName_p,
                                                static_cast<int>(d_lenName))) {
            d_decoder->setNumUnknownElementsSkipped(
                                   d_decoder->numUnknownElementsSkipped() + 1);
            Decoder_UnknownElementContext unknownElement;
            return unknownElement.beginParse(d_decoder);              // RETURN
        }

        if (0 != bdlat_ChoiceFunctions::makeSelection(
                                                object,
                                                d_elementName_p,
                                                static_cast<int>(d_lenName))) {
            BALXML_DECODER_LOG_ERROR(d_decoder)
                                              << "Unable to make selection: \""
                                              << d_elementName_p
                                              << "\"."
                                              << BALXML_DECODER_LOG_END;

            return k_FAILURE;                                         // RETURN
        }

        return bdlat_ChoiceFunctions::manipulateSelection(object, *this);
                                                                      // RETURN
    }

    typedef typename
    Decoder_InstantiateContext<
                               bdlat_TypeCategory::Choice, TYPE>::Type Context;

    Context context(object, formattingMode);

    return context.beginParse(d_decoder);
}

template <class TYPE>
int Decoder_ParseObject::executeImp(
                             TYPE                              *object,
                             int                                formattingMode,
                             bdlat_TypeCategory::NullableValue)
{
    enum { k_SUCCESS = 0, k_FAILURE = -1 };

    if (bdlat_NullableValueFunctions::isNull(*object)) {
        bdlat_NullableValueFunctions::makeValue(object);
    }

    bool isNillable = formattingMode & bdlat_FormattingMode::e_NILLABLE;

    if (isNillable) {
        Decoder_ParseNillableObject parseAsNillable(d_decoder, formattingMode);

        if (0 != bdlat_NullableValueFunctions::manipulateValue(
                                                            object,
                                                            parseAsNillable)) {
            return k_FAILURE;                                         // RETURN
        }

        if (parseAsNillable.isNil()) {
            // reset the object to null
            bdlat_ValueTypeFunctions::reset(object);
        }

        return k_SUCCESS;                                             // RETURN
    }

    Decoder_ParseObject_executeProxy proxy = { this, formattingMode };

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

template <class TYPE>
int Decoder_ParseObject::executeImp(
                            TYPE                               *object,
                            int                                 formattingMode,
                            bdlat_TypeCategory::CustomizedType)
{
    typedef typename
    Decoder_InstantiateContext<
                     bdlat_TypeCategory::CustomizedType, TYPE>::Type
                     Context;

    Context context(object, formattingMode);

    return context.beginParse(d_decoder);
}

template <class TYPE>
inline
int Decoder_ParseObject::executeImp(
                               TYPE                            *object,
                               int                              formattingMode,
                               bdlat_TypeCategory::DynamicType)
{
    Decoder_ParseObject_executeImpProxy proxy = {
        this, formattingMode
    };

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

template <class TYPE, class ANY_CATEGORY>
inline
int Decoder_ParseObject::executeImp(TYPE         *object,
                                    int           formattingMode,
                                    ANY_CATEGORY)
{
    typedef typename
    Decoder_InstantiateContext<ANY_CATEGORY, TYPE>::Type Context;

    Context context(object, formattingMode);

    return context.beginParse(d_decoder);
}

template <class TYPE>
int Decoder_ParseObject::executeArrayImp(TYPE                  *object,
                                         int                    formattingMode,
                                         CanBeListOrRepetition)
{
    if (formattingMode & bdlat_FormattingMode::e_LIST) {
        typedef Decoder_PushParserContext<TYPE, Decoder_ListParser<TYPE> >
            ListContext;

        ListContext listContext(object, formattingMode);

        return listContext.beginParse(d_decoder);                     // RETURN
    } else {
        return executeArrayRepetitionImp(object, formattingMode);     // RETURN
    }
}

template <class TYPE>
inline
int Decoder_ParseObject::executeArrayImp(TYPE                *object,
                                         int                  formattingMode,
                                         CanBeRepetitionOnly)
{
    return executeArrayRepetitionImp(object, formattingMode);
}

template <class TYPE>
int Decoder_ParseObject::executeArrayRepetitionImp(TYPE *object,
                                                   int   formattingMode)
{
    BSLS_REVIEW(! (formattingMode & bdlat_FormattingMode::e_TYPE_MASK
                                   & bdlat_FormattingMode::e_LIST));

    Decoder_ParseObject_executeProxy proxy = { this, formattingMode };

    const int i = static_cast<int>(bdlat_ArrayFunctions::size(*object));

    bdlat_ArrayFunctions::resize(object, i + 1);

    return bdlat_ArrayFunctions::manipulateElement(object, proxy, i);
}

// CREATORS
inline
Decoder_ParseObject::Decoder_ParseObject(Decoder     *decoder,
                                         const char  *elementName,
                                         bsl::size_t  lenName)
: d_decoder(decoder)
, d_elementName_p(elementName)
, d_lenName(lenName)
{
    BSLS_REVIEW(d_elementName_p);
    BSLS_REVIEW(d_decoder);
}

// MANIPULATORS
template <class TYPE, class INFO_TYPE>
inline
int Decoder_ParseObject::operator()(TYPE *object, const INFO_TYPE &info)
{
    return execute(object, info.formattingMode());
}

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

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

                     // ---------------------------------
                     // class Decoder_ParseNillableObject
                     // ---------------------------------

// IMPLEMENTATION MANIPULATORS
template <class TYPE>
inline
int Decoder_ParseNillableObject::executeImp(
                                       TYPE                            *object,
                                       bdlat_TypeCategory::DynamicType)
{
    Decoder_ParseNillableObject_executeImpProxy proxy = { this };

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

template <class TYPE, class ANY_CATEGORY>
inline
int Decoder_ParseNillableObject::executeImp(TYPE *object, ANY_CATEGORY)
{
    typedef typename
    Decoder_InstantiateContext<ANY_CATEGORY, TYPE>::Type Context;

    Context elementContext(object, d_formattingMode);

    d_nillableContext.setElementContext(&elementContext);

    return d_nillableContext.beginParse(d_decoder);
}

inline
Decoder_ParseNillableObject::Decoder_ParseNillableObject(
                                                       Decoder *decoder,
                                                       int      formattingMode)
: d_formattingMode(formattingMode)
, d_nillableContext()
, d_decoder(decoder)
{
}

// MANIPULATORS
template <class TYPE>
inline
int Decoder_ParseNillableObject::operator()(TYPE *object)
{
    typedef typename
    bdlat_TypeCategory::Select<TYPE>::Type TypeCategory;

    return executeImp(object, TypeCategory());
}

// ACCESSORS
inline
bool Decoder_ParseNillableObject::isNil() const
{
    return d_nillableContext.isNil();
}

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