Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
XMLReader.cxx
Go to the documentation of this file.
1 // @(#)root/core/utils:$Id: XMLReader.cxx 35213 2010-09-08 16:39:04Z axel $
2 // Author: Velislava Spasova, 2010-09-16
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2011, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 //////////////////////////////////////////////////////////////////////////
13 // //
14 // This class reads selection.xml files. //
15 // //
16 //////////////////////////////////////////////////////////////////////////
17 
18 
19 
20 #include "XMLReader.h"
21 #include "SelectionRules.h"
22 #include "TClingUtils.h"
23 
24 std::map<std::string, XMLReader::ETagNames> XMLReader::fgMapTagNames;
25 
26 /*
27  This is a static function - which in our context means it is populated only ones
28  */
29 void XMLReader::PopulateMap(){
30  if (!(fgMapTagNames.empty())) return; // if the map has already been populated, return, else populate it
31 
32  XMLReader::fgMapTagNames["class"] = kClass;
33  XMLReader::fgMapTagNames["/class"] = kEndClass;
34  XMLReader::fgMapTagNames["struct"] = kClass;
35  XMLReader::fgMapTagNames["/struct"] = kEndClass;
36  XMLReader::fgMapTagNames["namespace"] = kClass;
37  XMLReader::fgMapTagNames["/namespace"] = kEndClass;
38  XMLReader::fgMapTagNames["function"] = kFunction;
39  XMLReader::fgMapTagNames["variable"] = kVariable;
40  XMLReader::fgMapTagNames["enum"] = kEnum;
41  XMLReader::fgMapTagNames["method"] = kMethod;
42  XMLReader::fgMapTagNames["/method"] = kEndMethod;
43  XMLReader::fgMapTagNames["field"] = kField;
44  XMLReader::fgMapTagNames["/field"] = kEndField;
45  XMLReader::fgMapTagNames["member"] = kField; // field and member treated identically
46  XMLReader::fgMapTagNames["/member"] = kEndField; // field and member treated identically
47  XMLReader::fgMapTagNames["lcgdict"] = kLcgdict;
48  XMLReader::fgMapTagNames["/lcgdict"] = kEndLcgdict;
49  XMLReader::fgMapTagNames["rootdict"] = kLcgdict;
50  XMLReader::fgMapTagNames["/rootdict"] = kEndLcgdict;
51  XMLReader::fgMapTagNames["selection"] = kSelection;
52  XMLReader::fgMapTagNames["/selection"] = kEndSelection;
53  XMLReader::fgMapTagNames["exclusion"] = kExclusion;
54  XMLReader::fgMapTagNames["/exclusion"] = kEndExclusion;
55  XMLReader::fgMapTagNames["properties"] = kProperties;
56  XMLReader::fgMapTagNames["version"] = kVersion;
57  XMLReader::fgMapTagNames["ioread"] = kBeginIoread;
58  XMLReader::fgMapTagNames["/ioread"] = kEndIoread;
59  XMLReader::fgMapTagNames["read"] = kBeginIoread;
60  XMLReader::fgMapTagNames["/read"] = kEndIoread;
61  XMLReader::fgMapTagNames["readraw"] = kBeginIoreadRaw;
62  XMLReader::fgMapTagNames["/readraw"] = kEndIoreadRaw;
63  XMLReader::fgMapTagNames["typedef"] = kTypedef;
64 }
65 
66 /*
67  This function Gets the next tag from teh input file stream
68  file - the open input stream
69  out - we return the tag through that parameter
70  lineCount - we are counting the line numbers here in order to print error messages in case of an error
71  */
72 bool XMLReader::GetNextTag(std::ifstream& file, std::string& out, int& lineCount)
73 {
74  int c;
75  std::string str;
76  bool angleBraceLevel = false;
77  bool quotes = false;
78  bool comment = false;
79  bool tagIsComment = false;
80  bool xmlDecl = false;
81  bool tagIsXMLDecl = false; // like <?xml version="1.0" encoding="ISO-8859-1"?>
82  bool isCR=false;
83  bool isInlineComment = false ; // Support comments like in c++ "// Mycomment"
84  int charMinus1= '@';
85  int charMinus2= '@';
86  int charMinus3= '@';
87  while(file.good())
88  {
89  c = file.get();
90  // Temp fix: the stream should become a string
91  if (c=='&'){
92  std::string pattern;
93  int i=0;
94  for (;i<3 && file.good();++i){
95  pattern+=file.get();
96  }
97  if (pattern == "lt;"){
98  c = '<';
99  }
100  else if (pattern == "gt;"){
101  c = '>';
102  }
103  else {
104  for (;i!=0 && file.good();--i){
105  file.unget();
106  }
107  }
108  }
109 
110  if (file.good()){
111  bool br = false; // break - we Set it when we have found the end of the tag
112 
113  //count quotes - we don't want to count < and > inside quotes as opening/closing brackets
114  switch (c) {
115  case '\r': // skip these
116  isCR=true;
117  break;
118  case '\n': ++lineCount; // if new line increment lineCount
119  break;
120  case '"': quotes = !quotes; // we are allowed to have only pair number of quotes per tag - for the attr. values
121  break;
122  case '<':
123  if (!quotes) angleBraceLevel = !angleBraceLevel; // we count < only outside quotes (i.e. quotes = false)
124  if (!angleBraceLevel && !comment) return false; // if angleBraceLevel = true, we have < outside quotes - this is error
125  break;
126  case '>':
127  if (!quotes && !comment) angleBraceLevel = !angleBraceLevel; // we count > only outside quotes (i.e. quotes = false)
128  if (!angleBraceLevel && !comment) br = true; // if angleBraceLevel = true, we have > outside quotes - this is end of tag => break
129  if (!angleBraceLevel && comment && charMinus2=='-' && charMinus1=='-') br = true;
130  if (charMinus2=='-' && charMinus1=='-'){
131  if (comment) { tagIsComment=true; br=true; } // comment ended!
132  else { return false; } // a comment ends w/o starting
133  }
134  if (charMinus1=='?'){
135  if (xmlDecl) {tagIsXMLDecl=true;br=true;} // xmlDecl ended
136  else {return false;} // an xmlDecl ends w/o starting
137  }
138  break;
139  case '-':
140  if (charMinus3=='<' && charMinus2=='!' && charMinus1=='-') comment = !comment; // We are in a comment
141  break;
142  case '?': // treat the xml standard declaration
143  if (charMinus1=='<') xmlDecl=!xmlDecl;
144  break;
145  case '/': // if char is /, preceeding is / and we are not between a < > pair or an xml comment:
146  if (charMinus1=='/' && !angleBraceLevel && !comment){
147  isInlineComment=true;
148  }
149  break;
150  }
151  if (isCR){
152  isCR=false;
153  continue;
154  }
155  if (isInlineComment){
156  out.erase(out.size()-1,1);
157  while (file.good() && c!='\n'){ // continue up to the end of the line or the file
158  c = file.get();
159  }
160  break;
161  }
162  charMinus3=charMinus2;
163  charMinus2=charMinus1;
164  charMinus1=c;
165  // check if the comment ended
166  if (comment && !(charMinus3=='-' && charMinus2=='-' && charMinus1=='>')){
167  continue;
168  }
169  out += c; // if c != {<,>,"}, add it to the tag
170  if (br) break; // if br = true, we have reached the end of the tag and we stop reading from the input stream
171 
172  }
173  }
174 
175 
176  // Trim Both leading and trailing spaces
177  int startpos = out.find_first_not_of(" \t\n"); // Find the first character position after excluding leading blank spaces
178  int endpos = out.find_last_not_of(" \t\n"); // Find the first character position from reverse af
179 
180  // if all spaces or empty return an empty string
181  if (((int) std::string::npos == startpos ) || ((int) std::string::npos == endpos))
182  {
183  out = "";
184  }
185  else
186  out = out.substr( startpos, endpos-startpos+1 );
187 
188  // if tag isn't empty, check if everything is OK with the tag format
189  if (!out.empty()){
190  bool isTagOk = CheckIsTagOK(out);
191  if (tagIsComment || tagIsXMLDecl){
192  out="";
193  return GetNextTag(file,out,lineCount);
194  }
195  else{
196  return isTagOk;
197  }
198  }
199  else
200  return true;
201 }
202 
203 //////////////////////////////////////////////////////////////////////////////////////////
204 
205 /*
206  Checks if the tag is OK with respect to the opening and closing <>
207  */
208 
209 bool XMLReader::CheckIsTagOK(const std::string& tag)
210 {
211  if (tag.length()<3){
212  ROOT::TMetaUtils::Error(0,"This is not a tag!\n");
213  return false;
214  }
215 
216  // if tag doesn't begin with <, this is not a tag
217  if (tag.at(0) != '<'){
218  ROOT::TMetaUtils::Error(0,"Malformed tag %s (tag doesn't begin with <)!\n", tag.c_str());
219  return false;
220  }
221 
222  // if the second symbol is space - this is malformed tag - name of the tag should go directly after the <
223  if (isspace(tag.at(1))){
224  ROOT::TMetaUtils::Error(0,"Malformed tag %s (there should be no white-spaces between < and name-of-tag)!\n", tag.c_str());
225  return false;
226  }
227 
228  // this for checks if there are spaces between / and the closing >
229  int countWSp = 0;
230  for (std::string::size_type i = tag.length()-2; true /*see break below*/; --i) {
231  char c = tag[i];
232 
233  if (isspace(c)) {
234  ++countWSp;
235  }
236  else {
237  if (c == '/' && countWSp>0) {
238  ROOT::TMetaUtils::Error(0,"Malformed tag %s (there should be no white-spaces between / and >)!\n", tag.c_str());
239  return false;
240  }
241  break;
242  }
243  if (i == 0) break;
244  }
245 
246 
247  // here we are checking for a situation in which we have forgotten to close quotes and the next tag has entered in an
248  // attribute value of the current tag (example: <class name="a > <fild name="b" />).
249  // NOTE: this will only work if tags like <class pattern = "something><" /> arent valid because in any case they will
250  // be processed as invalid tags
251  int pos1 = tag.find(">");
252  if (pos1>-1) {
253  for (std::string::size_type i = pos1+1, e = tag.length(); i < e; ++i) {
254  char c = tag[i];
255 
256  if (isspace(c)){
257  continue;
258  }
259  if (c == '<'){
260  return false;
261  }
262  else{
263  break;
264  }
265  }
266  }
267 
268  return true;
269 }
270 
271 //////////////////////////////////////////////////////////////////////////////////////////
272 /*
273  Returns true if the tag is standalone. By standlone I mean <something />
274  */
275 bool XMLReader::IsStandaloneTag(const std::string& tag)
276 {
277  std::string tagEnd = tag.substr(tag.length()-2, 2);
278  return (tagEnd == "/>");
279 }
280 
281 //////////////////////////////////////////////////////////////////////////////////////////
282 /*
283  Returns true if the tag is closing tag, t.e. </class>
284  */
285 bool XMLReader::IsClosingTag(const std::string& tag)
286 {
287  std::string tagBegin = tag.substr(0, 2);
288  return (tagBegin == "</");
289 }
290 
291 //////////////////////////////////////////////////////////////////////////////////////////
292 /*
293  Returns name of the tag (class, function, method, selection, ...). If the name is not amongst the names populated in the
294  map, return kInvalid
295  */
296 XMLReader::ETagNames XMLReader::GetNameOfTag(const std::string& tag, std::string& name)
297 {
298  for (std::string::size_type i = 0, e = tag.length(); i < e; ++i) {
299  char c = tag[i];
300  if (isspace(c)) break;
301  if ((c != '<') && (c != '>'))
302  name += c;
303  }
304 
305  std::map<std::string, ETagNames>::iterator it;
306  it = XMLReader::fgMapTagNames.find(name);
307  if (it != XMLReader::fgMapTagNames.end())
308  return XMLReader::fgMapTagNames[name];
309  else
310  return kInvalid;
311 }
312 
313 
314 /////////////////////////////////////////////////////////////////////////////////////////
315 /*
316  We Get the attributes (if any) of the tag as {attribute_name, attribute_value} couples
317  If there are no attributes, I don't fill the out vector and after that in the Parse()
318  method check if out is empty. All the error handling conserning attributes is done here
319  and this is the reason why the logic is somtimes a bit obscure.
320  */
321 bool XMLReader::GetAttributes(const std::string& tag, std::vector<Attributes>& out, const char* lineNum)
322 {
323  // Get position of first symbol of the name of the tag
324  std::string name;
325  GetNameOfTag(tag,name);
326 
327  bool standalone = IsStandaloneTag(tag);
328 
329  // cut off the name of the tag and the trailing /> or >
330  std::string::size_type cutend = tag.length() - 1 - name.length();
331  if (standalone) --cutend;
332  std::string attrstr = tag.substr(1 /*for '<'*/ + name.length(), cutend);
333 
334  if (attrstr.length() > 4) { //ELSE ERROR HANDLING; - no need for it - I check in Parse()
335  //cut off any last spaces, tabs or end of lines
336  int pos = attrstr.find_last_not_of(" \t\n");
337  attrstr = attrstr.substr(1, pos+1);
338 
339  /*
340  The logic here is the following - we have bool name - it shows if we have read (or are reading) an attribute name
341  bool equalfound - shows if we have found the = symbol after the name
342  bool value - shows if we have found or are reading the attribute value
343  bool newattr - do we have other attributes to read
344  char lastsymbol - I use it to detect a situation like name = xx"value"
345  */
346  std::string attrtemp;
347  bool namefound = false;
348  bool equalfound = false;
349  bool value = false;
350  bool newattr = true;
351  bool inString = false;
352  std::string attr_name;
353  std::string attr_value;
354  char lastsymbol = '\0';
355 
356  for (std::string::size_type i = 0, e = attrstr.length()-1; i < e; ++i) {
357  char c = attrstr[i];
358 
359  if (c == '=') {
360  if (!namefound){ // if no name was read, report error (i.e. <class ="x">)
361  ROOT::TMetaUtils::Error(0,"At line %s. No name of attribute\n", lineNum);
362  return false;
363  }
364  else {
365  equalfound = true;
366  if (!value) // do not do that if we are reading a value. There can be an = in it
367  lastsymbol = '=';
368  else
369  attr_value += c; // in case we are in a value, we save also the =
370 
371  }
372  }
373  else if (isspace(c) && !inString) continue;
374  else if (c == '"') {
375  inString=!inString;
376  lastsymbol = '"';
377  if (namefound && equalfound){ //if name was read and = was found
378  if (!value){ // in this case we are starting to read the value of the attribute
379  value = true;
380  }
381  else { // if !value is false, then value is true which means that these are the closing quotes for the
382  // attribute value
383  if (attr_name.length() == 0) { // checks if attribute name is empty
384  ROOT::TMetaUtils::Error(0,"At line %s. Attribute - missing attribute name!\n", lineNum);
385  return false;
386  }
387  // Lift this: one may had an empty attribute value
388 // if (attr_value.length() == 0) { // checks if the attribute value is empty
389 // ROOT::TMetaUtils::Error(0,"Attribute - missing attibute value!\n");
390 // return false;
391 // }
392 
393  // creates new Attributes object and pushes it back in the vector
394  // then Sets the variables in the initial state - if there are other attributes to be read
395 
396  // For the moment the proto pattern is not implemented. The current ROOT6 architecture
397  // relies on ABI compatibility for calling functions, no stub functions are present.
398  // The concept of selecting/excluding functions is not defined.
399 // if (attr_name == "proto_pattern") {
400 // printf("XMLReader::GetAttributes(): proto_pattern selection not implemented yet!\n");
401 // }
402  ROOT::TMetaUtils::Info(0, "*** Attribute: %s = \"%s\"\n", attr_name.c_str(), attr_value.c_str());
403  if (attr_name=="pattern" && attr_value.find("*") == std::string::npos){
404  ROOT::TMetaUtils::Warning(0,"At line %s. A pattern, \"%s\", without wildcards is being used. This selection rule would not have any effect. Transforming it to a rule based on name.\n", lineNum, attr_value.c_str());
405  attr_name="name";
406  }
407  out.emplace_back(attr_name, attr_value);
408  attr_name = "";
409  attr_value = "";
410  namefound = false;
411  value = false;
412  equalfound = false;
413  newattr = true;
414  }
415  }
416  else { // this is the case in which (name && equalfound) is false i.e. we miss either the attribute name or the
417  // = symbol
418  ROOT::TMetaUtils::Error(0,"At line %s. Attribute - missing attribute name or =\n", lineNum);
419  return false;
420  }
421  }
422  else if (lastsymbol == '=') { // this is the case in which the symbol is not ", space or = and the last symbol read
423  // (diferent than space) is =. This is a situation which is represented by for example <class name = x"value">
424  // this is an error
425  ROOT::TMetaUtils::Error(0,"At line %s. Wrong quotes placement or lack of quotes\n", lineNum);
426  return false;
427  }
428  else if ((newattr || namefound) && !value){ // else - if name or newattr is Set, we should write in the attr_name variable
429  newattr = false;
430  namefound = true;
431  attr_name += c;
432  lastsymbol = c;
433  }
434  else if (value) {
435  attr_value += c; // if not, we should write in the attr_value variable
436  }
437  }
438 
439  if (namefound && (!equalfound || !value)) { // this catches the situation <class name = "value" something >
440  ROOT::TMetaUtils::Error(0,"At line %s. Attribute - missing attribute value\n", lineNum);
441  return false;
442  }
443  }
444  return true;
445 }
446 
447 //////////////////////////////////////////////////////////////////////////////////////////
448 /*
449  This is where the actual work is done - this method parses the XML file tag by tag
450  and for every tag extracts the atrributes. Here is done some error checking as well -
451  mostly conserning missing or excessive closing tags, nesting problems, etc.
452  */
453 bool XMLReader::Parse(const std::string &fileName, SelectionRules& out)
454 {
455 
456  std::ifstream file(fileName);
457 
458  PopulateMap();
459 
460  int lineNum = 1;
461  bool exclusion = false;
462  bool selection = false;
463  bool sel = false;
464  bool selEnd = false;
465  bool exclEnd = false;
466  bool excl = false;
467  bool inIoread = false;
468  bool inClass = false;
469  bool inMethod = false;
470  bool inField = false;
471 
472  BaseSelectionRule *bsr = 0; // Pointer to the base class, in it is written information about the current sel. rule
473  BaseSelectionRule *bsrChild = 0; // The same but keeps information for method or field children of a class
474  std::unique_ptr<ClassSelectionRule> csr;
475  std::unique_ptr<FunctionSelectionRule> fsr;
476  std::unique_ptr<VariableSelectionRule> vsr;
477  std::unique_ptr<EnumSelectionRule> esr;
478 
479  while(file.good()){
480  std::string tagStr;
481 
482  bool tagOK = GetNextTag(file, tagStr, lineNum);
483 
484  const char* tagStrCharp = tagStr.c_str();
485  // convert number to string
486  std::ostringstream buf;
487  buf << lineNum;
488  std::string lineNumStr = buf.str();
489  const char* lineNumCharp = lineNumStr.c_str();
490  if (!tagOK){
491  ROOT::TMetaUtils::Error(0,"At line %s. Bad tag: %s\n", lineNumCharp, tagStrCharp);
492  out.ClearSelectionRules();
493  return false;
494  }
495 
496  if (!tagStr.empty()){
497  std::vector<Attributes> attrs;
498  std::string name;
499  ETagNames tagKind = GetNameOfTag(tagStr, name);
500  bool attrError = GetAttributes(tagStr, attrs, lineNumCharp);
501  if (!attrError) {
502  ROOT::TMetaUtils::Error(0,"Attribute at line %s. Bad tag: %s\n", lineNumCharp, tagStrCharp);
503  out.ClearSelectionRules();
504  return false;
505  }
506 
507  // after we have the name of the tag, we react according to the type of the tag
508  switch (tagKind){
509  case kInvalid:
510  {
511  ROOT::TMetaUtils::Error(0,"At line %s. Unrecognized name of tag %s\n", lineNumCharp, tagStrCharp);
512  out.ClearSelectionRules(); //Clear the selection rules up to now
513  return false;
514  }
515  case kClass:
516  {
517  if (inClass){
518  //this is an error
519  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
520  out.ClearSelectionRules();
521  return false;
522  }
523  if (!IsStandaloneTag(tagStr)){ // if the class tag is not standalone, then it has (probably) some child nodes
524  inClass = true;
525  }
526  csr.reset(new ClassSelectionRule(fCount++, fInterp, fileName.c_str(), lineNum)); // create new class selection rule
527  csr->SetRequestStreamerInfo(true);
528  bsr = csr.get(); // we could access it through the base class pointer
529  break;
530  }
531  case kEndClass:
532  {
533  if (inClass) { // if this is closing a parent class element, clear the parent information
534  inClass = false;
535  out.AddClassSelectionRule(*csr); // if we have a closing tag - we should write the class selection rule to the
536  // SelectionRules object; for standalone class tags we write the class sel rule at the end of the tag processing
537  }
538  else { // if we don't have parent information, it means that this closing tag doesn't have opening tag
539  ROOT::TMetaUtils::Error(0,"Single </class> tag at line %s",lineNumCharp);
540  out.ClearSelectionRules();
541  return false;
542  }
543  break;
544  }
545  case kVersion:
546  {
547  if (!inClass){
548  ROOT::TMetaUtils::Error(0,"Version tag not within class element at line %s",lineNumCharp);
549  out.ClearSelectionRules();
550  return false;
551  }
552  break;
553  }
554  case kBeginIoread:
555  case kBeginIoreadRaw:
556  {
557  inIoread = true;
558  // Try to see if we have CDATA to be put into the attributes
559  std::streampos initialPos(file.tellg());
560  const unsigned int lineCharsSize=1000;
561  char lineChars[lineCharsSize];
562  file.getline(lineChars,lineCharsSize);
563  std::string lineStr(lineChars);
564  // skip potential empty lines
565  while (lineStr == "" ||
566  std::count(lineStr.begin(),lineStr.end(),' ') == (int)lineStr.size()){
567  file.getline(lineChars,lineCharsSize);
568  lineStr=lineChars;
569  }
570  // look for the start of the data section
571  size_t dataBeginPos = lineStr.find("<![CDATA[");
572  if (dataBeginPos==std::string::npos){ // no data
573  file.seekg(initialPos);
574  break;
575  }
576 
577  // we put ourselves after the <![CDATA[
578  lineStr = lineStr.substr(dataBeginPos+9);
579 
580  // if we are here, we have data. Let's put it in a string which
581  // will become the code attribute
582  std::string codeAttrVal;
583  while(true){
584  // while loop done to read the data
585  // if we find ]]>, it means we are at the end of the data,
586  // we need to stop
587  size_t dataEndPos = lineStr.find("]]>");
588  if (dataEndPos!=std::string::npos) {
589  // add code that may be before the ]]>
590  codeAttrVal+=lineStr.substr(0,dataEndPos);
591  break;
592  }
593  codeAttrVal+=lineStr; // here because data can be on one line!
594  codeAttrVal+="\n";
595  file.getline(lineChars,lineCharsSize);
596  lineStr=lineChars;
597  }
598  attrs.emplace_back("code", codeAttrVal);
599  break;
600  }
601  case kEndIoread:
602  case kEndIoreadRaw:
603  {
604  if (!inIoread){
605  ROOT::TMetaUtils::Error(0,"Single </ioread> at line %s",lineNumCharp);
606  out.ClearSelectionRules();
607  return false;
608  }
609  inIoread = false;
610  break;
611  }
612  case kSelection:
613  {
614  if (inClass){
615  //this is an error
616  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
617  out.ClearSelectionRules();
618  return false;
619  }
620  sel = true; // we need both selection (indicates that we are in the selection section) and sel (indicates that
621  // we had an opening <selection> tag)
622  selection = true;
623  exclusion = false;
624  break;
625  }
626  case kEndSelection:
627  {
628  if (inClass){
629  //this is an error
630  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
631  out.ClearSelectionRules();
632  return false;
633  }
634  if (selection) { // if we had opening selection tag, everything is OK
635  selection = false;
636  selEnd = true;
637  }
638  else { // if not, this is a closing tag without an opening such
639  ROOT::TMetaUtils::Error(0,"At line %s. Missing <selection> tag", lineNumCharp);
640  out.ClearSelectionRules();
641  return false;
642  }
643  break;
644  }
645  case kExclusion:
646  {
647  if (inClass){
648  //this is an error
649  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
650  out.ClearSelectionRules();
651  return false;
652  }
653  excl = true; // we need both exclusion (indicates that we are in the exclusion section) and excl (indicates we had
654  // at a certain time an opening <exclusion> tag)
655  if (selection) { // if selection is true, we didn't have fEndSelection type of tag
656  ROOT::TMetaUtils::Error(0,"At line %s. Missing </selection> tag", lineNumCharp);
657  out.ClearSelectionRules();
658  return false;
659  }
660  exclusion=true;
661  break;
662  }
663  case kEndExclusion:
664  {
665  if (inClass){
666  //this is an error
667  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
668  out.ClearSelectionRules();
669  return false;
670  }
671  if (exclusion) { // if exclusion is Set, everything is OK
672  exclusion=false;
673  exclEnd = true;
674  }
675  else { // if not we have a closing </exclusion> tag without an opening <exclusion> tag
676  ROOT::TMetaUtils::Error(0,"At line %s. Missing <exclusion> tag", lineNumCharp);
677  out.ClearSelectionRules();
678  return false;
679  }
680  break;
681  }
682  case kField:
683  {
684  if (!inClass){ // if we have a <field>, <method> or <properties> tag outside a parent <clas>s tag,
685  //this is an error
686  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
687  out.ClearSelectionRules();
688  return false;
689  }
690  if (!IsStandaloneTag(tagStr)){
691  inField=true;
692  }
693  vsr.reset(new VariableSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum)); // the field is variable selection rule object
694  bsrChild = vsr.get();
695  break;
696  }
697  case kEndField:
698  {
699  if (!inField){
700  ROOT::TMetaUtils::Error(0,"At line %s. Closing field tag which was not opened\n", lineNumCharp);
701  out.ClearSelectionRules();
702  return false;
703  }
704  inField=false;
705  ROOT::TMetaUtils::Info(0,"At line %s. A field is not supposed to have an end-tag (this message will become a warning).\n", lineNumCharp);
706  break;
707  }
708  case kMethod:
709  {
710  if (!inClass){ // if we have a <field>, <method> or <properties> tag outside a parent <clas>s tag,
711  //this is an error
712  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
713  out.ClearSelectionRules();
714  return false;
715  }
716  if (!IsStandaloneTag(tagStr)){
717  inMethod=true;
718  }
719  fsr.reset(new FunctionSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum)); // the method is function selection rule object
720  bsrChild = fsr.get();
721  break;
722  }
723  case kEndMethod:
724  {
725  if (!inMethod){
726  ROOT::TMetaUtils::Error(0,"At line %s. Closing method tag which was not opened\n", lineNumCharp);
727  out.ClearSelectionRules();
728  return false;
729  }
730  inMethod=false;
731  ROOT::TMetaUtils::Info(0,"At line %s. A method is not supposed to have an end-tag (this message will become a warning).\n", lineNumCharp);
732  break;
733  }
734  case kProperties:
735  {
736  if (!inClass){ // if we have a <field>, <method> or <properties> tag outside a parent <clas>s tag,
737  //this is an error
738  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
739  out.ClearSelectionRules();
740  return false;
741  }
742  if (!IsStandaloneTag(tagStr)) {
743  ROOT::TMetaUtils::Error(0,"At line %s. Tag should be standalone\n", lineNumCharp);
744  out.ClearSelectionRules();
745  return false;
746  }
747  // we don't create separate selection object for properties - we include them as attribute-value pairs for the class
748  break;
749  }
750  case kFunction:
751  {
752  if (inClass){
753  //this is an error
754  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
755  out.ClearSelectionRules();
756  return false;
757  }
758  fsr.reset(new FunctionSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum));
759  bsr = fsr.get();
760  break;
761  }
762  case kVariable:
763  {
764  if (inClass){
765  //this is an error
766  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
767  out.ClearSelectionRules();
768  return false;
769  }
770  vsr.reset(new VariableSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum));
771  bsr = vsr.get();
772  break;
773  }
774  case kTypedef:
775  {
776  if (inClass){
777  //this is an error
778  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
779  out.ClearSelectionRules();
780  return false;
781  }
782  csr.reset(new ClassSelectionRule(fCount++, fInterp));
783  attrs.emplace_back("fromTypedef", "true");
784  bsr = csr.get();
785  break;
786  }
787  case kEnum:
788  {
789  if (inClass){
790  //this is an error
791  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
792  out.ClearSelectionRules();
793  return false;
794  }
795  esr.reset(new EnumSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum));
796  bsr = esr.get();
797  break;
798  }
799  case kLcgdict:
800  {}
801  case kEndLcgdict:
802  {
803  if (inClass){
804  //this is an error
805  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
806  out.ClearSelectionRules();
807  return false;
808  }
809  break;
810  }
811  default: ROOT::TMetaUtils::Error(0,"Unknown tag name: %s \n",tagStrCharp);
812  }
813 
814 
815  // Take care of ioread rules
816  if (tagKind == kBeginIoread || tagKind == kBeginIoreadRaw){
817  // A first sanity check
818  if (attrs.empty()){
819  ROOT::TMetaUtils::Error(0,"At line %s. ioread element has no attributes.\n",lineNumCharp);
820  return false;
821  }
822  // Loop over the attrs to get the info to build the linkdef-like string
823  // Cache the name and the value
824  std::string iAttrName;
825  std::string iAttrValue;
826  // save attributes in a map to then format the new line which is of the form
827  // #pragma read sourceClass="class1" targetClass="class2" version="[1-]" source="" target="transient_" code="{ newObj->initializeTransientss(); }";
828  // where "#pragma read" should not appear
829  // The check for the sanity of the pragma is delegated to the ProcessReadPragma routine
830 
831  std::map<std::string,std::string> pragmaArgs;
832  for (int i = 0, n = attrs.size(); i < n; ++i) {
833  pragmaArgs[attrs[i].fName]=attrs[i].fValue;
834  }
835 
836  std::stringstream pragmaLineStream;
837  const std::string attrs[11] ={"sourceClass",
838  "version",
839  "targetClass",
840  "target",
841  "targetType",
842  "source",
843  "code",
844  "checksum",
845  "embed",
846  "include",
847  "attributes"};
848  std::string value;
849  for (unsigned int i=0;i<11;++i) {
850  const std::string& attr = attrs[i];
851  if ( pragmaArgs.count(attr) == 1){
852  value = pragmaArgs[attr];
853  if (attr == "code") value= "{"+value+"}";
854  pragmaLineStream << " " << attr << "=\""<< value << "\"";
855  }
856  }
857 
858  // Now send them to the pragma processor. The info will be put
859  // in a global then read by the TMetaUtils
860  ROOT::TMetaUtils::Info(0,"Pragma generated for ioread rule: %s\n", pragmaLineStream.str().c_str());
861  std::string error_string;
862  if (tagKind == kBeginIoread)
863  ROOT::ProcessReadPragma( pragmaLineStream.str().c_str(), error_string );
864  else // this is a raw rule
865  ROOT::ProcessReadRawPragma( pragmaLineStream.str().c_str(), error_string );
866  if (!error_string.empty())
867  ROOT::TMetaUtils::Error(0, "%s", error_string.c_str());
868  continue; // no need to go further
869  } // end of ioread rules
870 
871 
872  // We do not want to propagate in the meta the values in the
873  // version tag
874  if (!tagStr.empty() && tagKind != kVersion) {
875 
876  if (!exclusion && !IsClosingTag(tagStr)) { // exclusion should be false, we are not interested in closing tags
877  // as well as in key-tags such as <selection> and <lcgdict>
878  if (tagKind == kLcgdict || tagKind == kSelection)
879  ;// DEBUG std::cout<<"Don't care (don't create sel rule)"<<std::endl;
880  else {
881  // DEBUG std::cout<<"Yes"<<std::endl;
882  if (tagKind == kField || tagKind == kMethod) bsrChild->SetSelected(BaseSelectionRule::kYes); // if kMethod or kField - add to child
883  else bsr->SetSelected(BaseSelectionRule::kYes);
884  }
885  }
886  else { // if exclusion = true
887  if (IsStandaloneTag(tagStr)){
888  // DEBUG std::cout<<"No"<<std::endl;
889  if (tagKind == kField || tagKind == kMethod) bsrChild->SetSelected(BaseSelectionRule::kNo);
890  else bsr->SetSelected(BaseSelectionRule::kNo);
891  }
892  else if (tagKind == kClass) {
893  // DEBUG std::cout<<"Don't care (create sel rule)"<<std::endl; // if it is not a standalone tag,
894  //this means it is a parent class tag
895  // In that case we don't care about the class, but we do care about the children, for which the selection
896  // rule should be No. So for the parent class it is - Don't care; for the children it is No
897  bsr->SetSelected(BaseSelectionRule::kDontCare); // this is for the parent
898  }
899  // DEBUG else std::cout<<"Don't care (don't create sel rule)"<<std::endl;
900  }
901 
902 // // DEBUG std::cout<<"Is child: ";
903 // if (inClass){
904 // if (((tagKind == kClass)) || tagKind == kEndClass) // if this is the same tag as the parent
905 // // or it is a closing tag, the tag is not a child
906 // ;// DEBUG std::cout<<"No"<<std::endl;
907 // // else if tagKind is one of the following, it means that we have a missing </class> tag
908 // // because these tag kinds cannot be children for a parent <class> tag
909 // else if (tagKind == kClass || tagKind == kEnum || tagKind == kVariable || tagKind == kFunction ||
910 // tagKind == kEndSelection || tagKind == kExclusion || tagKind == kEndExclusion){
911 // ROOT::TMetaUtils::Error(0,"XML at line %s. Missing </class> tag\n",lineNumCharp);
912 // out.ClearSelectionRules();
913 // return false;
914 // }
915 // // DEBUG else std::cout<<"Yes"<<std::endl;
916 // }
917 // // DEBUG else std::cout<<"No"<<std::endl;
918 
919 
920  if (!attrs.empty()){
921  // Cache the name and the value
922  std::string iAttrName;
923  std::string iAttrValue;
924  for (int i = 0, n = attrs.size(); i < n; ++i) {
925  iAttrName=attrs[i].fName;
926  iAttrValue=attrs[i].fValue;
927 
928  // request no streamer
929  if (tagKind == kClass && csr && "noStreamer" == iAttrName){
930  if (iAttrValue == "true") {
931  csr->SetRequestNoStreamer(true);
932  } else if (iAttrValue != "false") {
933  ROOT::TMetaUtils::Error(0,
934  "XML at line %s: class attribute 'noStreamer' must be 'true' or 'false' (it was %s)\n",
935  lineNumCharp, iAttrValue.c_str());
936  }
937  }
938 
939  // request no input operator
940  if (tagKind == kClass && csr && "noInputOperator" == iAttrName){
941  if (iAttrValue == "true") {
942  csr->SetRequestNoInputOperator(true);
943  } else if (iAttrValue != "false") {
944  ROOT::TMetaUtils::Error(0,
945  "XML at line %s: class attribute 'noInputOperator' must be 'true' or 'false' (it was %s)\n",
946  lineNumCharp, iAttrValue.c_str());
947  }
948  }
949 
950  // Set the class version
951  if (tagKind == kClass &&
952  csr &&
953  "ClassVersion" == iAttrName){
954  csr->SetRequestedVersionNumber(atoi(iAttrValue.c_str()));
955  continue;
956  }
957 
958  if (tagKind == kClass ||
959  tagKind == kTypedef ||
960  tagKind == kProperties ||
961  tagKind == kEnum ||
962  tagKind == kFunction ||
963  tagKind == kVariable) {
964  if (bsr->HasAttributeWithName(iAttrName)) {
965  std::string preExistingValue;
966  bsr->GetAttributeValue(iAttrName,preExistingValue);
967  if (preExistingValue!=iAttrValue){ // If different from before
968  ROOT::TMetaUtils::Error(0,
969  "Line %s: assigning new value %s to attribue %s (it was %s)\n",
970  lineNumCharp,iAttrValue.c_str(),iAttrName.c_str(),preExistingValue.c_str());
971  out.ClearSelectionRules();
972  return false;
973  }
974  }
975  bsr->SetAttributeValue(iAttrName, iAttrValue);
976  if ((iAttrName == "file_name" || iAttrName == "file_pattern") && tagKind == kClass){
977  bsr->SetAttributeValue("pattern","*");
978  out.SetHasFileNameRule(true);
979  }
980  }
981  else {
982  if (bsrChild->HasAttributeWithName(iAttrName)) {
983  std::string preExistingValue;
984  bsrChild->GetAttributeValue(iAttrName,preExistingValue);
985  if (preExistingValue!=iAttrValue){ // If different from before
986  ROOT::TMetaUtils::Error(0,
987  "Line %s: assigning new value %s to attribue %s (it was %s)\n",
988  lineNumCharp,iAttrValue.c_str(),iAttrName.c_str(),preExistingValue.c_str());
989  out.ClearSelectionRules();
990  return false;
991  }
992  }
993  bsrChild->SetAttributeValue(iAttrName, iAttrValue);
994  }
995  }
996  }
997  }
998 
999  // add selection rule to the SelectionRules object
1000  // if field or method - add to the class selection rule object
1001  // if parent class, don't add here, add when kEndClass is reached
1002  switch(tagKind) {
1003  case kClass:
1004  if (!inClass) out.AddClassSelectionRule(*csr);
1005  break;
1006  case kTypedef:
1007  out.AddClassSelectionRule(*csr);
1008  break;
1009  case kFunction:
1010  out.AddFunctionSelectionRule(*fsr);
1011  break;
1012  case kVariable:
1013  out.AddVariableSelectionRule(*vsr);
1014  break;
1015  case kEnum:
1016  out.AddEnumSelectionRule(*esr);
1017  break;
1018  case kField:
1019  csr->AddFieldSelectionRule(*vsr);
1020  break;
1021  case kMethod:
1022  csr->AddMethodSelectionRule(*fsr);
1023  break;
1024  default:
1025  break;
1026  }
1027  }
1028  }
1029  // we are outside of the while cycle which means that we have read the whole XML document
1030 
1031  if (sel && !selEnd) { // if selEnd is true, it menas that we never had a closing </selection> tag
1032  ROOT::TMetaUtils::Error(0,"Error - missing </selection> tag\n");
1033  out.ClearSelectionRules();
1034  return false;
1035  }
1036  if (excl && !exclEnd ) { // if excl is true and exclEnd is false, it means that we had an opening <exclusion> tag but we
1037  // never had the closing </exclusion> tag
1038  ROOT::TMetaUtils::Error(0,"Error - missing </selection> tag\n");
1039  out.ClearSelectionRules();
1040  return false;
1041  }
1042  return true;
1043 
1044 }