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
14namespace 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:20