| // Copyright (C) 2011 Milo Yip |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| #ifndef RAPIDJSON_PRETTYWRITER_H_ |
| #define RAPIDJSON_PRETTYWRITER_H_ |
| |
| #include "writer.h" |
| |
| #ifdef __GNUC__ |
| RAPIDJSON_DIAG_PUSH |
| RAPIDJSON_DIAG_OFF(effc++) |
| #endif |
| |
| RAPIDJSON_NAMESPACE_BEGIN |
| |
| //! Writer with indentation and spacing. |
| /*! |
| \tparam OutputStream Type of ouptut os. |
| \tparam SourceEncoding Encoding of source string. |
| \tparam TargetEncoding Encoding of output stream. |
| \tparam StackAllocator Type of allocator for allocating memory of stack. |
| */ |
| template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator> |
| class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> { |
| public: |
| typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> Base; |
| typedef typename Base::Ch Ch; |
| |
| //! Constructor |
| /*! \param os Output stream. |
| \param allocator User supplied allocator. If it is null, it will create a private one. |
| \param levelDepth Initial capacity of stack. |
| */ |
| PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : |
| Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} |
| |
| //! Set custom indentation. |
| /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). |
| \param indentCharCount Number of indent characters for each indentation level. |
| \note The default indentation is 4 spaces. |
| */ |
| PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { |
| RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); |
| indentChar_ = indentChar; |
| indentCharCount_ = indentCharCount; |
| return *this; |
| } |
| |
| /*! @name Implementation of Handler |
| \see Handler |
| */ |
| //@{ |
| |
| bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } |
| bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } |
| bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } |
| bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } |
| bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } |
| bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } |
| bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } |
| |
| bool String(const Ch* str, SizeType length, bool copy = false) { |
| (void)copy; |
| PrettyPrefix(kStringType); |
| return Base::WriteString(str, length); |
| } |
| |
| bool StartObject() { |
| PrettyPrefix(kObjectType); |
| new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false); |
| return Base::WriteStartObject(); |
| } |
| |
| bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } |
| |
| bool EndObject(SizeType memberCount = 0) { |
| (void)memberCount; |
| RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); |
| RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); |
| bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; |
| |
| if (!empty) { |
| Base::os_->Put('\n'); |
| WriteIndent(); |
| } |
| if (!Base::WriteEndObject()) |
| return false; |
| if (Base::level_stack_.Empty()) // end of json text |
| Base::os_->Flush(); |
| return true; |
| } |
| |
| bool StartArray() { |
| PrettyPrefix(kArrayType); |
| new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true); |
| return Base::WriteStartArray(); |
| } |
| |
| bool EndArray(SizeType memberCount = 0) { |
| (void)memberCount; |
| RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); |
| RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray); |
| bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; |
| |
| if (!empty) { |
| Base::os_->Put('\n'); |
| WriteIndent(); |
| } |
| if (!Base::WriteEndArray()) |
| return false; |
| if (Base::level_stack_.Empty()) // end of json text |
| Base::os_->Flush(); |
| return true; |
| } |
| |
| //@} |
| |
| /*! @name Convenience extensions */ |
| //@{ |
| |
| //! Simpler but slower overload. |
| bool String(const Ch* str) { return String(str, internal::StrLen(str)); } |
| bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } |
| |
| //@} |
| protected: |
| void PrettyPrefix(Type type) { |
| (void)type; |
| if (Base::level_stack_.GetSize() != 0) { // this value is not at root |
| typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>(); |
| |
| if (level->inArray) { |
| if (level->valueCount > 0) { |
| Base::os_->Put(','); // add comma if it is not the first element in array |
| Base::os_->Put('\n'); |
| } |
| else |
| Base::os_->Put('\n'); |
| WriteIndent(); |
| } |
| else { // in object |
| if (level->valueCount > 0) { |
| if (level->valueCount % 2 == 0) { |
| Base::os_->Put(','); |
| Base::os_->Put('\n'); |
| } |
| else { |
| Base::os_->Put(':'); |
| Base::os_->Put(' '); |
| } |
| } |
| else |
| Base::os_->Put('\n'); |
| |
| if (level->valueCount % 2 == 0) |
| WriteIndent(); |
| } |
| if (!level->inArray && level->valueCount % 2 == 0) |
| RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name |
| level->valueCount++; |
| } |
| else { |
| RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. |
| Base::hasRoot_ = true; |
| } |
| } |
| |
| void WriteIndent() { |
| size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; |
| PutN(*Base::os_, indentChar_, count); |
| } |
| |
| Ch indentChar_; |
| unsigned indentCharCount_; |
| |
| private: |
| // Prohibit copy constructor & assignment operator. |
| PrettyWriter(const PrettyWriter&); |
| PrettyWriter& operator=(const PrettyWriter&); |
| }; |
| |
| RAPIDJSON_NAMESPACE_END |
| |
| #ifdef __GNUC__ |
| RAPIDJSON_DIAG_POP |
| #endif |
| |
| #endif // RAPIDJSON_RAPIDJSON_H_ |