Logo ROOT   6.08/07
Reference Guide
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 "TMetaUtils.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  */
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  if (tagKind == kBeginIoread)
862  ROOT::ProcessReadPragma( pragmaLineStream.str().c_str() );
863  else // this is a raw rule
864  ROOT::ProcessReadRawPragma( pragmaLineStream.str().c_str() );
865  continue; // no need to go further
866  } // end of ioread rules
867 
868 
869  // We do not want to propagate in the meta the values in the
870  // version tag
871  if (!tagStr.empty() && tagKind != kVersion) {
872 
873  if (!exclusion && !IsClosingTag(tagStr)) { // exclusion should be false, we are not interested in closing tags
874  // as well as in key-tags such as <selection> and <lcgdict>
875  if (tagKind == kLcgdict || tagKind == kSelection)
876  ;// DEBUG std::cout<<"Don't care (don't create sel rule)"<<std::endl;
877  else {
878  // DEBUG std::cout<<"Yes"<<std::endl;
879  if (tagKind == kField || tagKind == kMethod) bsrChild->SetSelected(BaseSelectionRule::kYes); // if kMethod or kField - add to child
881  }
882  }
883  else { // if exclusion = true
884  if (IsStandaloneTag(tagStr)){
885  // DEBUG std::cout<<"No"<<std::endl;
886  if (tagKind == kField || tagKind == kMethod) bsrChild->SetSelected(BaseSelectionRule::kNo);
888  }
889  else if (tagKind == kClass) {
890  // DEBUG std::cout<<"Don't care (create sel rule)"<<std::endl; // if it is not a standalone tag,
891  //this means it is a parent class tag
892  // In that case we don't care about the class, but we do care about the children, for which the selection
893  // rule should be No. So for the parent class it is - Don't care; for the children it is No
894  bsr->SetSelected(BaseSelectionRule::kDontCare); // this is for the parent
895  }
896  // DEBUG else std::cout<<"Don't care (don't create sel rule)"<<std::endl;
897  }
898 
899 // // DEBUG std::cout<<"Is child: ";
900 // if (inClass){
901 // if (((tagKind == kClass)) || tagKind == kEndClass) // if this is the same tag as the parent
902 // // or it is a closing tag, the tag is not a child
903 // ;// DEBUG std::cout<<"No"<<std::endl;
904 // // else if tagKind is one of the following, it means that we have a missing </class> tag
905 // // because these tag kinds cannot be children for a parent <class> tag
906 // else if (tagKind == kClass || tagKind == kEnum || tagKind == kVariable || tagKind == kFunction ||
907 // tagKind == kEndSelection || tagKind == kExclusion || tagKind == kEndExclusion){
908 // ROOT::TMetaUtils::Error(0,"XML at line %s. Missing </class> tag\n",lineNumCharp);
909 // out.ClearSelectionRules();
910 // return false;
911 // }
912 // // DEBUG else std::cout<<"Yes"<<std::endl;
913 // }
914 // // DEBUG else std::cout<<"No"<<std::endl;
915 
916 
917  if (!attrs.empty()){
918  // Cache the name and the value
919  std::string iAttrName;
920  std::string iAttrValue;
921  for (int i = 0, n = attrs.size(); i < n; ++i) {
922  iAttrName=attrs[i].fName;
923  iAttrValue=attrs[i].fValue;
924  // Set the class version
925  if (tagKind == kClass &&
926  csr &&
927  "ClassVersion" == iAttrName){
928  csr->SetRequestedVersionNumber(atoi(iAttrValue.c_str()));
929  continue;
930  }
931 
932  if (tagKind == kClass ||
933  tagKind == kTypedef ||
934  tagKind == kProperties ||
935  tagKind == kEnum ||
936  tagKind == kFunction ||
937  tagKind == kVariable) {
938  if (bsr->HasAttributeWithName(iAttrName)) {
939  std::string preExistingValue;
940  bsr->GetAttributeValue(iAttrName,preExistingValue);
941  if (preExistingValue!=iAttrValue){ // If different from before
943  "Line %s: assigning new value %s to attribue %s (it was %s)\n",
944  lineNumCharp,iAttrValue.c_str(),iAttrName.c_str(),preExistingValue.c_str());
945  out.ClearSelectionRules();
946  return false;
947  }
948  }
949  bsr->SetAttributeValue(iAttrName, iAttrValue);
950  if ((iAttrName == "file_name" || iAttrName == "file_pattern") && tagKind == kClass){
951  bsr->SetAttributeValue("pattern","*");
952  out.SetHasFileNameRule(true);
953  }
954  }
955  else {
956  if (bsrChild->HasAttributeWithName(iAttrName)) {
957  std::string preExistingValue;
958  bsrChild->GetAttributeValue(iAttrName,preExistingValue);
959  if (preExistingValue!=iAttrValue){ // If different from before
961  "Line %s: assigning new value %s to attribue %s (it was %s)\n",
962  lineNumCharp,iAttrValue.c_str(),iAttrName.c_str(),preExistingValue.c_str());
963  out.ClearSelectionRules();
964  return false;
965  }
966  }
967  bsrChild->SetAttributeValue(iAttrName, iAttrValue);
968  }
969  }
970  }
971  }
972 
973  // add selection rule to the SelectionRules object
974  // if field or method - add to the class selection rule object
975  // if parent class, don't add here, add when kEndClass is reached
976  switch(tagKind) {
977  case kClass:
978  if (!inClass) out.AddClassSelectionRule(*csr);
979  break;
980  case kTypedef:
981  out.AddClassSelectionRule(*csr);
982  break;
983  case kFunction:
984  out.AddFunctionSelectionRule(*fsr);
985  break;
986  case kVariable:
987  out.AddVariableSelectionRule(*vsr);
988  break;
989  case kEnum:
990  out.AddEnumSelectionRule(*esr);
991  break;
992  case kField:
993  csr->AddFieldSelectionRule(*vsr);
994  break;
995  case kMethod:
996  csr->AddMethodSelectionRule(*fsr);
997  break;
998  default:
999  break;
1000  }
1001  }
1002  }
1003  // we are outside of the while cycle which means that we have read the whole XML document
1004 
1005  if (sel && !selEnd) { // if selEnd is true, it menas that we never had a closing </selection> tag
1006  ROOT::TMetaUtils::Error(0,"Error - missing </selection> tag\n");
1007  out.ClearSelectionRules();
1008  return false;
1009  }
1010  if (excl && !exclEnd ) { // if excl is true and exclEnd is false, it means that we had an opening <exclusion> tag but we
1011  // never had the closing </exclusion> tag
1012  ROOT::TMetaUtils::Error(0,"Error - missing </selection> tag\n");
1013  out.ClearSelectionRules();
1014  return false;
1015  }
1016  return true;
1017 
1018 }
VariableSelectionRule EnumSelectionRule
return c
static const std::string comment("comment")
void Error(const char *location, const char *va_(fmt),...)
Definition: TMetaUtils.h:767
long fCount
Definition: XMLReader.h:46
void SetRequestStreamerInfo(bool needStreamerInfo)
static bool GetAttributes(const std::string &tag, std::vector< Attributes > &out, const char *lineNum)
Definition: XMLReader.cxx:321
static bool GetNextTag(std::ifstream &file, std::string &out, int &lineCount)
Definition: XMLReader.cxx:72
void SetHasFileNameRule(bool file_rule)
static std::map< std::string, ETagNames > fgMapTagNames
Definition: XMLReader.h:84
static bool IsStandaloneTag(const std::string &tag)
Definition: XMLReader.cxx:275
void ClearSelectionRules()
static const std::string pattern("pattern")
void AddFunctionSelectionRule(const FunctionSelectionRule &funcSel)
static void PopulateMap()
Definition: XMLReader.cxx:29
The class representing the collection of selection rules.
void Info(const char *location, const char *va_(fmt),...)
Definition: TMetaUtils.h:787
static bool IsClosingTag(const std::string &tag)
Definition: XMLReader.cxx:285
void AddMethodSelectionRule(const FunctionSelectionRule &method)
void AddFieldSelectionRule(const VariableSelectionRule &field)
void SetRequestedVersionNumber(int version)
void AddVariableSelectionRule(const VariableSelectionRule &varSel)
bool GetAttributeValue(const std::string &attributeName, std::string &returnValue) const
void ProcessReadRawPragma(const char *args)
I am being called then a readraw pragma is encountered.
void ProcessReadPragma(const char *args)
I am being called when a read pragma is encountered.
void SetSelected(ESelect sel)
you should not use this method at all Int_t Int_t Double_t Double_t Double_t e
Definition: TRolke.cxx:630
void Warning(const char *location, const char *va_(fmt),...)
Definition: TMetaUtils.h:797
void AddClassSelectionRule(const ClassSelectionRule &classSel)
void AddEnumSelectionRule(const EnumSelectionRule &enumSel)
VariableSelectionRule FunctionSelectionRule
void SetAttributeValue(const std::string &attributeName, const std::string &attributeValue)
static bool CheckIsTagOK(const std::string &tag)
Definition: XMLReader.cxx:209
Definition: file.py:1
static ETagNames GetNameOfTag(const std::string &tag, std::string &name)
Definition: XMLReader.cxx:296
bool HasAttributeWithName(const std::string &attributeName) const
cling::Interpreter & fInterp
Definition: XMLReader.h:47
bool Parse(const std::string &fileName, SelectionRules &out)
Definition: XMLReader.cxx:453
const Int_t n
Definition: legend1.C:16
char name[80]
Definition: TGX11.cxx:109