// balxml_encoder.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_ENCODER #define INCLUDED_BALXML_ENCODER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide an XML encoder utility. // //@CLASSES: // balxml::Encoder: XML encoder utility class // //@SEE_ALSO: balxml_decoder, balber_berencoder // //@DESCRIPTION: This component provides a class for encoding value-semantic // objects in XML format. In particular, the 'balxml::Encoder' 'class' // contains a parameterized 'encode' function that encodes a specified // value-semantic object into a specified stream. There are three overloaded // versions of this function: // //: o writes to an 'bsl::streambuf' //: o writes to an 'bsl::ostream' //: o writes to an 'balxml::Formatter' // // The 'encode' function encodes objects in XML format, which is a very useful // format for debugging. For more efficient performance, a binary encoding // (such as BER) should be used. // // This component can be used with types supported by the 'bdlat' framework. // In particular, types generated by the 'bas_codegen.pl' tool can be used. // ///Usage ///----- // The following snippets of code illustrate the usage of this component. // Suppose we have an XML schema inside a file named 'employee.xsd': //.. // <?xml version='1.0' encoding='UTF-8'?> // <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' // xmlns:test='http://bloomberg.com/schemas/test' // targetNamespace='http://bloomberg.com/schemas/test' // elementFormDefault='unqualified'> // // <xs:complexType name='Address'> // <xs:sequence> // <xs:element name='street' type='xs:string'/> // <xs:element name='city' type='xs:string'/> // <xs:element name='state' type='xs:string'/> // </xs:sequence> // </xs:complexType> // // <xs:complexType name='Employee'> // <xs:sequence> // <xs:element name='name' type='xs:string'/> // <xs:element name='homeAddress' type='test:Address'/> // <xs:element name='age' type='xs:int'/> // </xs:sequence> // </xs:complexType> // </xs:schema> //.. // Using the 'bas_codegen.pl' tool, we generate C++ classes for this schema as // follows: //.. // $ bas_codegen.pl -m msg -p test employee.xsd //.. // This tool will generate the header and implementation files for the // 'test_messages' components in the current directory. // // Now suppose we wanted to encode information about a particular employee // using XML encoding to the standard output, and using the 'PRETTY' option for // formatting the output. The following function will do this: //.. // #include <test_messages.h> // // #include <balxml_encoder.h> // #include <balxml_encodingstyle.h> // // #include <bsl_iostream.h> // #include <bsl_sstream.h> // // using namespace BloombergLP; // // void usageExample() // { // test::Employee bob; // // bob.name() = "Bob"; // bob.homeAddress().street() = "Some Street"; // bob.homeAddress().city() = "Some City"; // bob.homeAddress().state() = "Some State"; // bob.age() = 21; // // balxml::EncoderOptions options; // options.setEncodingStyle(balxml::EncodingStyle::BAEXML_PRETTY); // // balxml::Encoder encoder(&options, &bsl::cerr, &bsl::cerr); // // const bsl::string EXPECTED_OUTPUT = // "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" // "<Employee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" // " <name>Bob</name>\n" // " <homeAddress>\n" // " <street>Some Street</street>\n" // " <city>Some City</city>\n" // " <state>Some State</state>\n" // " </homeAddress>\n" // " <age>21</age>\n" // "</Employee>\n"; // // bsl::ostringstream os; // const int rc = encoder.encodeToStream(os, bob); // // assert(0 == rc); // assert(EXPECTED_OUTPUT == os.str()); // } //.. #include <balscm_version.h> #include <balxml_encoderoptions.h> #include <balxml_encodingstyle.h> #include <balxml_errorinfo.h> // for Severity #include <balxml_formatter.h> #include <balxml_typesprintutil.h> #include <bdlat_arrayfunctions.h> #include <bdlat_choicefunctions.h> #include <bdlat_nullablevaluefunctions.h> #include <bdlat_sequencefunctions.h> #include <bdlat_typecategory.h> #include <bdlat_typename.h> #include <bdlsb_memoutstreambuf.h> #include <bslma_allocator.h> #include <bslma_default.h> #include <bslma_usesbslmaallocator.h> #include <bslmf_nestedtraitdeclaration.h> #include <bsls_assert.h> #include <bsls_objectbuffer.h> #include <bsls_review.h> #include <bsl_ostream.h> #include <bsl_string.h> #include <bsl_vector.h> namespace BloombergLP { namespace balxml { class Encoder_Context; // ============= // class Encoder // ============= class Encoder { // This 'class' contains the parameterized 'encode' functions that encode // 'bdlat' types in XML format. // FRIENDS friend class Encoder_Context; private: // 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. // // Although the compiler should generate this destructor // implicitly, xlC 8 breaks when the destructor is called by name // unless it is explicitly declared. // 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. }; private: // DATA const EncoderOptions *d_options; // 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 ErrorInfo::Severity d_severity; // error severity bsl::ostream *d_errorStream; // held, not owned bsl::ostream *d_warningStream; // held, not owned // PRIVATE MANIPULATORS ErrorInfo::Severity logError(const char *text, const bsl::string_view& tag, int formattingMode, int index = -1); bsl::ostream& logStream(); // Return the stream for logging. Note the if stream has not been // created yet, it will be created during this call. public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(Encoder, bslma::UsesBslmaAllocator); // CREATORS Encoder(const EncoderOptions *options, bslma::Allocator *basicAllocator); Encoder(const EncoderOptions *options, bsl::ostream *errorStream = 0, bsl::ostream *warningStream = 0, bslma::Allocator *basicAllocator = 0); // Construct a encoder object using the specified 'options'. Errors // and warnings will be rendered to the optionally specified // 'errorStream' and 'warningStream' respectively. ~Encoder(); // Destroy this object. This destruction has no effect on objects // pointed-to by the pointers provided at construction. template <class TYPE> int encode(bsl::streambuf *buffer, const TYPE& object); // Encode the specified non-modifiable 'object' to the specified // 'buffer'. Return 0 on success, and a non-zero value otherwise. // Note that the encoder will use encoder options, error and warning // streams specified at the construction time. template <class TYPE> int encodeToStream(bsl::ostream& stream, const TYPE& object); // Encode the specified non-modifiable 'object' to the specified // 'stream'. Return 0 on success, and a non-zero value otherwise. // Note that the encoder will use encoder options, error and warning // streams specified at the construction time. template <class TYPE> bsl::ostream& encode(bsl::ostream& stream, const TYPE& object); // Encode the specified non-modifiable 'object' to the specified // 'stream'. Return a reference to 'stream'. If an encoding error is // detected, 'stream.fail()' will be true on return. Note that the // encoder will use encoder options, error and warning streams // specified at the construction time. IMPORTANT: The use of // 'stream.fail()' to communicate errors to the caller has two // consequences: 1) if 'stream' is the same as the 'errorStream' // passed to the constructor, then the error message may be suppressed // (because of the output/error stream becoming invalidated) and 2) it // is important to call 'stream.clear()' after testing the stream // state. To avoid these issues, we recommend that you use use // 'encodeToStream', above, instead of this version of 'encode'. template <class TYPE> int encode(Formatter& formatter, const TYPE& object); // Encode the specified non-modifiable 'object' to the specified // 'formatter'. Return 0 on success, and a non-zero value otherwise. // Note that encoder will use encoder options, error and warning // streams specified at the construction time. //ACCESSORS const EncoderOptions *options() const; // Return the encoder options. bool isCompact() const; // Return 'true' if the encoding style in the encoder options is // defined as 'EncodingStyle::BAEXML_COMPACT', and 'false' otherwise. bsl::ostream *errorStream() const; // Return pointer to the error stream. bsl::ostream *warningStream() const; // Return pointer to the warning stream. ErrorInfo::Severity errorSeverity() const; // Return the severity of the most severe warning or error encountered // during the last call to the 'encode' method. The severity is reset // each time 'encode' is called. bslstl::StringRef loggedMessages() const; // Return a string containing any error, warning, or trace messages // that were logged during the last call to the 'encode' method. The // log is reset each time 'encode' is called. }; // ---- Anything below this line is implementation specific. Do not use. ---- // ====================== // struct Encoder_Context // ====================== class Encoder_Context { // This 'struct' contains state that is maintained during encoding. It // also contains methods for switching between pretty formatting and // compact formatting, based on the encoding options. // DATA Formatter *d_formatter; Encoder *d_encoder; // NOT IMPLEMENTED Encoder_Context(const Encoder_Context& other); Encoder_Context& operator=(const Encoder_Context& other); public: // CREATORS Encoder_Context(Formatter *formatter, Encoder *encoder); // MANIPULATORS template <class NAME_TYPE, class VALUE_TYPE> void addAttribute(const NAME_TYPE& name, const VALUE_TYPE& value); template<class NAME_TYPE, class VALUE_TYPE> void addAttribute(const NAME_TYPE& name, const VALUE_TYPE& value, int formattingMode); template <class NAME_TYPE> void closeElement(const NAME_TYPE& name); void invalidate(); ErrorInfo::Severity logError(const char *text, const bsl::string_view& tag, int formattingMode, int index = -1); template <class NAME_TYPE> void openElement(const NAME_TYPE& name); bsl::ostream& rawOutputStream(); // ACCESSORS const EncoderOptions& encoderOptions() const; int status() const; }; // ======================================= // struct Encoder_OptionsCompatibilityUtil // ======================================= struct Encoder_OptionsCompatibilityUtil { // Component-private 'struct'. Do not use. // // This struct provides a namespace for a suite of functions used to // compute the options for the underlying XML formatter used by the // encoder, given the encoder's options. private: // PRIVATE CLASS METHODS static int getFormatterInitialIndentLevel( const EncoderOptions& encoderOptions); // Return the value of the 'InitialIndentLevel' field for a // 'FormatterOptions' object that corresponds to the specified // 'encoderOptions', which is 0 if the 'EncodingStyle' of // 'encoderOptions' is 'EncodingStyle::e_COMPACT', and is the value of // 'InitialIndentLevel' of 'encoderOptions' otherwise. static int getFormatterSpacesPerLevel( const EncoderOptions& encoderOptions); // Return the value of the 'SpacesPerLevel' field for a // 'FormatterOptions' object that corresponds to the specified // 'encoderOptions', which is 0 if the 'EncodingStyle' of // 'encoderOptions' is 'EncodingStyle::e_COMPACT', and is the value of // 'SpacesPerLevel' of 'encoderOptions' otherwise. static int getFormatterWrapColumn(const EncoderOptions& encoderOptions); // Return the value of the 'WrapColumn' field for a // 'FormatterOptions' object that corresponds to the specified // 'encoderOptions', which is -1 if the 'EncodingStyle' of // 'encoderOptions' is 'EncodingStyle::e_COMPACT', and is the value of // 'WrapColumn' of 'encoderOptions' otherwise. public: // CLASS METHODS static void getFormatterOptions( int *formatterIndentLevel, int *formatterSpacesPerLevel, int *formatterWrapColumn, EncoderOptions *formatterOptions, const EncoderOptions& encoderOptions); // Load to the specified 'formatterIndentLevel', // 'formatterSpacesPerLevel', and 'formatterWrapColumn', the number of // spaces to indent the first element in the XML document, the number // of spaces to use for indenting each level of nesting in the // document, and the maximum horizontal column number after which the // encoder should insert a line break, respectively, based on the // specified 'encoderOptions'. Load to the specified // 'formatterOptions' the options that the formatter should use to emit // XML, based on the 'encoderOptions'. The behavior is undefined // unless 'formatterOptions' has the default value. }; // ========================== // class Encoder_EncodeObject // ========================== class Encoder_EncodeObject { // Component-private class. Do not use. // // This struct encodes an object *with* enclosing tags. Compared to the // 'EncoderUtil_EncodeValue' class below, this class prefixes the value // with an opening tag, and suffixes the value with a closing tag. In // pseudocode, this is equivalent to: //.. // openTag() // Encoder_EncodeValue() // closeTag() //.. // There is an overloaded version of 'bsl::vector<char>' because, based on // the formatting mode, this class needs to switch between encoding the // value in a single tag (i.e., when using BASE64, TEXT, IS_LIST or HEX) // and encoding the value in multiple tags (i.e., when repetition is used). // PRIVATE TYPES struct CanBeListOrRepetition { }; struct CanBeRepetitionOnly { }; // PRIVATE DATA MEMBERS Encoder_Context *d_context_p; public: // IMPLEMENTATION MANIPULATORS template <class TYPE> int executeImp(const TYPE& object, const bsl::string_view& tag, int formattingMode, bdlat_TypeCategory::Array); template <class TYPE> int executeImp(const TYPE& object, const bsl::string_view& tag, int formattingMode, bdlat_TypeCategory::NullableValue); template <class TYPE> int executeImp(const TYPE& object, const bsl::string_view& tag, int formattingMode, bdlat_TypeCategory::DynamicType); template <class TYPE, class ANY_CATEGORY> int executeImp(const TYPE& object, const bsl::string_view& tag, int formattingMode, ANY_CATEGORY); int executeImp(const bsl::vector<char>& object, const bsl::string_view& tag, int formattingMode, bdlat_TypeCategory::Array); template <class TYPE> int executeArrayListImp(const TYPE& object, const bsl::string_view& tag); template <class TYPE> int executeArrayRepetitionImp(const TYPE& object, const bsl::string_view& tag, int formattingMode); private: // NOT IMPLEMENTED Encoder_EncodeObject(const Encoder_EncodeObject&); Encoder_EncodeObject& operator=(const Encoder_EncodeObject&); public: // CREATORS explicit Encoder_EncodeObject(Encoder_Context *context); // Using compiler generated destructor: // ~Encoder_EncodeObject(); // MANIPULATORS template <class TYPE, class INFO_TYPE> int operator()(const TYPE& object, const INFO_TYPE& info); template <class TYPE> int execute(const TYPE& object, const bsl::string_view& tag, int formattingMode); }; // ========================= // class Encoder_EncodeValue // ========================= class Encoder_EncodeValue { // Component-private class. Do not use. // // This class just encodes a value *without* any enclosing tags. // PRIVATE DATA MEMBERS Encoder_Context *d_context_p; public: // IMPLEMENTATION MANIPULATORS template <class TYPE> int executeImp(const TYPE& object, int formattingMode, bdlat_TypeCategory::Sequence); template <class TYPE> int executeImp(const TYPE& object, int formattingMode, bdlat_TypeCategory::Choice); template <class TYPE> int executeImp(const TYPE& object, int formattingMode, bdlat_TypeCategory::DynamicType); template <class TYPE, class ANY_CATEGORY> int executeImp(const TYPE& object, int formattingMode, ANY_CATEGORY); private: // NOT IMPLEMENTED Encoder_EncodeValue(const Encoder_EncodeValue&); Encoder_EncodeValue& operator=(const Encoder_EncodeValue&); public: // CREATORS explicit Encoder_EncodeValue(Encoder_Context *context); // Using compiler generated destructor: // ~Encoder_EncodeValue(); // MANIPULATORS template <class TYPE, class INFO_TYPE> int operator()(const TYPE& object, const INFO_TYPE& info); template <class TYPE> int execute(const TYPE& object, int formattingMode); }; // =============================== // class Encoder_SequenceFirstPass // =============================== class Encoder_SequenceFirstPass { // Component private class. Do not use. // // This class is used as the first pass when encoding elements of a // sequence. It basically does two things: // o encode elements with the // 'bdlat_FormattingMode::e_IS_ATTRIBUTE' flag using the // 'Formatter::addAttribute' method. // o looks for an element with the // 'bdlat_FormattingMode::e_IS_SIMPLE_CONTENT' flag and, if // found, provides accessors to obtain the 'id' of the element. // Note that the behavior is undefined unless there is only one // element with 'IS_SIMPLE_CONTENT' flag and, if this element exist, // all other elements must have 'IS_ATTRIBUTE' flag. // PRIVATE DATA MEMBERS Encoder_Context *d_context_p; // held, not owned bool d_hasSubElements; // true if an element with // neither 'IS_ATTRIBUTE' nor // 'IS_SIMPLE_CONTENT' is // found bdlb::NullableValue<int> d_simpleContentId; // the 'id' of the element // with 'IS_SIMPLE_CONTENT' // flag, if found public: // IMPLEMENTATION MANIPULATORS template <class TYPE> int addAttributeImp(const TYPE& object, const bsl::string_view& name, int formattingMode, bdlat_TypeCategory::NullableValue); template <class TYPE> int addAttributeImp(const TYPE& object, const bsl::string_view& name, int formattingMode, bdlat_TypeCategory::DynamicType); template <class TYPE, class ANY_CATEGORY> int addAttributeImp(const TYPE& object, const bsl::string_view& name, int formattingMode, ANY_CATEGORY); // Add an attribute with the specified 'name', the value of the // specified 'object', using the specified 'formattingMode'. Note that // the last argument is used for overloading purposes only. template <class TYPE> int addAttribute(const TYPE& object, const bsl::string_view& name, int formattingMode); // Add an attribute with the specified 'name', the value of the // specified 'object', using the specified 'formattingMode'. private: // NOT IMPLEMENTED Encoder_SequenceFirstPass(const Encoder_SequenceFirstPass&); Encoder_SequenceFirstPass& operator=(const Encoder_SequenceFirstPass&); public: // CREATORS explicit Encoder_SequenceFirstPass(Encoder_Context *context); // Create a visitor for first pass for sequences. // Generated by compiler: // ~Encoder_SequenceFirstPass(); // MANIPULATORS template <class TYPE, class INFO_TYPE> int operator()(const TYPE& object, const INFO_TYPE& info); // Called back when an element is visited. // ACCESSORS const bool& hasSubElements() const; // Return true if a sub-element is found, and false otherwise. const bdlb::NullableValue<int>& simpleContentId() const; // Return a null value if there is no element with 'IS_SIMPLE_CONTENT' // flag, or a non-null value with the integer 'id' of the element // otherwise. }; // ================================ // class Encoder_SequenceSecondPass // ================================ class Encoder_SequenceSecondPass { // Component-private class. Do not use. // // This class is used as the second pass when encoding elements of a // sequence. It basically calls 'EncoderUtil_EncodeObject' for elements // that do not have 'IS_ATTRIBUTE' flag. Note that the behavior is // undefined if there is an element with the 'IS_SIMPLE_CONTENT' flag. // DATA Encoder_EncodeObject d_encodeObjectFunctor; // functor used to encode sub-elements // NOT IMPLEMENTED Encoder_SequenceSecondPass(const Encoder_SequenceSecondPass&); Encoder_SequenceSecondPass& operator=(const Encoder_SequenceSecondPass&); public: // CREATORS explicit Encoder_SequenceSecondPass(Encoder_Context *context); // Create a visitor for the second pass for sequences. // Generated by compiler: // ~Encoder_SequenceSecondPass(); // MANIPULATORS template <class TYPE, class INFO_TYPE> int operator()(const TYPE& object, const INFO_TYPE& info); // Called back when an element is visited. }; // ============================================================================ // PROXY CLASSES // ============================================================================ // ======================================== // struct Encoder_EncodeObject_executeProxy // ======================================== struct Encoder_EncodeObject_executeProxy { // Component-private struct. Do not use. // DATA MEMBERS Encoder_EncodeObject *d_instance_p; const bsl::string_view *d_tag_p; int d_formattingMode; // CREATORS // Creators have been omitted to allow simple static initialization of this // struct. // FUNCTIONS template <class TYPE> inline int operator()(const TYPE& object) { return d_instance_p->execute(object, *d_tag_p, d_formattingMode); } }; // =========================================== // struct Encoder_EncodeObject_executeImpProxy // =========================================== struct Encoder_EncodeObject_executeImpProxy { // Component-private struct. Do not use. // DATA Encoder_EncodeObject *d_instance_p; const bsl::string_view *d_tag_p; int d_formattingMode; // CREATORS // Creators have been omitted to allow simple static initialization of this // struct. // FUNCTIONS template <class TYPE> inline int operator()(const TYPE&, bslmf::Nil) { BSLS_ASSERT_SAFE(0); return -1; } template <class TYPE, class ANY_CATEGORY> inline int operator()(const TYPE& object, ANY_CATEGORY category) { return d_instance_p->executeImp(object, *d_tag_p, d_formattingMode, category); } }; // ========================================== // struct Encoder_EncodeValue_executeImpProxy // ========================================== struct Encoder_EncodeValue_executeImpProxy { // Component-private struct. Do not use. // DATA MEMBERS Encoder_EncodeValue *d_instance_p; int d_formattingMode; // CREATORS // Creators have been omitted to allow simple static initialization of this // struct. // FUNCTIONS template <class TYPE> inline int operator()(const TYPE&, bslmf::Nil) { BSLS_ASSERT_SAFE(0); return -1; } template <class TYPE, class ANY_CATEGORY> inline int operator()(const TYPE& object, ANY_CATEGORY category) { return d_instance_p->executeImp(object, d_formattingMode, category); } }; // ================================================== // struct Encoder_SequenceFirstPass_addAttributeProxy // ================================================== struct Encoder_SequenceFirstPass_addAttributeProxy { // Component-private struct. Do not use. // DATA MEMBERS Encoder_SequenceFirstPass *d_instance_p; const bsl::string_view *d_name_p; int d_formattingMode; // CREATORS // Creators have been omitted to allow simple static initialization of this // struct. // FUNCTIONS template <class TYPE> inline int operator()(const TYPE& object) { return d_instance_p->addAttribute(object, *d_name_p, d_formattingMode); } }; // ===================================================== // struct Encoder_SequenceFirstPass_addAttributeImpProxy // ===================================================== struct Encoder_SequenceFirstPass_addAttributeImpProxy { // Component-private struct. Do not use. // DATA MEMBERS Encoder_SequenceFirstPass *d_instance_p; const bsl::string_view *d_name_p; int d_formattingMode; // CREATORS // Creators have been omitted to allow simple static initialization of this // struct. // FUNCTIONS template <class TYPE> inline int operator()(const TYPE&, bslmf::Nil) { BSLS_ASSERT_SAFE(0); return -1; } template <class TYPE, class ANY_CATEGORY> inline int operator()(const TYPE& object, ANY_CATEGORY category) { return d_instance_p->addAttributeImp(object, *d_name_p, d_formattingMode, category); } }; } // close package namespace // ============================================================================ // INLINE DEFINITIONS // ============================================================================ namespace balxml { // ------------------------------ // class BerEncoder::MemOutStream // ------------------------------ inline Encoder::MemOutStream::MemOutStream(bslma::Allocator *basicAllocator) : bsl::ostream(0) , d_sb(bslma::Default::allocator(basicAllocator)) { rdbuf(&d_sb); } // MANIPULATORS inline void Encoder::MemOutStream::reset() { d_sb.reset(); } // ACCESSORS inline const char *Encoder::MemOutStream::data() const { return d_sb.data(); } inline int Encoder::MemOutStream::length() const { return (int)d_sb.length(); } // ------------- // class Encoder // ------------- inline bool Encoder::isCompact() const { return EncodingStyle::e_COMPACT == d_options->encodingStyle(); } inline const EncoderOptions *Encoder::options() const { return d_options; } inline bsl::ostream *Encoder::errorStream() const { return d_errorStream; } inline bsl::ostream *Encoder::warningStream() const { return d_warningStream; } inline ErrorInfo::Severity Encoder::errorSeverity() const { return d_severity; } inline bslstl::StringRef Encoder::loggedMessages() const { if (d_logStream) { return bslstl::StringRef(d_logStream->data(), d_logStream->length()); // RETURN } return bslstl::StringRef(); } inline bsl::ostream& Encoder::logStream() { if (0 == d_logStream) { d_logStream = new(d_logArea.buffer()) MemOutStream(d_allocator); } return *d_logStream; } template <class TYPE> inline int Encoder::encode(bsl::streambuf *buffer, const TYPE& object) { int indentLevel = 0; int spacesPerLevel = 0; int wrapColumn = 0; EncoderOptions formatterEncoderOptions; Encoder_OptionsCompatibilityUtil::getFormatterOptions( &indentLevel, &spacesPerLevel, &wrapColumn, &formatterEncoderOptions, *d_options); Formatter formatter(buffer, formatterEncoderOptions, indentLevel, spacesPerLevel, wrapColumn); const int rc = encode(formatter, object); buffer->pubsync(); return rc; } template <class TYPE> inline int Encoder::encodeToStream(bsl::ostream& stream, const TYPE& object) { return encode(stream.rdbuf(), object); } template <class TYPE> inline bsl::ostream& Encoder::encode(bsl::ostream& stream, const TYPE& object) { int indentLevel = 0; int spacesPerLevel = 0; int wrapColumn = 0; EncoderOptions formatterEncoderOptions; Encoder_OptionsCompatibilityUtil::getFormatterOptions( &indentLevel, &spacesPerLevel, &wrapColumn, &formatterEncoderOptions, *d_options); Formatter formatter(stream, formatterEncoderOptions, indentLevel, spacesPerLevel, wrapColumn); encode(formatter, object); stream.flush(); return stream; } template <class TYPE> int Encoder::encode(Formatter& formatter, const TYPE& object) { d_severity = ErrorInfo::e_NO_ERROR; if (d_logStream != 0) { d_logStream->reset(); } Encoder_Context context(&formatter,this); if (d_options->outputXMLHeader()) { formatter.addHeader(); } const char *tag = d_options->tag().empty() ? bdlat_TypeName::xsdName(object, d_options->formattingMode()) : d_options->tag().c_str(); context.openElement(tag); if (!d_options->objectNamespace().empty()) { context.addAttribute("xmlns", d_options->objectNamespace()); if (d_options->outputXSIAlias()) { // Only declare the "xsi" namespace and schema location if an // object namespace was provided because only then can validation // happen. context.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); if (!d_options->schemaLocation().empty()) { context.addAttribute("xsi:schemaLocation", d_options->objectNamespace() + " " + d_options->schemaLocation()); } } } else if (d_options->outputXSIAlias()) { context.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); } Encoder_EncodeValue encodeValue(&context); int rc = 0; if (0 != encodeValue.execute(object,d_options->formattingMode())) { logError("Failed to encode", tag, d_options->formattingMode()); context.invalidate(); rc = -1; } else { context.closeElement(tag); } switch (d_severity) { case ErrorInfo::e_NO_ERROR: { } break; case ErrorInfo::e_WARNING: { if (d_warningStream) { *d_warningStream << loggedMessages(); } } break; default: { if (d_errorStream) { *d_errorStream << loggedMessages(); } } break; } return rc; } // --------------------- // class Encoder_Context // --------------------- // MANIPULATORS template <class NAME_TYPE, class VALUE_TYPE> inline void Encoder_Context::addAttribute(const NAME_TYPE& name, const VALUE_TYPE& value) { d_formatter->addAttribute(name, value, bdlat_FormattingMode::e_DEFAULT); } template <class NAME_TYPE, class VALUE_TYPE> inline void Encoder_Context::addAttribute(const NAME_TYPE& name, const VALUE_TYPE& value, int formattingMode) { d_formatter->addAttribute(name, value, formattingMode); } template <class NAME_TYPE> inline void Encoder_Context::closeElement(const NAME_TYPE& name) { d_formatter->closeElement(name); } inline void Encoder_Context::invalidate() { rawOutputStream().setstate(bsl::ios_base::failbit); } inline ErrorInfo::Severity Encoder_Context::logError( const char *text, const bsl::string_view& tag, int formattingMode, int index) { return d_encoder->logError(text, tag, formattingMode, index); } template <class NAME_TYPE> inline void Encoder_Context::openElement(const NAME_TYPE& name) { d_formatter->openElement(name); } inline bsl::ostream& Encoder_Context::rawOutputStream() { return d_formatter->rawOutputStream(); } // ACCESSORS inline const EncoderOptions& Encoder_Context::encoderOptions() const { return *d_encoder->options(); } inline int Encoder_Context::status() const { return d_formatter->status(); } // -------------------------- // class Encoder_EncodeObject // -------------------------- // IMPLEMENTATION MANIPULATORS template <class TYPE> inline int Encoder_EncodeObject::executeImp(const TYPE& object, const bsl::string_view& tag, int formattingMode, bdlat_TypeCategory::Array) { if (formattingMode & bdlat_FormattingMode::e_LIST) { return executeArrayListImp(object, tag); // RETURN } // else { return ... } removed, to prevent warning with gcc-4.1.1 (reach // end of non-void function), instead, have unconditional: return executeArrayRepetitionImp(object, tag, formattingMode); } template <class TYPE> inline int Encoder_EncodeObject::executeImp( const TYPE& object, const bsl::string_view& tag, int formattingMode, bdlat_TypeCategory::NullableValue) { enum { k_SUCCESS = 0 }; if (bdlat_NullableValueFunctions::isNull(object)) { if (formattingMode & bdlat_FormattingMode::e_NILLABLE) { if (!d_context_p->encoderOptions().objectNamespace().empty() && d_context_p->encoderOptions().outputXSIAlias()) { // Only add the "xsi:nil" attribute if an object namespace was // provided because only then can validation happen. d_context_p->openElement(tag); d_context_p->addAttribute("xsi:nil", "true"); d_context_p->closeElement(tag); } } return d_context_p->status(); // RETURN } Encoder_EncodeObject_executeProxy proxy = { this, &tag, formattingMode }; return bdlat_NullableValueFunctions::accessValue(object, proxy); } template <class TYPE> inline int Encoder_EncodeObject::executeImp( const TYPE& object, const bsl::string_view& tag, int formattingMode, bdlat_TypeCategory::DynamicType) { Encoder_EncodeObject_executeImpProxy proxy = { this, &tag, formattingMode }; return bdlat_TypeCategoryUtil::accessByCategory(object, proxy); } template <class TYPE, class ANY_CATEGORY> int Encoder_EncodeObject::executeImp(const TYPE& object, const bsl::string_view& tag, int formattingMode, ANY_CATEGORY) { enum { k_FAILURE = -1 }; bool isUntagged = formattingMode & bdlat_FormattingMode::e_UNTAGGED; if (!isUntagged) { d_context_p->openElement(tag); } Encoder_EncodeValue encodeValue(d_context_p); if (0 != encodeValue.execute(object, formattingMode)) { d_context_p->logError("Unable to encode value", tag, formattingMode); return k_FAILURE; // RETURN } if (!isUntagged) { d_context_p->closeElement(tag); } int ret = d_context_p->status(); if (ret) { d_context_p->logError("Formatter was invalidated for", tag, formattingMode); } return ret; } template <class TYPE> int Encoder_EncodeObject::executeArrayListImp(const TYPE& object, const bsl::string_view& tag) { d_context_p->openElement(tag); TypesPrintUtil::printList(d_context_p->rawOutputStream(), object, &d_context_p->encoderOptions()); d_context_p->closeElement(tag); int ret = d_context_p->status(); if (ret) { d_context_p->logError( "Error while encoding list for", tag, EncoderOptions::DEFAULT_INITIALIZER_FORMATTING_MODE); } return ret; } template <class TYPE> int Encoder_EncodeObject::executeArrayRepetitionImp( const TYPE& object, const bsl::string_view& tag, int formattingMode) { enum { k_SUCCESS = 0, k_FAILURE = -1 }; const int size = (int)bdlat_ArrayFunctions::size(object); Encoder_EncodeObject_executeProxy proxy = { this, &tag, formattingMode }; for (int i = 0; i < size; ++i) { if (0 != bdlat_ArrayFunctions::accessElement(object, proxy, i)) { d_context_p->logError( "Error while encoding array element", tag, formattingMode, i); return k_FAILURE; // RETURN } } return k_SUCCESS; } // CREATORS inline Encoder_EncodeObject::Encoder_EncodeObject(Encoder_Context *context) : d_context_p(context) { BSLS_ASSERT(d_context_p); } // MANIPULATORS template <class TYPE, class INFO_TYPE> inline int Encoder_EncodeObject::operator()(const TYPE& object, const INFO_TYPE& info) { bsl::string_view name(info.name(), info.nameLength()); return execute(object, name, info.formattingMode()); } template <class TYPE> inline int Encoder_EncodeObject::execute(const TYPE& object, const bsl::string_view& tag, int formattingMode) { typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory; return executeImp(object, tag, formattingMode, TypeCategory()); } // ------------------------- // class Encoder_EncodeValue // ------------------------- // IMPLEMENTATION MANIPULATORS template <class TYPE> inline int Encoder_EncodeValue::executeImp( const TYPE& object, int formattingMode, bdlat_TypeCategory::Sequence) { enum { k_SUCCESS = 0, k_FAILURE = -1 }; #if defined(BSLS_ASSERT_SAFE_IS_ACTIVE) int type = formattingMode & bdlat_FormattingMode::e_TYPE_MASK; BSLS_ASSERT_SAFE(bdlat_FormattingMode::e_DEFAULT == type); #else (void) formattingMode; #endif Encoder_SequenceFirstPass firstPass(d_context_p); if (0 != bdlat_SequenceFunctions::accessAttributes(object, firstPass)) { return k_FAILURE; // RETURN } if (!firstPass.simpleContentId().isNull()) { Encoder_EncodeValue encodeValue(d_context_p); return bdlat_SequenceFunctions::accessAttribute( object, encodeValue, firstPass.simpleContentId().value()); // RETURN } if (firstPass.hasSubElements()) { Encoder_SequenceSecondPass secondPass(d_context_p); return bdlat_SequenceFunctions::accessAttributes(object, secondPass); // RETURN } return k_SUCCESS; } template <class TYPE> inline int Encoder_EncodeValue::executeImp(const TYPE& object, int formattingMode, bdlat_TypeCategory::Choice) { enum { k_FAILURE = -1 }; #if defined(BSLS_ASSERT_SAFE_IS_ACTIVE) int type = formattingMode & bdlat_FormattingMode::e_TYPE_MASK; BSLS_ASSERT_SAFE(bdlat_FormattingMode::e_DEFAULT == type); #endif if (bdlat_ChoiceFunctions::k_UNDEFINED_SELECTION_ID == bdlat_ChoiceFunctions::selectionId(object)) { d_context_p->logError("Undefined selection is not allowed ", "???", formattingMode); return k_FAILURE; // RETURN } Encoder_EncodeObject encodeObject(d_context_p); return bdlat_ChoiceFunctions::accessSelection(object, encodeObject); } template <class TYPE> inline int Encoder_EncodeValue::executeImp( const TYPE& object, int formattingMode, bdlat_TypeCategory::DynamicType) { Encoder_EncodeValue_executeImpProxy proxy = { this, formattingMode }; return bdlat_TypeCategoryUtil::accessByCategory(object, proxy); } template <class TYPE, class ANY_CATEGORY> inline int Encoder_EncodeValue::executeImp(const TYPE& object, int formattingMode, ANY_CATEGORY) { TypesPrintUtil::print(d_context_p->rawOutputStream(), object, formattingMode, &d_context_p->encoderOptions()); return d_context_p->status(); } // CREATORS inline Encoder_EncodeValue::Encoder_EncodeValue(Encoder_Context *context) : d_context_p(context) { BSLS_ASSERT(d_context_p); } // MANIPULATORS template <class TYPE, class INFO_TYPE> inline int Encoder_EncodeValue::operator()(const TYPE& object, const INFO_TYPE& info) { typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory; return executeImp(object, info.formattingMode(), TypeCategory()); } template <class TYPE> inline int Encoder_EncodeValue::execute(const TYPE& object, int formattingMode) { typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory; return executeImp(object, formattingMode, TypeCategory()); } // ------------------------------- // class Encoder_SequenceFirstPass // ------------------------------- // IMPLEMENTATION MANIPULATORS template <class TYPE> inline int Encoder_SequenceFirstPass::addAttributeImp( const TYPE& object, const bsl::string_view& name, int formattingMode, bdlat_TypeCategory::NullableValue) { enum { k_SUCCESS = 0 }; if (bdlat_NullableValueFunctions::isNull(object)) { return k_SUCCESS; // RETURN } Encoder_SequenceFirstPass_addAttributeProxy proxy = { this, &name, formattingMode }; return bdlat_NullableValueFunctions::accessValue(object, proxy); } template <class TYPE> inline int Encoder_SequenceFirstPass::addAttributeImp( const TYPE& object, const bsl::string_view& name, int formattingMode, bdlat_TypeCategory::DynamicType) { Encoder_SequenceFirstPass_addAttributeImpProxy proxy = { this, &name, formattingMode }; return bdlat_TypeCategoryUtil::accessByCategory(object, proxy); } template <class TYPE, class ANY_CATEGORY> inline int Encoder_SequenceFirstPass::addAttributeImp( const TYPE& object, const bsl::string_view& name, int formattingMode, ANY_CATEGORY) { d_context_p->addAttribute(name, object, formattingMode); int ret = d_context_p->status(); if (ret) { d_context_p->logError("Failed to encode attribute", name, formattingMode); } return ret; } template <class TYPE> inline int Encoder_SequenceFirstPass::addAttribute( const TYPE& object, const bsl::string_view& name, int formattingMode) { typedef typename bdlat_TypeCategory::Select<TYPE>::Type TypeCategory; return addAttributeImp(object, name, formattingMode, TypeCategory()); } // CREATORS inline Encoder_SequenceFirstPass::Encoder_SequenceFirstPass(Encoder_Context *context) : d_context_p(context) , d_hasSubElements(false) { BSLS_ASSERT(d_context_p); BSLS_ASSERT(d_simpleContentId.isNull()); // {DRQS 153551134<GO>}: gcc can occasionally mis-diagnose // 'd_simpleContentId' as uninitialized. This workaround avoids that // problem (which can cause build failures if '-Wmaybe-uninitialized' and // '-Werror' are set). See also {DRQS 75130685<GO>} and {DRQS // 115347303<GO>}. d_simpleContentId.makeValue(0); d_simpleContentId.reset(); } // MANIPULATORS template <class TYPE, class INFO_TYPE> int Encoder_SequenceFirstPass::operator()(const TYPE& object, const INFO_TYPE& info) { enum { k_SUCCESS = 0 }; int formattingMode = info.formattingMode(); bool isSimpleContent = formattingMode & bdlat_FormattingMode::e_SIMPLE_CONTENT; bool isAttribute = formattingMode & bdlat_FormattingMode::e_ATTRIBUTE; if (isSimpleContent) { BSLS_ASSERT(!isAttribute); BSLS_ASSERT(!d_hasSubElements); BSLS_ASSERT(d_simpleContentId.isNull()); d_simpleContentId.makeValue(info.id()); } else if (isAttribute) { bsl::string_view name(info.name(), info.nameLength()); return addAttribute(object, name, formattingMode); // RETURN } else { BSLS_ASSERT(d_simpleContentId.isNull()); d_hasSubElements = true; } return k_SUCCESS; } // ACCESSORS inline const bool& Encoder_SequenceFirstPass::hasSubElements() const { return d_hasSubElements; } inline const bdlb::NullableValue<int>& Encoder_SequenceFirstPass::simpleContentId() const { return d_simpleContentId; } // -------------------------------- // class Encoder_SequenceSecondPass // -------------------------------- // CREATORS inline Encoder_SequenceSecondPass::Encoder_SequenceSecondPass( Encoder_Context* context) : d_encodeObjectFunctor(context) { } // MANIPULATORS template <class TYPE, class INFO_TYPE> int Encoder_SequenceSecondPass::operator()(const TYPE& object, const INFO_TYPE& info) { enum { k_SUCCESS = 0 }; int formattingMode = info.formattingMode(); BSLS_ASSERT( !(formattingMode & bdlat_FormattingMode::e_SIMPLE_CONTENT)); if (!(formattingMode & bdlat_FormattingMode::e_ATTRIBUTE)) { return d_encodeObjectFunctor(object, info); // RETURN } return k_SUCCESS; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------