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