Jamoma API  0.6.0.a19
JSONWriter.cpp
1 #include "JSONNode.h"
2 #ifdef JSON_WRITE_PRIORITY
3 #include "JSONWorker.h"
4 #include "JSONGlobals.h"
5 
6 extern bool used_ascii_one;
7 
8 #ifdef JSON_INDENT
9  inline json_string makeIndent(unsigned int amount) json_nothrow json_write_priority;
10  inline json_string makeIndent(unsigned int amount) json_nothrow {
11  if (amount == 0xFFFFFFFF) return json_global(EMPTY_JSON_STRING);
12  json_string result;
13  result.reserve(amount * json_global(INDENT).length());
14  for(unsigned int i = 0; i < amount; ++i){
15  result += json_global(INDENT);
16  }
17  JSON_ASSERT(result.capacity() == amount * json_global(INDENT).length(), JSON_TEXT("makeIndent made a string too big"));
18  return result;
19  }
20 #else
21  inline json_string makeIndent(unsigned int amount) json_nothrow {
22  if (amount == 0xFFFFFFFF) return json_global(EMPTY_JSON_STRING);
23  if (json_likely(amount < 8)){
24  static const json_string cache[] = {
25  json_string(),
26  json_string(JSON_TEXT("\t")),
27  json_string(JSON_TEXT("\t\t")),
28  json_string(JSON_TEXT("\t\t\t")),
29  json_string(JSON_TEXT("\t\t\t\t")),
30  json_string(JSON_TEXT("\t\t\t\t\t")),
31  json_string(JSON_TEXT("\t\t\t\t\t\t")),
32  json_string(JSON_TEXT("\t\t\t\t\t\t\t"))
33  };
34  return cache[amount];
35  }
36  #ifndef JSON_LESS_MEMORY
37  if (json_likely(amount < 16)){
38  static const json_string cache[] = {
39  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t")),
40  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t")),
41  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t")),
42  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t")),
43  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t")),
44  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t")),
45  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t")),
46  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"))
47  };
48  return cache[amount - 8];
49  }
50  #if JSON_WRITE_PRIORITY == HIGH
51  if (json_likely(amount < 24)){
52  static const json_string cache[] = {
53  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")),
54  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")),
55  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")),
56  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")),
57  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")),
58  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")),
59  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")),
60  json_string(JSON_TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"))
61  };
62  return cache[amount - 16];
63  }
64  #endif
65  #endif
66  return json_string(amount, JSON_TEXT('\t'));
67  }
68 #endif
69 
70 void internalJSONNode::WriteName(bool formatted, bool arrayChild, json_string & output) const json_nothrow {
71  if (!arrayChild){
72  output += JSON_TEXT("\"");
73  JSONWorker::UnfixString(_name, _name_encoded, output);
74  output += ((formatted) ? JSON_TEXT("\" : ") : JSON_TEXT("\":"));
75  }
76 }
77 
78 void internalJSONNode::WriteChildren(unsigned int indent, json_string & output) const json_nothrow {
79  //Iterate through the children and write them
80  if (json_likely(CHILDREN -> empty())) return;
81 
82  json_string indent_plus_one;
83  //handle whether or not it's formatted JSON
84  if (indent != 0xFFFFFFFF){ //it's formatted, make the indentation strings
85  indent_plus_one = json_global(NEW_LINE) + makeIndent(++indent);
86  }
87 
88  //else it's not formatted, leave the indentation strings empty
89  const size_t size_minus_one = CHILDREN -> size() - 1;
90  size_t i = 0;
91  JSONNode ** it = CHILDREN -> begin();
92  for(JSONNode ** it_end = CHILDREN -> end(); it != it_end; ++it, ++i){
93 
94  output += indent_plus_one;
95  (*it) -> internal -> Write(indent, type() == JSON_ARRAY, output);
96  if (json_likely(i < size_minus_one)) output += JSON_TEXT(','); //the last one does not get a comma, but all of the others do
97  }
98  if (indent != 0xFFFFFFFF){
99  output += json_global(NEW_LINE);
100  output += makeIndent(indent - 1);
101  }
102 }
103 
104 #ifdef JSON_ARRAY_SIZE_ON_ONE_LINE
105  void internalJSONNode::WriteChildrenOneLine(unsigned int indent, json_string & output) const json_nothrow {
106  //Iterate through the children and write them
107  if (json_likely(CHILDREN -> empty())) return;
108  if ((*CHILDREN -> begin()) -> internal -> isContainer()) return WriteChildren(indent, output);
109 
110  json_string comma(JSON_TEXT(","));
111  if (indent != 0xFFFFFFFF){
112  comma += JSON_TEXT(' ');
113  }
114 
115  //else it's not formatted, leave the indentation strings empty
116  const size_t size_minus_one = CHILDREN -> size() - 1;
117  size_t i = 0;
118  JSONNode ** it = CHILDREN -> begin();
119  for(JSONNode ** it_end = CHILDREN -> end(); it != it_end; ++it, ++i){
120  (*it) -> internal -> Write(indent, type() == JSON_ARRAY, output);
121  if (json_likely(i < size_minus_one)) output += comma; //the last one does not get a comma, but all of the others do
122  }
123  }
124 #endif
125 
126 #ifdef JSON_COMMENTS
127  void internalJSONNode::WriteComment(unsigned int indent, json_string & output) const json_nothrow {
128  if (indent == 0xFFFFFFFF) return;
129  if (json_likely(_comment.empty())) return;
130  size_t pos = _comment.find(JSON_TEXT('\n'));
131 
132  const json_string current_indent(json_global(NEW_LINE) + makeIndent(indent));
133 
134  if (json_likely(pos == json_string::npos)){ //Single line comment
135  output += current_indent;
136  output += json_global(SINGLELINE_COMMENT);
137  output.append(_comment.begin(), _comment.end());
138  output += current_indent;
139  return;
140  }
141 
142  /*
143  Multiline comments
144  */
145  output += current_indent;
146  #if !(defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS))
147  const json_string current_indent_plus_one(json_global(NEW_LINE) + makeIndent(indent + 1));
148  output += JSON_TEXT("/*");
149  output += current_indent_plus_one;
150  #endif
151  size_t old = 0;
152  while(pos != json_string::npos){
153  if (json_unlikely(pos && _comment[pos - 1] == JSON_TEXT('\r'))) --pos;
154  #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS)
155  output += json_global(SINGLELINE_COMMENT);
156  #endif
157  output.append(_comment.begin() + old, _comment.begin() + pos);
158 
159  #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS)
160  output += current_indent;
161  #else
162  output += current_indent_plus_one;
163  #endif
164  old = (_comment[pos] == JSON_TEXT('\r')) ? pos + 2 : pos + 1;
165  pos = _comment.find(JSON_TEXT('\n'), old);
166  }
167  #if defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS)
168  output += json_global(SINGLELINE_COMMENT);
169  #endif
170  output.append(_comment.begin() + old, _comment.end());
171  output += current_indent;
172  #if !(defined(JSON_WRITE_BASH_COMMENTS) || defined(JSON_WRITE_SINGLE_LINE_COMMENTS))
173  output += JSON_TEXT("*/");
174  output += current_indent;
175  #endif
176  }
177 #else
178  inline void internalJSONNode::WriteComment(unsigned int, json_string &) const json_nothrow {}
179 #endif
180 
181 void internalJSONNode::DumpRawString(json_string & output) const json_nothrow {
182  //first remove the \1 characters
183  if (used_ascii_one){ //if it hasn't been used yet, don't bother checking
184  json_string result(_string.begin(), _string.end());
185  for(json_string::iterator beg = result.begin(), en = result.end(); beg != en; ++beg){
186  if (*beg == JSON_TEXT('\1')) *beg = JSON_TEXT('\"');
187  }
188  output += result;
189  return;
190  } else {
191  output.append(_string.begin(), _string.end());
192  }
193 }
194 
195 void internalJSONNode::Write(unsigned int indent, bool arrayChild, json_string & output) const json_nothrow {
196  const bool formatted = indent != 0xFFFFFFFF;
197  WriteComment(indent, output);
198 
199  #if !defined(JSON_PREPARSE) && defined(JSON_READ_PRIORITY)
200  if (!(formatted || fetched)){ //It's not formatted or fetched, just do a raw dump
201  WriteName(false, arrayChild, output);
202  //first remove the \1 characters
203  DumpRawString(output);
204  return;
205  }
206  #endif
207 
208  WriteName(formatted, arrayChild, output);
209  //It's either formatted or fetched
210  switch (_type){
211  case JSON_NODE: //got members, write the members
212  Fetch();
213  output += JSON_TEXT("{");
214  WriteChildren(indent, output);
215  output += JSON_TEXT("}");
216  return;
217  case JSON_ARRAY: //write out the child nodes int he array
218  Fetch();
219  output += JSON_TEXT("[");
220  #ifdef JSON_ARRAY_SIZE_ON_ONE_LINE
221  if (size() <= JSON_ARRAY_SIZE_ON_ONE_LINE){
222  WriteChildrenOneLine(indent, output);
223  } else {
224  #endif
225  WriteChildren(indent, output);
226  #ifdef JSON_ARRAY_SIZE_ON_ONE_LINE
227  }
228  #endif
229  output += JSON_TEXT("]");
230  return;
231  case JSON_NUMBER: //write out a literal, without quotes
232  case JSON_NULL:
233  case JSON_BOOL:
234  output.append(_string.begin(), _string.end());
235  return;
236  }
237 
238  JSON_ASSERT(_type == JSON_STRING, JSON_TEXT("Unknown json node type"));
239  //If it go here, then it's a json_string
240  #if !defined(JSON_PREPARSE) && defined(JSON_READ_PRIORITY)
241  if (json_likely(fetched)){
242  #endif
243  output += JSON_TEXT("\"");
244  JSONWorker::UnfixString(_string, _string_encoded, output); //It's already been fetched, meaning that it's unescaped
245  output += JSON_TEXT("\"");
246  #if !defined(JSON_PREPARSE) && defined(JSON_READ_PRIORITY)
247  } else {
248  DumpRawString(output); //it hasn't yet been fetched, so it's already unescaped, just do a dump
249  }
250  #endif
251 }
252 #endif