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