source: trunk/FACT++/src/Converter.cc@ 10252

Last change on this file since 10252 was 10219, checked in by tbretz, 10 years ago
Added conversion to a vector of boost::any, added more format options for this case and added special treatment for quoted strings and bools.
File size: 15.4 KB
Line 
1// **************************************************************************
2/** @class Converter
3
4@brief An interpreter to convert a command line into a command plus data memory
5
6The Converter class interprets arguments in a string accoring to the
7given format definition and produces a corresponding memory block from it
8which can be attached to an event later.
9
10The format is given according to the Dim format description:
11
12 The format parameter specifies the contents of the structure in the
13 form T:N[;T:N]*[;T] where T is the item type: (I)nteger, (C)haracter,
14 (L)ong, (S)hort, (F)loat, (D)ouble, X(tra long==long long) and N is the
15 number of such items. The type alone at the end means all following items
16 are of the same type. Example: "I:3;F:2;C" means 3 Integers, 2 Floats and
17 Characters until the end. The format parameter is used for
18 communicating between different platforms.
19
20For example:
21
22\code
23 Converter c(cout, "I:1;F:2;I:2", "COMMAND 1 2.5 4.2 3 4");
24\endcode
25
26would produce a 20 byte data block with the integers 1, the floats
272.5 and 4.2, and the intergers 3 and 4, in this order.
28
29The opposite direction is also possible
30
31\code
32 Converter c(cout, "I:1;F:2;I:2", pointer, size);
33 \endcode
34
35In addition to converting the options from a string into binary format
36or back all found values are also put into a vector of boost::any objects.
37They can be accessed using e.g.
38
39\code
40Converter c(cout, "I:1;F:1;B:1;A:1;C", "112 5.5 on this is a test");
41
42 cout << c.Get<int>(0) << endl; // prints '112'
43 cout << c.Get<float>(1) << endl; // prints '5.5'
44 cout << c.Get<bool>(2) << endl; // prints '1'
45 cout << c.Get<string>(3) << endl; // prints 'this'
46 cout << c.Get<string>(4) << endl; // prints 'is a test'
47\endcode
48
49The format parameter \b W(ord) is dedicated to this kind of conversion and
50not understood by Dim. In addition there are \b O(ptions) which are like
51Words but can be omitted. They should only be used at the end of the string.
52\b B(ool) is also special. It evaluates true/false, yes/no, on/off, 1/0.
53
54Access with Converter::Get() is exception safe. Access with Converter::At()
55would throw an exception if the index is out of bounds or the conversion
56fails. (This is the prefered method if the type is well known, to easily
57detect programing faults)
58
59@remark
60 Most probably we support more formats than dim does...
61
62*/
63// **************************************************************************
64#include "Converter.h"
65
66#include <iomanip>
67#include <sstream>
68
69#include <cctype> // std::tolower
70#include <algorithm> // std::transform
71
72#include <boost/regex.hpp>
73
74#include "Readline.h"
75#include "WindowLog.h"
76
77using namespace std;
78
79template <class T>
80 void Converter::EvalImp(int i, std::stringstream &line, std::vector<char> &v, const T &val)
81{
82 if (!line)
83 wout << " arg[" << i << "]";
84 else
85 wout << " (" << val << ")";
86
87 vec.push_back(val);
88
89 v.insert(v.end(),
90 reinterpret_cast<const char*>(&val),
91 reinterpret_cast<const char*>(&val+1));
92}
93
94
95// --------------------------------------------------------------------------
96//
97//! Gets a value of the template type T from the stringstream and adds the
98//! value as binary data to the end of the vector. If a value couldn't be
99//! obtained it is set to 0.
100//!
101//! Adds the value to Converter::vec.
102//!
103//! @param i
104//! The number of the processed argument (currently used for some debug
105//! output when an argument is not available and artificially set to 0)
106//!
107//! @param line
108//! The stringstream from which the data should be read
109//!
110//! @param vec
111//! The vector of bytes at which end the data is added in binary format
112//!
113template <class T>
114 void Converter::Eval(int i, std::stringstream &line, std::vector<char> &v)
115{
116 T val;
117 line >> val;
118
119 EvalImp(i, line, v, val);
120}
121
122// --------------------------------------------------------------------------
123//
124//! This functions works similar the the template Eval but is dedicated to
125//! bools. It evaluates yes/no, on/off, true/false and 1/0.
126//!
127//! Sets the failbit of the stream if the "value" is not known.
128//!
129//! Adds the value to Converter::vec.
130//!
131//! @param line
132//! The stringstream from which the data should be read
133//!
134//! @param vec
135//! The vector of bytes at which end the data is added in binary format
136//!
137//! @returns
138//! The bool evaluated
139//!
140void Converter::EvalBool(int i, std::stringstream &line, std::vector<char> &v)
141{
142 string buf;
143 line >> buf;
144 transform(buf.begin(), buf.end(), buf.begin(), (int(*)(int)) std::tolower);
145
146 if (buf=="yes" || buf=="true" || buf=="on" || buf=="1")
147 {
148 EvalImp(i, line, v, bool(true));
149 return;
150 }
151
152 if (buf=="no" || buf=="false" || buf=="off" || buf=="0")
153 {
154 EvalImp(i, line, v, bool(false));
155 return;
156 }
157
158 line.clear(ios::failbit);
159}
160
161void Converter::EvalString(int i, std::stringstream &line, std::vector<char> &v)
162{
163 while (line.peek()==' ')
164 line.get();
165
166 string buf;
167 if (line.peek()=='\"')
168 {
169 line.get();
170 getline(line, buf, '\"');
171 }
172 else
173 line >> buf;
174
175 EvalImp(i, line, v, buf);
176}
177
178// --------------------------------------------------------------------------
179//
180//! Constructs a data block from the given string according to the given
181//! format. (See also the class reference for more details).
182//!
183//! The data block is stored in a vector<char>. It's content can be
184//! retrieved using the member functions Ptr() and Size(). Whether parsing
185//! was successfull or not can be checked with GetRc().
186//! If parsing was not successfull, either the format contained something
187//! odd, the conversion failed (e.g. 5.5 for an int) or the string contained
188//! a wrong number of arguments.
189//!
190//! @param out
191//! The ostream to which errors and debug messages should be printed.
192//!
193//! @param fmt
194//! The format descriptor according to the dim definition
195//!
196//! @param str
197//! The string which should be interpreted, e.g. "1 2 5.5 abcdef"
198//!
199Converter::Converter(std::ostream &out, const std::string &fmt, const std::string &str)
200: rc(false), wout(out)
201{
202 // If the format is empty we are already done
203 if (fmt.empty())
204 {
205 if (!str.empty())
206 {
207 wout << endl;
208 wout << kRed << "Data string not empty as it ought to be!" << endl;
209 return;
210 }
211
212 wout << endl;
213 rc = true;
214 return;
215 }
216
217 // Access both, the data and the format through a stringstream
218 stringstream line(str);
219 stringstream stream(fmt);
220
221 // For better performance we could use sregex
222 static const boost::regex expr("^[ ]*([OBWCSILFDX])[ ]*(:[ ]*([1-9]+[0-9]*))?[ ]*$");
223
224 // Tokenize the format
225 int arg = 0;
226 string buffer;
227 while (getline(stream, buffer, ';'))
228 {
229 boost::smatch what;
230 if (!boost::regex_match(buffer, what, expr))
231 {
232 wout << endl;
233 wout << kRed << "Wrong format string '" << buffer << "'!" << endl;
234 return;
235 }
236
237 const string t = what[1]; // type id
238 const string n = what[3]; // counter
239
240 int cnt = atoi(n.c_str());
241
242 // Check if the format is just C (without a number)
243 // That would mean that it is a \0 terminated string
244 if (t[0]=='C' && cnt==0)
245 {
246 // Remove leading whitespaces
247 while (line.peek()==' ')
248 line.get();
249
250 line >> noskipws;
251
252 const istream_iterator<char> eol; // end-of-line iteartor
253 const string s(istream_iterator<char>(line), eol);
254
255 vec.push_back(s);
256
257 data.insert(data.end(), s.begin(), s.end());
258 data.push_back(0);
259
260 line.clear(ios::eofbit);
261
262 continue;
263 }
264
265 // if the :N part was not given assume 1
266 if (cnt==0)
267 cnt=1;
268
269 // Get as many items from the input line as requested
270 for (int j=0; j<cnt; j++)
271 switch (t[0])
272 {
273 case 'C': // Skip whitespaces when checking for characters
274 if (j>0)
275 line >> noskipws;
276 Eval<char>(arg++, line, data);
277 line >> skipws;
278 break;
279 case 'B': EvalBool (arg++, line, data); break;
280 case 'S': Eval<short> (arg++, line, data); break;
281 case 'I': Eval<int> (arg++, line, data); break;
282 case 'L': Eval<long> (arg++, line, data); break;
283 case 'F': Eval<float> (arg++, line, data); break;
284 case 'D': Eval<double> (arg++, line, data); break;
285 case 'X': Eval<long long>(arg++, line, data); break;
286 case 'W': EvalString (arg++, line, data); break;
287 case 'O': EvalString (arg++, line, data); line.clear(ios::goodbit); break;
288 default:
289 // This should never happen!
290 wout << endl << kRed << "Format '" << t[0] << " not known!" << endl;
291 break;
292 }
293
294 //wout << "{" << line.eof() << line.good() << line.fail() << "}";
295 if (!line)
296 break;
297 }
298 //wout << "{" << line.eof() << line.good() << line.fail() << "}";
299
300 wout << " [" << fmt << "]=" << data.size() << endl;
301
302 // Something wrong with the conversion (e.g. 5.5 for an int)
303 if (line.fail() && !line.eof())
304 {
305 line.clear(); // This is necesasary to get a proper response from tellg()
306 wout << kRed << "Error converting argument at " << arg << " [fmt=" << fmt << "]!" << endl;
307 wout << kRed << str << endl;
308 wout << kRed << setw(int(line.tellg())) << " " << "^" << endl;
309 return;
310 }
311
312 // Not enough arguments, we have not reached the end
313 if (line.fail() && line.eof())
314 {
315 line.clear();
316 wout << kRed << "Not enough arguments [fmt=" << fmt << "]!" << endl;
317 wout << kRed << str << endl;
318 wout << kRed << setw(int(line.tellg())+1) << " " << "^" << endl;
319 return;
320 }
321
322 // Too many arguments, we have not reached the end
323 // Unfortunately, this can also mean that there is something
324 // wrong with the last argument
325 if (line.good() && !line.eof())
326 {
327 wout << kRed << "More arguments available than expected [" << fmt << "]!" << endl;
328 wout << kRed << str << endl;
329 wout << kRed << setw(int(line.tellg())+1) << " " << "^" << endl;
330 return;
331 }
332
333 // Set return code to true (successfull)
334 rc = true;
335}
336
337// --------------------------------------------------------------------------
338//
339//! Gets the value as a binary from the ptr and return it as a string.
340//! The pointer is increased accordingly.
341//!
342//! Adds the value to Converter::vec.
343//!
344//! @param ptr
345//! A reference to a pointer to the data which should be converted.
346//! The pointer is increased according to the size of the data.
347//!
348//! @returns
349//! The data converted into a string
350//!
351template<class T>
352string Converter::Get(const char* &ptr)
353{
354 const T &t = *reinterpret_cast<const T*>(ptr);
355
356 vec.push_back(t);
357
358 ostringstream stream;
359 stream << t;
360 ptr += sizeof(T);
361
362 return stream.str();
363}
364
365// --------------------------------------------------------------------------
366//
367//! Constructs a string from the given data block according to the specified
368//! format. (See also the class reference for more details).
369//!
370//! The resulting string is stored in vector<char> and 0-terminated.
371//! It can be accessed through Ptr().
372//!
373//! If the conversion faild GetRc will return false. In this case the data
374//! contents might not be well defined.
375//!
376//! If no format is given (size == 0) but the data size is finite (>0)
377//! then the data is converted into a hex representation.
378//!
379//! @remark
380//! In cases of failures the stored data might be inexisting and
381//! Ptr() might return NULL. If you output NULL to our streams
382//! they might not show any further output anymore.
383//!
384//! @param fmt
385//! The format descriptor according to the dim definition
386//!
387//! @param out
388//! The ostream to which errors and debug messages should be printed.
389//!
390//! @param dat
391//! Pointer to the start of the binary data
392//!
393//! @param size
394//! Size of the binary data region
395//!
396Converter::Converter(ostream &out, const string &fmt, const void *dat, int size)
397: rc(false), wout(out)
398{
399 const char *ptr = reinterpret_cast<const char *>(dat);
400
401 ostringstream text;
402
403 // Structure: print hex representation
404 if (fmt.size()==0)
405 {
406 if (size==0)
407 {
408 data.push_back(0);
409 rc = true;
410 return;
411 }
412
413 text << hex;
414
415 for (int i=0; i<size; i++)
416 text << setw(2) << ptr[i] << " ";
417
418 const string &ref = text.str();
419 data.insert(data.begin(), ref.begin(), ref.end());
420 data.push_back(0);
421 return;
422 }
423
424 // Access both, the data and the format through a stringstream
425 stringstream stream(fmt);
426
427 // For better performance we could use sregex
428 static const boost::regex expr("^[ ]*([CSILFDX])[ ]*(:[ ]*([1-9]+[0-9]*))?[ ]*$");
429
430 // Tokenize the format
431 string buffer;
432 while (getline(stream, buffer, ';'))
433 {
434 if (ptr-size>=dat)
435 {
436 wout << kRed << "Format description '" << fmt << "' exceeds available data size (" << size << ")" << endl;
437 return;
438 }
439
440 boost::smatch what;
441 if (!boost::regex_match(buffer, what, expr))
442 {
443 wout << kRed << "Wrong format string '" << buffer << "'!" << endl;
444 return;
445 }
446
447 const string t = what[1]; // type id
448 const string n = what[3]; // counter
449
450 int cnt = atoi(n.c_str());
451
452 // Check if the format is just C (without a number)
453 // That would mean that it is a \0 terminated string
454 if (t[0]=='C' && cnt==0)
455 {
456 const string str(ptr);
457 text << ' ' << str;
458 ptr += str.length()+1;
459
460 vec.push_back(str);
461
462 break;
463 }
464
465 // if the :N part was not given assume 1
466 if (cnt==0)
467 cnt=1;
468
469 // Get as many items from the input line as requested
470 for (int j=0; j<cnt; j++)
471 {
472 text << ' ';
473
474 switch (t[0])
475 {
476 case 'C': text << Get<char> (ptr); break;
477 case 'S': text << Get<short> (ptr); break;
478 case 'I': text << Get<int> (ptr); break;
479 case 'L': text << Get<long> (ptr); break;
480 case 'F': text << Get<float> (ptr); break;
481 case 'D': text << Get<double> (ptr); break;
482 case 'X': text << Get<long long>(ptr); break;
483 default:
484 // This should never happen!
485 wout << kRed << "Format '" << t[0] << " not known!" << endl;
486 return;
487 }
488 }
489 }
490
491 if (ptr-size!=dat)
492 {
493 wout << kRed << "Data block size (" << size << ") doesn't fit format description '" << fmt << "'" << endl;
494 return;
495 }
496
497 rc = true;
498
499 const string &ref = text.str();
500 data.insert(data.begin(), ref.begin()+1, ref.end());
501 data.push_back(0);
502}
503
504
505
506vector<string> Converter::Regex(const string &expr, const string &line)
507{
508 const boost::regex reg(expr);
509
510 boost::smatch what;
511 if (!boost::regex_match(line, what, reg, boost::match_extra))
512 return vector<string>();
513
514 vector<string> ret;
515 for (unsigned int i=0; i<what.size(); i++)
516 ret.push_back(what[i]);
517
518 return ret;
519}
Note: See TracBrowser for help on using the repository browser.