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