1 // Tencent is pleased to support the open source community by making RapidJSON available.
2 //
3 // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
4 //
5 // Licensed under the MIT License (the "License"); you may not use this file except
6 // in compliance with the License. You may obtain a copy of the License at
7 //
8 // http://opensource.org/licenses/MIT
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
13 // specific language governing permissions and limitations under the License.
14 
15 #ifndef RAPIDJSON_PRETTYWRITER_H_
16 #define RAPIDJSON_PRETTYWRITER_H_
17 
18 #include "writer.h"
19 
20 #ifdef __GNUC__
21 RAPIDJSON_DIAG_PUSH
22 RAPIDJSON_DIAG_OFF(effc++)
23 #endif
24 
25 #if defined(__clang__)
26 RAPIDJSON_DIAG_PUSH
27 RAPIDJSON_DIAG_OFF(c++98-compat)
28 #endif
29 
30 RAPIDJSON_NAMESPACE_BEGIN
31 
32 //! Combination of PrettyWriter format flags.
33 /*! \see PrettyWriter::SetFormatOptions
34  */
35 enum PrettyFormatOptions {
36     kFormatDefault = 0,         //!< Default pretty formatting.
37     kFormatSingleLineArray = 1  //!< Format arrays on a single line.
38 };
39 
40 //! Writer with indentation and spacing.
41 /*!
42     \tparam OutputStream Type of output os.
43     \tparam SourceEncoding Encoding of source string.
44     \tparam TargetEncoding Encoding of output stream.
45     \tparam StackAllocator Type of allocator for allocating memory of stack.
46 */
47 template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
48 class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
49 public:
50     typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> Base;
51     typedef typename Base::Ch Ch;
52 
53     //! Constructor
54     /*! \param os Output stream.
55         \param allocator User supplied allocator. If it is null, it will create a private one.
56         \param levelDepth Initial capacity of stack.
57     */
58     explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
Base(os,allocator,levelDepth)59         Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
60 
61 
62     explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
Base(allocator,levelDepth)63         Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
64 
65 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
PrettyWriter(PrettyWriter && rhs)66     PrettyWriter(PrettyWriter&& rhs) :
67         Base(std::forward<PrettyWriter>(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {}
68 #endif
69 
70     //! Set custom indentation.
71     /*! \param indentChar       Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
72         \param indentCharCount  Number of indent characters for each indentation level.
73         \note The default indentation is 4 spaces.
74     */
SetIndent(Ch indentChar,unsigned indentCharCount)75     PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
76         RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
77         indentChar_ = indentChar;
78         indentCharCount_ = indentCharCount;
79         return *this;
80     }
81 
82     //! Set pretty writer formatting options.
83     /*! \param options Formatting options.
84     */
SetFormatOptions(PrettyFormatOptions options)85     PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
86         formatOptions_ = options;
87         return *this;
88     }
89 
90     /*! @name Implementation of Handler
91         \see Handler
92     */
93     //@{
94 
Null()95     bool Null()                 { PrettyPrefix(kNullType);   return Base::EndValue(Base::WriteNull()); }
Bool(bool b)96     bool Bool(bool b)           { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); }
Int(int i)97     bool Int(int i)             { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); }
Uint(unsigned u)98     bool Uint(unsigned u)       { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); }
Int64(int64_t i64)99     bool Int64(int64_t i64)     { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); }
Uint64(uint64_t u64)100     bool Uint64(uint64_t u64)   { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64));  }
Double(double d)101     bool Double(double d)       { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); }
102 
103     bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
104         RAPIDJSON_ASSERT(str != 0);
105         (void)copy;
106         PrettyPrefix(kNumberType);
107         return Base::EndValue(Base::WriteString(str, length));
108     }
109 
110     bool String(const Ch* str, SizeType length, bool copy = false) {
111         RAPIDJSON_ASSERT(str != 0);
112         (void)copy;
113         PrettyPrefix(kStringType);
114         return Base::EndValue(Base::WriteString(str, length));
115     }
116 
117 #if RAPIDJSON_HAS_STDSTRING
String(const std::basic_string<Ch> & str)118     bool String(const std::basic_string<Ch>& str) {
119         return String(str.data(), SizeType(str.size()));
120     }
121 #endif
122 
StartObject()123     bool StartObject() {
124         PrettyPrefix(kObjectType);
125         new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
126         return Base::WriteStartObject();
127     }
128 
129     bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
130 
131 #if RAPIDJSON_HAS_STDSTRING
Key(const std::basic_string<Ch> & str)132     bool Key(const std::basic_string<Ch>& str) {
133         return Key(str.data(), SizeType(str.size()));
134     }
135 #endif
136 
137     bool EndObject(SizeType memberCount = 0) {
138         (void)memberCount;
139         RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object
140         RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); // currently inside an Array, not Object
141         RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top<typename Base::Level>()->valueCount % 2); // Object has a Key without a Value
142 
143         bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
144 
145         if (!empty) {
146             Base::os_->Put('\n');
147             WriteIndent();
148         }
149         bool ret = Base::EndValue(Base::WriteEndObject());
150         (void)ret;
151         RAPIDJSON_ASSERT(ret == true);
152         if (Base::level_stack_.Empty()) // end of json text
153             Base::Flush();
154         return true;
155     }
156 
StartArray()157     bool StartArray() {
158         PrettyPrefix(kArrayType);
159         new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
160         return Base::WriteStartArray();
161     }
162 
163     bool EndArray(SizeType memberCount = 0) {
164         (void)memberCount;
165         RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
166         RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
167         bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
168 
169         if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
170             Base::os_->Put('\n');
171             WriteIndent();
172         }
173         bool ret = Base::EndValue(Base::WriteEndArray());
174         (void)ret;
175         RAPIDJSON_ASSERT(ret == true);
176         if (Base::level_stack_.Empty()) // end of json text
177             Base::Flush();
178         return true;
179     }
180 
181     //@}
182 
183     /*! @name Convenience extensions */
184     //@{
185 
186     //! Simpler but slower overload.
String(const Ch * str)187     bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
Key(const Ch * str)188     bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
189 
190     //@}
191 
192     //! Write a raw JSON value.
193     /*!
194         For user to write a stringified JSON as a value.
195 
196         \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
197         \param length Length of the json.
198         \param type Type of the root of json.
199         \note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
200     */
RawValue(const Ch * json,size_t length,Type type)201     bool RawValue(const Ch* json, size_t length, Type type) {
202         RAPIDJSON_ASSERT(json != 0);
203         PrettyPrefix(type);
204         return Base::EndValue(Base::WriteRawValue(json, length));
205     }
206 
207 protected:
PrettyPrefix(Type type)208     void PrettyPrefix(Type type) {
209         (void)type;
210         if (Base::level_stack_.GetSize() != 0) { // this value is not at root
211             typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
212 
213             if (level->inArray) {
214                 if (level->valueCount > 0) {
215                     Base::os_->Put(','); // add comma if it is not the first element in array
216                     if (formatOptions_ & kFormatSingleLineArray)
217                         Base::os_->Put(' ');
218                 }
219 
220                 if (!(formatOptions_ & kFormatSingleLineArray)) {
221                     Base::os_->Put('\n');
222                     WriteIndent();
223                 }
224             }
225             else {  // in object
226                 if (level->valueCount > 0) {
227                     if (level->valueCount % 2 == 0) {
228                         Base::os_->Put(',');
229                         Base::os_->Put('\n');
230                     }
231                     else {
232                         Base::os_->Put(':');
233                         Base::os_->Put(' ');
234                     }
235                 }
236                 else
237                     Base::os_->Put('\n');
238 
239                 if (level->valueCount % 2 == 0)
240                     WriteIndent();
241             }
242             if (!level->inArray && level->valueCount % 2 == 0)
243                 RAPIDJSON_ASSERT(type == kStringType);  // if it's in object, then even number should be a name
244             level->valueCount++;
245         }
246         else {
247             RAPIDJSON_ASSERT(!Base::hasRoot_);  // Should only has one and only one root.
248             Base::hasRoot_ = true;
249         }
250     }
251 
WriteIndent()252     void WriteIndent()  {
253         size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
254         PutN(*Base::os_, static_cast<typename OutputStream::Ch>(indentChar_), count);
255     }
256 
257     Ch indentChar_;
258     unsigned indentCharCount_;
259     PrettyFormatOptions formatOptions_;
260 
261 private:
262     // Prohibit copy constructor & assignment operator.
263     PrettyWriter(const PrettyWriter&);
264     PrettyWriter& operator=(const PrettyWriter&);
265 };
266 
267 RAPIDJSON_NAMESPACE_END
268 
269 #if defined(__clang__)
270 RAPIDJSON_DIAG_POP
271 #endif
272 
273 #ifdef __GNUC__
274 RAPIDJSON_DIAG_POP
275 #endif
276 
277 #endif // RAPIDJSON_RAPIDJSON_H_
278