tdaq-develop-2025-02-12
TableInfoReader.cc
1 #include "otsdaq/TableCore/TableInfoReader.h"
2 
3 #include "otsdaq/Macros/StringMacros.h"
4 #include "otsdaq/XmlUtilities/ConvertFromXML.h"
5 #include "otsdaq/XmlUtilities/DOMTreeErrorReporter.h"
6 
7 // #include "TimeFormatter.h"
8 
9 #include <xercesc/dom/DOMElement.hpp>
10 #include <xercesc/dom/DOMImplementation.hpp>
11 #include <xercesc/dom/DOMImplementationRegistry.hpp>
12 #include <xercesc/dom/DOMNodeList.hpp>
13 #include <xercesc/dom/DOMText.hpp>
14 #include <xercesc/parsers/XercesDOMParser.hpp>
15 // #include <xercesc/dom/DOMWriter.hpp>
16 
17 #include <xercesc/framework/LocalFileFormatTarget.hpp>
18 #include <xercesc/util/OutOfMemoryException.hpp>
19 
20 #include <iostream>
21 #include <sstream>
22 #include <stdexcept>
23 
24 #include <errno.h>
25 #include <sys/stat.h>
26 
27 #include "otsdaq/TableCore/TableBase.h"
28 
29 using namespace ots;
30 
31 #undef __COUT_HDR__
32 #define __COUT_HDR__ "TableInfoReader <>"
33 
36 #define CONFIGURATION_BACKEND_TYPE_ __ENV__("CONFIGURATION_TYPE")
37 
38 //==============================================================================
39 TableInfoReader::TableInfoReader(bool allowIllegalColumns)
40  : allowIllegalColumns_(allowIllegalColumns)
41 {
42  initPlatform();
43  rootTag_ = xercesc::XMLString::transcode("ROOT");
44  tableTag_ = xercesc::XMLString::transcode("TABLE");
45  tableNameAttributeTag_ = xercesc::XMLString::transcode("Name");
46  viewTag_ = xercesc::XMLString::transcode("VIEW");
47  viewNameAttributeTag_ = xercesc::XMLString::transcode("Name");
48  viewTypeAttributeTag_ = xercesc::XMLString::transcode("Type");
49  viewDescriptionAttributeTag_ = xercesc::XMLString::transcode("Description");
50  columnTag_ = xercesc::XMLString::transcode("COLUMN");
51  columnTypeAttributeTag_ = xercesc::XMLString::transcode("Type");
52  columnNameAttributeTag_ = xercesc::XMLString::transcode("Name");
53  columnStorageNameAttributeTag_ = xercesc::XMLString::transcode("StorageName");
54  columnDataTypeAttributeTag_ = xercesc::XMLString::transcode("DataType");
55  columnDataChoicesAttributeTag_ = xercesc::XMLString::transcode("DataChoices");
56  columnDefaultValueAttributeTag_ = xercesc::XMLString::transcode("DefaultValue");
57  columnMinValueAttributeTag_ = xercesc::XMLString::transcode("MinValue");
58  columnMaxValueAttributeTag_ = xercesc::XMLString::transcode("MaxValue");
59 }
60 
61 //==============================================================================
62 TableInfoReader::~TableInfoReader(void)
63 {
64  try
65  {
66  xercesc::XMLString::release(&rootTag_);
67  xercesc::XMLString::release(&tableTag_);
68  xercesc::XMLString::release(&tableNameAttributeTag_);
69  xercesc::XMLString::release(&viewTag_);
70  xercesc::XMLString::release(&viewNameAttributeTag_);
71  xercesc::XMLString::release(&viewTypeAttributeTag_);
72  xercesc::XMLString::release(&viewDescriptionAttributeTag_);
73  xercesc::XMLString::release(&columnTag_);
74  xercesc::XMLString::release(&columnTypeAttributeTag_);
75  xercesc::XMLString::release(&columnNameAttributeTag_);
76  xercesc::XMLString::release(&columnStorageNameAttributeTag_);
77  xercesc::XMLString::release(&columnDataTypeAttributeTag_);
78  xercesc::XMLString::release(&columnDataChoicesAttributeTag_);
79  xercesc::XMLString::release(&columnDefaultValueAttributeTag_);
80  xercesc::XMLString::release(&columnMinValueAttributeTag_);
81  xercesc::XMLString::release(&columnMaxValueAttributeTag_);
82  }
83  catch(...)
84  {
85  __COUT_ERR__ << "Unknown exception encountered in TagNames destructor" << __E__;
86  }
87  terminatePlatform();
88 }
89 
90 //==============================================================================
91 void TableInfoReader::initPlatform(void)
92 {
93  try
94  {
95  xercesc::XMLPlatformUtils::Initialize(); // Initialize Xerces infrastructure
96  }
97  catch(xercesc::XMLException& e)
98  {
99  __COUT_ERR__ << "XML toolkit initialization error: "
100  << XML_TO_CHAR(e.getMessage()) << __E__;
101  // throw exception here to return ERROR_XERCES_INIT
102  }
103 }
104 
105 //==============================================================================
106 void TableInfoReader::terminatePlatform(void)
107 {
108  try
109  {
110  xercesc::XMLPlatformUtils::Terminate(); // Terminate after release of memory
111  }
112  catch(xercesc::XMLException& e)
113  {
114  __COUT_ERR__ << "XML tolkit teardown error: " << XML_TO_CHAR(e.getMessage())
115  << __E__;
116  }
117 }
118 
119 //==============================================================================
120 void TableInfoReader::setAllowColumnErrors(bool setValue)
121 {
122  allowIllegalColumns_ = setValue;
123 }
124 //==============================================================================
125 const bool& TableInfoReader::getAllowColumnErrors(void) { return allowIllegalColumns_; }
126 
127 //==============================================================================
128 bool TableInfoReader::checkViewType(std::string type)
129 {
130  std::vector<std::string> types;
131  int currentIndex = 0;
132  while(type.find(',', currentIndex) != std::string::npos)
133  {
134  types.push_back(
135  type.substr(currentIndex, type.find(',', currentIndex) - currentIndex));
136  currentIndex = type.find(',', currentIndex) + 1;
137  }
138  types.push_back(type.substr(currentIndex, type.size()));
139 
140  const std::string systemType = CONFIGURATION_BACKEND_TYPE_;
141 
142  for(unsigned int i = 0; i < types.size(); i++)
143  {
144  if(types[i] == systemType)
145  return true;
146  }
147  // In case I don't succeed let's check if maybe there is something wrong with the
148  // names
149  const unsigned int allowedNamesSize = 3;
150  const std::string allowedNames[allowedNamesSize] = {
151  "File", "Database", "DatabaseTest"};
152  if(systemType != allowedNames[0] && systemType != allowedNames[1] &&
153  systemType != allowedNames[2])
154  {
155  __COUT__ << "The type defined in CONFIGURATION_BACKEND_TYPE (" << systemType
156  << ") doesn't match with any of the allowed types: File,Database or "
157  "DatabaseTest"
158  << __E__;
159 
160  throw(std::runtime_error("Illegal table type"));
161  }
162  for(unsigned int i = 0; i < types.size(); i++)
163  {
164  if(types[i] != allowedNames[0] && types[i] != allowedNames[1] &&
165  types[i] != allowedNames[2])
166  {
167  __COUT__ << "The type defined in the info file (" << types[i]
168  << ") doesn't match with any of the allowed types: "
169  << allowedNames[0] << ", " << allowedNames[1] << " or "
170  << allowedNames[2] << __E__;
171  throw(std::runtime_error("Illegal Type!"));
172  }
173  }
174 
175  return false;
176 }
177 
178 //==============================================================================
179 xercesc::DOMNode* TableInfoReader::getNode(XMLCh* tagName,
180  xercesc::DOMNode* parent,
181  unsigned int itemNumber)
182 {
183  return getNode(tagName, dynamic_cast<xercesc::DOMElement*>(parent), itemNumber);
184 }
185 
186 //==============================================================================
187 xercesc::DOMNode* TableInfoReader::getNode(XMLCh* tagName,
188  xercesc::DOMElement* parent,
189  unsigned int itemNumber)
190 {
191  xercesc::DOMNodeList* nodeList = parent->getElementsByTagName(tagName);
192  if(!nodeList)
193  {
194  throw(std::runtime_error(std::string("Can't find ") + XML_TO_CHAR(tagName) +
195  " tag!"));
196  __COUT__ << (std::string("Can't find ") + XML_TO_CHAR(tagName) + " tag!")
197  << __E__;
198  }
199  // __COUT__<< "Name: " << XML_TO_CHAR(nodeList->item(itemNumber)->getNodeName())
200  // << __E__; if( nodeList->item(itemNumber)->getFirstChild() != 0 )
201  // __COUT__<< "Value: " <<
202  // XML_TO_CHAR(nodeList->item(itemNumber)->getFirstChild()->getNodeValue()) <<
203  // __E__;
204  return nodeList->item(itemNumber);
205 }
206 
207 //==============================================================================
208 xercesc::DOMElement* TableInfoReader::getElement(XMLCh* tagName,
209  xercesc::DOMNode* parent,
210  unsigned int itemNumber)
211 {
212  return dynamic_cast<xercesc::DOMElement*>(getNode(tagName, parent, itemNumber));
213 }
214 
215 //==============================================================================
216 xercesc::DOMElement* TableInfoReader::getElement(XMLCh* tagName,
217  xercesc::DOMElement* parent,
218  unsigned int itemNumber)
219 {
220  return dynamic_cast<xercesc::DOMElement*>(getNode(tagName, parent, itemNumber));
221 }
222 
223 //==============================================================================
224 std::string TableInfoReader::read(TableBase& table)
225 {
226  std::string accumulatedExceptions = "";
227 
228  // KEEP For debugging... added by Gennadiy...
229  // if table name starts with "TestTable00" then turn off the reading of information
230  // auto tmp_test_table_prefix = std::string{"TestTable00"};
231  // auto tmp_table_name = table.getTableName();
232  // if (std::equal(tmp_test_table_prefix.begin(), tmp_test_table_prefix.end(),
233  // tmp_table_name.begin())) return accumulatedExceptions; KEEP End debugging... for
234  // Gennadiy...
235 
236  // These environment variables are required
237  if(__ENV__("CONFIGURATION_TYPE") == NULL)
238  __COUT__ << "Missing env variable: CONFIGURATION_TYPE. It must be set!" << __E__;
239  // if(__ENV__("CONFIGURATION_DATA_PATH") == NULL) __COUT__ << "Missing env variable:
240  // CONFIGURATION_DATA_PATH. It must be set!" << __E__;
241  if(__ENV__("TABLE_INFO_PATH") == NULL)
242  __COUT__ << "Missing env variable: TABLE_INFO_PATH. It must be set!" << __E__;
243 
244  // example c++ setting of necessary environment variables
245  // setenv("CONFIGURATION_TYPE","File",1);
246  // setenv("CONFIGURATION_DATA_PATH",(std::string(__ENV__("USER_DATA")) +
247  // "/TableDataExamples").c_str(),1);
248  // setenv("TABLE_INFO_PATH",(std::string(__ENV__("USER_DATA")) +
249  // "/TableInfo").c_str(),1);
250 
251  std::string tableDataDir = std::string(__ENV__("TABLE_INFO_PATH")) + "/";
252  std::string tableFile = tableDataDir + table.getTableName() + "Info.xml";
253  //__COUT__ << tableFile << __E__;
254  struct stat fileStatus;
255 
256  int iretStat = stat(tableFile.c_str(), &fileStatus);
257  if(iretStat == ENOENT)
258  {
259  __SS__ << ("Path file_name does not exist, or path is an empty std::string.")
260  << __E__;
261  __COUT_ERR__ << ss.str();
262  __SS_THROW__;
263  }
264  else if(iretStat == ENOTDIR)
265  {
266  __SS__ << ("A component of the path is not a directory.") << __E__;
267  __COUT_ERR__ << ss.str();
268  __SS_THROW__;
269  }
270  else if(iretStat == ELOOP)
271  {
272  __SS__ << ("Too many symbolic links encountered while traversing the path.")
273  << __E__;
274  __COUT_ERR__ << ss.str();
275  __SS_THROW__;
276  }
277  else if(iretStat == EACCES)
278  {
279  __SS__ << ("Permission denied.") << __E__;
280  __COUT_ERR__ << ss.str();
281  __SS_THROW__;
282  }
283  else if(iretStat == ENAMETOOLONG)
284  {
285  __SS__ << ("File can not be read. Name too long.") << __E__;
286  __COUT_ERR__ << ss.str();
287  __SS_THROW__;
288  }
289 
290  xercesc::XercesDOMParser* parser = new xercesc::XercesDOMParser;
291  // Configure DOM parser.
292  parser->setValidationScheme(xercesc::XercesDOMParser::Val_Auto); // Val_Never
293  parser->setDoNamespaces(true);
294  parser->setDoSchema(true);
295  parser->useCachedGrammarInParse(false);
296 
297  DOMTreeErrorReporter* errorHandler = new DOMTreeErrorReporter();
298  parser->setErrorHandler(errorHandler);
299  try
300  {
301  parser->parse(tableFile.c_str());
302 
303  // no need to free this pointer - owned by the parent parser object
304  xercesc::DOMDocument* xmlDocument = parser->getDocument();
305 
306  // Get the top-level element: Name is "root". No attributes for "root"
307  xercesc::DOMElement* elementRoot = xmlDocument->getDocumentElement();
308  if(!elementRoot)
309  {
310  delete parser;
311  delete errorHandler;
312  throw(std::runtime_error("empty XML document"));
313  }
314 
315  //<TABLE>
316  xercesc::DOMElement* tableElement = getElement(tableTag_, elementRoot, 0);
317  if(table.getTableName() !=
318  XML_TO_CHAR(tableElement->getAttribute(tableNameAttributeTag_)))
319  {
320  __SS__ << "In " << tableFile << " the table name "
321  << XML_TO_CHAR(tableElement->getAttribute(tableNameAttributeTag_))
322  << " doesn't match the the class table name " << table.getTableName()
323  << __E__;
324 
325  delete parser;
326  delete errorHandler;
327  __COUT_ERR__ << "\n" << ss.str();
328  throw(std::runtime_error(ss.str()));
329  }
330  //<VIEW>
331  xercesc::DOMNodeList* viewNodeList = tableElement->getElementsByTagName(viewTag_);
332  bool storageTypeFound = false;
333 
334  if(viewNodeList->getLength() != 1)
335  {
336  __SS__ << "In " << tableFile << " the table name "
337  << XML_TO_CHAR(tableElement->getAttribute(tableNameAttributeTag_))
338  << " there must only be one view. There were "
339  << viewNodeList->getLength() << " found." << __E__;
340 
341  delete parser;
342  delete errorHandler;
343  __COUT_ERR__ << "\n" << ss.str();
344  throw(std::runtime_error(ss.str()));
345  }
346 
347  for(XMLSize_t view = 0; view < viewNodeList->getLength(); view++)
348  {
349  if(!viewNodeList->item(view)->getNodeType() ||
350  viewNodeList->item(view)->getNodeType() !=
351  xercesc::DOMNode::ELEMENT_NODE) // true is not 0 && is element
352  continue;
353  xercesc::DOMElement* viewElement =
354  dynamic_cast<xercesc::DOMElement*>(viewNodeList->item(view));
355  std::string viewType =
356  XML_TO_CHAR(viewElement->getAttribute(viewTypeAttributeTag_));
357  if(!checkViewType(viewType))
358  continue;
359  storageTypeFound = true;
360 
361  // table name is now constant, set by parent TableBase
362  // table.getMockupViewP()->setTableName(XML_TO_CHAR(viewElement->getAttribute(viewNameAttributeTag_)));
363  // check for consistency, and show warning
364  if(std::string(XML_TO_CHAR(viewElement->getAttribute(
365  viewNameAttributeTag_))) != table.getMockupViewP()->getTableName())
366  __COUT_WARN__ << "Table Info name mismatch: "
367  << std::string(XML_TO_CHAR(
368  viewElement->getAttribute(viewNameAttributeTag_)))
369  << " vs " << table.getMockupViewP()->getTableName()
370  << __E__;
371 
372  xercesc::DOMNodeList* columnNodeList =
373  viewElement->getElementsByTagName(columnTag_);
374 
375  for(XMLSize_t column = 0; column < columnNodeList->getLength(); column++)
376  {
377  //<COLUMN>
378  xercesc::DOMElement* columnElement =
379  dynamic_cast<xercesc::DOMElement*>(columnNodeList->item(column));
380  //__COUT__ <<
381  // XML_TO_CHAR(columnElement->getAttribute(columnNameAttributeTag_)) <<
382  // __E__;
383 
384  // Check for default value tag being there (for backwards compatibility)
385  // Documentation :https://xerces.apache.org/xerces-c/apiDocs-3/classDOMElement.html#a9d6a102d853eafe6619be4324c1555c3
386  std::string defaultValue;
387  bool isDefaultValue =
388  columnElement->getAttributeNode(columnDefaultValueAttributeTag_)
389  ? true
390  : false;
391  if(isDefaultValue)
392  {
393  defaultValue = StringMacros::decodeURIComponent(XML_TO_CHAR(
394  columnElement->getAttribute(columnDefaultValueAttributeTag_)));
395 
396  //__COUT__ << "FOUND default value! " << defaultValue << __E__;
397  }
398  std::string minValue;
399  bool isMinValue =
400  columnElement->getAttributeNode(columnMinValueAttributeTag_) ? true
401  : false;
402  if(isMinValue)
403  {
404  minValue = StringMacros::decodeURIComponent(XML_TO_CHAR(
405  columnElement->getAttribute(columnMinValueAttributeTag_)));
406 
407  // __COUT__ << "FOUND min value! " << minValue << __E__;
408  }
409  std::string maxValue;
410  bool isMaxValue =
411  columnElement->getAttributeNode(columnMaxValueAttributeTag_) ? true
412  : false;
413  if(isMaxValue)
414  {
415  maxValue = StringMacros::decodeURIComponent(XML_TO_CHAR(
416  columnElement->getAttribute(columnMaxValueAttributeTag_)));
417 
418  // __COUT__ << "FOUND max value! " << maxValue << __E__;
419  }
420  // TODO read min/max, almost done??
421  else
422  {
423  //__COUT__ << "DID NOT find default value! or min and max values!" << __E__;
424  }
425 
426  // automatically delete the persistent version of the column info
427  std::string capturedException;
428  // TODO add reading of min / max
429  table.getMockupViewP()->getColumnsInfoP()->push_back(TableViewColumnInfo(
430  XML_TO_CHAR(columnElement->getAttribute(columnTypeAttributeTag_)),
431  XML_TO_CHAR(columnElement->getAttribute(columnNameAttributeTag_)),
432  XML_TO_CHAR(
433  columnElement->getAttribute(columnStorageNameAttributeTag_)),
434  XML_TO_CHAR(columnElement->getAttribute(columnDataTypeAttributeTag_)),
435  isDefaultValue ? &defaultValue : 0,
436  XML_TO_CHAR(
437  columnElement->getAttribute(columnDataChoicesAttributeTag_)),
438  isMinValue ? &minValue : 0,
439  isMaxValue ? &maxValue : 0,
440  allowIllegalColumns_
441  ? &capturedException
442  : 0)); // capture exception string if allowing illegal columns
443 
444  // if error detected (this implies allowing illegal columns)
445  // accumulate and return accumulated errors at end
446  if(capturedException != "")
447  accumulatedExceptions +=
448  std::string("\n\nColumn Error:") + capturedException;
449 
450  //</COLUMN>
451  }
452 
453  // handle view description (which is actually the table
454  // description since only one view allowed)
455  std::string tableDescription =
456  XML_TO_CHAR(viewElement->getAttribute(viewDescriptionAttributeTag_));
457 
458  table.setTableDescription(StringMacros::decodeURIComponent(tableDescription));
459  //__COUT__ << "tableDescription = " << tableDescription << __E__;
460 
461  //</VIEW>
462  }
463  if(!storageTypeFound)
464  {
465  __COUT__ << "The type defined in CONFIGURATION_BACKEND_TYPE ("
466  << CONFIGURATION_BACKEND_TYPE_
467  << ") doesn't match with any of the types defined in " << tableFile
468  << __E__;
469 
470  delete parser;
471  delete errorHandler;
472  throw(std::runtime_error("Table Type mismatch!"));
473  }
474 
475  //</TABLE>
476  }
477  catch(xercesc::XMLException& e)
478  {
479  std::ostringstream errBuf;
480  errBuf << "Error parsing file: " << XML_TO_CHAR(e.getMessage()) << std::flush;
481  }
482  delete parser;
483  delete errorHandler;
484 
485  //__COUT__ << __E__;
486 
487  // if exceptions have been accumulated
488  // then in allowIllegalColumns mode
489  // return accumulated exception strings to next level
490  return accumulatedExceptions;
491 }
492 
493 //==============================================================================
496 std::string TableInfoReader::read(TableBase* table) { return read(*table); }
const std::string & getTableName(void) const
Getters.
Definition: TableBase.cc:681
static std::string decodeURIComponent(const std::string &data)