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. All rights reserved.
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 ouptut 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> 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), eol_(true) {}
60 
61 
62     explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
Base(allocator,levelDepth)63         Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), eol_(true) {}
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_), eol_(true) {}
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 // NCBI - added
SetWriteEol(bool eol)83     PrettyWriter& SetWriteEol(bool eol) {
84         eol_ = eol;
85         return *this;
86     }
87 
88     //! Set pretty writer formatting options.
89     /*! \param options Formatting options.
90     */
SetFormatOptions(PrettyFormatOptions options)91     PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
92         formatOptions_ = options;
93         return *this;
94     }
95 
96     /*! @name Implementation of Handler
97         \see Handler
98     */
99     //@{
100 
Null()101     bool Null()                 { PrettyPrefix(kNullType);   return Base::WriteNull(); }
Bool(bool b)102     bool Bool(bool b)           { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
Int(int i)103     bool Int(int i)             { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
Uint(unsigned u)104     bool Uint(unsigned u)       { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
Int64(int64_t i64)105     bool Int64(int64_t i64)     { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
Uint64(uint64_t u64)106     bool Uint64(uint64_t u64)   { PrettyPrefix(kNumberType); return Base::WriteUint64(u64);  }
Double(double d)107     bool Double(double d)       { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
108 
109     bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
110         RAPIDJSON_ASSERT(str != 0);
111         (void)copy;
112         PrettyPrefix(kNumberType);
113         return Base::WriteString(str, length);
114     }
115 
116     bool String(const Ch* str, SizeType length, bool copy = false) {
117         RAPIDJSON_ASSERT(str != 0);
118         (void)copy;
119         PrettyPrefix(kStringType);
120         return Base::WriteString(str, length);
121     }
122 
123 #if RAPIDJSON_HAS_STDSTRING
String(const std::basic_string<Ch> & str)124     bool String(const std::basic_string<Ch>& str) {
125         return String(str.data(), SizeType(str.size()));
126     }
127 #endif
128 
StartObject()129     bool StartObject() {
130         PrettyPrefix(kObjectType);
131         new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
132         return Base::WriteStartObject();
133     }
134 
135     bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
136 
137 #if RAPIDJSON_HAS_STDSTRING
Key(const std::basic_string<Ch> & str)138     bool Key(const std::basic_string<Ch>& str) {
139         return Key(str.data(), SizeType(str.size()));
140     }
141 #endif
142 
143     bool EndObject(SizeType memberCount = 0) {
144         (void)memberCount;
145         RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
146         RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
147         bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
148 
149         if (!empty) {
150             WriteEol();
151             WriteIndent();
152         }
153         bool ret = Base::WriteEndObject();
154         (void)ret;
155         RAPIDJSON_ASSERT(ret == true);
156         if (Base::level_stack_.Empty()) // end of json text
157             Base::os_->Flush();
158         return true;
159     }
160 
StartArray()161     bool StartArray() {
162         PrettyPrefix(kArrayType);
163         new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
164         return Base::WriteStartArray();
165     }
166 
167     bool EndArray(SizeType memberCount = 0) {
168         (void)memberCount;
169         RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
170         RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
171         bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
172 
173         if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
174             WriteEol();
175             WriteIndent();
176         }
177         bool ret = Base::WriteEndArray();
178         (void)ret;
179         RAPIDJSON_ASSERT(ret == true);
180         if (Base::level_stack_.Empty()) // end of json text
181             Base::os_->Flush();
182         return true;
183     }
184 
185     //@}
186 
187     /*! @name Convenience extensions */
188     //@{
189 
190     //! Simpler but slower overload.
String(const Ch * str)191     bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
Key(const Ch * str)192     bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
193 
194     //@}
195 
196     //! Write a raw JSON value.
197     /*!
198         For user to write a stringified JSON as a value.
199 
200         \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
201         \param length Length of the json.
202         \param type Type of the root of json.
203         \note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
204     */
RawValue(const Ch * json,size_t length,Type type)205     bool RawValue(const Ch* json, size_t length, Type type) {
206         RAPIDJSON_ASSERT(json != 0);
207         PrettyPrefix(type);
208         return Base::WriteRawValue(json, length);
209     }
210 
211 protected:
PrettyPrefix(Type type)212     void PrettyPrefix(Type type) {
213         (void)type;
214         if (Base::level_stack_.GetSize() != 0) { // this value is not at root
215             typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
216 
217             if (level->inArray) {
218                 if (level->valueCount > 0) {
219                     Base::os_->Put(','); // add comma if it is not the first element in array
220                     if (formatOptions_ & kFormatSingleLineArray)
221                         Base::os_->Put(' ');
222                 }
223 
224                 if (!(formatOptions_ & kFormatSingleLineArray)) {
225                     WriteEol();
226                     WriteIndent();
227                 }
228             }
229             else {  // in object
230                 if (level->valueCount > 0) {
231                     if (level->valueCount % 2 == 0) {
232                         Base::os_->Put(',');
233                         WriteEol();
234                     }
235                     else {
236                         Base::os_->Put(':');
237                         Base::os_->Put(' ');
238                     }
239                 }
240                 else
241                     WriteEol();
242 
243                 if (level->valueCount % 2 == 0)
244                     WriteIndent();
245             }
246             if (!level->inArray && level->valueCount % 2 == 0)
247                 RAPIDJSON_ASSERT(type == kStringType);  // if it's in object, then even number should be a name
248             level->valueCount++;
249         }
250         else {
251             RAPIDJSON_ASSERT(!Base::hasRoot_);  // Should only has one and only one root.
252             Base::hasRoot_ = true;
253         }
254     }
255 
WriteIndent()256     void WriteIndent()  {
257         size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
258         PutN(*Base::os_, static_cast<typename OutputStream::Ch>(indentChar_), count);
259     }
260 
261 // NCBI
262 // added WriteEol
WriteEol()263     void WriteEol()  {
264         if (eol_) {
265             Base::os_->Put('\n');
266         }
267     }
268 
269     Ch indentChar_;
270     unsigned indentCharCount_;
271     PrettyFormatOptions formatOptions_;
272     bool eol_;
273 
274 private:
275     // Prohibit copy constructor & assignment operator.
276     PrettyWriter(const PrettyWriter&);
277     PrettyWriter& operator=(const PrettyWriter&);
278 };
279 
280 RAPIDJSON_NAMESPACE_END
281 
282 #if defined(__clang__)
283 RAPIDJSON_DIAG_POP
284 #endif
285 
286 #ifdef __GNUC__
287 RAPIDJSON_DIAG_POP
288 #endif
289 
290 #endif // RAPIDJSON_RAPIDJSON_H_
291