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