All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
prettywriter.h
1 // Copyright (C) 2011 Milo Yip
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 // THE SOFTWARE.
20 
21 #ifndef RAPIDJSON_PRETTYWRITER_H_
22 #define RAPIDJSON_PRETTYWRITER_H_
23 
24 #include "writer.h"
25 
26 #ifdef __GNUC__
27 RAPIDJSON_DIAG_PUSH
28 RAPIDJSON_DIAG_OFF(effc++)
29 #endif
30 
31 namespace rapidjson {
32 
33 //! Writer with indentation and spacing.
34 /*!
35  \tparam OutputStream Type of ouptut os.
36  \tparam SourceEncoding Encoding of source string.
37  \tparam TargetEncoding Encoding of output stream.
38  \tparam StackAllocator Type of allocator for allocating memory of stack.
39 */
40 template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator>
41 class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator> {
42 public:
44  typedef typename Base::Ch Ch;
45 
46  //! Constructor
47  /*! \param os Output stream.
48  \param allocator User supplied allocator. If it is null, it will create a private one.
49  \param levelDepth Initial capacity of stack.
50  */
51  PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
52  Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
53 
54  //! Set custom indentation.
55  /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
56  \param indentCharCount Number of indent characters for each indentation level.
57  \note The default indentation is 4 spaces.
58  */
59  PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
60  RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
61  indentChar_ = indentChar;
62  indentCharCount_ = indentCharCount;
63  return *this;
64  }
65 
66  /*! @name Implementation of Handler
67  \see Handler
68  */
69  //@{
70 
71  bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
72  bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
73  bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
74  bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
75  bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
76  bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
77  bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
78 
79  bool String(const Ch* str, SizeType length, bool copy = false) {
80  (void)copy;
81  PrettyPrefix(kStringType);
82  return Base::WriteString(str, length);
83  }
84 
85  bool StartObject() {
86  PrettyPrefix(kObjectType);
87  new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
88  return Base::WriteStartObject();
89  }
90 
91  bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
92 
93  bool EndObject(SizeType memberCount = 0) {
94  (void)memberCount;
95  RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
96  RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
97  bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
98 
99  if (!empty) {
100  Base::os_->Put('\n');
101  WriteIndent();
102  }
103  if (!Base::WriteEndObject())
104  return false;
105  if (Base::level_stack_.Empty()) // end of json text
106  Base::os_->Flush();
107  return true;
108  }
109 
110  bool StartArray() {
111  PrettyPrefix(kArrayType);
112  new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
113  return Base::WriteStartArray();
114  }
115 
116  bool EndArray(SizeType memberCount = 0) {
117  (void)memberCount;
118  RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
119  RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
120  bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
121 
122  if (!empty) {
123  Base::os_->Put('\n');
124  WriteIndent();
125  }
126  if (!Base::WriteEndArray())
127  return false;
128  if (Base::level_stack_.Empty()) // end of json text
129  Base::os_->Flush();
130  return true;
131  }
132 
133  //@}
134 
135  /*! @name Convenience extensions */
136  //@{
137 
138  //! Simpler but slower overload.
139  bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
140  bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
141 
142  //@}
143 protected:
144  void PrettyPrefix(Type type) {
145  (void)type;
146  if (Base::level_stack_.GetSize() != 0) { // this value is not at root
147  typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
148 
149  if (level->inArray) {
150  if (level->valueCount > 0) {
151  Base::os_->Put(','); // add comma if it is not the first element in array
152  Base::os_->Put('\n');
153  }
154  else
155  Base::os_->Put('\n');
156  WriteIndent();
157  }
158  else { // in object
159  if (level->valueCount > 0) {
160  if (level->valueCount % 2 == 0) {
161  Base::os_->Put(',');
162  Base::os_->Put('\n');
163  }
164  else {
165  Base::os_->Put(':');
166  Base::os_->Put(' ');
167  }
168  }
169  else
170  Base::os_->Put('\n');
171 
172  if (level->valueCount % 2 == 0)
173  WriteIndent();
174  }
175  if (!level->inArray && level->valueCount % 2 == 0)
176  RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
177  level->valueCount++;
178  }
179  else {
180  RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
181  Base::hasRoot_ = true;
182  }
183  }
184 
185  void WriteIndent() {
186  size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
187  PutN(*Base::os_, indentChar_, count);
188  }
189 
190  Ch indentChar_;
191  unsigned indentCharCount_;
192 
193 private:
194  // Prohibit copy constructor & assignment operator.
195  PrettyWriter(const PrettyWriter&);
196  PrettyWriter& operator=(const PrettyWriter&);
197 };
198 
199 } // namespace rapidjson
200 
201 #ifdef __GNUC__
202 RAPIDJSON_DIAG_POP
203 #endif
204 
205 #endif // RAPIDJSON_RAPIDJSON_H_