otsdaq  v2_00_00
ConfigurationView.h
1 #ifndef _ots_ConfigurationView_h_
2 #define _ots_ConfigurationView_h_
3 
4 #include "otsdaq-core/ConfigurationDataFormats/ViewColumnInfo.h"
5 #include "otsdaq-core/MessageFacility/MessageFacility.h"
6 #include "otsdaq-core/Macros/CoutHeaderMacros.h"
7 #include "otsdaq-core/ConfigurationDataFormats/ConfigurationVersion.h"
8 
9 
10 #include <string>
11 #include <typeinfo> // operator typeid
12 #include <iostream>
13 #include <sstream> // std::stringstream, std::stringbuf
14 #include <vector>
15 #include <cassert>
16 #include <set>
17 #include <stdlib.h>
18 #include <time.h> /* time_t, time, ctime */
19 
20 std::string ots_demangle(const char* name);
21 
22 namespace ots
23 {
24 
26 {
27 
28 public:
29 
30  static const unsigned int INVALID;
31  typedef std::vector<std::vector<std::string> > DataView;
32  typedef DataView::iterator iterator;
33  typedef DataView::const_iterator const_iterator;
34 
35  ConfigurationView (const std::string &name="");
36  virtual ~ConfigurationView (void);
37  ConfigurationView& copy (const ConfigurationView &src, ConfigurationVersion destinationVersion, const std::string &author);
38 
39 
40  void init(void);
41 
42 
43 
44  //==============================================================================
45  template<class T>
46  unsigned int findRow(unsigned int col, const T& value,
47  unsigned int offsetRow=0) const
48  {
49  std::istringstream s(value);
50  return findRow(col,s.str(),offsetRow);
51  }
52  unsigned int findRow (unsigned int col, const std::string& value, unsigned int offsetRow=0) const;
53  //==============================================================================
54  template<class T>
55  unsigned int findRowInGroup(unsigned int col, const T& value,
56  const std::string &groupId, const std::string &childLinkIndex, unsigned int offsetRow=0) const
57  {
58  std::istringstream s(value);
59  return findRowInGroup(col,s.str(),groupId,childLinkIndex,offsetRow);
60  }
61  unsigned int findRowInGroup (unsigned int col, const std::string &value, const std::string &groupId, const std::string &childLinkIndex, unsigned int offsetRow=0) const;
62  unsigned int findCol (const std::string &name) const;
63  unsigned int findColByType (const std::string &type, int startingCol = 0) const;
64 
65  //Getters
66  const std::string& getUniqueStorageIdentifier (void) const;
67  const std::string& getTableName (void) const;
68  const ConfigurationVersion& getVersion (void) const;
69  const std::string& getComment (void) const;
70  const std::string& getAuthor (void) const;
71  const time_t& getCreationTime (void) const;
72  const time_t& getLastAccessTime (void) const;
73  const bool& getLooseColumnMatching (void) const;
74  const unsigned int getDataColumnSize (void) const;
75  const unsigned int& getSourceColumnMismatch (void) const;
76  const unsigned int& getSourceColumnMissing (void) const;
77  const std::set<std::string>& getSourceColumnNames (void) const;
78  std::set<std::string> getColumnNames (void) const;
79  std::set<std::string> getColumnStorageNames (void) const;
80  std::vector<std::string> getDefaultRowValues (void) const;
81 
82  unsigned int getNumberOfRows (void) const;
83  unsigned int getNumberOfColumns (void) const;
84  const unsigned int getColUID (void) const;
85  const unsigned int getColStatus (void) const;
86 
87  //Note: Group link handling should be done in this ConfigurationView class
88  // only by using isEntryInGroup ...
89  // This is so that multiple group handling is consistent
90 private:
91  bool isEntryInGroupCol (const unsigned int& row, const unsigned int& groupCol, const std::string& groupNeedle, std::set<std::string>* groupIDList = 0) const;
92 public:
93  //std::set<std::string /*GroupId*/>
94  std::set<std::string> getSetOfGroupIDs (const std::string& childLinkIndex, unsigned int row = -1) const;
95  bool isEntryInGroup (const unsigned int& row, const std::string& childLinkIndex, const std::string& groupNeedle) const;
96  const bool getChildLink (const unsigned int& col, bool& isGroup, std::pair<unsigned int /*link col*/, unsigned int /*link id col*/>& linkPair) const;
97  const unsigned int getColLinkGroupID (const std::string& childLinkIndex) const;
98  void addRowToGroup (const unsigned int& row, const unsigned int& col, const std::string& groupID);//, const std::string& colDefault);
99  bool removeRowFromGroup (const unsigned int& row, const unsigned int& col, const std::string& groupID, bool deleteRowIfNoGroupLeft=false);
100 
101  //==============================================================================
102  //Philosophy: force type validation by passing value to fill by reference..
103  // don't allow string to be returned.. (at least not easily)
104  //Since there is no environmental variable that can just be a number they will all be converted no matter what.
105  template<class T>
106  void getValue(T& value, unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
107  {
108  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
109  {
110  __SS__ << "Invalid row col requested" << std::endl;
111  __COUT_ERR__ << "\n" << ss.str();
112  throw std::runtime_error(ss.str());
113  }
114 
115  value = validateValueForColumn<T>(theDataView_[row][col],col,convertEnvironmentVariables);
116  }
117  //special version of getValue for string type
118  // Note: necessary because types of std::basic_string<char> cause compiler problems if no string specific function
119  void getValue(std::string& value, unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const;
120 
121  //==============================================================================
122  //validateValueForColumn
123  // validates value against data rules for specified column.
124  // throws exception if invalid.
125  //
126  // on success returns what the value would be for get value
127  template<class T>
128  T validateValueForColumn(const std::string& value, unsigned int col,
129  bool convertEnvironmentVariables=true) const
130  {
131  if(col >= columnsInfo_.size())
132  {
133  __SS__ << "Invalid col requested" << std::endl;
134  __COUT_ERR__ << "\n" << ss.str();
135  throw std::runtime_error(ss.str());
136  }
137 
138  T retValue;
139 
140  try
141  {
142  if(columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_NUMBER) //handle numbers
143  {
144  std::string data = convertEnvironmentVariables?convertEnvVariables(value):
145  value;
146 
147  if(!isNumber(data))
148  {
149  __SS__ << (data + " is not a number!") << std::endl;
150  __COUT__ << "\n" << ss.str();
151  throw std::runtime_error(ss.str());
152  }
153 
154  if(typeid(double) == typeid(retValue))
155  retValue = strtod(data.c_str(),0);
156  else if(typeid(float) == typeid(retValue))
157  retValue = strtof(data.c_str(),0);
158  else if(data.size() > 2 && data[1] == 'x') //assume hex value
159  retValue = strtol(data.c_str(),0,16);
160  else if(data.size() > 1 && data[0] == 'b') //assume binary value
161  retValue = strtol(data.substr(1).c_str(),0,2); //skip first 'b' character
162  else
163  retValue = strtol(data.c_str(),0,10);
164 
165  return retValue;
166  }
167  else if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_FIXED_CHOICE_DATA &&
168  columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_STRING &&
169  (typeid(int) == typeid(retValue) ||
170  typeid(unsigned int) == typeid(retValue)))
171  {
172  //this case is for if fixed choice type but int is requested
173  // then return index in fixed choice list
174  // (always consider DEFAULT as index 0)
175  //throw error if no match
176 
177  if(value == ViewColumnInfo::DATATYPE_STRING_DEFAULT)
178  retValue = 0;
179  else
180  {
181  std::vector<std::string> choices = columnsInfo_[col].getDataChoices();
182 
183  // for(const auto& choice: choices)
184  // __COUT__ << "choice " << choice << __E__;
185 
186  //consider arbitrary bool
187  bool skipOne = (choices.size() &&
188  choices[0].find("arbitraryBool=") == 0);
189 
190  for(retValue=1 + (skipOne?1:0);retValue-1<(T)choices.size();++retValue)
191  if(value == choices[retValue-1])
192  return retValue - (skipOne?1:0); //value has been set to selected choice index, so return
193 
194  __SS__ << "\tInvalid value for column data type: " << columnsInfo_[col].getDataType()
195  << " in configuration " << tableName_
196  << " at column=" << columnsInfo_[col].getName()
197  << " for getValue with type '" << ots_demangle(typeid(retValue).name())
198  << ".'"
199  << "Attempting to get index of '" << value
200  << " in fixed choice array, but was not found in array. "
201  << "Here are the valid choices:\n";
202  ss << "\t" << ViewColumnInfo::DATATYPE_STRING_DEFAULT << "\n";
203  for(const auto &choice:choices)
204  ss << "\t" << choice << "\n";
205  __COUT__ << "\n" << ss.str();
206  throw std::runtime_error(ss.str());
207  }
208 
209  return retValue;
210  }
211  else if(columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_STRING &&
212  typeid(bool) == typeid(retValue)) //handle bool
213  {
214  if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_ON_OFF)
215  retValue = (value == ViewColumnInfo::TYPE_VALUE_ON) ? true:false;
216  else if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_TRUE_FALSE)
217  retValue = (value == ViewColumnInfo::TYPE_VALUE_TRUE) ? true:false;
218  else if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_YES_NO)
219  retValue = (value == ViewColumnInfo::TYPE_VALUE_YES) ? true:false;
220  else if(value.length() && value[0] == '1') //for converting pure string types
221  retValue = true;
222  else if(value.length() && value[0] == '0') //for converting pure string types
223  retValue = false;
224  else
225  {
226  __SS__ << "Invalid boolean value encountered: " << value << __E__;
227  __SS_THROW__;
228  }
229 
230  return retValue;
231  }
232  else if(columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_STRING &&
233  typeid(std::string) != typeid(retValue))
234  {
235  std::string data = convertEnvironmentVariables?convertEnvVariables(value):
236  value;
237 
238  if(isNumber(data))
239  {
240  //allow string conversion to integer for ease of use
241 
242  if(typeid(double) == typeid(retValue))
243  retValue = strtod(data.c_str(),0);
244  else if(typeid(float) == typeid(retValue))
245  retValue = strtof(data.c_str(),0);
246  else if(typeid(unsigned int) == typeid(retValue) ||
247  typeid(int) == typeid(retValue) ||
248  typeid(unsigned long long) == typeid(retValue) ||
249  typeid(long long) == typeid(retValue) ||
250  typeid(unsigned long) == typeid(retValue) ||
251  typeid(long) == typeid(retValue) ||
252  typeid(unsigned short) == typeid(retValue) ||
253  typeid(short) == typeid(retValue) ||
254  typeid(uint8_t) == typeid(retValue))
255  {
256  if(data.size() > 2 && data[1] == 'x') //assume hex value
257  retValue = (T)strtol(data.c_str(),0,16);
258  else if(data.size() > 1 && data[0] == 'b') //assume binary value
259  retValue = (T)strtol(data.substr(1).c_str(),0,2); //skip first 'b' character
260  else
261  retValue = (T)strtol(data.c_str(),0,10);
262  }
263  else
264  {
265  __SS__ << "Invalid type requested for a numeric string." << __E__;
266  __SS_THROW__;
267  }
268 
269  return retValue;
270  }
271  else
272  {
273  __SS__ << "Invalid type requested for a non-numeric string (must request std::string)." << __E__;
274  __SS_THROW__;
275  }
276  }
277 
278  //if here, then there was a problem
279  throw std::runtime_error("Error.");
280  }
281  catch(const std::runtime_error& e)
282  {
283  __SS__ << "\tUnrecognized column data type: " << columnsInfo_[col].getDataType()
284  << " and column type: " << columnsInfo_[col].getType()
285  << ", in configuration " << tableName_
286  << " at column=" << columnsInfo_[col].getName()
287  << " for getValue with type '" << ots_demangle(typeid(retValue).name())
288  << "'" << std::endl;
289 
290  if(columnsInfo_[col].getType() == ViewColumnInfo::TYPE_FIXED_CHOICE_DATA)
291  ss << "For column type " << ViewColumnInfo::TYPE_FIXED_CHOICE_DATA
292  << " the only valid numeric types are 'int' and 'unsigned int.'" << __E__;
293 
294  ss << e.what() << __E__;
295  throw std::runtime_error(ss.str());
296  }
297  } // end validateValueForColumn()
298  //special version of getValue for string type
299  // Note: necessary because types of std::basic_string<char> cause compiler problems if no string specific function
300  std::string validateValueForColumn(const std::string& value, unsigned int col, bool convertEnvironmentVariables=true) const;
301 
302  std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const;
303  std::string getEscapedValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const;
304  bool isURIEncodedCommentTheSame(const std::string &comment) const;
305 
306  const DataView& getDataView (void) const;
307  const std::vector<ViewColumnInfo>& getColumnsInfo (void) const;
308  std::vector<ViewColumnInfo>* getColumnsInfoP(void);
309  const ViewColumnInfo& getColumnInfo (unsigned int column) const;
310 
311  //Setters
312 
313  void setUniqueStorageIdentifier (const std::string &storageUID);
314  void setTableName (const std::string &name );
315  void setComment (const std::string &comment );
316  void setURIEncodedComment (const std::string &uriComment );
317  void setAuthor (const std::string &author );
318  void setCreationTime (time_t t );
319  void setLastAccessTime (time_t t = time(0) );
320  void setLooseColumnMatching (bool setValue );
321 
322  //==============================================================================
323  template<class T>
324  void setVersion (const T &version)
325  {
326  version_ = ConfigurationVersion(version);
327  }
328 
329 
330  //==============================================================================
331  //These two methods check for basic type consistency
332  template<class T>
333  void setValue(const T &value, unsigned int row, unsigned int col)
334  {
335  if(!(col < columnsInfo_.size() && row < getNumberOfRows()))
336  {
337  __SS__ << "Invalid row (" << row << ") col (" << col << ") requested!" << std::endl;
338  throw std::runtime_error(ss.str());
339  }
340 
341  if(columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_NUMBER)
342  {
343  std::stringstream ss;
344  ss << value;
345  theDataView_[row][col] = ss.str();
346  }
347  else if(columnsInfo_[col].getDataType() == ViewColumnInfo::DATATYPE_TIME &&
348  typeid(time_t) == typeid(value))
349  {
350  //save DATATYPE_TIME as unix timestamp, but get as nice string
351  std::stringstream ss;
352  ss << value;
353  theDataView_[row][col] = ss.str();
354  }
355  else
356  {
357  __SS__ << "\tUnrecognized view data type: " << columnsInfo_[col].getDataType()
358  << " in configuration " << tableName_
359  << " at column=" << columnsInfo_[col].getName()
360  << " for setValue with type '" << ots_demangle(typeid(value).name())
361  << "'" << std::endl;
362  throw std::runtime_error(ss.str());
363  }
364  }
365  void setValue (const std::string &value, unsigned int row, unsigned int col);
366  void setValue (const char *value, unsigned int row, unsigned int col);
367 
368  //Careful: The setValueAsString method is used to set the value without any consistency check with the data type
369  void setValueAsString (const std::string &value, unsigned int row, unsigned int col);
370 
371  //==============================================================================
372  void resizeDataView (unsigned int nRows, unsigned int nCols);
373  int addRow (const std::string &author = "", bool incrementUniqueData = false, std::string baseNameAutoUID = ""); //returns index of added row, always is last row
374  void deleteRow (int r);
375 
376  //Lore did not like this.. wants special access through separate Supervisor for "Database Management" int addColumn(std::string name, std::string viewName, std::string viewType); //returns index of added column, always is last column unless
377 
378 
379  iterator begin (void) {return theDataView_.begin();}
380  iterator end (void) {return theDataView_.end();}
381  const_iterator begin (void) const {return theDataView_.begin();}
382  const_iterator end (void) const {return theDataView_.end();}
383  void reset (void);
384  void print (std::ostream &out = std::cout) const;
385  void printJSON (std::ostream &out = std::cout) const;
386  int fillFromJSON (const std::string &json);
387  int fillFromCSV (const std::string &data, const int &dataOffset = 0, const std::string &author = "") throw(std::runtime_error);
388  bool setURIEncodedValue (const std::string &value, const unsigned int &row, const unsigned int &col, const std::string &author = "");
389 
390  static std::string decodeURIComponent (const std::string& data);
391 
392 private:
393  const unsigned int getOrInitColUID (void);
394  const unsigned int getOrInitColStatus (void);
395 
396  //operator= is purposely undefined and private (DO NOT USE) - should use copy()
397  ConfigurationView& operator= (const ConfigurationView src);
398 
399  // Return "" if there is no conversion
400  std::string convertEnvVariables (const std::string& data) const;
401  bool isNumber (const std::string& s) const;
402 
403  std::string uniqueStorageIdentifier_; //starts empty "", used to implement re-writeable views ("temporary views") in artdaq db
404  std::string tableName_ ; //View name (extensionTableName in xml)
405  ConfigurationVersion version_ ; //Configuration version
406  std::string comment_ ; //Configuration version comment
407  std::string author_ ;
408  time_t creationTime_ ; //used more like "construction"(constructor) time
409  time_t lastAccessTime_ ; //last time the ConfigurationInterface:get() retrieved this view
410  unsigned int colUID_, colStatus_; //special column pointers
411  std::map<std::string, unsigned int> colLinkGroupIDs_; //map from child link index to column
412 
413  bool fillWithLooseColumnMatching_;
414  unsigned int sourceColumnMismatchCount_, sourceColumnMissingCount_;
415  std::set<std::string> sourceColumnNames_;
416 
417  std::vector<ViewColumnInfo> columnsInfo_ ;
418  DataView theDataView_ ;
419 };
420 }
421 
422 
423 
424 #endif