otsdaq  v2_01_00
StringMacros.h
1 #ifndef _ots_String_Macros_h_
2 #define _ots_String_Macros_h_
3 
4 #include "otsdaq-core/Macros/CoutMacros.h"
5 
6 #include <vector>
7 #include <set>
8 #include <map>
9 #include <typeinfo> // operator typeid
10 
11 namespace ots
12 {
13 
15 {
16 
17  //Here is the list of static helper functions:
18  //
19  // wildCardMatch
20  // inWildCardSet
21  // getWildCardMatchFromMap
22  //
23  // decodeURIComponent
24  // convertEnvironmentVariables
25  // isNumber
26  // extractAndCalculateNumber
27  // validateValueForDefaultStringDataType
28  //
29  // getSetFromString
30  // getVectorFromString
31  // getMapFromString
32  //
33  // setToString
34  // vectorToString
35  // mapToString
36  //
37  // demangleTypeName
38 
39 static bool wildCardMatch (const std::string& needle, const std::string& haystack, unsigned int* priorityIndex = 0);
40 static bool inWildCardSet (const std::string needle, const std::set<std::string>& haystack);
41 
42 
43 //========================================================================================================================
44 //getWildCardMatchFromMap ~
45 // returns value if needle is in haystack otherwise throws exception
46 // (considering wildcards AND match priority as defined by StringMacros::wildCardMatch)
47 template<class T>
48 static const T& getWildCardMatchFromMap (const std::string needle, const std::map<std::string,T>& haystack)
49 {
50  unsigned int matchPriority, bestMatchPriority = 0; //lowest number matters most for matches
51 
52  if(!haystack.size())
53  {
54  __SS__ << "Needle '" << needle << "' not found in EMPTY wildcard haystack:" << __E__;
55  throw std::runtime_error(ss.str());
56  }
57 
58  //__COUT__ << StringMacros::mapToString(haystack) << __E__;
59 
60  std::string bestMatchKey;
61  for(const auto& haystackPair : haystack)
62  //use wildcard match, flip needle parameter.. because we want haystack to have the wildcards
63  // check resulting priority to see if we are done with search (when priority=1, can be done)
64  if(StringMacros::wildCardMatch(haystackPair.first,needle,
65  &matchPriority))
66  {
67  if(matchPriority == 1) //found best possible match, so done
68  return haystackPair.second;
69 
70  if(!bestMatchPriority || matchPriority < bestMatchPriority)
71  {
72  //found new best match
73  bestMatchPriority = matchPriority;
74  bestMatchKey = haystackPair.first;
75  }
76  }
77 
78  if(bestMatchPriority) //found a match, although not exact, i.e. wildcard was used
79  {
80  //__COUTV__(bestMatchPriority);
81  //__COUTV__(bestMatchKey);
82  //__COUT__ << "value found: " << haystack.at(bestMatchKey) << __E__;
83  return haystack.at(bestMatchKey);
84  }
85 
86  __SS__ << "Needle '" << needle << "' not found in wildcard haystack:" << __E__;
87  bool first = true;
88  for(const auto& haystackPair : haystack)
89  if(first)
90  {
91  ss << ", " << haystackPair.first;
92  first = false;
93  }
94  else
95  ss << ", " << haystackPair.first;
96  throw std::runtime_error(ss.str());
97 }
98 
99 static std::string decodeURIComponent (const std::string& data);
100 static std::string convertEnvironmentVariables (const std::string& data);
101 static bool isNumber (const std::string& s);
102 
103 template<class T>
104 static bool getNumber (const std::string& s, T& retValue)
105 {
106  //extract set of potential numbers and operators
107  std::vector<std::string> numbers;
108  std::vector<char> ops;
109 
110  StringMacros::getVectorFromString(s,numbers,
111  /*delimiter*/ std::set<char>({'+','-','*','/'}),
112  /*whitespace*/ std::set<char>({' ','\t','\n','\r'}),
113  &ops);
114 
115  retValue = 0; //initialize
116 
117  T tmpValue;
118  unsigned int i = 0;
119  bool verified;
120  for(const auto& number:numbers)
121  {
122  if(number.size() == 0) continue; //skip empty numbers
123 
124  //verify that this number looks like a number
125  // for integer types, allow hex and binary
126  // for all types allow base10
127 
128  verified = false;
129 
130  //check integer types
131  if(typeid(unsigned int) == typeid(retValue) ||
132  typeid(int) == typeid(retValue) ||
133  typeid(unsigned long long) == typeid(retValue) ||
134  typeid(long long) == typeid(retValue) ||
135  typeid(unsigned long) == typeid(retValue) ||
136  typeid(long) == typeid(retValue) ||
137  typeid(unsigned short) == typeid(retValue) ||
138  typeid(short) == typeid(retValue) ||
139  typeid(uint8_t) == typeid(retValue))
140  {
141  if(number.find("0x") == 0) //indicates hex
142  {
143  //__COUT__ << "0x found" << std::endl;
144  for(unsigned int i=2;i<number.size();++i)
145  {
146  if(!((number[i] >= '0' && number[i] <= '9') ||
147  (number[i] >= 'A' && number[i] <= 'F') ||
148  (number[i] >= 'a' && number[i] <= 'f')
149  ))
150  {
151  //__COUT__ << "prob " << number[i] << std::endl;
152  return false;
153  }
154  }
155  verified = true;
156  }
157  else if(number[0] == 'b') //indicates binary
158  {
159  //__COUT__ << "b found" << std::endl;
160 
161  for(unsigned int i=1;i<number.size();++i)
162  {
163  if(!((number[i] >= '0' && number[i] <= '1')
164  ))
165  {
166  //__COUT__ << "prob " << number[i] << std::endl;
167  return false;
168  }
169  }
170  verified = true;
171  }
172  }
173 
174  //if not verified above, for all types, check base10
175  if(!verified)
176  for(unsigned int i=0;i<number.size();++i)
177  if(!((number[i] >= '0' && number[i] <= '9') ||
178  number[i] == '.' ||
179  number[i] == '+' ||
180  number[i] == '-'))
181  return false;
182 
183  //at this point, this number is confirmed to be a number of some sort
184  // so convert to temporary number
185  if(typeid(double) == typeid(retValue))
186  tmpValue = strtod(number.c_str(),0);
187  else if(typeid(float) == typeid(retValue))
188  tmpValue = strtof(number.c_str(),0);
189  else if(typeid(unsigned int) == typeid(retValue) ||
190  typeid(int) == typeid(retValue) ||
191  typeid(unsigned long long) == typeid(retValue) ||
192  typeid(long long) == typeid(retValue) ||
193  typeid(unsigned long) == typeid(retValue) ||
194  typeid(long) == typeid(retValue) ||
195  typeid(unsigned short) == typeid(retValue) ||
196  typeid(short) == typeid(retValue) ||
197  typeid(uint8_t) == typeid(retValue))
198  {
199  if(number.size() > 2 && number[1] == 'x') //assume hex value
200  tmpValue = (T)strtol(number.c_str(),0,16);
201  else if(number.size() > 1 && number[0] == 'b') //assume binary value
202  tmpValue = (T)strtol(number.substr(1).c_str(),0,2); //skip first 'b' character
203  else
204  tmpValue = (T)strtol(number.c_str(),0,10);
205  }
206  else
207  {
208  __SS__ << "Invalid type '" <<
209  StringMacros::demangleTypeName(typeid(retValue).name()) <<
210  "' requested for a numeric string. Data was '" <<
211  number << "'" << __E__;
212  __SS_THROW__;
213  }
214 
215  //apply operation
216  if(i == 0) //first value, no operation, just take value
217  retValue = tmpValue;
218  else //there is some sort of op
219  {
220  if(0 && i==1) //display what we are dealing with
221  {
222  __COUTV__(StringMacros::vectorToString(numbers));
223  __COUTV__(StringMacros::vectorToString(ops));
224  }
225  //__COUT__ << "Intermediate value = " << retValue << __E__;
226 
227  switch(ops[i-1])
228  {
229  case '+':
230  retValue += tmpValue;
231  break;
232  case '-':
233  retValue -= tmpValue;
234  break;
235  case '*':
236  retValue *= tmpValue;
237  break;
238  case '/':
239  retValue /= tmpValue;
240  break;
241  default:
242  __SS__ << "Unrecognized operation '" << ops[i-1] << "' found!" << __E__ <<
243  "Numbers: " <<
244  StringMacros::vectorToString(numbers) << __E__ <<
245  "Operations: " <<
246  StringMacros::vectorToString(ops) << __E__;
247  __SS_THROW__;
248  }
249  //__COUT__ << "Op " << ops[i-1] << " intermediate value = " << retValue << __E__;
250  }
251 
252  ++i; //increment index for next number/op
253  } //end number loop
254 
255  return true; //number was valid and is passed by reference in retValue
256 } //end static getNumber()
257 
258 
259 //special validation ignoring any table info - just assuming type string
260 template<class T>
261 static T validateValueForDefaultStringDataType (const std::string& value, bool doConvertEnvironmentVariables=true)
262 {
263  T retValue;
264  try
265  {
266  //__COUTV__(value);
267  std::string data = doConvertEnvironmentVariables?convertEnvironmentVariables(value):
268  value;
269 
270  //Note: allow string column types to convert to number (since user's would likely expect this to work)
271  if(StringMacros::getNumber(data,retValue))
272  return retValue;
273  else
274  {
275  __SS__ << "Invalid type '" <<
276  StringMacros::demangleTypeName(typeid(retValue).name()) <<
277  "' requested for a non-numeric string (must request std::string). Data was '" <<
278  data << "'" << __E__;
279  __SS_THROW__;
280  }
281  }
282  catch(const std::runtime_error& e)
283  {
284  __SS__ << "Failed to validate the string value for the data type '" <<
285  StringMacros::demangleTypeName(typeid(retValue).name()) <<
286  "' requested. " << __E__ << e.what() << __E__;
287  __SS_THROW__;
288  }
289 }
290 static std::string validateValueForDefaultStringDataType (const std::string& value, bool doConvertEnvironmentVariables=true);
291 
292 
293 static void getSetFromString (const std::string& inputString, std::set<std::string>& setToReturn, const std::set<char>& delimiter = {',','|','&'}, const std::set<char>& whitespace = {' ','\t','\n','\r'});
294 static void getVectorFromString (const std::string& inputString, std::vector<std::string>& listToReturn, const std::set<char>& delimiter = {',','|','&'}, const std::set<char>& whitespace = {' ','\t','\n','\r'}, std::vector<char>* listOfDelimiters = 0);
295 template<class T>
296 static void getMapFromString (const std::string& inputString, std::map<std::string,T>& mapToReturn, const std::set<char>& pairPairDelimiter = {',','|','&'}, const std::set<char>& nameValueDelimiter = {'=',':'}, const std::set<char>& whitespace = {' ','\t','\n','\r'})
297 try
298 {
299  unsigned int i=0;
300  unsigned int j=0;
301  std::string name;
302  bool needValue = false;
303 
304  //go through the full string extracting map pairs
305  //add each found pair to map
306  for(;j<inputString.size();++j)
307  if(!needValue) //finding name
308  {
309  if((whitespace.find(inputString[j]) != whitespace.end() || //ignore leading white space or delimiter
310  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.end())
311  && i == j)
312  ++i;
313  else if((whitespace.find(inputString[j]) != whitespace.end() || //trailing white space or delimiter indicates end
314  nameValueDelimiter.find(inputString[j]) != nameValueDelimiter.end())
315  && i != j) // assume end of map name
316  {
317  //__COUT__ << "Map name found: " <<
318  // inputString.substr(i,j-i) << std::endl;
319 
320  name = inputString.substr(i,j-i); //save name, for concluding pair
321 
322  needValue = true; //need value now
323 
324  //setup i and j for next find
325  i = j+1;
326  }
327  }
328  else //finding value
329  {
330  if((whitespace.find(inputString[j]) != whitespace.end() || //ignore leading white space or delimiter
331  nameValueDelimiter.find(inputString[j]) != nameValueDelimiter.end())
332  && i == j)
333  ++i;
334  else if((whitespace.find(inputString[j]) != whitespace.end() || //trailing white space or delimiter indicates end
335  pairPairDelimiter.find(inputString[j]) != pairPairDelimiter.end())
336  && i != j) // assume end of value name
337  {
338  //__COUT__ << "Map value found: " <<
339  // inputString.substr(i,j-i) << std::endl;
340 
341  auto /*pair<it,success>*/ emplaceReturn = mapToReturn.emplace(std::pair<std::string,T>(
342  name,
343  validateValueForDefaultStringDataType<T>(
344  inputString.substr(i,j-i)) //value
345  ));
346 
347  if(!emplaceReturn.second)
348  {
349  __COUT__ << "Ignoring repetitive value ('" << inputString.substr(i,j-i) <<
350  "') and keeping current value ('" << emplaceReturn.first->second << "'). " << __E__;
351  }
352 
353  needValue = false; //need name now
354 
355  //setup i and j for next find
356  i = j+1;
357  }
358  }
359 
360  if(i != j) //last value (for case when no concluding ' ' or delimiter)
361  {
362  auto /*pair<it,success>*/ emplaceReturn = mapToReturn.emplace(std::pair<std::string,T>(
363  name,
364  validateValueForDefaultStringDataType<T>(
365  inputString.substr(i,j-i)) //value
366  ));
367 
368  if(!emplaceReturn.second)
369  {
370  __COUT__ << "Ignoring repetitive value ('" << inputString.substr(i,j-i) <<
371  "') and keeping current value ('" << emplaceReturn.first->second << "'). " << __E__;
372  }
373  }
374 }
375 catch(const std::runtime_error &e)
376 {
377  __SS__ << "Error while extracting a map from the string '" <<
378  inputString << "'... is it a valid map?" << __E__ << e.what() << __E__;
379  __SS_THROW__;
380 }
381 
382 static void getMapFromString (const std::string& inputString, std::map<std::string,std::string>& mapToReturn, const std::set<char>& pairPairDelimiter = {',','|','&'}, const std::set<char>& nameValueDelimiter = {'=',':'}, const std::set<char>& whitespace = {' ','\t','\n','\r'});
383 template<class T>
384 static std::string mapToString (const std::map<std::string,T>& mapToReturn, const std::string& primaryDelimeter = ",", const std::string& secondaryDelimeter = ": ")
385 {
386  std::stringstream ss;
387  bool first = true;
388  for(auto& mapPair:mapToReturn)
389  {
390  if(first) first = false;
391  else ss << primaryDelimeter;
392  ss << mapPair.first << secondaryDelimeter <<
393  mapPair.second;
394  }
395  return ss.str();
396 }
397 static std::string mapToString (const std::map<std::string,uint8_t>& mapToReturn, const std::string& primaryDelimeter = ",", const std::string& secondaryDelimeter = ": ");
398 template<class T>
399 static std::string setToString (const std::set<T>& setToReturn, const std::string& delimeter = ",")
400 {
401  std::stringstream ss;
402  bool first = true;
403  for(auto& setValue:setToReturn)
404  {
405  if(first) first = false;
406  else ss << delimeter;
407  ss << setValue;
408  }
409  return ss.str();
410 }
411 static std::string setToString (const std::set<uint8_t>& setToReturn, const std::string& delimeter = ",");
412 template<class T>
413 static std::string vectorToString (const std::vector<T>& setToReturn, const std::string& delimeter = ",")
414 {
415  std::stringstream ss;
416  bool first = true;
417  for(auto& setValue:setToReturn)
418  {
419  if(first) first = false;
420  else ss << delimeter;
421  ss << setValue;
422  }
423  return ss.str();
424 }
425 static std::string vectorToString (const std::vector<uint8_t>& setToReturn, const std::string& delimeter = ",");
426 
427 static std::string demangleTypeName (const char* name);
428 
429 }; //end class
430 } //end namespace
431 #endif