| // Tencent is pleased to support the open source community by making RapidJSON available. |
| // |
| // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. |
| // |
| // Licensed under the MIT License (the "License"); you may not use this file except |
| // in compliance with the License. You may obtain a copy of the License at |
| // |
| // http://opensource.org/licenses/MIT |
| // |
| // 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. |
| |
| #ifndef RAPIDJSON_PRETTYWRITER_H_ |
| #define RAPIDJSON_PRETTYWRITER_H_ |
| |
| #include "writer.h" |
| |
| #ifdef __GNUC__ |
| RAPIDJSON_DIAG_PUSH |
| RAPIDJSON_DIAG_OFF(effc++) |
| #endif |
| |
| #if defined(__clang__) |
| RAPIDJSON_DIAG_PUSH |
| RAPIDJSON_DIAG_OFF(c++98-compat) |
| #endif |
| |
| RAPIDJSON_NAMESPACE_BEGIN |
| |
| //! Combination of PrettyWriter format flags. |
| /*! \see PrettyWriter::SetFormatOptions |
| */ |
| enum PrettyFormatOptions { |
| kFormatDefault = 0, //!< Default pretty formatting. |
| kFormatSingleLineArray = 1 //!< Format arrays on a single line. |
| }; |
| |
| //! Writer with indentation and spacing. |
| /*! |
| \tparam OutputStream Type of output 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, unsigned writeFlags = kWriteDefaultFlags> |
| class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> { |
| public: |
| typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> 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. |
| */ |
| explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : |
| Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} |
| |
| |
| explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : |
| Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} |
| |
| #if RAPIDJSON_HAS_CXX11_RVALUE_REFS |
| PrettyWriter(PrettyWriter&& rhs) : |
| Base(std::forward<PrettyWriter>(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} |
| #endif |
| |
| //! 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; |
| } |
| |
| //! Set pretty writer formatting options. |
| /*! \param options Formatting options. |
| */ |
| PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { |
| formatOptions_ = options; |
| return *this; |
| } |
| |
| /*! @name Implementation of Handler |
| \see Handler |
| */ |
| //@{ |
| |
| bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } |
| bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } |
| bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } |
| bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } |
| bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } |
| bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } |
| bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } |
| |
| bool RawNumber(const Ch* str, SizeType length, bool copy = false) { |
| RAPIDJSON_ASSERT(str != 0); |
| (void)copy; |
| PrettyPrefix(kNumberType); |
| return Base::EndValue(Base::WriteString(str, length)); |
| } |
| |
| bool String(const Ch* str, SizeType length, bool copy = false) { |
| RAPIDJSON_ASSERT(str != 0); |
| (void)copy; |
| PrettyPrefix(kStringType); |
| return Base::EndValue(Base::WriteString(str, length)); |
| } |
| |
| #if RAPIDJSON_HAS_STDSTRING |
| bool String(const std::basic_string<Ch>& str) { |
| return String(str.data(), SizeType(str.size())); |
| } |
| #endif |
| |
| 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); } |
| |
| #if RAPIDJSON_HAS_STDSTRING |
| bool Key(const std::basic_string<Ch>& str) { |
| return Key(str.data(), SizeType(str.size())); |
| } |
| #endif |
| |
| bool EndObject(SizeType memberCount = 0) { |
| (void)memberCount; |
| RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object |
| RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); // currently inside an Array, not Object |
| RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top<typename Base::Level>()->valueCount % 2); // Object has a Key without a Value |
| |
| bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; |
| |
| if (!empty) { |
| Base::os_->Put('\n'); |
| WriteIndent(); |
| } |
| bool ret = Base::EndValue(Base::WriteEndObject()); |
| (void)ret; |
| RAPIDJSON_ASSERT(ret == true); |
| if (Base::level_stack_.Empty()) // end of json text |
| Base::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 && !(formatOptions_ & kFormatSingleLineArray)) { |
| Base::os_->Put('\n'); |
| WriteIndent(); |
| } |
| bool ret = Base::EndValue(Base::WriteEndArray()); |
| (void)ret; |
| RAPIDJSON_ASSERT(ret == true); |
| if (Base::level_stack_.Empty()) // end of json text |
| Base::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)); } |
| |
| //@} |
| |
| //! Write a raw JSON value. |
| /*! |
| For user to write a stringified JSON as a value. |
| |
| \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. |
| \param length Length of the json. |
| \param type Type of the root of json. |
| \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. |
| */ |
| bool RawValue(const Ch* json, size_t length, Type type) { |
| RAPIDJSON_ASSERT(json != 0); |
| PrettyPrefix(type); |
| return Base::EndValue(Base::WriteRawValue(json, length)); |
| } |
| |
| 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 |
| if (formatOptions_ & kFormatSingleLineArray) |
| Base::os_->Put(' '); |
| } |
| |
| if (!(formatOptions_ & kFormatSingleLineArray)) { |
| 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_, static_cast<typename OutputStream::Ch>(indentChar_), count); |
| } |
| |
| Ch indentChar_; |
| unsigned indentCharCount_; |
| PrettyFormatOptions formatOptions_; |
| |
| private: |
| // Prohibit copy constructor & assignment operator. |
| PrettyWriter(const PrettyWriter&); |
| PrettyWriter& operator=(const PrettyWriter&); |
| }; |
| |
| RAPIDJSON_NAMESPACE_END |
| |
| #if defined(__clang__) |
| RAPIDJSON_DIAG_POP |
| #endif |
| |
| #ifdef __GNUC__ |
| RAPIDJSON_DIAG_POP |
| #endif |
| |
| #endif // RAPIDJSON_RAPIDJSON_H_ |