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