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

Last change on this file since 10278 was 10278, checked in by tbretz, 14 years ago
Skipped processing of the terminating entry in the list.
File size: 23.5 KB
Line 
1// **************************************************************************
2/** @class Converter
3
4@brief A compiler for the DIM data format string
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", );
24 vector<char> v = c.GetVector("COMMAND 1 2.5 4.2 3 4");
25\endcode
26
27would produce a 20 byte data block with the integers 1, the floats
282.5 and 4.2, and the intergers 3 and 4, in this order.
29
30The opposite direction is also possible
31
32\code
33 Converter c(cout, "I:1;F:2;I:2");
34 cout << c.GetString(pointer, size) << endl;
35 \endcode
36
37Other conversion functions also exist.
38
39A result of a conversion is valid if valid() returns true and either the
40result of the conversion is empty and the format string was valid empty
41or both contain contents.
42
43The format parameter \b W(ord) is dedicated to this kind of conversion and
44not understood by Dim. In addition there are \b O(ptions) which are like
45Words but can be omitted. They should only be used at the end of the string.
46\b B(ool) is also special. It evaluates true/false, yes/no, on/off, 1/0.
47
48The non-DIM like format options can be switched on and off by using the
49strict argument in the constructor.
50
51*/
52// **************************************************************************
53#include "Converter.h"
54
55#include <iostream>
56#include <iomanip>
57#include <sstream>
58
59#include <cctype> // std::tolower
60#include <algorithm> // std::transform
61
62#include <boost/regex.hpp>
63
64#include "Readline.h"
65#include "WindowLog.h"
66
67using namespace std;
68/*
69In addition to converting the options from a string into binary format
70or back all found values are also put into a vector of boost::any objects.
71They can be accessed using e.g.
72
73\code
74Converter c(cout, "I:1;F:1;B:1;A:1;C", "112 5.5 on this is a test");
75
76 cout << c.Get<int>(0) << endl; // prints '112'
77 cout << c.Get<float>(1) << endl; // prints '5.5'
78 cout << c.Get<bool>(2) << endl; // prints '1'
79 cout << c.Get<string>(3) << endl; // prints 'this'
80 cout << c.Get<string>(4) << endl; // prints 'is a test'
81\endcode
82*/
83
84// --------------------------------------------------------------------------
85//
86//! This function is supposed to remove all whitespaces from the format
87//! string to allow easier regular expressions later.
88//!
89//! @param s
90//! string to be cleaned
91//!
92//! @returns
93//! string cleaned from whitespaces
94//
95std::string Converter::Clean(std::string s)
96{
97 while (1)
98 {
99 const size_t pos = s.find_last_of(' ');
100 if (pos==string::npos)
101 break;
102 s.erase(pos, pos+1);
103 }
104
105 return s;
106}
107
108// --------------------------------------------------------------------------
109//
110//! This is just a simplification. For the time being it is used to output
111//! the interpreted contents to the logging stream. Its main purpose
112//! is to add the contents of val in a binary representation to the
113//! vector v
114//!
115//! @tparam
116//! data type of the variable which should be added
117//!
118//! @param val
119//! reference to the data
120//!
121//! @param v
122//! vector<char> to which the binary copy should be added
123//!
124template <class T>
125void Converter::GetBinImp(std::vector<char> &v, const T &val) const
126{
127 wout << " (" << val << ")";
128
129 v.insert(v.end(),
130 reinterpret_cast<const char*>(&val),
131 reinterpret_cast<const char*>(&val+1));
132}
133
134// --------------------------------------------------------------------------
135//
136//! This is just a simplification. For the time being it is used to output
137//! the interpreted contents to the logging stream. Its main purpose
138//! is to add the contents of val as a boost::any object to the
139//! vector v
140//!
141//! @tparam
142//! data type of the variable which should be added
143//!
144//! @param val
145//! reference to the data
146//!
147//! @param v
148//! vector<boost::any> to which the value should be added
149//!
150template <class T>
151void Converter::GetBinImp(std::vector<boost::any> &v, const T &val) const
152{
153 wout << " (" << val << ")";
154
155 v.push_back(val);
156}
157
158// --------------------------------------------------------------------------
159//
160//! This is just a simplification. For the time being it is used to output
161//! the interpreted contents to the logging stream. Its main purpose
162//! is to add the contents of the provided string at the end of the vector v.
163//! vector v
164//!
165//! @param val
166//! reference to the string
167//!
168//! @param v
169//! vector<char> to which the value should be added
170//!
171void Converter::GetBinString(std::vector<char> &v, const string &val) const
172{
173 wout << " (" << val << ")";
174
175 v.insert(v.end(), val.begin(), val.end());
176}
177
178// --------------------------------------------------------------------------
179//
180//! This is just a simplification. For the time being it is used to output
181//! the interpreted contents to the logging stream. Its main purpose
182//! is to add the contents of the provided string at the end of the vector v.
183//! vector v
184//!
185//! @param val
186//! reference to the string
187//!
188//! @param v
189//! vector<boost::any> to which the value should be added
190//!
191void Converter::GetBinString(std::vector<boost::any> &v, const string &val) const
192{
193 wout << " (" << val << ")";
194
195 v.push_back(val);
196}
197
198// --------------------------------------------------------------------------
199//
200//! Converts from the stringstream into the provided type.
201//!
202//! @param line
203//! reference to the stringstream from which the data should be
204//! interpreted
205//!
206//! @tparam
207//! Type of the data to be returned
208//!
209//! @returns
210//! The interpreted data
211//!
212template <class T>
213T Converter::Get(std::stringstream &line) const
214{
215 T val;
216 line >> val;
217 return val;
218}
219
220// --------------------------------------------------------------------------
221//
222//! Converts from the stringstream into bool. It allows to use lexical
223//! boolean representations like yes/no, on/off, true/false and of
224//! course 0/1. If the conversion fails the failbit is set.
225//!
226//! @param line
227//! reference to the stringstream from which the data should be
228//! interpreted
229//!
230//! @returns
231//! The boolean. 0 in case of failure
232//!
233bool Converter::GetBool(std::stringstream &line) const
234{
235 string buf;
236 line >> buf;
237 transform(buf.begin(), buf.end(), buf.begin(), (int(*)(int)) std::tolower);
238
239 if (buf=="yes" || buf=="true" || buf=="on" || buf=="1")
240 return true;
241
242 if (buf=="no" || buf=="false" || buf=="off" || buf=="0")
243 return false;
244
245 line.clear(ios::failbit);
246
247 return false;
248}
249
250// --------------------------------------------------------------------------
251//
252//! Converts from the stringstream into a string. Leading whitespaces are
253//! skipped. Everything up to the next whitespace is returned.
254//! strings can be encapsulated into escape characters ("). Note, that
255//! they cannot be nested.
256//!
257//! @param line
258//! reference to the stringstream from which the data should be
259//! interpreted
260//!
261//! @returns
262//! The string
263//!
264string Converter::GetString(std::stringstream &line) const
265{
266 while (line.peek()==' ')
267 line.get();
268
269 string buf;
270 if (line.peek()=='\"')
271 {
272 line.get();
273 getline(line, buf, '\"');
274 }
275 else
276 line >> buf;
277
278 return buf;
279}
280
281// --------------------------------------------------------------------------
282//
283//! Converts from the stringstream into a string. Leading whitespaces are
284//! skipped. Everything until the end-of-line is returned. A trailing
285//! \0 is added.
286//!
287//! @param line
288//! reference to the stringstream from which the data should be
289//! interpreted
290//!
291//! @returns
292//! The string
293//!
294string Converter::GetStringEol(stringstream &line) const
295{
296 // Remove leading whitespaces
297 while (line.peek()==' ')
298 line.get();
299
300 line >> noskipws;
301
302 const istream_iterator<char> eol; // end-of-line iterator
303 const string s(istream_iterator<char>(line), eol);
304 return s + '\0';
305}
306
307// --------------------------------------------------------------------------
308//
309//! Converts from a binary block into a string. The type of the expected
310//! value is defined by the template parameter.
311//!
312//! @param ptr
313//! A refrenece to the pointer of the binary representation to be
314//! interpreted. The pointer is incremented by the sizeof the type.
315//!
316//! @tparam T
317//! Expected type
318//!
319//! @returns
320//! The string
321//!
322template<class T>
323string Converter::GetString(const char* &ptr) const
324{
325 const T &t = *reinterpret_cast<const T*>(ptr);
326
327 ostringstream stream;
328 stream << t;
329 ptr += sizeof(T);
330
331 return stream.str();
332}
333
334// --------------------------------------------------------------------------
335//
336//! Converts from a binary block into a hex representation.
337//!
338//! @param ptr
339//! Pointer to the data block
340//!
341//! @param size
342//! Size of the data block
343//!
344//! @returns
345//! The string
346//!
347string Converter::GetHex(const void *dat, int size)
348{
349 const char *ptr = reinterpret_cast<const char *>(dat);
350
351 ostringstream text;
352
353 text << hex;
354
355 for (int i=0; i<size; i++)
356 text << setw(2) << ptr[i] << " ";
357
358 return text.str();
359}
360
361// --------------------------------------------------------------------------
362//
363//! Convert the pointer using GetString into a string and add it (prefixed
364//! by a whaitespace) to the given string.
365//!
366//! @param str
367//! Reference to the string to which the ptr should be added
368//!
369//! @param ptr
370//! Pointer to the binary representation. It will be incremented
371//! according to the sze of the template argument
372//!
373//! @tparam T
374//! Type as which the binary data should be interpreted
375//!
376template<class T>
377void Converter::Add(string &str, const char* &ptr) const
378{
379 str += ' ' + GetString<T>(ptr);
380}
381
382// --------------------------------------------------------------------------
383//
384//! Convert the pointer into a boost::any object and add it to the
385//! provided vector
386//!
387//! @param vec
388//! Vector to which the boost::any object should be added
389//!
390//! @param ptr
391//! Pointer to the binary representation. It will be incremented
392//! according to the sze of the template argument
393//!
394//! @tparam T
395//! Type as which the binary data should be interpreted
396//!
397template<class T>
398void Converter::Add(vector<boost::any> &vec, const char* &ptr) const
399{
400 vec.push_back(*reinterpret_cast<const T*>(ptr));
401 ptr += sizeof(T);
402}
403
404// --------------------------------------------------------------------------
405//
406//! Add the string pointed to by ptr to the given string.
407//!
408//! @param str
409//! Reference to the string to which the ptr should be added
410//!
411//! @param ptr
412//! Pointer to the binary representation.
413//!
414void Converter::AddString(string &str, const string &ptr) const
415{
416 str += ' ' + ptr;
417}
418
419// --------------------------------------------------------------------------
420//
421//! Add the string pointed to by ptr as boost::any to the provided vector
422//!
423//! @param vec
424//! Vector to which the boost::any object should be added
425//!
426//! @param ptr
427//! Pointer to the binary representation of the string.
428//!
429void Converter::AddString(vector<boost::any> &vec, const string &ptr) const
430{
431 vec.push_back(ptr);
432}
433
434// --------------------------------------------------------------------------
435//
436//! Compiles the format string into fList. See Compile() for more details.
437//!
438//! @param out
439//! Output stream to which possible logging is redirected
440//!
441//! @param fmt
442//! Format to be compiled. For details see class reference
443//!
444//! @param strict
445//! Setting this to true allows non DIM options, whiel false
446//! will restrict the possible format strings to the ones also
447//! understood by DIM.
448//!
449Converter::Converter(std::ostream &out, const std::string &fmt, bool strict)
450: wout(out), fFormat(Clean(fmt)), fList(Compile(out, fmt, strict))
451{
452}
453
454// --------------------------------------------------------------------------
455//
456//! Compiles the format string into fList.
457//!
458//! Output by default is redirected to cout.
459//!
460//! @param fmt
461//! Format to be compiled. For details see class reference
462//!
463//! @param strict
464//! Setting this to true allows non DIM options, whiel false
465//! will restrict the possible format strings to the ones also
466//! understood by DIM.
467//!
468Converter::Converter(const std::string &fmt, bool strict)
469: wout(cout), fFormat(Clean(fmt)), fList(Compile(fmt, strict))
470{
471}
472
473// --------------------------------------------------------------------------
474//
475//! Converts the provided format string into a vector.
476//!
477//! @tparam T
478//! Kind of data to be returned. This can either be boost::any objects
479//! or a bnary data-block (char).
480//!
481//! @param fmt
482//! Format to be compiled. For details see class reference
483//!
484//! @returns
485//! A vector of the given template type containing the arguments. In
486//! case of failure an empty vector is returned.
487//!
488template <class T>
489vector<T> Converter::Get(const std::string &str) const
490{
491 if (!valid())
492 return vector<T>();
493
494 // If the format is empty we are already done
495 if (empty())
496 {
497 if (str.empty())
498 {
499 wout << endl;
500 return vector<T>();
501 }
502
503 wout << endl;
504 wout << kRed << "Data string not empty as it ought to be!" << endl;
505 return vector<T>();
506 }
507
508 int arg = 0;
509 stringstream line(str);
510
511 vector<T> data;
512
513 for (Converter::FormatList::const_iterator i=fList.begin(); i<fList.end()-1; i++)
514 {
515 if (*i->first.first == typeid(string))
516 {
517 GetBinString(data, GetStringEol(line));
518 line.clear(ios::eofbit);
519 continue;
520 }
521
522 // Get as many items from the input line as requested
523 for (int j=0; j<i->second.first; j++)
524 {
525 cout << j << ": " << i->first.first->name() << endl;
526 switch (i->first.first->name()[0])
527 {
528 case 'c': // Skip whitespaces when checking for characters
529 if (j>0)
530 line >> noskipws;
531 GetBinImp(data, Get<char>(line));
532 line >> skipws;
533 break;
534 case 'b': GetBinImp(data, GetBool(line)); break;
535 case 's': GetBinImp(data, Get<short> (line)); break;
536 case 'i': GetBinImp(data, Get<int> (line)); break;
537 case 'l': GetBinImp(data, Get<long> (line)); break;
538 case 'f': GetBinImp(data, Get<float> (line)); break;
539 case 'd': GetBinImp(data, Get<double> (line)); break;
540 case 'x': GetBinImp(data, Get<long long>(line)); break;
541 case 'N':
542 GetBinString(data, GetString(line));
543 if (*i->first.first == typeid(O))
544 line.clear(ios::goodbit);
545 break;
546 default:
547 // This should never happen!
548 wout << endl << kRed << "Format '" << i->first.first->name() << " not supported!" << endl;
549 break;
550 }
551
552 arg++;
553 }
554
555 //wout << "{" << line.eof() << line.good() << line.fail() << "}";
556 if (!line)
557 break;
558 }
559
560 wout << endl;
561
562 // Something wrong with the conversion (e.g. 5.5 for an int)
563 if (line.fail() && !line.eof())
564 {
565 line.clear(); // This is necesasary to get a proper response from tellg()
566 wout << kRed << "Error converting argument at " << arg << " [fmt=" << fFormat << "]!" << endl;
567 wout << kRed << line.str() << endl;
568 wout << kRed << setw(int(line.tellg())) << " " << "^" << endl;
569 return vector<T>();
570 }
571
572 // Not enough arguments, we have not reached the end
573 if (line.fail() && line.eof())
574 {
575 line.clear();
576 wout << kRed << "Not enough arguments [fmt=" << fFormat << "]!" << endl;
577 wout << kRed << line.str() << endl;
578 wout << kRed << setw(int(line.tellg())+1) << " " << "^" << endl;
579 return vector<T>();
580 }
581
582 // Too many arguments, we have not reached the end
583 // Unfortunately, this can also mean that there is something
584 // wrong with the last argument
585 if (line.good() && !line.eof())
586 {
587 wout << kRed << "More arguments available than expected [" << fFormat << "]!" << endl;
588 wout << kRed << line.str() << endl;
589 wout << kRed << setw(int(line.tellg())+1) << " " << "^" << endl;
590 return vector<T>();
591 }
592
593 return data;
594
595}
596
597std::vector<boost::any> Converter::GetAny(const std::string &str) const
598{
599 return Get<boost::any>(str);
600}
601
602std::vector<char> Converter::GetVector(const std::string &str) const
603{
604 return Get<char>(str);
605}
606
607
608// --------------------------------------------------------------------------
609//
610//! Converts the provided data block into a vector of boost::any or
611//! a string.
612//!
613//! @tparam T
614//! Kind of data to be returned. This can either be boost::any objects
615//! or a string
616//!
617//! @returns
618//! A vector of the given template type containing the arguments. In
619//! case of failure an empty vector is returned.
620//!
621template<class T>
622T Converter::Get(const void *dat, int size) const
623{
624 if (!valid())
625 return T();
626
627 const char *ptr = reinterpret_cast<const char *>(dat);
628
629 T text;
630 for (Converter::FormatList::const_iterator i=fList.begin(); i<fList.end()-1; i++)
631 {
632 if (ptr-size>=dat)
633 {
634 wout << kRed << "Format description '" << fFormat << "' exceeds available data size (" << size << ")" << endl;
635 return T();
636 }
637
638 if (*i->first.first == typeid(string))
639 {
640 const string str(ptr);
641 AddString(text, str);
642 ptr += str.length()+1;
643 break;
644 }
645
646 // Get as many items from the input line as requested
647 for (int j=0; j<i->second.first; j++)
648 {
649 switch (i->first.first->name()[0])
650 {
651 case 'b': Add<bool> (text, ptr); break;
652 case 'c': Add<char> (text, ptr); break;
653 case 's': Add<short> (text, ptr); break;
654 case 'i': Add<int> (text, ptr); break;
655 case 'l': Add<long> (text, ptr); break;
656 case 'f': Add<float> (text, ptr); break;
657 case 'd': Add<double> (text, ptr); break;
658 case 'x': Add<long long>(text, ptr); break;
659 case 'v':
660 // This should never happen!
661 wout << kRed << "Type 'void' not supported!" << endl;
662 return T();
663 default:
664 // This should never happen!
665 wout << kRed << "TypeId '" << i->first.first->name() << "' not known!" << endl;
666 return T();
667 }
668 }
669 }
670
671 if (ptr-size!=dat)
672 {
673 wout << kRed << "Data block size (" << size << ") doesn't fit format description '" << fFormat << "'" << endl;
674 return T();
675 }
676
677 return text;
678}
679
680std::vector<boost::any> Converter::GetAny(const void *dat, int size) const
681{
682 return Get<vector<boost::any>>(dat, size);
683}
684
685std::vector<char> Converter::GetVector(const void *dat, int size) const
686{
687 const string ref = GetString(dat, size);
688
689 vector<char> data;
690 data.insert(data.begin(), ref.begin()+1, ref.end());
691 data.push_back(0);
692
693 return data;
694}
695
696string Converter::GetString(const void *dat, int size) const
697{
698 const string s = Get<string>(dat, size);
699 return s.empty() ? s : s.substr(1);
700}
701
702template<class T>
703Converter::Type Converter::GetType()
704{
705 Type t;
706 t.first = &typeid(T);
707 t.second = sizeof(T);
708 return t;
709}
710
711template<class T>
712Converter::Type Converter::GetVoid()
713{
714 Type t;
715 t.first = &typeid(T);
716 t.second = 0;
717 return t;
718}
719
720// --------------------------------------------------------------------------
721//
722//! static function to compile a format string.
723//!
724//! @param out
725//! Output stream to which possible logging is redirected
726//!
727//! @param fmt
728//! Format to be compiled. For details see class reference
729//!
730//! @param strict
731//! Setting this to true allows non DIM options, whiel false
732//! will restrict the possible format strings to the ones also
733//! understood by DIM.
734//!
735Converter::FormatList Converter::Compile(std::ostream &out, const std::string &fmt, bool strict)
736{
737 ostringstream text;
738
739 // Access both, the data and the format through a stringstream
740 stringstream stream(fmt);
741
742 // For better performance we could use sregex
743 static const boost::regex expr1("^([CSILFDXBOW])(:([1-9]+[0-9]*))?$");
744 static const boost::regex expr2("^([CSILFDX])(:([1-9]+[0-9]*))?$");
745
746 FormatList list;
747 Format format;
748
749 // Tokenize the format
750 string buffer;
751 while (getline(stream, buffer, ';'))
752 {
753 boost::smatch what;
754 if (!boost::regex_match(buffer, what, strict?expr2:expr1))
755 {
756 out << kRed << "Wrong format string '" << buffer << "'!" << endl;
757 return FormatList();
758 }
759
760 const string t = what[1]; // type id
761 const string n = what[3]; // counter
762
763 const int cnt = atoi(n.c_str());
764
765 // if the :N part was not given assume 1
766 format.second.first = cnt == 0 ? 1: cnt;
767
768 // Check if the format is just C (without a number)
769 // That would mean that it is a \0 terminated string
770 if (t[0]=='C' && cnt==0)
771 {
772 format.first = GetType<string>();
773 list.push_back(format);
774 format.second.second = 0; // end position not known
775 break;
776 }
777
778 // Get as many items from the input line as requested
779 switch (t[0])
780 {
781 case 'B': format.first = GetType<bool>(); break;
782 case 'C': format.first = GetType<char>(); break;
783 case 'S': format.first = GetType<short>(); break;
784 case 'I': format.first = GetType<int>(); break;
785 case 'L': format.first = GetType<long>(); break;
786 case 'F': format.first = GetType<float>(); break;
787 case 'D': format.first = GetType<double>(); break;
788 case 'X': format.first = GetType<long long>(); break;
789 case 'O': format.first = GetVoid<O>(); break;
790 case 'W': format.first = GetVoid<W>(); break;
791 default:
792 // This should never happen!
793 out << kRed << "Format '" << t[0] << " not known!" << endl;
794 return list;
795 }
796
797 list.push_back(format);
798 format.second.second += format.first.second * format.second.first;
799 }
800
801 format.first = GetVoid<void>();
802 format.second.first = 0;
803
804 list.push_back(format);
805
806 return list;
807}
808
809// --------------------------------------------------------------------------
810//
811//! Same as Compile(ostream&,string&,bool) but cout is used as the default
812//! output stream.
813//!
814//!
815Converter::FormatList Converter::Compile(const std::string &fmt, bool strict)
816{
817 return Compile(cout, fmt, strict);
818}
819
820vector<string> Converter::Regex(const string &expr, const string &line)
821{
822 const boost::regex reg(expr);
823
824 boost::smatch what;
825 if (!boost::regex_match(line, what, reg, boost::match_extra))
826 return vector<string>();
827
828 vector<string> ret;
829 for (unsigned int i=0; i<what.size(); i++)
830 ret.push_back(what[i]);
831
832 return ret;
833}
Note: See TracBrowser for help on using the repository browser.