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