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