Atlas  0.7.0
Networking protocol for the Worldforge system.
XML.cpp
1 // This file may be redistributed and modified under the terms of the
2 // GNU Lesser General Public License (See COPYING for details).
3 // Copyright (C) 2000-2001 Michael Day, Stefanus Du Toit
4 
5 // $Id$
6 
7 #include "Atlas/Codecs/XML.h"
8 #include "Atlas/Message/Element.h"
9 
10 #include <iostream>
11 
12 #include <cstdlib>
13 
14 namespace Atlas {
15  namespace Codecs {
16 
17  XML::XML(std::istream &in, std::ostream &out, Atlas::Bridge &b)
18  : m_istream(in), m_ostream(out), m_bridge(b) {
19  m_token = TOKEN_DATA;
20  m_state.push(PARSE_NOTHING);
21  m_data.push("");
22  }
23 
24  void XML::tokenTag(char next) {
25  m_tag.erase();
26 
27  switch (next) {
28  case '/':
29  m_token = TOKEN_END_TAG;
30  break;
31 
32  case '>':
33  // FIXME signal error here
34  // unexpected character
35  break;
36 
37  default:
38  m_token = TOKEN_START_TAG;
39  m_tag += next;
40  break;
41  }
42  }
43 
44  void XML::tokenStartTag(char next) {
45  switch (next) {
46  case '<':
47  // FIXME signal error here
48  // unexpected character
49  break;
50 
51  case '>':
52  parseStartTag();
53  m_token = TOKEN_DATA;
54  m_data.push("");
55  break;
56 
57  case '/':
58  parseStartTag();
59  m_token = TOKEN_END_TAG;
60  m_data.push("");
61  break;
62 
63  default:
64  m_tag += next;
65  break;
66  }
67  }
68 
69  void XML::tokenEndTag(char next) {
70  switch (next) {
71  case '<':
72  // FIXME signal error here
73  // unexpected character
74  break;
75 
76  case '>':
77  parseEndTag();
78  m_token = TOKEN_DATA;
79  m_data.pop();
80  break;
81 
82  default:
83  m_tag += next;
84  break;
85  }
86  }
87 
88  void XML::tokenData(char next) {
89  switch (next) {
90  case '<':
91  m_token = TOKEN_TAG;
92  break;
93 
94  case '>':
95  // FIXME signal error here
96  // unexpected character
97  break;
98 
99  default:
100  m_data.top() += next;
101  break;
102  }
103  }
104 
105  void XML::parseStartTag() {
106  int tag_end = (int) m_tag.find(' ');
107  int name_start = (int) m_tag.find("name=\"") + 6;
108  int name_end = (int) m_tag.rfind('\"');
109 
110  if (name_start < name_end) {
111  m_name = unescape(
112  std::string(m_tag, (unsigned long) name_start, (unsigned long) (name_end - name_start)));
113  } else {
114  m_name.erase();
115  }
116 
117  m_tag = std::string(m_tag, 0, (unsigned long) tag_end);
118 
119  switch (m_state.top()) {
120  case PARSE_NOTHING:
121  if (m_tag == "atlas") {
122  m_bridge.streamBegin();
123  m_state.push(PARSE_STREAM);
124  } else {
125  // FIXME signal error here
126  // unexpected tag
127  }
128  break;
129 
130  case PARSE_STREAM:
131  if (m_tag == "map") {
132  m_bridge.streamMessage();
133  m_state.push(PARSE_MAP);
134  } else {
135  // FIXME signal error here
136  // unexpected tag
137  }
138  break;
139 
140  case PARSE_MAP:
141  if (m_tag == "map") {
142  m_bridge.mapMapItem(m_name);
143  m_state.push(PARSE_MAP);
144  } else if (m_tag == "list") {
145  m_bridge.mapListItem(m_name);
146  m_state.push(PARSE_LIST);
147  } else if (m_tag == "int") {
148  m_state.push(PARSE_INT);
149  } else if (m_tag == "float") {
150  m_state.push(PARSE_FLOAT);
151  } else if (m_tag == "string") {
152  m_state.push(PARSE_STRING);
153  } else if (m_tag == "none") {
154  m_state.push(PARSE_NONE);
155  } else {
156  // FIXME signal error here
157  // unexpected tag
158  }
159  break;
160 
161  case PARSE_LIST:
162  if (m_tag == "map") {
163  m_bridge.listMapItem();
164  m_state.push(PARSE_MAP);
165  } else if (m_tag == "list") {
166  m_bridge.listListItem();
167  m_state.push(PARSE_LIST);
168  } else if (m_tag == "int") {
169  m_state.push(PARSE_INT);
170  } else if (m_tag == "float") {
171  m_state.push(PARSE_FLOAT);
172  } else if (m_tag == "string") {
173  m_state.push(PARSE_STRING);
174  } else if (m_tag == "none") {
175  m_state.push(PARSE_NONE);
176  } else {
177  // FIXME signal error here
178  // unexpected tag
179  }
180  break;
181 
182  case PARSE_INT:
183  case PARSE_FLOAT:
184  case PARSE_STRING:
185  case PARSE_NONE:
186  // FIXME signal error here
187  // unexpected tag
188  break;
189  }
190  }
191 
192  void XML::parseEndTag() {
193  switch (m_state.top()) {
194  case PARSE_NOTHING:
195  // FIXME signal error here
196  // unexpected tag
197  break;
198 
199  case PARSE_STREAM:
200  if (m_tag == "atlas") {
201  m_bridge.streamEnd();
202  m_state.pop();
203  } else {
204  // FIXME signal error here
205  // unexpected tag
206  }
207  break;
208 
209  case PARSE_MAP:
210  if (m_tag == "map") {
211  m_bridge.mapEnd();
212  m_state.pop();
213  } else {
214  // FIXME signal error here
215  // unexpected tag
216  }
217  break;
218 
219  case PARSE_LIST:
220  if (m_tag == "list") {
221  m_bridge.listEnd();
222  m_state.pop();
223  } else {
224  // FIXME signal error here
225  // unexpected tag
226  }
227  break;
228 
229  case PARSE_INT:
230  if (m_tag == "int") {
231  m_state.pop();
232  try {
233  Atlas::Message::IntType value = 0;
234  auto data = m_data.top();
235  if (!data.empty()) {
236  value = std::stol(data);
237  }
238  if (m_state.top() == PARSE_MAP) {
239  m_bridge.mapIntItem(m_name, value);
240  } else {
241  m_bridge.listIntItem(value);
242  }
243  } catch (...) {
244  //Could not parse long; just ignore
245  }
246  } else {
247  // FIXME signal error here
248  // unexpected tag
249  }
250  break;
251 
252  case PARSE_FLOAT:
253  if (m_tag == "float") {
254  m_state.pop();
255  try {
256  Atlas::Message::FloatType value = 0;
257  auto data = m_data.top();
258  if (!data.empty()) {
259  value = std::stod(data);
260  }
261  if (m_state.top() == PARSE_MAP) {
262  m_bridge.mapFloatItem(m_name, value);
263  } else {
264  m_bridge.listFloatItem(value);
265  }
266  } catch (...) {
267  //Could not parse double; just ignore.
268  }
269  } else {
270  // FIXME signal error here
271  // unexpected tag
272  }
273  break;
274 
275  case PARSE_STRING:
276  if (m_tag == "string") {
277  m_state.pop();
278  if (m_state.top() == PARSE_MAP) {
279  m_bridge.mapStringItem(m_name, unescape(m_data.top()));
280  } else {
281  m_bridge.listStringItem(unescape(m_data.top()));
282  }
283  } else {
284  // FIXME signal error here
285  // unexpected tag
286  }
287  break;
288  case PARSE_NONE:
289  if (m_tag == "none") {
290  m_state.pop();
291  if (m_state.top() == PARSE_MAP) {
292  m_bridge.mapNoneItem(m_name);
293  } else {
294  m_bridge.listNoneItem();
295  }
296  } else {
297  // FIXME signal error here
298  // unexpected tag
299  }
300  break;
301  }
302  }
303 
304  void XML::poll() {
305  m_istream.peek();
306 
307  std::streamsize count;
308 
309  while ((count = m_istream.rdbuf()->in_avail()) > 0) {
310 
311  for (std::streamsize i = 0; i < count; ++i) {
312 
313  char next = m_istream.rdbuf()->sbumpc();
314 
315  switch (m_token) {
316  case TOKEN_TAG:
317  tokenTag(next);
318  break;
319  case TOKEN_START_TAG:
320  tokenStartTag(next);
321  break;
322  case TOKEN_END_TAG:
323  tokenEndTag(next);
324  break;
325  case TOKEN_DATA:
326  tokenData(next);
327  break;
328  }
329  }
330  }
331  }
332 
333  void XML::streamBegin() {
334  m_ostream << "<atlas>";
335  }
336 
337  void XML::streamEnd() {
338  m_ostream << "</atlas>";
339  }
340 
341  void XML::streamMessage() {
342  m_ostream << "<map>";
343  }
344 
345  void XML::mapMapItem(std::string name) {
346  m_ostream << "<map name=\"" << escape(name) << "\">";
347  }
348 
349  void XML::mapListItem(std::string name) {
350  m_ostream << "<list name=\"" << escape(name) << "\">";
351  }
352 
353  void XML::mapIntItem(std::string name, std::int64_t data) {
354  m_ostream << "<int name=\"" << escape(name) << "\">" << data << "</int>";
355  }
356 
357  void XML::mapFloatItem(std::string name, double data) {
358  m_ostream << "<float name=\"" << escape(name) << "\">" << data << "</float>";
359  }
360 
361  void XML::mapStringItem(std::string name, std::string data) {
362  m_ostream << "<string name=\"" << escape(name) << "\">" << escape(data) << "</string>";
363  }
364 
365  void XML::mapNoneItem(std::string name) {
366  m_ostream << "<none name=\"" << escape(name) << "\"></none>";
367  }
368 
369  void XML::mapEnd() {
370  m_ostream << "</map>";
371  }
372 
373  void XML::listMapItem() {
374  m_ostream << "<map>";
375  }
376 
377  void XML::listListItem() {
378  m_ostream << "<list>";
379  }
380 
381  void XML::listIntItem(std::int64_t data) {
382  m_ostream << "<int>" << data << "</int>";
383  }
384 
385  void XML::listFloatItem(double data) {
386  m_ostream << "<float>" << data << "</float>";
387  }
388 
389  void XML::listStringItem(std::string data) {
390  m_ostream << "<string>" << escape(data) << "</string>";
391  }
392 
393  void XML::listNoneItem() {
394  m_ostream << "<none></none>";
395  }
396 
397  void XML::listEnd() {
398  m_ostream << "</list>";
399  }
400 
401  std::string XML::escape(const std::string &original) {
402  std::string buffer;
403  buffer.reserve(original.size() + (original.size() / 2));
404  for (size_t pos = 0; pos != original.size(); ++pos) {
405  switch (original[pos]) {
406  case '&':
407  buffer.append("&amp;");
408  break;
409  case '\"':
410  buffer.append("&quot;");
411  break;
412  case '\'':
413  buffer.append("&apos;");
414  break;
415  case '<':
416  buffer.append("&lt;");
417  break;
418  case '>':
419  buffer.append("&gt;");
420  break;
421  default:
422  buffer.append(1, original[pos]);
423  break;
424  }
425  }
426  return buffer;
427  }
428 
429  std::string XML::unescape(const std::string &original) {
430  std::string buffer;
431  buffer.reserve(original.size());
432  for (size_t pos = 0; pos != original.size(); ++pos) {
433  if (original[pos] == '&') {
434  if (original.size() - pos >= 3) {
435  if (original[pos + 1] == 'l' && original[pos + 2] == 't' && original[pos + 3] == ';') {
436  buffer.append(1, '<');
437  pos += 3;
438  continue;
439  } else if (original[pos + 1] == 'g' && original[pos + 2] == 't' && original[pos + 3] == ';') {
440  buffer.append(1, '>');
441  pos += 3;
442  continue;
443  }
444  }
445  if (original.size() - pos >= 4) {
446  if (original[pos + 1] == 'a' && original[pos + 2] == 'm' && original[pos + 3] == 'p' &&
447  original[pos + 4] == ';') {
448  buffer.append(1, '&');
449  pos += 4;
450  continue;
451  }
452  }
453  if (original.size() - pos >= 5) {
454  if (original[pos + 1] == 'q' && original[pos + 2] == 'u' && original[pos + 3] == 'o' &&
455  original[pos + 4] == 't' && original[pos + 5] == ';') {
456  buffer.append(1, '"');
457  pos += 5;
458  continue;
459  } else if (original[pos + 1] == 'a' && original[pos + 2] == 'p' && original[pos + 3] == 'o' &&
460  original[pos + 4] == 's' && original[pos + 5] == ';') {
461  buffer.append(1, '\'');
462  pos += 5;
463  continue;
464  }
465  }
466  }
467  buffer.append(1, original[pos]);
468  }
469  return buffer;
470  }
471 
472 
473  }
474 } //namespace Atlas::Codecs
Definition: Bridge.h:21