Drizzled Public API Documentation

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