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, 1 month 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

# User Rev Content
1 pippijn 1.2 /*
2     * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
3 pippijn 1.3 * Rights to this code are as documented in doc/pod/license.pod.
4 pippijn 1.2 *
5     * JSON-RPC for Ermyth
6     *
7 pippijn 1.3 * $Id: writer.C,v 1.2 2007-08-28 17:08:12 pippijn Exp $
8 pippijn 1.2 */
9    
10 pippijn 1.1 //>>>>>>>>>>> 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 pippijn 1.2 result.reserve (maxsize); // to avoid lots of allocs
82 pippijn 1.1 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