mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +00:00
932 lines
21 KiB
C++
932 lines
21 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
Copyright (c) 2011-2013, OpenCoin, Inc.
|
|
*/
|
|
//==============================================================================
|
|
|
|
namespace Json
|
|
{
|
|
|
|
static bool isControlCharacter (char ch)
|
|
{
|
|
return ch > 0 && ch <= 0x1F;
|
|
}
|
|
|
|
static bool containsControlCharacter ( const char* str )
|
|
{
|
|
while ( *str )
|
|
{
|
|
if ( isControlCharacter ( * (str++) ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
static void uintToString ( unsigned int value,
|
|
char*& current )
|
|
{
|
|
*--current = 0;
|
|
|
|
do
|
|
{
|
|
*--current = (value % 10) + '0';
|
|
value /= 10;
|
|
}
|
|
while ( value != 0 );
|
|
}
|
|
|
|
std::string valueToString ( Int value )
|
|
{
|
|
char buffer[32];
|
|
char* current = buffer + sizeof (buffer);
|
|
bool isNegative = value < 0;
|
|
|
|
if ( isNegative )
|
|
value = -value;
|
|
|
|
uintToString ( UInt (value), current );
|
|
|
|
if ( isNegative )
|
|
*--current = '-';
|
|
|
|
assert ( current >= buffer );
|
|
return current;
|
|
}
|
|
|
|
|
|
std::string valueToString ( UInt value )
|
|
{
|
|
char buffer[32];
|
|
char* current = buffer + sizeof (buffer);
|
|
uintToString ( value, current );
|
|
assert ( current >= buffer );
|
|
return current;
|
|
}
|
|
|
|
std::string valueToString ( double value )
|
|
{
|
|
char buffer[32];
|
|
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
|
|
sprintf_s (buffer, sizeof (buffer), "%#f", value);
|
|
#else
|
|
sprintf (buffer, "%#f", value);
|
|
#endif
|
|
char* ch = buffer + strlen (buffer) - 1;
|
|
|
|
if (*ch != '0') return buffer; // nothing to truncate, so save time
|
|
|
|
while (ch > buffer && *ch == '0')
|
|
{
|
|
--ch;
|
|
}
|
|
|
|
char* last_nonzero = ch;
|
|
|
|
while (ch >= buffer)
|
|
{
|
|
switch (*ch)
|
|
{
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
--ch;
|
|
continue;
|
|
|
|
case '.':
|
|
// Truncate zeroes to save bytes in output, but keep one.
|
|
* (last_nonzero + 2) = '\0';
|
|
return buffer;
|
|
|
|
default:
|
|
return buffer;
|
|
}
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
|
|
std::string valueToString ( bool value )
|
|
{
|
|
return value ? "true" : "false";
|
|
}
|
|
|
|
std::string valueToQuotedString ( const char* value )
|
|
{
|
|
// Not sure how to handle unicode...
|
|
if (strpbrk (value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter ( value ))
|
|
return std::string ("\"") + value + "\"";
|
|
|
|
// We have to walk value and escape any special characters.
|
|
// Appending to std::string is not efficient, but this should be rare.
|
|
// (Note: forward slashes are *not* rare, but I am not escaping them.)
|
|
unsigned maxsize = strlen (value) * 2 + 3; // allescaped+quotes+NULL
|
|
std::string result;
|
|
result.reserve (maxsize); // to avoid lots of mallocs
|
|
result += "\"";
|
|
|
|
for (const char* c = value; *c != 0; ++c)
|
|
{
|
|
switch (*c)
|
|
{
|
|
case '\"':
|
|
result += "\\\"";
|
|
break;
|
|
|
|
case '\\':
|
|
result += "\\\\";
|
|
break;
|
|
|
|
case '\b':
|
|
result += "\\b";
|
|
break;
|
|
|
|
case '\f':
|
|
result += "\\f";
|
|
break;
|
|
|
|
case '\n':
|
|
result += "\\n";
|
|
break;
|
|
|
|
case '\r':
|
|
result += "\\r";
|
|
break;
|
|
|
|
case '\t':
|
|
result += "\\t";
|
|
break;
|
|
|
|
//case '/':
|
|
// Even though \/ is considered a legal escape in JSON, a bare
|
|
// slash is also legal, so I see no reason to escape it.
|
|
// (I hope I am not misunderstanding something.
|
|
// blep notes: actually escaping \/ may be useful in javascript to avoid </
|
|
// sequence.
|
|
// Should add a flag to allow this compatibility mode and prevent this
|
|
// sequence from occurring.
|
|
default:
|
|
if ( isControlCharacter ( *c ) )
|
|
{
|
|
std::ostringstream oss;
|
|
oss << "\\u" << std::hex << std::uppercase << std::setfill ('0') << std::setw (4) << static_cast<int> (*c);
|
|
result += oss.str ();
|
|
}
|
|
else
|
|
{
|
|
result += *c;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
result += "\"";
|
|
return result;
|
|
}
|
|
|
|
// Class Writer
|
|
// //////////////////////////////////////////////////////////////////
|
|
Writer::~Writer ()
|
|
{
|
|
}
|
|
|
|
|
|
// Class FastWriter
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
FastWriter::FastWriter ()
|
|
: yamlCompatiblityEnabled_ ( false )
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
FastWriter::enableYAMLCompatibility ()
|
|
{
|
|
yamlCompatiblityEnabled_ = true;
|
|
}
|
|
|
|
|
|
std::string
|
|
FastWriter::write ( const Value& root )
|
|
{
|
|
document_ = "";
|
|
writeValue ( root );
|
|
document_ += "\n";
|
|
return document_;
|
|
}
|
|
|
|
|
|
void
|
|
FastWriter::writeValue ( const Value& value )
|
|
{
|
|
switch ( value.type () )
|
|
{
|
|
case nullValue:
|
|
document_ += "null";
|
|
break;
|
|
|
|
case intValue:
|
|
document_ += valueToString ( value.asInt () );
|
|
break;
|
|
|
|
case uintValue:
|
|
document_ += valueToString ( value.asUInt () );
|
|
break;
|
|
|
|
case realValue:
|
|
document_ += valueToString ( value.asDouble () );
|
|
break;
|
|
|
|
case stringValue:
|
|
document_ += valueToQuotedString ( value.asCString () );
|
|
break;
|
|
|
|
case booleanValue:
|
|
document_ += valueToString ( value.asBool () );
|
|
break;
|
|
|
|
case arrayValue:
|
|
{
|
|
document_ += "[";
|
|
int size = value.size ();
|
|
|
|
for ( int index = 0; index < size; ++index )
|
|
{
|
|
if ( index > 0 )
|
|
document_ += ",";
|
|
|
|
writeValue ( value[index] );
|
|
}
|
|
|
|
document_ += "]";
|
|
}
|
|
break;
|
|
|
|
case objectValue:
|
|
{
|
|
Value::Members members ( value.getMemberNames () );
|
|
document_ += "{";
|
|
|
|
for ( Value::Members::iterator it = members.begin ();
|
|
it != members.end ();
|
|
++it )
|
|
{
|
|
const std::string& name = *it;
|
|
|
|
if ( it != members.begin () )
|
|
document_ += ",";
|
|
|
|
document_ += valueToQuotedString ( name.c_str () );
|
|
document_ += yamlCompatiblityEnabled_ ? ": "
|
|
: ":";
|
|
writeValue ( value[name] );
|
|
}
|
|
|
|
document_ += "}";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// Class StyledWriter
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
StyledWriter::StyledWriter ()
|
|
: rightMargin_ ( 74 )
|
|
, indentSize_ ( 3 )
|
|
{
|
|
}
|
|
|
|
|
|
std::string
|
|
StyledWriter::write ( const Value& root )
|
|
{
|
|
document_ = "";
|
|
addChildValues_ = false;
|
|
indentString_ = "";
|
|
writeCommentBeforeValue ( root );
|
|
writeValue ( root );
|
|
writeCommentAfterValueOnSameLine ( root );
|
|
document_ += "\n";
|
|
return document_;
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::writeValue ( const Value& value )
|
|
{
|
|
switch ( value.type () )
|
|
{
|
|
case nullValue:
|
|
pushValue ( "null" );
|
|
break;
|
|
|
|
case intValue:
|
|
pushValue ( valueToString ( value.asInt () ) );
|
|
break;
|
|
|
|
case uintValue:
|
|
pushValue ( valueToString ( value.asUInt () ) );
|
|
break;
|
|
|
|
case realValue:
|
|
pushValue ( valueToString ( value.asDouble () ) );
|
|
break;
|
|
|
|
case stringValue:
|
|
pushValue ( valueToQuotedString ( value.asCString () ) );
|
|
break;
|
|
|
|
case booleanValue:
|
|
pushValue ( valueToString ( value.asBool () ) );
|
|
break;
|
|
|
|
case arrayValue:
|
|
writeArrayValue ( value);
|
|
break;
|
|
|
|
case objectValue:
|
|
{
|
|
Value::Members members ( value.getMemberNames () );
|
|
|
|
if ( members.empty () )
|
|
pushValue ( "{}" );
|
|
else
|
|
{
|
|
writeWithIndent ( "{" );
|
|
indent ();
|
|
Value::Members::iterator it = members.begin ();
|
|
|
|
while ( true )
|
|
{
|
|
const std::string& name = *it;
|
|
const Value& childValue = value[name];
|
|
writeCommentBeforeValue ( childValue );
|
|
writeWithIndent ( valueToQuotedString ( name.c_str () ) );
|
|
document_ += " : ";
|
|
writeValue ( childValue );
|
|
|
|
if ( ++it == members.end () )
|
|
{
|
|
writeCommentAfterValueOnSameLine ( childValue );
|
|
break;
|
|
}
|
|
|
|
document_ += ",";
|
|
writeCommentAfterValueOnSameLine ( childValue );
|
|
}
|
|
|
|
unindent ();
|
|
writeWithIndent ( "}" );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::writeArrayValue ( const Value& value )
|
|
{
|
|
unsigned size = value.size ();
|
|
|
|
if ( size == 0 )
|
|
pushValue ( "[]" );
|
|
else
|
|
{
|
|
bool isArrayMultiLine = isMultineArray ( value );
|
|
|
|
if ( isArrayMultiLine )
|
|
{
|
|
writeWithIndent ( "[" );
|
|
indent ();
|
|
bool hasChildValue = !childValues_.empty ();
|
|
unsigned index = 0;
|
|
|
|
while ( true )
|
|
{
|
|
const Value& childValue = value[index];
|
|
writeCommentBeforeValue ( childValue );
|
|
|
|
if ( hasChildValue )
|
|
writeWithIndent ( childValues_[index] );
|
|
else
|
|
{
|
|
writeIndent ();
|
|
writeValue ( childValue );
|
|
}
|
|
|
|
if ( ++index == size )
|
|
{
|
|
writeCommentAfterValueOnSameLine ( childValue );
|
|
break;
|
|
}
|
|
|
|
document_ += ",";
|
|
writeCommentAfterValueOnSameLine ( childValue );
|
|
}
|
|
|
|
unindent ();
|
|
writeWithIndent ( "]" );
|
|
}
|
|
else // output on a single line
|
|
{
|
|
assert ( childValues_.size () == size );
|
|
document_ += "[ ";
|
|
|
|
for ( unsigned index = 0; index < size; ++index )
|
|
{
|
|
if ( index > 0 )
|
|
document_ += ", ";
|
|
|
|
document_ += childValues_[index];
|
|
}
|
|
|
|
document_ += " ]";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
StyledWriter::isMultineArray ( const Value& value )
|
|
{
|
|
int size = value.size ();
|
|
bool isMultiLine = size * 3 >= rightMargin_ ;
|
|
childValues_.clear ();
|
|
|
|
for ( int index = 0; index < size && !isMultiLine; ++index )
|
|
{
|
|
const Value& childValue = value[index];
|
|
isMultiLine = isMultiLine ||
|
|
( (childValue.isArray () || childValue.isObject ()) &&
|
|
childValue.size () > 0 );
|
|
}
|
|
|
|
if ( !isMultiLine ) // check if line length > max line length
|
|
{
|
|
childValues_.reserve ( size );
|
|
addChildValues_ = true;
|
|
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
|
|
|
for ( int index = 0; index < size && !isMultiLine; ++index )
|
|
{
|
|
writeValue ( value[index] );
|
|
lineLength += int ( childValues_[index].length () );
|
|
isMultiLine = isMultiLine && hasCommentForValue ( value[index] );
|
|
}
|
|
|
|
addChildValues_ = false;
|
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
|
}
|
|
|
|
return isMultiLine;
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::pushValue ( const std::string& value )
|
|
{
|
|
if ( addChildValues_ )
|
|
childValues_.push_back ( value );
|
|
else
|
|
document_ += value;
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::writeIndent ()
|
|
{
|
|
if ( !document_.empty () )
|
|
{
|
|
char last = document_[document_.length () - 1];
|
|
|
|
if ( last == ' ' ) // already indented
|
|
return;
|
|
|
|
if ( last != '\n' ) // Comments may add new-line
|
|
document_ += '\n';
|
|
}
|
|
|
|
document_ += indentString_;
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::writeWithIndent ( const std::string& value )
|
|
{
|
|
writeIndent ();
|
|
document_ += value;
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::indent ()
|
|
{
|
|
indentString_ += std::string ( indentSize_, ' ' );
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::unindent ()
|
|
{
|
|
assert ( int (indentString_.size ()) >= indentSize_ );
|
|
indentString_.resize ( indentString_.size () - indentSize_ );
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::writeCommentBeforeValue ( const Value& root )
|
|
{
|
|
if ( !root.hasComment ( commentBefore ) )
|
|
return;
|
|
|
|
document_ += normalizeEOL ( root.getComment ( commentBefore ) );
|
|
document_ += "\n";
|
|
}
|
|
|
|
|
|
void
|
|
StyledWriter::writeCommentAfterValueOnSameLine ( const Value& root )
|
|
{
|
|
if ( root.hasComment ( commentAfterOnSameLine ) )
|
|
document_ += " " + normalizeEOL ( root.getComment ( commentAfterOnSameLine ) );
|
|
|
|
if ( root.hasComment ( commentAfter ) )
|
|
{
|
|
document_ += "\n";
|
|
document_ += normalizeEOL ( root.getComment ( commentAfter ) );
|
|
document_ += "\n";
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
StyledWriter::hasCommentForValue ( const Value& value )
|
|
{
|
|
return value.hasComment ( commentBefore )
|
|
|| value.hasComment ( commentAfterOnSameLine )
|
|
|| value.hasComment ( commentAfter );
|
|
}
|
|
|
|
|
|
std::string
|
|
StyledWriter::normalizeEOL ( const std::string& text )
|
|
{
|
|
std::string normalized;
|
|
normalized.reserve ( text.length () );
|
|
const char* begin = text.c_str ();
|
|
const char* end = begin + text.length ();
|
|
const char* current = begin;
|
|
|
|
while ( current != end )
|
|
{
|
|
char c = *current++;
|
|
|
|
if ( c == '\r' ) // mac or dos EOL
|
|
{
|
|
if ( *current == '\n' ) // convert dos EOL
|
|
++current;
|
|
|
|
normalized += '\n';
|
|
}
|
|
else // handle unix EOL & other char
|
|
normalized += c;
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
|
|
// Class StyledStreamWriter
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
StyledStreamWriter::StyledStreamWriter ( std::string indentation )
|
|
: document_ (NULL)
|
|
, rightMargin_ ( 74 )
|
|
, indentation_ ( indentation )
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::write ( std::ostream& out, const Value& root )
|
|
{
|
|
document_ = &out;
|
|
addChildValues_ = false;
|
|
indentString_ = "";
|
|
writeCommentBeforeValue ( root );
|
|
writeValue ( root );
|
|
writeCommentAfterValueOnSameLine ( root );
|
|
*document_ << "\n";
|
|
document_ = NULL; // Forget the stream, for safety.
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::writeValue ( const Value& value )
|
|
{
|
|
switch ( value.type () )
|
|
{
|
|
case nullValue:
|
|
pushValue ( "null" );
|
|
break;
|
|
|
|
case intValue:
|
|
pushValue ( valueToString ( value.asInt () ) );
|
|
break;
|
|
|
|
case uintValue:
|
|
pushValue ( valueToString ( value.asUInt () ) );
|
|
break;
|
|
|
|
case realValue:
|
|
pushValue ( valueToString ( value.asDouble () ) );
|
|
break;
|
|
|
|
case stringValue:
|
|
pushValue ( valueToQuotedString ( value.asCString () ) );
|
|
break;
|
|
|
|
case booleanValue:
|
|
pushValue ( valueToString ( value.asBool () ) );
|
|
break;
|
|
|
|
case arrayValue:
|
|
writeArrayValue ( value);
|
|
break;
|
|
|
|
case objectValue:
|
|
{
|
|
Value::Members members ( value.getMemberNames () );
|
|
|
|
if ( members.empty () )
|
|
pushValue ( "{}" );
|
|
else
|
|
{
|
|
writeWithIndent ( "{" );
|
|
indent ();
|
|
Value::Members::iterator it = members.begin ();
|
|
|
|
while ( true )
|
|
{
|
|
const std::string& name = *it;
|
|
const Value& childValue = value[name];
|
|
writeCommentBeforeValue ( childValue );
|
|
writeWithIndent ( valueToQuotedString ( name.c_str () ) );
|
|
*document_ << " : ";
|
|
writeValue ( childValue );
|
|
|
|
if ( ++it == members.end () )
|
|
{
|
|
writeCommentAfterValueOnSameLine ( childValue );
|
|
break;
|
|
}
|
|
|
|
*document_ << ",";
|
|
writeCommentAfterValueOnSameLine ( childValue );
|
|
}
|
|
|
|
unindent ();
|
|
writeWithIndent ( "}" );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::writeArrayValue ( const Value& value )
|
|
{
|
|
unsigned size = value.size ();
|
|
|
|
if ( size == 0 )
|
|
pushValue ( "[]" );
|
|
else
|
|
{
|
|
bool isArrayMultiLine = isMultineArray ( value );
|
|
|
|
if ( isArrayMultiLine )
|
|
{
|
|
writeWithIndent ( "[" );
|
|
indent ();
|
|
bool hasChildValue = !childValues_.empty ();
|
|
unsigned index = 0;
|
|
|
|
while ( true )
|
|
{
|
|
const Value& childValue = value[index];
|
|
writeCommentBeforeValue ( childValue );
|
|
|
|
if ( hasChildValue )
|
|
writeWithIndent ( childValues_[index] );
|
|
else
|
|
{
|
|
writeIndent ();
|
|
writeValue ( childValue );
|
|
}
|
|
|
|
if ( ++index == size )
|
|
{
|
|
writeCommentAfterValueOnSameLine ( childValue );
|
|
break;
|
|
}
|
|
|
|
*document_ << ",";
|
|
writeCommentAfterValueOnSameLine ( childValue );
|
|
}
|
|
|
|
unindent ();
|
|
writeWithIndent ( "]" );
|
|
}
|
|
else // output on a single line
|
|
{
|
|
assert ( childValues_.size () == size );
|
|
*document_ << "[ ";
|
|
|
|
for ( unsigned index = 0; index < size; ++index )
|
|
{
|
|
if ( index > 0 )
|
|
*document_ << ", ";
|
|
|
|
*document_ << childValues_[index];
|
|
}
|
|
|
|
*document_ << " ]";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
StyledStreamWriter::isMultineArray ( const Value& value )
|
|
{
|
|
int size = value.size ();
|
|
bool isMultiLine = size * 3 >= rightMargin_ ;
|
|
childValues_.clear ();
|
|
|
|
for ( int index = 0; index < size && !isMultiLine; ++index )
|
|
{
|
|
const Value& childValue = value[index];
|
|
isMultiLine = isMultiLine ||
|
|
( (childValue.isArray () || childValue.isObject ()) &&
|
|
childValue.size () > 0 );
|
|
}
|
|
|
|
if ( !isMultiLine ) // check if line length > max line length
|
|
{
|
|
childValues_.reserve ( size );
|
|
addChildValues_ = true;
|
|
int lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]'
|
|
|
|
for ( int index = 0; index < size && !isMultiLine; ++index )
|
|
{
|
|
writeValue ( value[index] );
|
|
lineLength += int ( childValues_[index].length () );
|
|
isMultiLine = isMultiLine && hasCommentForValue ( value[index] );
|
|
}
|
|
|
|
addChildValues_ = false;
|
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
|
}
|
|
|
|
return isMultiLine;
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::pushValue ( const std::string& value )
|
|
{
|
|
if ( addChildValues_ )
|
|
childValues_.push_back ( value );
|
|
else
|
|
*document_ << value;
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::writeIndent ()
|
|
{
|
|
/*
|
|
Some comments in this method would have been nice. ;-)
|
|
|
|
if ( !document_.empty() )
|
|
{
|
|
char last = document_[document_.length()-1];
|
|
if ( last == ' ' ) // already indented
|
|
return;
|
|
if ( last != '\n' ) // Comments may add new-line
|
|
*document_ << '\n';
|
|
}
|
|
*/
|
|
*document_ << '\n' << indentString_;
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::writeWithIndent ( const std::string& value )
|
|
{
|
|
writeIndent ();
|
|
*document_ << value;
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::indent ()
|
|
{
|
|
indentString_ += indentation_;
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::unindent ()
|
|
{
|
|
assert ( indentString_.size () >= indentation_.size () );
|
|
indentString_.resize ( indentString_.size () - indentation_.size () );
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::writeCommentBeforeValue ( const Value& root )
|
|
{
|
|
if ( !root.hasComment ( commentBefore ) )
|
|
return;
|
|
|
|
*document_ << normalizeEOL ( root.getComment ( commentBefore ) );
|
|
*document_ << "\n";
|
|
}
|
|
|
|
|
|
void
|
|
StyledStreamWriter::writeCommentAfterValueOnSameLine ( const Value& root )
|
|
{
|
|
if ( root.hasComment ( commentAfterOnSameLine ) )
|
|
*document_ << " " + normalizeEOL ( root.getComment ( commentAfterOnSameLine ) );
|
|
|
|
if ( root.hasComment ( commentAfter ) )
|
|
{
|
|
*document_ << "\n";
|
|
*document_ << normalizeEOL ( root.getComment ( commentAfter ) );
|
|
*document_ << "\n";
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
StyledStreamWriter::hasCommentForValue ( const Value& value )
|
|
{
|
|
return value.hasComment ( commentBefore )
|
|
|| value.hasComment ( commentAfterOnSameLine )
|
|
|| value.hasComment ( commentAfter );
|
|
}
|
|
|
|
|
|
std::string
|
|
StyledStreamWriter::normalizeEOL ( const std::string& text )
|
|
{
|
|
std::string normalized;
|
|
normalized.reserve ( text.length () );
|
|
const char* begin = text.c_str ();
|
|
const char* end = begin + text.length ();
|
|
const char* current = begin;
|
|
|
|
while ( current != end )
|
|
{
|
|
char c = *current++;
|
|
|
|
if ( c == '\r' ) // mac or dos EOL
|
|
{
|
|
if ( *current == '\n' ) // convert dos EOL
|
|
++current;
|
|
|
|
normalized += '\n';
|
|
}
|
|
else // handle unix EOL & other char
|
|
normalized += c;
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
|
|
std::ostream& operator<< ( std::ostream& sout, const Value& root )
|
|
{
|
|
Json::StyledStreamWriter writer;
|
|
writer.write (sout, root);
|
|
return sout;
|
|
}
|
|
|
|
|
|
} // namespace Json
|