Go to the documentation of this file.00001 #include <bwi_tools/json/writer.h>
00002 #include <utility>
00003 #include <assert.h>
00004 #include <stdio.h>
00005 #include <string.h>
00006 #include <iostream>
00007 #include <sstream>
00008 #include <iomanip>
00009
00010 #if _MSC_VER >= 1400 // VC++ 8.0
00011 #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
00012 #endif
00013
00014 namespace Json {
00015
00016 static bool isControlCharacter(char ch)
00017 {
00018 return ch > 0 && ch <= 0x1F;
00019 }
00020
00021 static bool containsControlCharacter( const char* str )
00022 {
00023 while ( *str )
00024 {
00025 if ( isControlCharacter( *(str++) ) )
00026 return true;
00027 }
00028 return false;
00029 }
00030 static void uintToString( unsigned int value,
00031 char *¤t )
00032 {
00033 *--current = 0;
00034 do
00035 {
00036 *--current = (value % 10) + '0';
00037 value /= 10;
00038 }
00039 while ( value != 0 );
00040 }
00041
00042 std::string valueToString( Int value )
00043 {
00044 char buffer[32];
00045 char *current = buffer + sizeof(buffer);
00046 bool isNegative = value < 0;
00047 if ( isNegative )
00048 value = -value;
00049 uintToString( UInt(value), current );
00050 if ( isNegative )
00051 *--current = '-';
00052 assert( current >= buffer );
00053 return current;
00054 }
00055
00056
00057 std::string valueToString( UInt value )
00058 {
00059 char buffer[32];
00060 char *current = buffer + sizeof(buffer);
00061 uintToString( value, current );
00062 assert( current >= buffer );
00063 return current;
00064 }
00065
00066 std::string valueToString( double value )
00067 {
00068 char buffer[32];
00069 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
00070 sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
00071 #else
00072 sprintf(buffer, "%#.16g", value);
00073 #endif
00074 char* ch = buffer + strlen(buffer) - 1;
00075 if (*ch != '0') return buffer;
00076 while(ch > buffer && *ch == '0'){
00077 --ch;
00078 }
00079 char* last_nonzero = ch;
00080 while(ch >= buffer){
00081 switch(*ch){
00082 case '0':
00083 case '1':
00084 case '2':
00085 case '3':
00086 case '4':
00087 case '5':
00088 case '6':
00089 case '7':
00090 case '8':
00091 case '9':
00092 --ch;
00093 continue;
00094 case '.':
00095
00096 *(last_nonzero+2) = '\0';
00097 return buffer;
00098 default:
00099 return buffer;
00100 }
00101 }
00102 return buffer;
00103 }
00104
00105
00106 std::string valueToString( bool value )
00107 {
00108 return value ? "true" : "false";
00109 }
00110
00111 std::string valueToQuotedString( const char *value )
00112 {
00113
00114 if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
00115 return std::string("\"") + value + "\"";
00116
00117
00118
00119 unsigned maxsize = strlen(value)*2 + 3;
00120 std::string result;
00121 result.reserve(maxsize);
00122 result += "\"";
00123 for (const char* c=value; *c != 0; ++c)
00124 {
00125 switch(*c)
00126 {
00127 case '\"':
00128 result += "\\\"";
00129 break;
00130 case '\\':
00131 result += "\\\\";
00132 break;
00133 case '\b':
00134 result += "\\b";
00135 break;
00136 case '\f':
00137 result += "\\f";
00138 break;
00139 case '\n':
00140 result += "\\n";
00141 break;
00142 case '\r':
00143 result += "\\r";
00144 break;
00145 case '\t':
00146 result += "\\t";
00147 break;
00148
00149
00150
00151
00152
00153
00154
00155
00156 default:
00157 if ( isControlCharacter( *c ) )
00158 {
00159 std::ostringstream oss;
00160 oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
00161 result += oss.str();
00162 }
00163 else
00164 {
00165 result += *c;
00166 }
00167 break;
00168 }
00169 }
00170 result += "\"";
00171 return result;
00172 }
00173
00174
00175
00176 Writer::~Writer()
00177 {
00178 }
00179
00180
00181
00182
00183
00184 FastWriter::FastWriter()
00185 : yamlCompatiblityEnabled_( false )
00186 {
00187 }
00188
00189
00190 void
00191 FastWriter::enableYAMLCompatibility()
00192 {
00193 yamlCompatiblityEnabled_ = true;
00194 }
00195
00196
00197 std::string
00198 FastWriter::write( const Value &root )
00199 {
00200 document_ = "";
00201 writeValue( root );
00202 document_ += "\n";
00203 return document_;
00204 }
00205
00206
00207 void
00208 FastWriter::writeValue( const Value &value )
00209 {
00210 switch ( value.type() )
00211 {
00212 case nullValue:
00213 document_ += "null";
00214 break;
00215 case intValue:
00216 document_ += valueToString( value.asInt() );
00217 break;
00218 case uintValue:
00219 document_ += valueToString( value.asUInt() );
00220 break;
00221 case realValue:
00222 document_ += valueToString( value.asDouble() );
00223 break;
00224 case stringValue:
00225 document_ += valueToQuotedString( value.asCString() );
00226 break;
00227 case booleanValue:
00228 document_ += valueToString( value.asBool() );
00229 break;
00230 case arrayValue:
00231 {
00232 document_ += "[";
00233 int size = value.size();
00234 for ( int index =0; index < size; ++index )
00235 {
00236 if ( index > 0 )
00237 document_ += ",";
00238 writeValue( value[index] );
00239 }
00240 document_ += "]";
00241 }
00242 break;
00243 case objectValue:
00244 {
00245 Value::Members members( value.getMemberNames() );
00246 document_ += "{";
00247 for ( Value::Members::iterator it = members.begin();
00248 it != members.end();
00249 ++it )
00250 {
00251 const std::string &name = *it;
00252 if ( it != members.begin() )
00253 document_ += ",";
00254 document_ += valueToQuotedString( name.c_str() );
00255 document_ += yamlCompatiblityEnabled_ ? ": "
00256 : ":";
00257 writeValue( value[name] );
00258 }
00259 document_ += "}";
00260 }
00261 break;
00262 }
00263 }
00264
00265
00266
00267
00268
00269 StyledWriter::StyledWriter()
00270 : rightMargin_( 74 )
00271 , indentSize_( 3 )
00272 {
00273 }
00274
00275
00276 std::string
00277 StyledWriter::write( const Value &root )
00278 {
00279 document_ = "";
00280 addChildValues_ = false;
00281 indentString_ = "";
00282 writeCommentBeforeValue( root );
00283 writeValue( root );
00284 writeCommentAfterValueOnSameLine( root );
00285 document_ += "\n";
00286 return document_;
00287 }
00288
00289
00290 void
00291 StyledWriter::writeValue( const Value &value )
00292 {
00293 switch ( value.type() )
00294 {
00295 case nullValue:
00296 pushValue( "null" );
00297 break;
00298 case intValue:
00299 pushValue( valueToString( value.asInt() ) );
00300 break;
00301 case uintValue:
00302 pushValue( valueToString( value.asUInt() ) );
00303 break;
00304 case realValue:
00305 pushValue( valueToString( value.asDouble() ) );
00306 break;
00307 case stringValue:
00308 pushValue( valueToQuotedString( value.asCString() ) );
00309 break;
00310 case booleanValue:
00311 pushValue( valueToString( value.asBool() ) );
00312 break;
00313 case arrayValue:
00314 writeArrayValue( value);
00315 break;
00316 case objectValue:
00317 {
00318 Value::Members members( value.getMemberNames() );
00319 if ( members.empty() )
00320 pushValue( "{}" );
00321 else
00322 {
00323 writeWithIndent( "{" );
00324 indent();
00325 Value::Members::iterator it = members.begin();
00326 while ( true )
00327 {
00328 const std::string &name = *it;
00329 const Value &childValue = value[name];
00330 writeCommentBeforeValue( childValue );
00331 writeWithIndent( valueToQuotedString( name.c_str() ) );
00332 document_ += " : ";
00333 writeValue( childValue );
00334 if ( ++it == members.end() )
00335 {
00336 writeCommentAfterValueOnSameLine( childValue );
00337 break;
00338 }
00339 document_ += ",";
00340 writeCommentAfterValueOnSameLine( childValue );
00341 }
00342 unindent();
00343 writeWithIndent( "}" );
00344 }
00345 }
00346 break;
00347 }
00348 }
00349
00350
00351 void
00352 StyledWriter::writeArrayValue( const Value &value )
00353 {
00354 unsigned size = value.size();
00355 if ( size == 0 )
00356 pushValue( "[]" );
00357 else
00358 {
00359 bool isArrayMultiLine = isMultineArray( value );
00360 if ( isArrayMultiLine )
00361 {
00362 writeWithIndent( "[" );
00363 indent();
00364 bool hasChildValue = !childValues_.empty();
00365 unsigned index =0;
00366 while ( true )
00367 {
00368 const Value &childValue = value[index];
00369 writeCommentBeforeValue( childValue );
00370 if ( hasChildValue )
00371 writeWithIndent( childValues_[index] );
00372 else
00373 {
00374 writeIndent();
00375 writeValue( childValue );
00376 }
00377 if ( ++index == size )
00378 {
00379 writeCommentAfterValueOnSameLine( childValue );
00380 break;
00381 }
00382 document_ += ",";
00383 writeCommentAfterValueOnSameLine( childValue );
00384 }
00385 unindent();
00386 writeWithIndent( "]" );
00387 }
00388 else
00389 {
00390 assert( childValues_.size() == size );
00391 document_ += "[ ";
00392 for ( unsigned index =0; index < size; ++index )
00393 {
00394 if ( index > 0 )
00395 document_ += ", ";
00396 document_ += childValues_[index];
00397 }
00398 document_ += " ]";
00399 }
00400 }
00401 }
00402
00403
00404 bool
00405 StyledWriter::isMultineArray( const Value &value )
00406 {
00407 int size = value.size();
00408 bool isMultiLine = size*3 >= rightMargin_ ;
00409 childValues_.clear();
00410 for ( int index =0; index < size && !isMultiLine; ++index )
00411 {
00412 const Value &childValue = value[index];
00413 isMultiLine = isMultiLine ||
00414 ( (childValue.isArray() || childValue.isObject()) &&
00415 childValue.size() > 0 );
00416 }
00417 if ( !isMultiLine )
00418 {
00419 childValues_.reserve( size );
00420 addChildValues_ = true;
00421 int lineLength = 4 + (size-1)*2;
00422 for ( int index =0; index < size && !isMultiLine; ++index )
00423 {
00424 writeValue( value[index] );
00425 lineLength += int( childValues_[index].length() );
00426 isMultiLine = isMultiLine && hasCommentForValue( value[index] );
00427 }
00428 addChildValues_ = false;
00429 isMultiLine = isMultiLine || lineLength >= rightMargin_;
00430 }
00431 return isMultiLine;
00432 }
00433
00434
00435 void
00436 StyledWriter::pushValue( const std::string &value )
00437 {
00438 if ( addChildValues_ )
00439 childValues_.push_back( value );
00440 else
00441 document_ += value;
00442 }
00443
00444
00445 void
00446 StyledWriter::writeIndent()
00447 {
00448 if ( !document_.empty() )
00449 {
00450 char last = document_[document_.length()-1];
00451 if ( last == ' ' )
00452 return;
00453 if ( last != '\n' )
00454 document_ += '\n';
00455 }
00456 document_ += indentString_;
00457 }
00458
00459
00460 void
00461 StyledWriter::writeWithIndent( const std::string &value )
00462 {
00463 writeIndent();
00464 document_ += value;
00465 }
00466
00467
00468 void
00469 StyledWriter::indent()
00470 {
00471 indentString_ += std::string( indentSize_, ' ' );
00472 }
00473
00474
00475 void
00476 StyledWriter::unindent()
00477 {
00478 assert( int(indentString_.size()) >= indentSize_ );
00479 indentString_.resize( indentString_.size() - indentSize_ );
00480 }
00481
00482
00483 void
00484 StyledWriter::writeCommentBeforeValue( const Value &root )
00485 {
00486 if ( !root.hasComment( commentBefore ) )
00487 return;
00488 document_ += normalizeEOL( root.getComment( commentBefore ) );
00489 document_ += "\n";
00490 }
00491
00492
00493 void
00494 StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
00495 {
00496 if ( root.hasComment( commentAfterOnSameLine ) )
00497 document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
00498
00499 if ( root.hasComment( commentAfter ) )
00500 {
00501 document_ += "\n";
00502 document_ += normalizeEOL( root.getComment( commentAfter ) );
00503 document_ += "\n";
00504 }
00505 }
00506
00507
00508 bool
00509 StyledWriter::hasCommentForValue( const Value &value )
00510 {
00511 return value.hasComment( commentBefore )
00512 || value.hasComment( commentAfterOnSameLine )
00513 || value.hasComment( commentAfter );
00514 }
00515
00516
00517 std::string
00518 StyledWriter::normalizeEOL( const std::string &text )
00519 {
00520 std::string normalized;
00521 normalized.reserve( text.length() );
00522 const char *begin = text.c_str();
00523 const char *end = begin + text.length();
00524 const char *current = begin;
00525 while ( current != end )
00526 {
00527 char c = *current++;
00528 if ( c == '\r' )
00529 {
00530 if ( *current == '\n' )
00531 ++current;
00532 normalized += '\n';
00533 }
00534 else
00535 normalized += c;
00536 }
00537 return normalized;
00538 }
00539
00540
00541
00542
00543
00544 StyledStreamWriter::StyledStreamWriter( std::string indentation )
00545 : document_(NULL)
00546 , rightMargin_( 74 )
00547 , indentation_( indentation )
00548 {
00549 }
00550
00551
00552 void
00553 StyledStreamWriter::write( std::ostream &out, const Value &root )
00554 {
00555 document_ = &out;
00556 addChildValues_ = false;
00557 indentString_ = "";
00558 writeCommentBeforeValue( root );
00559 writeValue( root );
00560 writeCommentAfterValueOnSameLine( root );
00561 *document_ << "\n";
00562 document_ = NULL;
00563 }
00564
00565
00566 void
00567 StyledStreamWriter::writeValue( const Value &value )
00568 {
00569 switch ( value.type() )
00570 {
00571 case nullValue:
00572 pushValue( "null" );
00573 break;
00574 case intValue:
00575 pushValue( valueToString( value.asInt() ) );
00576 break;
00577 case uintValue:
00578 pushValue( valueToString( value.asUInt() ) );
00579 break;
00580 case realValue:
00581 pushValue( valueToString( value.asDouble() ) );
00582 break;
00583 case stringValue:
00584 pushValue( valueToQuotedString( value.asCString() ) );
00585 break;
00586 case booleanValue:
00587 pushValue( valueToString( value.asBool() ) );
00588 break;
00589 case arrayValue:
00590 writeArrayValue( value);
00591 break;
00592 case objectValue:
00593 {
00594 Value::Members members( value.getMemberNames() );
00595 if ( members.empty() )
00596 pushValue( "{}" );
00597 else
00598 {
00599 writeWithIndent( "{" );
00600 indent();
00601 Value::Members::iterator it = members.begin();
00602 while ( true )
00603 {
00604 const std::string &name = *it;
00605 const Value &childValue = value[name];
00606 writeCommentBeforeValue( childValue );
00607 writeWithIndent( valueToQuotedString( name.c_str() ) );
00608 *document_ << " : ";
00609 writeValue( childValue );
00610 if ( ++it == members.end() )
00611 {
00612 writeCommentAfterValueOnSameLine( childValue );
00613 break;
00614 }
00615 *document_ << ",";
00616 writeCommentAfterValueOnSameLine( childValue );
00617 }
00618 unindent();
00619 writeWithIndent( "}" );
00620 }
00621 }
00622 break;
00623 }
00624 }
00625
00626
00627 void
00628 StyledStreamWriter::writeArrayValue( const Value &value )
00629 {
00630 unsigned size = value.size();
00631 if ( size == 0 )
00632 pushValue( "[]" );
00633 else
00634 {
00635 bool isArrayMultiLine = isMultineArray( value );
00636 if ( isArrayMultiLine )
00637 {
00638 writeWithIndent( "[" );
00639 indent();
00640 bool hasChildValue = !childValues_.empty();
00641 unsigned index =0;
00642 while ( true )
00643 {
00644 const Value &childValue = value[index];
00645 writeCommentBeforeValue( childValue );
00646 if ( hasChildValue )
00647 writeWithIndent( childValues_[index] );
00648 else
00649 {
00650 writeIndent();
00651 writeValue( childValue );
00652 }
00653 if ( ++index == size )
00654 {
00655 writeCommentAfterValueOnSameLine( childValue );
00656 break;
00657 }
00658 *document_ << ",";
00659 writeCommentAfterValueOnSameLine( childValue );
00660 }
00661 unindent();
00662 writeWithIndent( "]" );
00663 }
00664 else
00665 {
00666 assert( childValues_.size() == size );
00667 *document_ << "[ ";
00668 for ( unsigned index =0; index < size; ++index )
00669 {
00670 if ( index > 0 )
00671 *document_ << ", ";
00672 *document_ << childValues_[index];
00673 }
00674 *document_ << " ]";
00675 }
00676 }
00677 }
00678
00679
00680 bool
00681 StyledStreamWriter::isMultineArray( const Value &value )
00682 {
00683 int size = value.size();
00684 bool isMultiLine = size*3 >= rightMargin_ ;
00685 childValues_.clear();
00686 for ( int index =0; index < size && !isMultiLine; ++index )
00687 {
00688 const Value &childValue = value[index];
00689 isMultiLine = isMultiLine ||
00690 ( (childValue.isArray() || childValue.isObject()) &&
00691 childValue.size() > 0 );
00692 }
00693 if ( !isMultiLine )
00694 {
00695 childValues_.reserve( size );
00696 addChildValues_ = true;
00697 int lineLength = 4 + (size-1)*2;
00698 for ( int index =0; index < size && !isMultiLine; ++index )
00699 {
00700 writeValue( value[index] );
00701 lineLength += int( childValues_[index].length() );
00702 isMultiLine = isMultiLine && hasCommentForValue( value[index] );
00703 }
00704 addChildValues_ = false;
00705 isMultiLine = isMultiLine || lineLength >= rightMargin_;
00706 }
00707 return isMultiLine;
00708 }
00709
00710
00711 void
00712 StyledStreamWriter::pushValue( const std::string &value )
00713 {
00714 if ( addChildValues_ )
00715 childValues_.push_back( value );
00716 else
00717 *document_ << value;
00718 }
00719
00720
00721 void
00722 StyledStreamWriter::writeIndent()
00723 {
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736 *document_ << '\n' << indentString_;
00737 }
00738
00739
00740 void
00741 StyledStreamWriter::writeWithIndent( const std::string &value )
00742 {
00743 writeIndent();
00744 *document_ << value;
00745 }
00746
00747
00748 void
00749 StyledStreamWriter::indent()
00750 {
00751 indentString_ += indentation_;
00752 }
00753
00754
00755 void
00756 StyledStreamWriter::unindent()
00757 {
00758 assert( indentString_.size() >= indentation_.size() );
00759 indentString_.resize( indentString_.size() - indentation_.size() );
00760 }
00761
00762
00763 void
00764 StyledStreamWriter::writeCommentBeforeValue( const Value &root )
00765 {
00766 if ( !root.hasComment( commentBefore ) )
00767 return;
00768 *document_ << normalizeEOL( root.getComment( commentBefore ) );
00769 *document_ << "\n";
00770 }
00771
00772
00773 void
00774 StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
00775 {
00776 if ( root.hasComment( commentAfterOnSameLine ) )
00777 *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
00778
00779 if ( root.hasComment( commentAfter ) )
00780 {
00781 *document_ << "\n";
00782 *document_ << normalizeEOL( root.getComment( commentAfter ) );
00783 *document_ << "\n";
00784 }
00785 }
00786
00787
00788 bool
00789 StyledStreamWriter::hasCommentForValue( const Value &value )
00790 {
00791 return value.hasComment( commentBefore )
00792 || value.hasComment( commentAfterOnSameLine )
00793 || value.hasComment( commentAfter );
00794 }
00795
00796
00797 std::string
00798 StyledStreamWriter::normalizeEOL( const std::string &text )
00799 {
00800 std::string normalized;
00801 normalized.reserve( text.length() );
00802 const char *begin = text.c_str();
00803 const char *end = begin + text.length();
00804 const char *current = begin;
00805 while ( current != end )
00806 {
00807 char c = *current++;
00808 if ( c == '\r' )
00809 {
00810 if ( *current == '\n' )
00811 ++current;
00812 normalized += '\n';
00813 }
00814 else
00815 normalized += c;
00816 }
00817 return normalized;
00818 }
00819
00820
00821 std::ostream& operator<<( std::ostream &sout, const Value &root )
00822 {
00823 Json::StyledStreamWriter writer;
00824 writer.write(sout, root);
00825 return sout;
00826 }
00827
00828
00829 }