json_writer.cpp
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 *&current )
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; // nothing to truncate, so save time
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        // Truncate zeroes to save bytes in output, but keep one.
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    // Not sure how to handle unicode...
00114    if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
00115       return std::string("\"") + value + "\"";
00116    // We have to walk value and escape any special characters.
00117    // Appending to std::string is not efficient, but this should be rare.
00118    // (Note: forward slashes are *not* rare, but I am not escaping them.)
00119    unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
00120    std::string result;
00121    result.reserve(maxsize); // to avoid lots of mallocs
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          //case '/':
00149             // Even though \/ is considered a legal escape in JSON, a bare
00150             // slash is also legal, so I see no reason to escape it.
00151             // (I hope I am not misunderstanding something.
00152             // blep notes: actually escaping \/ may be useful in javascript to avoid </ 
00153             // sequence.
00154             // Should add a flag to allow this compatibility mode and prevent this 
00155             // sequence from occurring.
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 // Class Writer
00175 // //////////////////////////////////////////////////////////////////
00176 Writer::~Writer()
00177 {
00178 }
00179 
00180 
00181 // Class FastWriter
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 // Class StyledWriter
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 // output on a single line
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 ) // check if line length > max line length
00418    {
00419       childValues_.reserve( size );
00420       addChildValues_ = true;
00421       int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
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 == ' ' )     // already indented
00452          return;
00453       if ( last != '\n' )    // Comments may add new-line
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' ) // mac or dos EOL
00529       {
00530          if ( *current == '\n' ) // convert dos EOL
00531             ++current;
00532          normalized += '\n';
00533       }
00534       else // handle unix EOL & other char
00535          normalized += c;
00536    }
00537    return normalized;
00538 }
00539 
00540 
00541 // Class StyledStreamWriter
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; // Forget the stream, for safety.
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 // output on a single line
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 ) // check if line length > max line length
00694    {
00695       childValues_.reserve( size );
00696       addChildValues_ = true;
00697       int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
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     Some comments in this method would have been nice. ;-)
00726 
00727    if ( !document_.empty() )
00728    {
00729       char last = document_[document_.length()-1];
00730       if ( last == ' ' )     // already indented
00731          return;
00732       if ( last != '\n' )    // Comments may add new-line
00733          *document_ << '\n';
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' ) // mac or dos EOL
00809       {
00810          if ( *current == '\n' ) // convert dos EOL
00811             ++current;
00812          normalized += '\n';
00813       }
00814       else // handle unix EOL & other char
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 } // namespace Json


bwi_tools
Author(s): Piyush Khandelwal
autogenerated on Fri Aug 28 2015 10:14:30