ian@0: // Copyright 2011 Baptiste Lepilleur ian@0: // Distributed under MIT license, or public domain if desired and ian@0: // recognized in your jurisdiction. ian@0: // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE ian@0: ian@0: #if !defined(JSON_IS_AMALGAMATION) ian@0: # include ian@0: # include "json_tool.h" ian@0: #endif // if !defined(JSON_IS_AMALGAMATION) ian@0: #include ian@0: #include ian@0: #include ian@0: #include ian@0: #include ian@0: #include ian@0: #include ian@0: ian@0: #if _MSC_VER >= 1400 // VC++ 8.0 ian@0: #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. ian@0: #endif ian@0: ian@0: namespace Json { ian@0: ian@0: static bool containsControlCharacter( const char* str ) ian@0: { ian@0: while ( *str ) ian@0: { ian@0: if ( isControlCharacter( *(str++) ) ) ian@0: return true; ian@0: } ian@0: return false; ian@0: } ian@0: ian@0: ian@0: std::string valueToString( LargestInt value ) ian@0: { ian@0: UIntToStringBuffer buffer; ian@0: char *current = buffer + sizeof(buffer); ian@0: bool isNegative = value < 0; ian@0: if ( isNegative ) ian@0: value = -value; ian@0: uintToString( LargestUInt(value), current ); ian@0: if ( isNegative ) ian@0: *--current = '-'; ian@0: assert( current >= buffer ); ian@0: return current; ian@0: } ian@0: ian@0: ian@0: std::string valueToString( LargestUInt value ) ian@0: { ian@0: UIntToStringBuffer buffer; ian@0: char *current = buffer + sizeof(buffer); ian@0: uintToString( value, current ); ian@0: assert( current >= buffer ); ian@0: return current; ian@0: } ian@0: ian@0: #if defined(JSON_HAS_INT64) ian@0: ian@0: std::string valueToString( Int value ) ian@0: { ian@0: return valueToString( LargestInt(value) ); ian@0: } ian@0: ian@0: ian@0: std::string valueToString( UInt value ) ian@0: { ian@0: return valueToString( LargestUInt(value) ); ian@0: } ian@0: ian@0: #endif // # if defined(JSON_HAS_INT64) ian@0: ian@0: ian@0: std::string valueToString( double value ) ian@0: { ian@0: char buffer[32]; ian@0: #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. ian@0: sprintf_s(buffer, sizeof(buffer), "%#.16g", value); ian@0: #else ian@0: sprintf(buffer, "%#.16g", value); ian@0: #endif ian@0: char* ch = buffer + strlen(buffer) - 1; ian@0: if (*ch != '0') return buffer; // nothing to truncate, so save time ian@0: while(ch > buffer && *ch == '0'){ ian@0: --ch; ian@0: } ian@0: char* last_nonzero = ch; ian@0: while(ch >= buffer){ ian@0: switch(*ch){ ian@0: case '0': ian@0: case '1': ian@0: case '2': ian@0: case '3': ian@0: case '4': ian@0: case '5': ian@0: case '6': ian@0: case '7': ian@0: case '8': ian@0: case '9': ian@0: --ch; ian@0: continue; ian@0: case '.': ian@0: // Truncate zeroes to save bytes in output, but keep one. ian@0: *(last_nonzero+2) = '\0'; ian@0: return buffer; ian@0: default: ian@0: return buffer; ian@0: } ian@0: } ian@0: return buffer; ian@0: } ian@0: ian@0: ian@0: std::string valueToString( bool value ) ian@0: { ian@0: return value ? "true" : "false"; ian@0: } ian@0: ian@0: std::string valueToQuotedString( const char *value ) ian@0: { ian@0: if (value == NULL) ian@0: return ""; ian@0: // Not sure how to handle unicode... ian@0: if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) ian@0: return std::string("\"") + value + "\""; ian@0: // We have to walk value and escape any special characters. ian@0: // Appending to std::string is not efficient, but this should be rare. ian@0: // (Note: forward slashes are *not* rare, but I am not escaping them.) ian@0: std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL ian@0: std::string result; ian@0: result.reserve(maxsize); // to avoid lots of mallocs ian@0: result += "\""; ian@0: for (const char* c=value; *c != 0; ++c) ian@0: { ian@0: switch(*c) ian@0: { ian@0: case '\"': ian@0: result += "\\\""; ian@0: break; ian@0: case '\\': ian@0: result += "\\\\"; ian@0: break; ian@0: case '\b': ian@0: result += "\\b"; ian@0: break; ian@0: case '\f': ian@0: result += "\\f"; ian@0: break; ian@0: case '\n': ian@0: result += "\\n"; ian@0: break; ian@0: case '\r': ian@0: result += "\\r"; ian@0: break; ian@0: case '\t': ian@0: result += "\\t"; ian@0: break; ian@0: //case '/': ian@0: // Even though \/ is considered a legal escape in JSON, a bare ian@0: // slash is also legal, so I see no reason to escape it. ian@0: // (I hope I am not misunderstanding something. ian@0: // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); ian@0: result += oss.str(); ian@0: } ian@0: else ian@0: { ian@0: result += *c; ian@0: } ian@0: break; ian@0: } ian@0: } ian@0: result += "\""; ian@0: return result; ian@0: } ian@0: ian@0: // Class Writer ian@0: // ////////////////////////////////////////////////////////////////// ian@0: Writer::~Writer() ian@0: { ian@0: } ian@0: ian@0: ian@0: // Class FastWriter ian@0: // ////////////////////////////////////////////////////////////////// ian@0: ian@0: FastWriter::FastWriter() ian@0: : yamlCompatiblityEnabled_( false ) ian@0: { ian@0: } ian@0: ian@0: ian@0: void ian@0: FastWriter::enableYAMLCompatibility() ian@0: { ian@0: yamlCompatiblityEnabled_ = true; ian@0: } ian@0: ian@0: ian@0: std::string ian@0: FastWriter::write( const Value &root ) ian@0: { ian@0: document_ = ""; ian@0: writeValue( root ); ian@0: document_ += "\n"; ian@0: return document_; ian@0: } ian@0: ian@0: ian@0: void ian@0: FastWriter::writeValue( const Value &value ) ian@0: { ian@0: switch ( value.type() ) ian@0: { ian@0: case nullValue: ian@0: document_ += "null"; ian@0: break; ian@0: case intValue: ian@0: document_ += valueToString( value.asLargestInt() ); ian@0: break; ian@0: case uintValue: ian@0: document_ += valueToString( value.asLargestUInt() ); ian@0: break; ian@0: case realValue: ian@0: document_ += valueToString( value.asDouble() ); ian@0: break; ian@0: case stringValue: ian@0: document_ += valueToQuotedString( value.asCString() ); ian@0: break; ian@0: case booleanValue: ian@0: document_ += valueToString( value.asBool() ); ian@0: break; ian@0: case arrayValue: ian@0: { ian@0: document_ += "["; ian@0: int size = value.size(); ian@0: for ( int index =0; index < size; ++index ) ian@0: { ian@0: if ( index > 0 ) ian@0: document_ += ","; ian@0: writeValue( value[index] ); ian@0: } ian@0: document_ += "]"; ian@0: } ian@0: break; ian@0: case objectValue: ian@0: { ian@0: Value::Members members( value.getMemberNames() ); ian@0: document_ += "{"; ian@0: for ( Value::Members::iterator it = members.begin(); ian@0: it != members.end(); ian@0: ++it ) ian@0: { ian@0: const std::string &name = *it; ian@0: if ( it != members.begin() ) ian@0: document_ += ","; ian@0: document_ += valueToQuotedString( name.c_str() ); ian@0: document_ += yamlCompatiblityEnabled_ ? ": " ian@0: : ":"; ian@0: writeValue( value[name] ); ian@0: } ian@0: document_ += "}"; ian@0: } ian@0: break; ian@0: } ian@0: } ian@0: ian@0: ian@0: // Class StyledWriter ian@0: // ////////////////////////////////////////////////////////////////// ian@0: ian@0: StyledWriter::StyledWriter() ian@0: : rightMargin_( 74 ) ian@0: , indentSize_( 3 ) ian@0: , addChildValues_() ian@0: { ian@0: } ian@0: ian@0: ian@0: std::string ian@0: StyledWriter::write( const Value &root ) ian@0: { ian@0: document_ = ""; ian@0: addChildValues_ = false; ian@0: indentString_ = ""; ian@0: writeCommentBeforeValue( root ); ian@0: writeValue( root ); ian@0: writeCommentAfterValueOnSameLine( root ); ian@0: document_ += "\n"; ian@0: return document_; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::writeValue( const Value &value ) ian@0: { ian@0: switch ( value.type() ) ian@0: { ian@0: case nullValue: ian@0: pushValue( "null" ); ian@0: break; ian@0: case intValue: ian@0: pushValue( valueToString( value.asLargestInt() ) ); ian@0: break; ian@0: case uintValue: ian@0: pushValue( valueToString( value.asLargestUInt() ) ); ian@0: break; ian@0: case realValue: ian@0: pushValue( valueToString( value.asDouble() ) ); ian@0: break; ian@0: case stringValue: ian@0: pushValue( valueToQuotedString( value.asCString() ) ); ian@0: break; ian@0: case booleanValue: ian@0: pushValue( valueToString( value.asBool() ) ); ian@0: break; ian@0: case arrayValue: ian@0: writeArrayValue( value); ian@0: break; ian@0: case objectValue: ian@0: { ian@0: Value::Members members( value.getMemberNames() ); ian@0: if ( members.empty() ) ian@0: pushValue( "{}" ); ian@0: else ian@0: { ian@0: writeWithIndent( "{" ); ian@0: indent(); ian@0: Value::Members::iterator it = members.begin(); ian@0: for (;;) ian@0: { ian@0: const std::string &name = *it; ian@0: const Value &childValue = value[name]; ian@0: writeCommentBeforeValue( childValue ); ian@0: writeWithIndent( valueToQuotedString( name.c_str() ) ); ian@0: document_ += " : "; ian@0: writeValue( childValue ); ian@0: if ( ++it == members.end() ) ian@0: { ian@0: writeCommentAfterValueOnSameLine( childValue ); ian@0: break; ian@0: } ian@0: document_ += ","; ian@0: writeCommentAfterValueOnSameLine( childValue ); ian@0: } ian@0: unindent(); ian@0: writeWithIndent( "}" ); ian@0: } ian@0: } ian@0: break; ian@0: } ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::writeArrayValue( const Value &value ) ian@0: { ian@0: unsigned size = value.size(); ian@0: if ( size == 0 ) ian@0: pushValue( "[]" ); ian@0: else ian@0: { ian@0: bool isArrayMultiLine = isMultineArray( value ); ian@0: if ( isArrayMultiLine ) ian@0: { ian@0: writeWithIndent( "[" ); ian@0: indent(); ian@0: bool hasChildValue = !childValues_.empty(); ian@0: unsigned index =0; ian@0: for (;;) ian@0: { ian@0: const Value &childValue = value[index]; ian@0: writeCommentBeforeValue( childValue ); ian@0: if ( hasChildValue ) ian@0: writeWithIndent( childValues_[index] ); ian@0: else ian@0: { ian@0: writeIndent(); ian@0: writeValue( childValue ); ian@0: } ian@0: if ( ++index == size ) ian@0: { ian@0: writeCommentAfterValueOnSameLine( childValue ); ian@0: break; ian@0: } ian@0: document_ += ","; ian@0: writeCommentAfterValueOnSameLine( childValue ); ian@0: } ian@0: unindent(); ian@0: writeWithIndent( "]" ); ian@0: } ian@0: else // output on a single line ian@0: { ian@0: assert( childValues_.size() == size ); ian@0: document_ += "[ "; ian@0: for ( unsigned index =0; index < size; ++index ) ian@0: { ian@0: if ( index > 0 ) ian@0: document_ += ", "; ian@0: document_ += childValues_[index]; ian@0: } ian@0: document_ += " ]"; ian@0: } ian@0: } ian@0: } ian@0: ian@0: ian@0: bool ian@0: StyledWriter::isMultineArray( const Value &value ) ian@0: { ian@0: int size = value.size(); ian@0: bool isMultiLine = size*3 >= rightMargin_ ; ian@0: childValues_.clear(); ian@0: for ( int index =0; index < size && !isMultiLine; ++index ) ian@0: { ian@0: const Value &childValue = value[index]; ian@0: isMultiLine = isMultiLine || ian@0: ( (childValue.isArray() || childValue.isObject()) && ian@0: childValue.size() > 0 ); ian@0: } ian@0: if ( !isMultiLine ) // check if line length > max line length ian@0: { ian@0: childValues_.reserve( size ); ian@0: addChildValues_ = true; ian@0: int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' ian@0: for ( int index =0; index < size && !isMultiLine; ++index ) ian@0: { ian@0: writeValue( value[index] ); ian@0: lineLength += int( childValues_[index].length() ); ian@0: isMultiLine = isMultiLine && hasCommentForValue( value[index] ); ian@0: } ian@0: addChildValues_ = false; ian@0: isMultiLine = isMultiLine || lineLength >= rightMargin_; ian@0: } ian@0: return isMultiLine; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::pushValue( const std::string &value ) ian@0: { ian@0: if ( addChildValues_ ) ian@0: childValues_.push_back( value ); ian@0: else ian@0: document_ += value; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::writeIndent() ian@0: { ian@0: if ( !document_.empty() ) ian@0: { ian@0: char last = document_[document_.length()-1]; ian@0: if ( last == ' ' ) // already indented ian@0: return; ian@0: if ( last != '\n' ) // Comments may add new-line ian@0: document_ += '\n'; ian@0: } ian@0: document_ += indentString_; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::writeWithIndent( const std::string &value ) ian@0: { ian@0: writeIndent(); ian@0: document_ += value; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::indent() ian@0: { ian@0: indentString_ += std::string( indentSize_, ' ' ); ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::unindent() ian@0: { ian@0: assert( int(indentString_.size()) >= indentSize_ ); ian@0: indentString_.resize( indentString_.size() - indentSize_ ); ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::writeCommentBeforeValue( const Value &root ) ian@0: { ian@0: if ( !root.hasComment( commentBefore ) ) ian@0: return; ian@0: document_ += normalizeEOL( root.getComment( commentBefore ) ); ian@0: document_ += "\n"; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) ian@0: { ian@0: if ( root.hasComment( commentAfterOnSameLine ) ) ian@0: document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); ian@0: ian@0: if ( root.hasComment( commentAfter ) ) ian@0: { ian@0: document_ += "\n"; ian@0: document_ += normalizeEOL( root.getComment( commentAfter ) ); ian@0: document_ += "\n"; ian@0: } ian@0: } ian@0: ian@0: ian@0: bool ian@0: StyledWriter::hasCommentForValue( const Value &value ) ian@0: { ian@0: return value.hasComment( commentBefore ) ian@0: || value.hasComment( commentAfterOnSameLine ) ian@0: || value.hasComment( commentAfter ); ian@0: } ian@0: ian@0: ian@0: std::string ian@0: StyledWriter::normalizeEOL( const std::string &text ) ian@0: { ian@0: std::string normalized; ian@0: normalized.reserve( text.length() ); ian@0: const char *begin = text.c_str(); ian@0: const char *end = begin + text.length(); ian@0: const char *current = begin; ian@0: while ( current != end ) ian@0: { ian@0: char c = *current++; ian@0: if ( c == '\r' ) // mac or dos EOL ian@0: { ian@0: if ( *current == '\n' ) // convert dos EOL ian@0: ++current; ian@0: normalized += '\n'; ian@0: } ian@0: else // handle unix EOL & other char ian@0: normalized += c; ian@0: } ian@0: return normalized; ian@0: } ian@0: ian@0: ian@0: // Class StyledStreamWriter ian@0: // ////////////////////////////////////////////////////////////////// ian@0: ian@0: StyledStreamWriter::StyledStreamWriter( std::string indentation ) ian@0: : document_(NULL) ian@0: , rightMargin_( 74 ) ian@0: , indentation_( indentation ) ian@0: , addChildValues_() ian@0: { ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::write( std::ostream &out, const Value &root ) ian@0: { ian@0: document_ = &out; ian@0: addChildValues_ = false; ian@0: indentString_ = ""; ian@0: writeCommentBeforeValue( root ); ian@0: writeValue( root ); ian@0: writeCommentAfterValueOnSameLine( root ); ian@0: *document_ << "\n"; ian@0: document_ = NULL; // Forget the stream, for safety. ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::writeValue( const Value &value ) ian@0: { ian@0: switch ( value.type() ) ian@0: { ian@0: case nullValue: ian@0: pushValue( "null" ); ian@0: break; ian@0: case intValue: ian@0: pushValue( valueToString( value.asLargestInt() ) ); ian@0: break; ian@0: case uintValue: ian@0: pushValue( valueToString( value.asLargestUInt() ) ); ian@0: break; ian@0: case realValue: ian@0: pushValue( valueToString( value.asDouble() ) ); ian@0: break; ian@0: case stringValue: ian@0: pushValue( valueToQuotedString( value.asCString() ) ); ian@0: break; ian@0: case booleanValue: ian@0: pushValue( valueToString( value.asBool() ) ); ian@0: break; ian@0: case arrayValue: ian@0: writeArrayValue( value); ian@0: break; ian@0: case objectValue: ian@0: { ian@0: Value::Members members( value.getMemberNames() ); ian@0: if ( members.empty() ) ian@0: pushValue( "{}" ); ian@0: else ian@0: { ian@0: writeWithIndent( "{" ); ian@0: indent(); ian@0: Value::Members::iterator it = members.begin(); ian@0: for (;;) ian@0: { ian@0: const std::string &name = *it; ian@0: const Value &childValue = value[name]; ian@0: writeCommentBeforeValue( childValue ); ian@0: writeWithIndent( valueToQuotedString( name.c_str() ) ); ian@0: *document_ << " : "; ian@0: writeValue( childValue ); ian@0: if ( ++it == members.end() ) ian@0: { ian@0: writeCommentAfterValueOnSameLine( childValue ); ian@0: break; ian@0: } ian@0: *document_ << ","; ian@0: writeCommentAfterValueOnSameLine( childValue ); ian@0: } ian@0: unindent(); ian@0: writeWithIndent( "}" ); ian@0: } ian@0: } ian@0: break; ian@0: } ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::writeArrayValue( const Value &value ) ian@0: { ian@0: unsigned size = value.size(); ian@0: if ( size == 0 ) ian@0: pushValue( "[]" ); ian@0: else ian@0: { ian@0: bool isArrayMultiLine = isMultineArray( value ); ian@0: if ( isArrayMultiLine ) ian@0: { ian@0: writeWithIndent( "[" ); ian@0: indent(); ian@0: bool hasChildValue = !childValues_.empty(); ian@0: unsigned index =0; ian@0: for (;;) ian@0: { ian@0: const Value &childValue = value[index]; ian@0: writeCommentBeforeValue( childValue ); ian@0: if ( hasChildValue ) ian@0: writeWithIndent( childValues_[index] ); ian@0: else ian@0: { ian@0: writeIndent(); ian@0: writeValue( childValue ); ian@0: } ian@0: if ( ++index == size ) ian@0: { ian@0: writeCommentAfterValueOnSameLine( childValue ); ian@0: break; ian@0: } ian@0: *document_ << ","; ian@0: writeCommentAfterValueOnSameLine( childValue ); ian@0: } ian@0: unindent(); ian@0: writeWithIndent( "]" ); ian@0: } ian@0: else // output on a single line ian@0: { ian@0: assert( childValues_.size() == size ); ian@0: *document_ << "[ "; ian@0: for ( unsigned index =0; index < size; ++index ) ian@0: { ian@0: if ( index > 0 ) ian@0: *document_ << ", "; ian@0: *document_ << childValues_[index]; ian@0: } ian@0: *document_ << " ]"; ian@0: } ian@0: } ian@0: } ian@0: ian@0: ian@0: bool ian@0: StyledStreamWriter::isMultineArray( const Value &value ) ian@0: { ian@0: int size = value.size(); ian@0: bool isMultiLine = size*3 >= rightMargin_ ; ian@0: childValues_.clear(); ian@0: for ( int index =0; index < size && !isMultiLine; ++index ) ian@0: { ian@0: const Value &childValue = value[index]; ian@0: isMultiLine = isMultiLine || ian@0: ( (childValue.isArray() || childValue.isObject()) && ian@0: childValue.size() > 0 ); ian@0: } ian@0: if ( !isMultiLine ) // check if line length > max line length ian@0: { ian@0: childValues_.reserve( size ); ian@0: addChildValues_ = true; ian@0: int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' ian@0: for ( int index =0; index < size && !isMultiLine; ++index ) ian@0: { ian@0: writeValue( value[index] ); ian@0: lineLength += int( childValues_[index].length() ); ian@0: isMultiLine = isMultiLine && hasCommentForValue( value[index] ); ian@0: } ian@0: addChildValues_ = false; ian@0: isMultiLine = isMultiLine || lineLength >= rightMargin_; ian@0: } ian@0: return isMultiLine; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::pushValue( const std::string &value ) ian@0: { ian@0: if ( addChildValues_ ) ian@0: childValues_.push_back( value ); ian@0: else ian@0: *document_ << value; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::writeIndent() ian@0: { ian@0: /* ian@0: Some comments in this method would have been nice. ;-) ian@0: ian@0: if ( !document_.empty() ) ian@0: { ian@0: char last = document_[document_.length()-1]; ian@0: if ( last == ' ' ) // already indented ian@0: return; ian@0: if ( last != '\n' ) // Comments may add new-line ian@0: *document_ << '\n'; ian@0: } ian@0: */ ian@0: *document_ << '\n' << indentString_; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::writeWithIndent( const std::string &value ) ian@0: { ian@0: writeIndent(); ian@0: *document_ << value; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::indent() ian@0: { ian@0: indentString_ += indentation_; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::unindent() ian@0: { ian@0: assert( indentString_.size() >= indentation_.size() ); ian@0: indentString_.resize( indentString_.size() - indentation_.size() ); ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::writeCommentBeforeValue( const Value &root ) ian@0: { ian@0: if ( !root.hasComment( commentBefore ) ) ian@0: return; ian@0: *document_ << normalizeEOL( root.getComment( commentBefore ) ); ian@0: *document_ << "\n"; ian@0: } ian@0: ian@0: ian@0: void ian@0: StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) ian@0: { ian@0: if ( root.hasComment( commentAfterOnSameLine ) ) ian@0: *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); ian@0: ian@0: if ( root.hasComment( commentAfter ) ) ian@0: { ian@0: *document_ << "\n"; ian@0: *document_ << normalizeEOL( root.getComment( commentAfter ) ); ian@0: *document_ << "\n"; ian@0: } ian@0: } ian@0: ian@0: ian@0: bool ian@0: StyledStreamWriter::hasCommentForValue( const Value &value ) ian@0: { ian@0: return value.hasComment( commentBefore ) ian@0: || value.hasComment( commentAfterOnSameLine ) ian@0: || value.hasComment( commentAfter ); ian@0: } ian@0: ian@0: ian@0: std::string ian@0: StyledStreamWriter::normalizeEOL( const std::string &text ) ian@0: { ian@0: std::string normalized; ian@0: normalized.reserve( text.length() ); ian@0: const char *begin = text.c_str(); ian@0: const char *end = begin + text.length(); ian@0: const char *current = begin; ian@0: while ( current != end ) ian@0: { ian@0: char c = *current++; ian@0: if ( c == '\r' ) // mac or dos EOL ian@0: { ian@0: if ( *current == '\n' ) // convert dos EOL ian@0: ++current; ian@0: normalized += '\n'; ian@0: } ian@0: else // handle unix EOL & other char ian@0: normalized += c; ian@0: } ian@0: return normalized; ian@0: } ian@0: ian@0: ian@0: std::ostream& operator<<( std::ostream &sout, const Value &root ) ian@0: { ian@0: Json::StyledStreamWriter writer; ian@0: writer.write(sout, root); ian@0: return sout; ian@0: } ian@0: ian@0: ian@0: } // namespace Json