ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/rpc/json/writer.C
Revision: 1.3
Committed: Wed Sep 5 11:23:15 2007 UTC (19 years, 2 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +2 -2 lines
Log Message:
removed GPLed code and put license back to BSD

File Contents

# Content
1 /*
2 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
3 * Rights to this code are as documented in doc/pod/license.pod.
4 *
5 * JSON-RPC for Ermyth
6 *
7 * $Id: writer.C,v 1.2 2007-08-28 17:08:12 pippijn Exp $
8 */
9
10 //>>>>>>>>>>> writer.C <<<<<<<<<<<//
11 #include <json/value.h>
12 #include <json/writer.h>
13
14 #include <utility>
15 #include <iostream>
16
17 #include <cassert>
18 #include <cstdio>
19
20 namespace json
21 {
22 static void uintToString (unsigned value,
23 char *&current)
24 {
25 *--current = 0;
26 do
27 {
28 *--current = (value % 10) + '0';
29 value /= 10;
30 }
31 while (value != 0);
32 }
33
34 std::string valueToString (int value)
35 {
36 char buffer[32];
37 char *current = buffer + sizeof (buffer);
38 bool isNegative = value < 0;
39 if (isNegative)
40 value = -value;
41 uintToString (unsigned (value), current);
42 if (isNegative)
43 *--current = '-';
44 assert (current >= buffer);
45 return current;
46 }
47
48
49 std::string valueToString (unsigned value)
50 {
51 char buffer[32];
52 char *current = buffer + sizeof (buffer);
53 uintToString (value, current);
54 assert (current >= buffer);
55 return current;
56 }
57
58 std::string valueToString (double value)
59 {
60 char buffer[32];
61 sprintf (buffer, "%.16g", value);
62 return buffer;
63 }
64
65
66 std::string valueToString (bool value)
67 {
68 return value ? "true" : "false";
69 }
70
71 std::string valueToQuotedString (char const *value)
72 {
73 // Not sure how to handle unicode...
74 if (std::strpbrk (value, "\"\\\b\f\n\r\t") == NULL)
75 return std::string ("\"") + value + "\"";
76 // We have to walk value and escape any special characters.
77 // Appending to std::string is not efficient, but this should be rare.
78 // (Note: forward slashes are *not* rare, but I am not escaping them.)
79 unsigned maxsize = strlen (value) * 2 + 3; // allescaped+quotes+NULL
80 std::string result;
81 result.reserve (maxsize); // to avoid lots of allocs
82 result += "\"";
83 for (char const* c=value; *c != 0; ++c){
84 switch (*c){
85 case '\"':
86 result += "\\\"";
87 break;
88 case '\\':
89 result += "\\\\";
90 break;
91 case '\b':
92 result += "\\b";
93 break;
94 case '\f':
95 result += "\\f";
96 break;
97 case '\n':
98 result += "\\n";
99 break;
100 case '\r':
101 result += "\\r";
102 break;
103 case '\t':
104 result += "\\t";
105 break;
106 case '/':
107 // Even though \/ is considered a legal escape in JSON, a bare
108 // slash is also legal, so I see no reason to escape it.
109 // (I hope I am not misunderstanding something.)
110 default:
111 result += *c;
112 }
113 }
114 result += "\"";
115 return result;
116 }
117
118 // Class Writer
119 Writer::~Writer ()
120 {
121 }
122
123
124 // Class FastWriter
125 FastWriter::FastWriter ()
126 : yamlCompatiblityEnabled_ (false)
127 {
128 }
129
130
131 void
132 FastWriter::enableYAMLCompatibility ()
133 {
134 yamlCompatiblityEnabled_ = true;
135 }
136
137
138 std::string
139 FastWriter::write (const Value &root)
140 {
141 document_ = "";
142 writeValue (root);
143 document_ += "\n";
144 return document_;
145 }
146
147
148 void
149 FastWriter::writeValue (const Value &value)
150 {
151 switch (value.type ())
152 {
153 case nullValue:
154 document_ += "null";
155 break;
156 case intValue:
157 document_ += valueToString (static_cast<int> (value));
158 break;
159 case uintValue:
160 document_ += valueToString (static_cast<unsigned> (value));
161 break;
162 case realValue:
163 document_ += valueToString (static_cast<double> (value));
164 break;
165 case stringValue:
166 document_ += valueToQuotedString (static_cast<char const *> (value));
167 break;
168 case booleanValue:
169 document_ += valueToString (static_cast<bool> (value));
170 break;
171 case arrayValue:
172 {
173 document_ += "[";
174 int size = value.size ();
175 for (int index = 0; index < size; ++index)
176 {
177 if (index > 0)
178 document_ += ",";
179 writeValue (value[index]);
180 }
181 document_ += "]";
182 }
183 break;
184 case objectValue:
185 {
186 Value::Members members (value.getMemberNames ());
187 document_ += "{";
188 for (Value::Members::iterator it = members.begin ();
189 it != members.end ();
190 ++it)
191 {
192 const std::string &name = *it;
193 if (it != members.begin ())
194 document_ += ",";
195 document_ += valueToQuotedString (name.c_str ());
196 document_ += yamlCompatiblityEnabled_ ? ": "
197 : ":";
198 writeValue (value[name]);
199 }
200 document_ += "}";
201 }
202 break;
203 }
204 }
205
206
207 // Class StyledWriter
208 StyledWriter::StyledWriter ()
209 : rightMargin_ (74)
210 , indentSize_ (3)
211 {
212 }
213
214
215 std::string
216 StyledWriter::write (const Value &root)
217 {
218 document_ = "";
219 addChildValues_ = false;
220 indentString_ = "";
221 writeCommentBeforeValue (root);
222 writeValue (root);
223 writeCommentAfterValueOnSameLine (root);
224 document_ += "\n";
225 return document_;
226 }
227
228
229 void
230 StyledWriter::writeValue (const Value &value)
231 {
232 switch (value.type ())
233 {
234 case nullValue:
235 pushValue ("null");
236 break;
237 case intValue:
238 pushValue (valueToString (static_cast<int> (value)));
239 break;
240 case uintValue:
241 pushValue (valueToString (static_cast<unsigned> (value)));
242 break;
243 case realValue:
244 pushValue (valueToString (static_cast<double> (value)));
245 break;
246 case stringValue:
247 pushValue (valueToQuotedString (static_cast<char const *> (value)));
248 break;
249 case booleanValue:
250 pushValue (valueToString (static_cast<bool> (value)));
251 break;
252 case arrayValue:
253 writeArrayValue (value);
254 break;
255 case objectValue:
256 {
257 Value::Members members (value.getMemberNames ());
258 if (members.empty ())
259 pushValue ("{}");
260 else
261 {
262 writeWithIndent ("{");
263 indent ();
264 Value::Members::iterator it = members.begin ();
265 while (true)
266 {
267 const std::string &name = *it;
268 const Value &childValue = value[name];
269 writeCommentBeforeValue (childValue);
270 writeWithIndent (valueToQuotedString (name.c_str ()));
271 document_ += " : ";
272 writeValue (childValue);
273 if (++it == members.end ())
274 {
275 writeCommentAfterValueOnSameLine (childValue);
276 break;
277 }
278 document_ += ",";
279 writeCommentAfterValueOnSameLine (childValue);
280 }
281 unindent ();
282 writeWithIndent ("}");
283 }
284 }
285 break;
286 }
287 }
288
289
290 void
291 StyledWriter::writeArrayValue (const Value &value)
292 {
293 unsigned size = value.size ();
294 if (size == 0)
295 pushValue ("[]");
296 else
297 {
298 bool isArrayMultiLine = isMultineArray (value);
299 if (isArrayMultiLine)
300 {
301 writeWithIndent ("[");
302 indent ();
303 bool hasChildValue = !childValues_.empty ();
304 unsigned index = 0;
305 while (true)
306 {
307 const Value &childValue = value[index];
308 writeCommentBeforeValue (childValue);
309 if (hasChildValue)
310 writeWithIndent (childValues_[index]);
311 else
312 {
313 writeIndent ();
314 writeValue (childValue);
315 }
316 if (++index == size)
317 {
318 writeCommentAfterValueOnSameLine (childValue);
319 break;
320 }
321 document_ += ",";
322 writeCommentAfterValueOnSameLine (childValue);
323 }
324 unindent ();
325 writeWithIndent ("]");
326 }
327 else // output on a single line
328 {
329 assert (childValues_.size () == size);
330 document_ += "[ ";
331 for (unsigned index = 0; index < size; ++index)
332 {
333 if (index > 0)
334 document_ += ", ";
335 document_ += childValues_[index];
336 }
337 document_ += " ]";
338 }
339 }
340 }
341
342
343 bool
344 StyledWriter::isMultineArray (const Value &value)
345 {
346 int size = value.size ();
347 bool isMultiLine = size*3 >= rightMargin_ ;
348 childValues_.clear ();
349 for (int index = 0; index < size && !isMultiLine; ++index)
350 {
351 const Value &childValue = value[index];
352 isMultiLine = isMultiLine || ( (childValue.isArray () || childValue.isObject ()) && childValue.size () > 0);
353 }
354 if (!isMultiLine) // check if line length > max line length
355 {
356 childValues_.reserve (size);
357 addChildValues_ = true;
358 int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
359 for (int index = 0; index < size && !isMultiLine; ++index)
360 {
361 writeValue (value[index]);
362 lineLength += int (childValues_[index].length ());
363 isMultiLine = isMultiLine && hasCommentForValue (value[index]);
364 }
365 addChildValues_ = false;
366 isMultiLine = isMultiLine || lineLength >= rightMargin_;
367 }
368 return isMultiLine;
369 }
370
371
372 void
373 StyledWriter::pushValue (const std::string &value)
374 {
375 if (addChildValues_)
376 childValues_.push_back (value);
377 else
378 document_ += value;
379 }
380
381
382 void
383 StyledWriter::writeIndent ()
384 {
385 if (!document_.empty ())
386 {
387 char last = document_[document_.length ()-1];
388 if (last == ' ') // already indented
389 return;
390 if (last != '\n') // Comments may add new-line
391 document_ += '\n';
392 }
393 document_ += indentString_;
394 }
395
396
397 void
398 StyledWriter::writeWithIndent (const std::string &value)
399 {
400 writeIndent ();
401 document_ += value;
402 }
403
404
405 void
406 StyledWriter::indent ()
407 {
408 indentString_ += std::string (indentSize_, ' ');
409 }
410
411
412 void
413 StyledWriter::unindent ()
414 {
415 assert (int (indentString_.size ()) >= indentSize_);
416 indentString_.resize (indentString_.size () - indentSize_);
417 }
418
419
420 void
421 StyledWriter::writeCommentBeforeValue (const Value &root)
422 {
423 if (!root.hasComment (commentBefore))
424 return;
425 document_ += normalizeEOL (root.getComment (commentBefore));
426 document_ += "\n";
427 }
428
429
430 void
431 StyledWriter::writeCommentAfterValueOnSameLine (const Value &root)
432 {
433 if (root.hasComment (commentAfterOnSameLine))
434 document_ += " " + normalizeEOL (root.getComment (commentAfterOnSameLine));
435
436 if (root.hasComment (commentAfter))
437 {
438 document_ += "\n";
439 document_ += normalizeEOL (root.getComment (commentAfter));
440 document_ += "\n";
441 }
442 }
443
444
445 bool
446 StyledWriter::hasCommentForValue (const Value &value)
447 {
448 return value.hasComment (commentBefore)
449 || value.hasComment (commentAfterOnSameLine)
450 || value.hasComment (commentAfter);
451 }
452
453
454 std::string
455 StyledWriter::normalizeEOL (const std::string &text)
456 {
457 std::string normalized;
458 normalized.reserve (text.length ());
459 char const *begin = text.c_str ();
460 char const *end = begin + text.length ();
461 char const *current = begin;
462 while (current != end)
463 {
464 char c = *current++;
465 if (c == '\r') // mac or dos EOL
466 {
467 if (*current == '\n') // convert dos EOL
468 ++current;
469 normalized += '\n';
470 }
471 else // handle unix EOL & other char
472 normalized += c;
473 }
474 return normalized;
475 }
476
477 std::ostream& operator << (std::ostream &sout, const Value &root)
478 {
479 StyledWriter writer;
480 sout << writer.write (root);
481 return sout;
482 }
483 } // namespace json