1
/* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
* JSON Library, originally from http://jsoncpp.sourceforge.net/
5
* Copyright (C) 2011 Stewart Smith
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions are
12
* * Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
15
* * Redistributions in binary form must reproduce the above
16
* copyright notice, this list of conditions and the following disclaimer
17
* in the documentation and/or other materials provided with the
20
* * The names of its contributors may not be used to endorse or
21
* promote products derived from this software without specific prior
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
#include <plugin/json_server/json/writer.h>
49
static bool isControlCharacter(char ch)
51
return ch > 0 && ch <= 0x1F;
54
static bool containsControlCharacter( const char* str )
58
if ( isControlCharacter( *(str++) ) )
63
static void uintToString( unsigned int value,
69
*--current = (value % 10) + '0';
75
std::string valueToString( Int value )
78
char *current = buffer + sizeof(buffer);
79
bool isNegative = value < 0;
82
uintToString( UInt(value), current );
85
assert( current >= buffer );
90
std::string valueToString( UInt value )
93
char *current = buffer + sizeof(buffer);
94
uintToString( value, current );
95
assert( current >= buffer );
99
std::string valueToString( double value )
102
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
103
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
105
sprintf(buffer, "%#.16g", value);
107
char* ch = buffer + strlen(buffer) - 1;
108
if (*ch != '0') return buffer; // nothing to truncate, so save time
109
while(ch > buffer && *ch == '0'){
112
char* last_nonzero = ch;
128
// Truncate zeroes to save bytes in output, but keep one.
129
*(last_nonzero+2) = '\0';
139
std::string valueToString( bool value )
141
return value ? "true" : "false";
144
std::string valueToQuotedString( const char *value )
146
// Not sure how to handle unicode...
147
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
148
return std::string("\"") + value + "\"";
149
// We have to walk value and escape any special characters.
150
// Appending to std::string is not efficient, but this should be rare.
151
// (Note: forward slashes are *not* rare, but I am not escaping them.)
152
unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
154
result.reserve(maxsize); // to avoid lots of mallocs
156
for (const char* c=value; *c != 0; ++c)
182
// Even though \/ is considered a legal escape in JSON, a bare
183
// slash is also legal, so I see no reason to escape it.
184
// (I hope I am not misunderstanding something.
185
// blep notes: actually escaping \/ may be useful in javascript to avoid </
187
// Should add a flag to allow this compatibility mode and prevent this
188
// sequence from occurring.
190
if ( isControlCharacter( *c ) )
192
std::ostringstream oss;
193
oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
208
// //////////////////////////////////////////////////////////////////
215
// //////////////////////////////////////////////////////////////////
217
FastWriter::FastWriter()
218
: yamlCompatiblityEnabled_( false )
224
FastWriter::enableYAMLCompatibility()
226
yamlCompatiblityEnabled_ = true;
231
FastWriter::write( const Value &root )
241
FastWriter::writeValue( const Value &value )
243
switch ( value.type() )
249
document_ += valueToString( value.asInt() );
252
document_ += valueToString( value.asUInt() );
255
document_ += valueToString( value.asDouble() );
258
document_ += valueToQuotedString( value.asCString() );
261
document_ += valueToString( value.asBool() );
266
int size = value.size();
267
for ( int index =0; index < size; ++index )
271
writeValue( value[index] );
278
Value::Members members( value.getMemberNames() );
280
for ( Value::Members::iterator it = members.begin();
284
const std::string &name = *it;
285
if ( it != members.begin() )
287
document_ += valueToQuotedString( name.c_str() );
288
document_ += yamlCompatiblityEnabled_ ? ": "
290
writeValue( value[name] );
299
// Class StyledWriter
300
// //////////////////////////////////////////////////////////////////
302
StyledWriter::StyledWriter()
310
StyledWriter::write( const Value &root )
313
addChildValues_ = false;
315
writeCommentBeforeValue( root );
317
writeCommentAfterValueOnSameLine( root );
324
StyledWriter::writeValue( const Value &value )
326
switch ( value.type() )
332
pushValue( valueToString( value.asInt() ) );
335
pushValue( valueToString( value.asUInt() ) );
338
pushValue( valueToString( value.asDouble() ) );
341
pushValue( valueToQuotedString( value.asCString() ) );
344
pushValue( valueToString( value.asBool() ) );
347
writeArrayValue( value);
351
Value::Members members( value.getMemberNames() );
352
if ( members.empty() )
356
writeWithIndent( "{" );
358
Value::Members::iterator it = members.begin();
361
const std::string &name = *it;
362
const Value &childValue = value[name];
363
writeCommentBeforeValue( childValue );
364
writeWithIndent( valueToQuotedString( name.c_str() ) );
366
writeValue( childValue );
367
if ( ++it == members.end() )
369
writeCommentAfterValueOnSameLine( childValue );
373
writeCommentAfterValueOnSameLine( childValue );
376
writeWithIndent( "}" );
385
StyledWriter::writeArrayValue( const Value &value )
387
unsigned size = value.size();
392
bool isArrayMultiLine = isMultineArray( value );
393
if ( isArrayMultiLine )
395
writeWithIndent( "[" );
397
bool hasChildValue = !childValues_.empty();
401
const Value &childValue = value[index];
402
writeCommentBeforeValue( childValue );
404
writeWithIndent( childValues_[index] );
408
writeValue( childValue );
410
if ( ++index == size )
412
writeCommentAfterValueOnSameLine( childValue );
416
writeCommentAfterValueOnSameLine( childValue );
419
writeWithIndent( "]" );
421
else // output on a single line
423
assert( childValues_.size() == size );
425
for ( unsigned index =0; index < size; ++index )
429
document_ += childValues_[index];
438
StyledWriter::isMultineArray( const Value &value )
440
int size = value.size();
441
bool isMultiLine = size*3 >= rightMargin_ ;
442
childValues_.clear();
443
for ( int index =0; index < size && !isMultiLine; ++index )
445
const Value &childValue = value[index];
446
isMultiLine = isMultiLine ||
447
( (childValue.isArray() || childValue.isObject()) &&
448
childValue.size() > 0 );
450
if ( !isMultiLine ) // check if line length > max line length
452
childValues_.reserve( size );
453
addChildValues_ = true;
454
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
455
for ( int index =0; index < size && !isMultiLine; ++index )
457
writeValue( value[index] );
458
lineLength += int( childValues_[index].length() );
459
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
461
addChildValues_ = false;
462
isMultiLine = isMultiLine || lineLength >= rightMargin_;
469
StyledWriter::pushValue( const std::string &value )
471
if ( addChildValues_ )
472
childValues_.push_back( value );
479
StyledWriter::writeIndent()
481
if ( !document_.empty() )
483
char last = document_[document_.length()-1];
484
if ( last == ' ' ) // already indented
486
if ( last != '\n' ) // Comments may add new-line
489
document_ += indentString_;
494
StyledWriter::writeWithIndent( const std::string &value )
502
StyledWriter::indent()
504
indentString_ += std::string( indentSize_, ' ' );
509
StyledWriter::unindent()
511
assert( int(indentString_.size()) >= indentSize_ );
512
indentString_.resize( indentString_.size() - indentSize_ );
517
StyledWriter::writeCommentBeforeValue( const Value &root )
519
if ( !root.hasComment( commentBefore ) )
521
document_ += normalizeEOL( root.getComment( commentBefore ) );
527
StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
529
if ( root.hasComment( commentAfterOnSameLine ) )
530
document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
532
if ( root.hasComment( commentAfter ) )
535
document_ += normalizeEOL( root.getComment( commentAfter ) );
542
StyledWriter::hasCommentForValue( const Value &value )
544
return value.hasComment( commentBefore )
545
|| value.hasComment( commentAfterOnSameLine )
546
|| value.hasComment( commentAfter );
551
StyledWriter::normalizeEOL( const std::string &text )
553
std::string normalized;
554
normalized.reserve( text.length() );
555
const char *begin = text.c_str();
556
const char *end = begin + text.length();
557
const char *current = begin;
558
while ( current != end )
561
if ( c == '\r' ) // mac or dos EOL
563
if ( *current == '\n' ) // convert dos EOL
567
else // handle unix EOL & other char
574
// Class StyledStreamWriter
575
// //////////////////////////////////////////////////////////////////
577
StyledStreamWriter::StyledStreamWriter( std::string indentation )
580
, indentation_( indentation )
586
StyledStreamWriter::write( std::ostream &out, const Value &root )
589
addChildValues_ = false;
591
writeCommentBeforeValue( root );
593
writeCommentAfterValueOnSameLine( root );
595
document_ = NULL; // Forget the stream, for safety.
600
StyledStreamWriter::writeValue( const Value &value )
602
switch ( value.type() )
608
pushValue( valueToString( value.asInt() ) );
611
pushValue( valueToString( value.asUInt() ) );
614
pushValue( valueToString( value.asDouble() ) );
617
pushValue( valueToQuotedString( value.asCString() ) );
620
pushValue( valueToString( value.asBool() ) );
623
writeArrayValue( value);
627
Value::Members members( value.getMemberNames() );
628
if ( members.empty() )
632
writeWithIndent( "{" );
634
Value::Members::iterator it = members.begin();
637
const std::string &name = *it;
638
const Value &childValue = value[name];
639
writeCommentBeforeValue( childValue );
640
writeWithIndent( valueToQuotedString( name.c_str() ) );
642
writeValue( childValue );
643
if ( ++it == members.end() )
645
writeCommentAfterValueOnSameLine( childValue );
649
writeCommentAfterValueOnSameLine( childValue );
652
writeWithIndent( "}" );
661
StyledStreamWriter::writeArrayValue( const Value &value )
663
unsigned size = value.size();
668
bool isArrayMultiLine = isMultineArray( value );
669
if ( isArrayMultiLine )
671
writeWithIndent( "[" );
673
bool hasChildValue = !childValues_.empty();
677
const Value &childValue = value[index];
678
writeCommentBeforeValue( childValue );
680
writeWithIndent( childValues_[index] );
684
writeValue( childValue );
686
if ( ++index == size )
688
writeCommentAfterValueOnSameLine( childValue );
692
writeCommentAfterValueOnSameLine( childValue );
695
writeWithIndent( "]" );
697
else // output on a single line
699
assert( childValues_.size() == size );
701
for ( unsigned index =0; index < size; ++index )
705
*document_ << childValues_[index];
714
StyledStreamWriter::isMultineArray( const Value &value )
716
int size = value.size();
717
bool isMultiLine = size*3 >= rightMargin_ ;
718
childValues_.clear();
719
for ( int index =0; index < size && !isMultiLine; ++index )
721
const Value &childValue = value[index];
722
isMultiLine = isMultiLine ||
723
( (childValue.isArray() || childValue.isObject()) &&
724
childValue.size() > 0 );
726
if ( !isMultiLine ) // check if line length > max line length
728
childValues_.reserve( size );
729
addChildValues_ = true;
730
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
731
for ( int index =0; index < size && !isMultiLine; ++index )
733
writeValue( value[index] );
734
lineLength += int( childValues_[index].length() );
735
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
737
addChildValues_ = false;
738
isMultiLine = isMultiLine || lineLength >= rightMargin_;
745
StyledStreamWriter::pushValue( const std::string &value )
747
if ( addChildValues_ )
748
childValues_.push_back( value );
755
StyledStreamWriter::writeIndent()
758
Some comments in this method would have been nice. ;-)
760
if ( !document_.empty() )
762
char last = document_[document_.length()-1];
763
if ( last == ' ' ) // already indented
765
if ( last != '\n' ) // Comments may add new-line
769
*document_ << '\n' << indentString_;
774
StyledStreamWriter::writeWithIndent( const std::string &value )
782
StyledStreamWriter::indent()
784
indentString_ += indentation_;
789
StyledStreamWriter::unindent()
791
assert( indentString_.size() >= indentation_.size() );
792
indentString_.resize( indentString_.size() - indentation_.size() );
797
StyledStreamWriter::writeCommentBeforeValue( const Value &root )
799
if ( !root.hasComment( commentBefore ) )
801
*document_ << normalizeEOL( root.getComment( commentBefore ) );
807
StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
809
if ( root.hasComment( commentAfterOnSameLine ) )
810
*document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
812
if ( root.hasComment( commentAfter ) )
815
*document_ << normalizeEOL( root.getComment( commentAfter ) );
822
StyledStreamWriter::hasCommentForValue( const Value &value )
824
return value.hasComment( commentBefore )
825
|| value.hasComment( commentAfterOnSameLine )
826
|| value.hasComment( commentAfter );
831
StyledStreamWriter::normalizeEOL( const std::string &text )
833
std::string normalized;
834
normalized.reserve( text.length() );
835
const char *begin = text.c_str();
836
const char *end = begin + text.length();
837
const char *current = begin;
838
while ( current != end )
841
if ( c == '\r' ) // mac or dos EOL
843
if ( *current == '\n' ) // convert dos EOL
847
else // handle unix EOL & other char
854
std::ostream& operator<<( std::ostream &sout, const Value &root )
856
Json::StyledStreamWriter writer;
857
writer.write(sout, root);