annotate third_party/json/json_writer.cpp @ 0:add35537fdbb tip

Initial import
author irh <ian.r.hobson@gmail.com>
date Thu, 25 Aug 2011 11:05:55 +0100
parents
children
rev   line source
ian@0 1 // Copyright 2011 Baptiste Lepilleur
ian@0 2 // Distributed under MIT license, or public domain if desired and
ian@0 3 // recognized in your jurisdiction.
ian@0 4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
ian@0 5
ian@0 6 #if !defined(JSON_IS_AMALGAMATION)
ian@0 7 # include <json/writer.h>
ian@0 8 # include "json_tool.h"
ian@0 9 #endif // if !defined(JSON_IS_AMALGAMATION)
ian@0 10 #include <utility>
ian@0 11 #include <assert.h>
ian@0 12 #include <stdio.h>
ian@0 13 #include <string.h>
ian@0 14 #include <iostream>
ian@0 15 #include <sstream>
ian@0 16 #include <iomanip>
ian@0 17
ian@0 18 #if _MSC_VER >= 1400 // VC++ 8.0
ian@0 19 #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
ian@0 20 #endif
ian@0 21
ian@0 22 namespace Json {
ian@0 23
ian@0 24 static bool containsControlCharacter( const char* str )
ian@0 25 {
ian@0 26 while ( *str )
ian@0 27 {
ian@0 28 if ( isControlCharacter( *(str++) ) )
ian@0 29 return true;
ian@0 30 }
ian@0 31 return false;
ian@0 32 }
ian@0 33
ian@0 34
ian@0 35 std::string valueToString( LargestInt value )
ian@0 36 {
ian@0 37 UIntToStringBuffer buffer;
ian@0 38 char *current = buffer + sizeof(buffer);
ian@0 39 bool isNegative = value < 0;
ian@0 40 if ( isNegative )
ian@0 41 value = -value;
ian@0 42 uintToString( LargestUInt(value), current );
ian@0 43 if ( isNegative )
ian@0 44 *--current = '-';
ian@0 45 assert( current >= buffer );
ian@0 46 return current;
ian@0 47 }
ian@0 48
ian@0 49
ian@0 50 std::string valueToString( LargestUInt value )
ian@0 51 {
ian@0 52 UIntToStringBuffer buffer;
ian@0 53 char *current = buffer + sizeof(buffer);
ian@0 54 uintToString( value, current );
ian@0 55 assert( current >= buffer );
ian@0 56 return current;
ian@0 57 }
ian@0 58
ian@0 59 #if defined(JSON_HAS_INT64)
ian@0 60
ian@0 61 std::string valueToString( Int value )
ian@0 62 {
ian@0 63 return valueToString( LargestInt(value) );
ian@0 64 }
ian@0 65
ian@0 66
ian@0 67 std::string valueToString( UInt value )
ian@0 68 {
ian@0 69 return valueToString( LargestUInt(value) );
ian@0 70 }
ian@0 71
ian@0 72 #endif // # if defined(JSON_HAS_INT64)
ian@0 73
ian@0 74
ian@0 75 std::string valueToString( double value )
ian@0 76 {
ian@0 77 char buffer[32];
ian@0 78 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
ian@0 79 sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
ian@0 80 #else
ian@0 81 sprintf(buffer, "%#.16g", value);
ian@0 82 #endif
ian@0 83 char* ch = buffer + strlen(buffer) - 1;
ian@0 84 if (*ch != '0') return buffer; // nothing to truncate, so save time
ian@0 85 while(ch > buffer && *ch == '0'){
ian@0 86 --ch;
ian@0 87 }
ian@0 88 char* last_nonzero = ch;
ian@0 89 while(ch >= buffer){
ian@0 90 switch(*ch){
ian@0 91 case '0':
ian@0 92 case '1':
ian@0 93 case '2':
ian@0 94 case '3':
ian@0 95 case '4':
ian@0 96 case '5':
ian@0 97 case '6':
ian@0 98 case '7':
ian@0 99 case '8':
ian@0 100 case '9':
ian@0 101 --ch;
ian@0 102 continue;
ian@0 103 case '.':
ian@0 104 // Truncate zeroes to save bytes in output, but keep one.
ian@0 105 *(last_nonzero+2) = '\0';
ian@0 106 return buffer;
ian@0 107 default:
ian@0 108 return buffer;
ian@0 109 }
ian@0 110 }
ian@0 111 return buffer;
ian@0 112 }
ian@0 113
ian@0 114
ian@0 115 std::string valueToString( bool value )
ian@0 116 {
ian@0 117 return value ? "true" : "false";
ian@0 118 }
ian@0 119
ian@0 120 std::string valueToQuotedString( const char *value )
ian@0 121 {
ian@0 122 if (value == NULL)
ian@0 123 return "";
ian@0 124 // Not sure how to handle unicode...
ian@0 125 if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
ian@0 126 return std::string("\"") + value + "\"";
ian@0 127 // We have to walk value and escape any special characters.
ian@0 128 // Appending to std::string is not efficient, but this should be rare.
ian@0 129 // (Note: forward slashes are *not* rare, but I am not escaping them.)
ian@0 130 std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
ian@0 131 std::string result;
ian@0 132 result.reserve(maxsize); // to avoid lots of mallocs
ian@0 133 result += "\"";
ian@0 134 for (const char* c=value; *c != 0; ++c)
ian@0 135 {
ian@0 136 switch(*c)
ian@0 137 {
ian@0 138 case '\"':
ian@0 139 result += "\\\"";
ian@0 140 break;
ian@0 141 case '\\':
ian@0 142 result += "\\\\";
ian@0 143 break;
ian@0 144 case '\b':
ian@0 145 result += "\\b";
ian@0 146 break;
ian@0 147 case '\f':
ian@0 148 result += "\\f";
ian@0 149 break;
ian@0 150 case '\n':
ian@0 151 result += "\\n";
ian@0 152 break;
ian@0 153 case '\r':
ian@0 154 result += "\\r";
ian@0 155 break;
ian@0 156 case '\t':
ian@0 157 result += "\\t";
ian@0 158 break;
ian@0 159 //case '/':
ian@0 160 // Even though \/ is considered a legal escape in JSON, a bare
ian@0 161 // slash is also legal, so I see no reason to escape it.
ian@0 162 // (I hope I am not misunderstanding something.
ian@0 163 // blep notes: actually escaping \/ may be useful in javascript to avoid </
ian@0 164 // sequence.
ian@0 165 // Should add a flag to allow this compatibility mode and prevent this
ian@0 166 // sequence from occurring.
ian@0 167 default:
ian@0 168 if ( isControlCharacter( *c ) )
ian@0 169 {
ian@0 170 std::ostringstream oss;
ian@0 171 oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
ian@0 172 result += oss.str();
ian@0 173 }
ian@0 174 else
ian@0 175 {
ian@0 176 result += *c;
ian@0 177 }
ian@0 178 break;
ian@0 179 }
ian@0 180 }
ian@0 181 result += "\"";
ian@0 182 return result;
ian@0 183 }
ian@0 184
ian@0 185 // Class Writer
ian@0 186 // //////////////////////////////////////////////////////////////////
ian@0 187 Writer::~Writer()
ian@0 188 {
ian@0 189 }
ian@0 190
ian@0 191
ian@0 192 // Class FastWriter
ian@0 193 // //////////////////////////////////////////////////////////////////
ian@0 194
ian@0 195 FastWriter::FastWriter()
ian@0 196 : yamlCompatiblityEnabled_( false )
ian@0 197 {
ian@0 198 }
ian@0 199
ian@0 200
ian@0 201 void
ian@0 202 FastWriter::enableYAMLCompatibility()
ian@0 203 {
ian@0 204 yamlCompatiblityEnabled_ = true;
ian@0 205 }
ian@0 206
ian@0 207
ian@0 208 std::string
ian@0 209 FastWriter::write( const Value &root )
ian@0 210 {
ian@0 211 document_ = "";
ian@0 212 writeValue( root );
ian@0 213 document_ += "\n";
ian@0 214 return document_;
ian@0 215 }
ian@0 216
ian@0 217
ian@0 218 void
ian@0 219 FastWriter::writeValue( const Value &value )
ian@0 220 {
ian@0 221 switch ( value.type() )
ian@0 222 {
ian@0 223 case nullValue:
ian@0 224 document_ += "null";
ian@0 225 break;
ian@0 226 case intValue:
ian@0 227 document_ += valueToString( value.asLargestInt() );
ian@0 228 break;
ian@0 229 case uintValue:
ian@0 230 document_ += valueToString( value.asLargestUInt() );
ian@0 231 break;
ian@0 232 case realValue:
ian@0 233 document_ += valueToString( value.asDouble() );
ian@0 234 break;
ian@0 235 case stringValue:
ian@0 236 document_ += valueToQuotedString( value.asCString() );
ian@0 237 break;
ian@0 238 case booleanValue:
ian@0 239 document_ += valueToString( value.asBool() );
ian@0 240 break;
ian@0 241 case arrayValue:
ian@0 242 {
ian@0 243 document_ += "[";
ian@0 244 int size = value.size();
ian@0 245 for ( int index =0; index < size; ++index )
ian@0 246 {
ian@0 247 if ( index > 0 )
ian@0 248 document_ += ",";
ian@0 249 writeValue( value[index] );
ian@0 250 }
ian@0 251 document_ += "]";
ian@0 252 }
ian@0 253 break;
ian@0 254 case objectValue:
ian@0 255 {
ian@0 256 Value::Members members( value.getMemberNames() );
ian@0 257 document_ += "{";
ian@0 258 for ( Value::Members::iterator it = members.begin();
ian@0 259 it != members.end();
ian@0 260 ++it )
ian@0 261 {
ian@0 262 const std::string &name = *it;
ian@0 263 if ( it != members.begin() )
ian@0 264 document_ += ",";
ian@0 265 document_ += valueToQuotedString( name.c_str() );
ian@0 266 document_ += yamlCompatiblityEnabled_ ? ": "
ian@0 267 : ":";
ian@0 268 writeValue( value[name] );
ian@0 269 }
ian@0 270 document_ += "}";
ian@0 271 }
ian@0 272 break;
ian@0 273 }
ian@0 274 }
ian@0 275
ian@0 276
ian@0 277 // Class StyledWriter
ian@0 278 // //////////////////////////////////////////////////////////////////
ian@0 279
ian@0 280 StyledWriter::StyledWriter()
ian@0 281 : rightMargin_( 74 )
ian@0 282 , indentSize_( 3 )
ian@0 283 , addChildValues_()
ian@0 284 {
ian@0 285 }
ian@0 286
ian@0 287
ian@0 288 std::string
ian@0 289 StyledWriter::write( const Value &root )
ian@0 290 {
ian@0 291 document_ = "";
ian@0 292 addChildValues_ = false;
ian@0 293 indentString_ = "";
ian@0 294 writeCommentBeforeValue( root );
ian@0 295 writeValue( root );
ian@0 296 writeCommentAfterValueOnSameLine( root );
ian@0 297 document_ += "\n";
ian@0 298 return document_;
ian@0 299 }
ian@0 300
ian@0 301
ian@0 302 void
ian@0 303 StyledWriter::writeValue( const Value &value )
ian@0 304 {
ian@0 305 switch ( value.type() )
ian@0 306 {
ian@0 307 case nullValue:
ian@0 308 pushValue( "null" );
ian@0 309 break;
ian@0 310 case intValue:
ian@0 311 pushValue( valueToString( value.asLargestInt() ) );
ian@0 312 break;
ian@0 313 case uintValue:
ian@0 314 pushValue( valueToString( value.asLargestUInt() ) );
ian@0 315 break;
ian@0 316 case realValue:
ian@0 317 pushValue( valueToString( value.asDouble() ) );
ian@0 318 break;
ian@0 319 case stringValue:
ian@0 320 pushValue( valueToQuotedString( value.asCString() ) );
ian@0 321 break;
ian@0 322 case booleanValue:
ian@0 323 pushValue( valueToString( value.asBool() ) );
ian@0 324 break;
ian@0 325 case arrayValue:
ian@0 326 writeArrayValue( value);
ian@0 327 break;
ian@0 328 case objectValue:
ian@0 329 {
ian@0 330 Value::Members members( value.getMemberNames() );
ian@0 331 if ( members.empty() )
ian@0 332 pushValue( "{}" );
ian@0 333 else
ian@0 334 {
ian@0 335 writeWithIndent( "{" );
ian@0 336 indent();
ian@0 337 Value::Members::iterator it = members.begin();
ian@0 338 for (;;)
ian@0 339 {
ian@0 340 const std::string &name = *it;
ian@0 341 const Value &childValue = value[name];
ian@0 342 writeCommentBeforeValue( childValue );
ian@0 343 writeWithIndent( valueToQuotedString( name.c_str() ) );
ian@0 344 document_ += " : ";
ian@0 345 writeValue( childValue );
ian@0 346 if ( ++it == members.end() )
ian@0 347 {
ian@0 348 writeCommentAfterValueOnSameLine( childValue );
ian@0 349 break;
ian@0 350 }
ian@0 351 document_ += ",";
ian@0 352 writeCommentAfterValueOnSameLine( childValue );
ian@0 353 }
ian@0 354 unindent();
ian@0 355 writeWithIndent( "}" );
ian@0 356 }
ian@0 357 }
ian@0 358 break;
ian@0 359 }
ian@0 360 }
ian@0 361
ian@0 362
ian@0 363 void
ian@0 364 StyledWriter::writeArrayValue( const Value &value )
ian@0 365 {
ian@0 366 unsigned size = value.size();
ian@0 367 if ( size == 0 )
ian@0 368 pushValue( "[]" );
ian@0 369 else
ian@0 370 {
ian@0 371 bool isArrayMultiLine = isMultineArray( value );
ian@0 372 if ( isArrayMultiLine )
ian@0 373 {
ian@0 374 writeWithIndent( "[" );
ian@0 375 indent();
ian@0 376 bool hasChildValue = !childValues_.empty();
ian@0 377 unsigned index =0;
ian@0 378 for (;;)
ian@0 379 {
ian@0 380 const Value &childValue = value[index];
ian@0 381 writeCommentBeforeValue( childValue );
ian@0 382 if ( hasChildValue )
ian@0 383 writeWithIndent( childValues_[index] );
ian@0 384 else
ian@0 385 {
ian@0 386 writeIndent();
ian@0 387 writeValue( childValue );
ian@0 388 }
ian@0 389 if ( ++index == size )
ian@0 390 {
ian@0 391 writeCommentAfterValueOnSameLine( childValue );
ian@0 392 break;
ian@0 393 }
ian@0 394 document_ += ",";
ian@0 395 writeCommentAfterValueOnSameLine( childValue );
ian@0 396 }
ian@0 397 unindent();
ian@0 398 writeWithIndent( "]" );
ian@0 399 }
ian@0 400 else // output on a single line
ian@0 401 {
ian@0 402 assert( childValues_.size() == size );
ian@0 403 document_ += "[ ";
ian@0 404 for ( unsigned index =0; index < size; ++index )
ian@0 405 {
ian@0 406 if ( index > 0 )
ian@0 407 document_ += ", ";
ian@0 408 document_ += childValues_[index];
ian@0 409 }
ian@0 410 document_ += " ]";
ian@0 411 }
ian@0 412 }
ian@0 413 }
ian@0 414
ian@0 415
ian@0 416 bool
ian@0 417 StyledWriter::isMultineArray( const Value &value )
ian@0 418 {
ian@0 419 int size = value.size();
ian@0 420 bool isMultiLine = size*3 >= rightMargin_ ;
ian@0 421 childValues_.clear();
ian@0 422 for ( int index =0; index < size && !isMultiLine; ++index )
ian@0 423 {
ian@0 424 const Value &childValue = value[index];
ian@0 425 isMultiLine = isMultiLine ||
ian@0 426 ( (childValue.isArray() || childValue.isObject()) &&
ian@0 427 childValue.size() > 0 );
ian@0 428 }
ian@0 429 if ( !isMultiLine ) // check if line length > max line length
ian@0 430 {
ian@0 431 childValues_.reserve( size );
ian@0 432 addChildValues_ = true;
ian@0 433 int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
ian@0 434 for ( int index =0; index < size && !isMultiLine; ++index )
ian@0 435 {
ian@0 436 writeValue( value[index] );
ian@0 437 lineLength += int( childValues_[index].length() );
ian@0 438 isMultiLine = isMultiLine && hasCommentForValue( value[index] );
ian@0 439 }
ian@0 440 addChildValues_ = false;
ian@0 441 isMultiLine = isMultiLine || lineLength >= rightMargin_;
ian@0 442 }
ian@0 443 return isMultiLine;
ian@0 444 }
ian@0 445
ian@0 446
ian@0 447 void
ian@0 448 StyledWriter::pushValue( const std::string &value )
ian@0 449 {
ian@0 450 if ( addChildValues_ )
ian@0 451 childValues_.push_back( value );
ian@0 452 else
ian@0 453 document_ += value;
ian@0 454 }
ian@0 455
ian@0 456
ian@0 457 void
ian@0 458 StyledWriter::writeIndent()
ian@0 459 {
ian@0 460 if ( !document_.empty() )
ian@0 461 {
ian@0 462 char last = document_[document_.length()-1];
ian@0 463 if ( last == ' ' ) // already indented
ian@0 464 return;
ian@0 465 if ( last != '\n' ) // Comments may add new-line
ian@0 466 document_ += '\n';
ian@0 467 }
ian@0 468 document_ += indentString_;
ian@0 469 }
ian@0 470
ian@0 471
ian@0 472 void
ian@0 473 StyledWriter::writeWithIndent( const std::string &value )
ian@0 474 {
ian@0 475 writeIndent();
ian@0 476 document_ += value;
ian@0 477 }
ian@0 478
ian@0 479
ian@0 480 void
ian@0 481 StyledWriter::indent()
ian@0 482 {
ian@0 483 indentString_ += std::string( indentSize_, ' ' );
ian@0 484 }
ian@0 485
ian@0 486
ian@0 487 void
ian@0 488 StyledWriter::unindent()
ian@0 489 {
ian@0 490 assert( int(indentString_.size()) >= indentSize_ );
ian@0 491 indentString_.resize( indentString_.size() - indentSize_ );
ian@0 492 }
ian@0 493
ian@0 494
ian@0 495 void
ian@0 496 StyledWriter::writeCommentBeforeValue( const Value &root )
ian@0 497 {
ian@0 498 if ( !root.hasComment( commentBefore ) )
ian@0 499 return;
ian@0 500 document_ += normalizeEOL( root.getComment( commentBefore ) );
ian@0 501 document_ += "\n";
ian@0 502 }
ian@0 503
ian@0 504
ian@0 505 void
ian@0 506 StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
ian@0 507 {
ian@0 508 if ( root.hasComment( commentAfterOnSameLine ) )
ian@0 509 document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
ian@0 510
ian@0 511 if ( root.hasComment( commentAfter ) )
ian@0 512 {
ian@0 513 document_ += "\n";
ian@0 514 document_ += normalizeEOL( root.getComment( commentAfter ) );
ian@0 515 document_ += "\n";
ian@0 516 }
ian@0 517 }
ian@0 518
ian@0 519
ian@0 520 bool
ian@0 521 StyledWriter::hasCommentForValue( const Value &value )
ian@0 522 {
ian@0 523 return value.hasComment( commentBefore )
ian@0 524 || value.hasComment( commentAfterOnSameLine )
ian@0 525 || value.hasComment( commentAfter );
ian@0 526 }
ian@0 527
ian@0 528
ian@0 529 std::string
ian@0 530 StyledWriter::normalizeEOL( const std::string &text )
ian@0 531 {
ian@0 532 std::string normalized;
ian@0 533 normalized.reserve( text.length() );
ian@0 534 const char *begin = text.c_str();
ian@0 535 const char *end = begin + text.length();
ian@0 536 const char *current = begin;
ian@0 537 while ( current != end )
ian@0 538 {
ian@0 539 char c = *current++;
ian@0 540 if ( c == '\r' ) // mac or dos EOL
ian@0 541 {
ian@0 542 if ( *current == '\n' ) // convert dos EOL
ian@0 543 ++current;
ian@0 544 normalized += '\n';
ian@0 545 }
ian@0 546 else // handle unix EOL & other char
ian@0 547 normalized += c;
ian@0 548 }
ian@0 549 return normalized;
ian@0 550 }
ian@0 551
ian@0 552
ian@0 553 // Class StyledStreamWriter
ian@0 554 // //////////////////////////////////////////////////////////////////
ian@0 555
ian@0 556 StyledStreamWriter::StyledStreamWriter( std::string indentation )
ian@0 557 : document_(NULL)
ian@0 558 , rightMargin_( 74 )
ian@0 559 , indentation_( indentation )
ian@0 560 , addChildValues_()
ian@0 561 {
ian@0 562 }
ian@0 563
ian@0 564
ian@0 565 void
ian@0 566 StyledStreamWriter::write( std::ostream &out, const Value &root )
ian@0 567 {
ian@0 568 document_ = &out;
ian@0 569 addChildValues_ = false;
ian@0 570 indentString_ = "";
ian@0 571 writeCommentBeforeValue( root );
ian@0 572 writeValue( root );
ian@0 573 writeCommentAfterValueOnSameLine( root );
ian@0 574 *document_ << "\n";
ian@0 575 document_ = NULL; // Forget the stream, for safety.
ian@0 576 }
ian@0 577
ian@0 578
ian@0 579 void
ian@0 580 StyledStreamWriter::writeValue( const Value &value )
ian@0 581 {
ian@0 582 switch ( value.type() )
ian@0 583 {
ian@0 584 case nullValue:
ian@0 585 pushValue( "null" );
ian@0 586 break;
ian@0 587 case intValue:
ian@0 588 pushValue( valueToString( value.asLargestInt() ) );
ian@0 589 break;
ian@0 590 case uintValue:
ian@0 591 pushValue( valueToString( value.asLargestUInt() ) );
ian@0 592 break;
ian@0 593 case realValue:
ian@0 594 pushValue( valueToString( value.asDouble() ) );
ian@0 595 break;
ian@0 596 case stringValue:
ian@0 597 pushValue( valueToQuotedString( value.asCString() ) );
ian@0 598 break;
ian@0 599 case booleanValue:
ian@0 600 pushValue( valueToString( value.asBool() ) );
ian@0 601 break;
ian@0 602 case arrayValue:
ian@0 603 writeArrayValue( value);
ian@0 604 break;
ian@0 605 case objectValue:
ian@0 606 {
ian@0 607 Value::Members members( value.getMemberNames() );
ian@0 608 if ( members.empty() )
ian@0 609 pushValue( "{}" );
ian@0 610 else
ian@0 611 {
ian@0 612 writeWithIndent( "{" );
ian@0 613 indent();
ian@0 614 Value::Members::iterator it = members.begin();
ian@0 615 for (;;)
ian@0 616 {
ian@0 617 const std::string &name = *it;
ian@0 618 const Value &childValue = value[name];
ian@0 619 writeCommentBeforeValue( childValue );
ian@0 620 writeWithIndent( valueToQuotedString( name.c_str() ) );
ian@0 621 *document_ << " : ";
ian@0 622 writeValue( childValue );
ian@0 623 if ( ++it == members.end() )
ian@0 624 {
ian@0 625 writeCommentAfterValueOnSameLine( childValue );
ian@0 626 break;
ian@0 627 }
ian@0 628 *document_ << ",";
ian@0 629 writeCommentAfterValueOnSameLine( childValue );
ian@0 630 }
ian@0 631 unindent();
ian@0 632 writeWithIndent( "}" );
ian@0 633 }
ian@0 634 }
ian@0 635 break;
ian@0 636 }
ian@0 637 }
ian@0 638
ian@0 639
ian@0 640 void
ian@0 641 StyledStreamWriter::writeArrayValue( const Value &value )
ian@0 642 {
ian@0 643 unsigned size = value.size();
ian@0 644 if ( size == 0 )
ian@0 645 pushValue( "[]" );
ian@0 646 else
ian@0 647 {
ian@0 648 bool isArrayMultiLine = isMultineArray( value );
ian@0 649 if ( isArrayMultiLine )
ian@0 650 {
ian@0 651 writeWithIndent( "[" );
ian@0 652 indent();
ian@0 653 bool hasChildValue = !childValues_.empty();
ian@0 654 unsigned index =0;
ian@0 655 for (;;)
ian@0 656 {
ian@0 657 const Value &childValue = value[index];
ian@0 658 writeCommentBeforeValue( childValue );
ian@0 659 if ( hasChildValue )
ian@0 660 writeWithIndent( childValues_[index] );
ian@0 661 else
ian@0 662 {
ian@0 663 writeIndent();
ian@0 664 writeValue( childValue );
ian@0 665 }
ian@0 666 if ( ++index == size )
ian@0 667 {
ian@0 668 writeCommentAfterValueOnSameLine( childValue );
ian@0 669 break;
ian@0 670 }
ian@0 671 *document_ << ",";
ian@0 672 writeCommentAfterValueOnSameLine( childValue );
ian@0 673 }
ian@0 674 unindent();
ian@0 675 writeWithIndent( "]" );
ian@0 676 }
ian@0 677 else // output on a single line
ian@0 678 {
ian@0 679 assert( childValues_.size() == size );
ian@0 680 *document_ << "[ ";
ian@0 681 for ( unsigned index =0; index < size; ++index )
ian@0 682 {
ian@0 683 if ( index > 0 )
ian@0 684 *document_ << ", ";
ian@0 685 *document_ << childValues_[index];
ian@0 686 }
ian@0 687 *document_ << " ]";
ian@0 688 }
ian@0 689 }
ian@0 690 }
ian@0 691
ian@0 692
ian@0 693 bool
ian@0 694 StyledStreamWriter::isMultineArray( const Value &value )
ian@0 695 {
ian@0 696 int size = value.size();
ian@0 697 bool isMultiLine = size*3 >= rightMargin_ ;
ian@0 698 childValues_.clear();
ian@0 699 for ( int index =0; index < size && !isMultiLine; ++index )
ian@0 700 {
ian@0 701 const Value &childValue = value[index];
ian@0 702 isMultiLine = isMultiLine ||
ian@0 703 ( (childValue.isArray() || childValue.isObject()) &&
ian@0 704 childValue.size() > 0 );
ian@0 705 }
ian@0 706 if ( !isMultiLine ) // check if line length > max line length
ian@0 707 {
ian@0 708 childValues_.reserve( size );
ian@0 709 addChildValues_ = true;
ian@0 710 int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
ian@0 711 for ( int index =0; index < size && !isMultiLine; ++index )
ian@0 712 {
ian@0 713 writeValue( value[index] );
ian@0 714 lineLength += int( childValues_[index].length() );
ian@0 715 isMultiLine = isMultiLine && hasCommentForValue( value[index] );
ian@0 716 }
ian@0 717 addChildValues_ = false;
ian@0 718 isMultiLine = isMultiLine || lineLength >= rightMargin_;
ian@0 719 }
ian@0 720 return isMultiLine;
ian@0 721 }
ian@0 722
ian@0 723
ian@0 724 void
ian@0 725 StyledStreamWriter::pushValue( const std::string &value )
ian@0 726 {
ian@0 727 if ( addChildValues_ )
ian@0 728 childValues_.push_back( value );
ian@0 729 else
ian@0 730 *document_ << value;
ian@0 731 }
ian@0 732
ian@0 733
ian@0 734 void
ian@0 735 StyledStreamWriter::writeIndent()
ian@0 736 {
ian@0 737 /*
ian@0 738 Some comments in this method would have been nice. ;-)
ian@0 739
ian@0 740 if ( !document_.empty() )
ian@0 741 {
ian@0 742 char last = document_[document_.length()-1];
ian@0 743 if ( last == ' ' ) // already indented
ian@0 744 return;
ian@0 745 if ( last != '\n' ) // Comments may add new-line
ian@0 746 *document_ << '\n';
ian@0 747 }
ian@0 748 */
ian@0 749 *document_ << '\n' << indentString_;
ian@0 750 }
ian@0 751
ian@0 752
ian@0 753 void
ian@0 754 StyledStreamWriter::writeWithIndent( const std::string &value )
ian@0 755 {
ian@0 756 writeIndent();
ian@0 757 *document_ << value;
ian@0 758 }
ian@0 759
ian@0 760
ian@0 761 void
ian@0 762 StyledStreamWriter::indent()
ian@0 763 {
ian@0 764 indentString_ += indentation_;
ian@0 765 }
ian@0 766
ian@0 767
ian@0 768 void
ian@0 769 StyledStreamWriter::unindent()
ian@0 770 {
ian@0 771 assert( indentString_.size() >= indentation_.size() );
ian@0 772 indentString_.resize( indentString_.size() - indentation_.size() );
ian@0 773 }
ian@0 774
ian@0 775
ian@0 776 void
ian@0 777 StyledStreamWriter::writeCommentBeforeValue( const Value &root )
ian@0 778 {
ian@0 779 if ( !root.hasComment( commentBefore ) )
ian@0 780 return;
ian@0 781 *document_ << normalizeEOL( root.getComment( commentBefore ) );
ian@0 782 *document_ << "\n";
ian@0 783 }
ian@0 784
ian@0 785
ian@0 786 void
ian@0 787 StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
ian@0 788 {
ian@0 789 if ( root.hasComment( commentAfterOnSameLine ) )
ian@0 790 *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
ian@0 791
ian@0 792 if ( root.hasComment( commentAfter ) )
ian@0 793 {
ian@0 794 *document_ << "\n";
ian@0 795 *document_ << normalizeEOL( root.getComment( commentAfter ) );
ian@0 796 *document_ << "\n";
ian@0 797 }
ian@0 798 }
ian@0 799
ian@0 800
ian@0 801 bool
ian@0 802 StyledStreamWriter::hasCommentForValue( const Value &value )
ian@0 803 {
ian@0 804 return value.hasComment( commentBefore )
ian@0 805 || value.hasComment( commentAfterOnSameLine )
ian@0 806 || value.hasComment( commentAfter );
ian@0 807 }
ian@0 808
ian@0 809
ian@0 810 std::string
ian@0 811 StyledStreamWriter::normalizeEOL( const std::string &text )
ian@0 812 {
ian@0 813 std::string normalized;
ian@0 814 normalized.reserve( text.length() );
ian@0 815 const char *begin = text.c_str();
ian@0 816 const char *end = begin + text.length();
ian@0 817 const char *current = begin;
ian@0 818 while ( current != end )
ian@0 819 {
ian@0 820 char c = *current++;
ian@0 821 if ( c == '\r' ) // mac or dos EOL
ian@0 822 {
ian@0 823 if ( *current == '\n' ) // convert dos EOL
ian@0 824 ++current;
ian@0 825 normalized += '\n';
ian@0 826 }
ian@0 827 else // handle unix EOL & other char
ian@0 828 normalized += c;
ian@0 829 }
ian@0 830 return normalized;
ian@0 831 }
ian@0 832
ian@0 833
ian@0 834 std::ostream& operator<<( std::ostream &sout, const Value &root )
ian@0 835 {
ian@0 836 Json::StyledStreamWriter writer;
ian@0 837 writer.write(sout, root);
ian@0 838 return sout;
ian@0 839 }
ian@0 840
ian@0 841
ian@0 842 } // namespace Json