206 lines
7.9 KiB
C
206 lines
7.9 KiB
C
|
// 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
|
||
|
|
||
|
namespace rapidjson {
|
||
|
|
||
|
//! 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&);
|
||
|
};
|
||
|
|
||
|
} // namespace rapidjson
|
||
|
|
||
|
#ifdef __GNUC__
|
||
|
RAPIDJSON_DIAG_POP
|
||
|
#endif
|
||
|
|
||
|
#endif // RAPIDJSON_RAPIDJSON_H_
|