ROOT  6.06/09
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  char 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  char charMinus1= '@';
85  char charMinus2= '@';
86  char 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  bsr = csr.get(); // we could access it through the base class pointer
528  break;
529  }
530  case kEndClass:
531  {
532  if (inClass) { // if this is closing a parent class element, clear the parent information
533  inClass = false;
534  out.AddClassSelectionRule(*csr); // if we have a closing tag - we should write the class selection rule to the
535  // SelectionRules object; for standalone class tags we write the class sel rule at the end of the tag processing
536  }
537  else { // if we don't have parent information, it means that this closing tag doesn't have opening tag
538  ROOT::TMetaUtils::Error(0,"Single </class> tag at line %s",lineNumCharp);
539  out.ClearSelectionRules();
540  return false;
541  }
542  break;
543  }
544  case kVersion:
545  {
546  if (!inClass){
547  ROOT::TMetaUtils::Error(0,"Version tag not within class element at line %s",lineNumCharp);
548  out.ClearSelectionRules();
549  return false;
550  }
551  break;
552  }
553  case kBeginIoread:
554  case kBeginIoreadRaw:
555  {
556  inIoread = true;
557  // Try to see if we have CDATA to be put into the attributes
558  std::streampos initialPos(file.tellg());
559  const unsigned int lineCharsSize=1000;
560  char lineChars[lineCharsSize];
561  file.getline(lineChars,lineCharsSize);
562  std::string lineStr(lineChars);
563  // skip potential empty lines
564  while (lineStr == "" ||
565  std::count(lineStr.begin(),lineStr.end(),' ') == (int)lineStr.size()){
566  file.getline(lineChars,lineCharsSize);
567  lineStr=lineChars;
568  }
569  // look for the start of the data section
570  size_t dataBeginPos = lineStr.find("<![CDATA[");
571  if (dataBeginPos==std::string::npos){ // no data
572  file.seekg(initialPos);
573  break;
574  }
575 
576  // we put ourselves after the <![CDATA[
577  lineStr = lineStr.substr(dataBeginPos+9);
578 
579  // if we are here, we have data. Let's put it in a string which
580  // will become the code attribute
581  std::string codeAttrVal;
582  while(true){
583  // while loop done to read the data
584  // if we find ]]>, it means we are at the end of the data,
585  // we need to stop
586  size_t dataEndPos = lineStr.find("]]>");
587  if (dataEndPos!=std::string::npos) {
588  // add code that may be before the ]]>
589  codeAttrVal+=lineStr.substr(0,dataEndPos);
590  break;
591  }
592  codeAttrVal+=lineStr; // here because data can be on one line!
593  codeAttrVal+="\n";
594  file.getline(lineChars,lineCharsSize);
595  lineStr=lineChars;
596  }
597  attrs.emplace_back("code", codeAttrVal);
598  break;
599  }
600  case kEndIoread:
601  case kEndIoreadRaw:
602  {
603  if (!inIoread){
604  ROOT::TMetaUtils::Error(0,"Single </ioread> at line %s",lineNumCharp);
605  out.ClearSelectionRules();
606  return false;
607  }
608  inIoread = false;
609  break;
610  }
611  case kSelection:
612  {
613  if (inClass){
614  //this is an error
615  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
616  out.ClearSelectionRules();
617  return false;
618  }
619  sel = true; // we need both selection (indicates that we are in the selection section) and sel (indicates that
620  // we had an opening <selection> tag)
621  selection = true;
622  exclusion = false;
623  break;
624  }
625  case kEndSelection:
626  {
627  if (inClass){
628  //this is an error
629  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
630  out.ClearSelectionRules();
631  return false;
632  }
633  if (selection) { // if we had opening selection tag, everything is OK
634  selection = false;
635  selEnd = true;
636  }
637  else { // if not, this is a closing tag without an opening such
638  ROOT::TMetaUtils::Error(0,"At line %s. Missing <selection> tag", lineNumCharp);
639  out.ClearSelectionRules();
640  return false;
641  }
642  break;
643  }
644  case kExclusion:
645  {
646  if (inClass){
647  //this is an error
648  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
649  out.ClearSelectionRules();
650  return false;
651  }
652  excl = true; // we need both exclusion (indicates that we are in the exclusion section) and excl (indicates we had
653  // at a certain time an opening <exclusion> tag)
654  if (selection) { // if selection is true, we didn't have fEndSelection type of tag
655  ROOT::TMetaUtils::Error(0,"At line %s. Missing </selection> tag", lineNumCharp);
656  out.ClearSelectionRules();
657  return false;
658  }
659  exclusion=true;
660  break;
661  }
662  case kEndExclusion:
663  {
664  if (inClass){
665  //this is an error
666  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
667  out.ClearSelectionRules();
668  return false;
669  }
670  if (exclusion) { // if exclusion is Set, everything is OK
671  exclusion=false;
672  exclEnd = true;
673  }
674  else { // if not we have a closing </exclusion> tag without an opening <exclusion> tag
675  ROOT::TMetaUtils::Error(0,"At line %s. Missing <exclusion> tag", lineNumCharp);
676  out.ClearSelectionRules();
677  return false;
678  }
679  break;
680  }
681  case kField:
682  {
683  if (!inClass){ // if we have a <field>, <method> or <properties> tag outside a parent <clas>s tag,
684  //this is an error
685  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
686  out.ClearSelectionRules();
687  return false;
688  }
689  if (!IsStandaloneTag(tagStr)){
690  inField=true;
691  }
692  vsr.reset(new VariableSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum)); // the field is variable selection rule object
693  bsrChild = vsr.get();
694  break;
695  }
696  case kEndField:
697  {
698  if (!inField){
699  ROOT::TMetaUtils::Error(0,"At line %s. Closing field tag which was not opened\n", lineNumCharp);
700  out.ClearSelectionRules();
701  return false;
702  }
703  inField=false;
704  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);
705  break;
706  }
707  case kMethod:
708  {
709  if (!inClass){ // if we have a <field>, <method> or <properties> tag outside a parent <clas>s tag,
710  //this is an error
711  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
712  out.ClearSelectionRules();
713  return false;
714  }
715  if (!IsStandaloneTag(tagStr)){
716  inMethod=true;
717  }
718  fsr.reset(new FunctionSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum)); // the method is function selection rule object
719  bsrChild = fsr.get();
720  break;
721  }
722  case kEndMethod:
723  {
724  if (!inMethod){
725  ROOT::TMetaUtils::Error(0,"At line %s. Closing method tag which was not opened\n", lineNumCharp);
726  out.ClearSelectionRules();
727  return false;
728  }
729  inMethod=false;
730  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);
731  break;
732  }
733  case kProperties:
734  {
735  if (!inClass){ // if we have a <field>, <method> or <properties> tag outside a parent <clas>s tag,
736  //this is an error
737  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
738  out.ClearSelectionRules();
739  return false;
740  }
741  if (!IsStandaloneTag(tagStr)) {
742  ROOT::TMetaUtils::Error(0,"At line %s. Tag should be standalone\n", lineNumCharp);
743  out.ClearSelectionRules();
744  return false;
745  }
746  // we don't create separate selection object for properties - we include them as attribute-value pairs for the class
747  break;
748  }
749  case kFunction:
750  {
751  if (inClass){
752  //this is an error
753  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
754  out.ClearSelectionRules();
755  return false;
756  }
757  fsr.reset(new FunctionSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum));
758  bsr = fsr.get();
759  break;
760  }
761  case kVariable:
762  {
763  if (inClass){
764  //this is an error
765  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
766  out.ClearSelectionRules();
767  return false;
768  }
769  vsr.reset(new VariableSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum));
770  bsr = vsr.get();
771  break;
772  }
773  case kTypedef:
774  {
775  if (inClass){
776  //this is an error
777  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
778  out.ClearSelectionRules();
779  return false;
780  }
781  csr.reset(new ClassSelectionRule(fCount++, fInterp));
782  attrs.emplace_back("fromTypedef", "true");
783  bsr = csr.get();
784  break;
785  }
786  case kEnum:
787  {
788  if (inClass){
789  //this is an error
790  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
791  out.ClearSelectionRules();
792  return false;
793  }
794  esr.reset(new EnumSelectionRule(fCount++, fInterp,fileName.c_str(), lineNum));
795  bsr = esr.get();
796  break;
797  }
798  case kLcgdict:
799  {}
800  case kEndLcgdict:
801  {
802  if (inClass){
803  //this is an error
804  ROOT::TMetaUtils::Error(0,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
805  out.ClearSelectionRules();
806  return false;
807  }
808  break;
809  }
810  default: ROOT::TMetaUtils::Error(0,"Unknown tag name: %s \n",tagStrCharp);
811  }
812 
813 
814  // Take care of ioread rules
815  if (tagKind == kBeginIoread || tagKind == kBeginIoreadRaw){
816  // A first sanity check
817  if (attrs.empty()){
818  ROOT::TMetaUtils::Error(0,"At line %s. ioread element has no attributes.\n",lineNumCharp);
819  return false;
820  }
821  // Loop over the attrs to get the info to build the linkdef-like string
822  // Cache the name and the value
823  std::string iAttrName;
824  std::string iAttrValue;
825  // save attributes in a map to then format the new line which is of the form
826  // #pragma read sourceClass="class1" targetClass="class2" version="[1-]" source="" target="transient_" code="{ newObj->initializeTransientss(); }";
827  // where "#pragma read" should not appear
828  // The check for the sanity of the pragma is delegated to the ProcessReadPragma routine
829 
830  std::map<std::string,std::string> pragmaArgs;
831  for (int i = 0, n = attrs.size(); i < n; ++i) {
832  pragmaArgs[attrs[i].fName]=attrs[i].fValue;
833  }
834 
835  std::stringstream pragmaLineStream;
836  const std::string attrs[11] ={"sourceClass",
837  "version",
838  "targetClass",
839  "target",
840  "targetType",
841  "source",
842  "code",
843  "checksum",
844  "embed",
845  "include",
846  "attributes"};
847  std::string value;
848  for (unsigned int i=0;i<11;++i) {
849  const std::string& attr = attrs[i];
850  if ( pragmaArgs.count(attr) == 1){
851  value = pragmaArgs[attr];
852  if (attr == "code") value= "{"+value+"}";
853  pragmaLineStream << " " << attr << "=\""<< value << "\"";
854  }
855  }
856 
857  // Now send them to the pragma processor. The info will be put
858  // in a global then read by the TMetaUtils
859  ROOT::TMetaUtils::Info(0,"Pragma generated for ioread rule: %s\n", pragmaLineStream.str().c_str());
860  if (tagKind == kBeginIoread)
861  ROOT::ProcessReadPragma( pragmaLineStream.str().c_str() );
862  else // this is a raw rule
863  ROOT::ProcessReadRawPragma( pragmaLineStream.str().c_str() );
864  continue; // no need to go further
865  } // end of ioread rules
866 
867 
868  // We do not want to propagate in the meta the values in the
869  // version tag
870  if (!tagStr.empty() && tagKind != kVersion) {
871 
872  if (!exclusion && !IsClosingTag(tagStr)) { // exclusion should be false, we are not interested in closing tags
873  // as well as in key-tags such as <selection> and <lcgdict>
874  if (tagKind == kLcgdict || tagKind == kSelection)
875  ;// DEBUG std::cout<<"Don't care (don't create sel rule)"<<std::endl;
876  else {
877  // DEBUG std::cout<<"Yes"<<std::endl;
878  if (tagKind == kField || tagKind == kMethod) bsrChild->SetSelected(BaseSelectionRule::kYes); // if kMethod or kField - add to child
880  }
881  }
882  else { // if exclusion = true
883  if (IsStandaloneTag(tagStr)){
884  // DEBUG std::cout<<"No"<<std::endl;
885  if (tagKind == kField || tagKind == kMethod) bsrChild->SetSelected(BaseSelectionRule::kNo);
887  }
888  else if (tagKind == kClass) {
889  // DEBUG std::cout<<"Don't care (create sel rule)"<<std::endl; // if it is not a standalone tag,
890  //this means it is a parent class tag
891  // In that case we don't care about the class, but we do care about the children, for which the selection
892  // rule should be No. So for the parent class it is - Don't care; for the children it is No
893  bsr->SetSelected(BaseSelectionRule::kDontCare); // this is for the parent
894  }
895  // DEBUG else std::cout<<"Don't care (don't create sel rule)"<<std::endl;
896  }
897 
898 // // DEBUG std::cout<<"Is child: ";
899 // if (inClass){
900 // if (((tagKind == kClass)) || tagKind == kEndClass) // if this is the same tag as the parent
901 // // or it is a closing tag, the tag is not a child
902 // ;// DEBUG std::cout<<"No"<<std::endl;
903 // // else if tagKind is one of the following, it means that we have a missing </class> tag
904 // // because these tag kinds cannot be children for a parent <class> tag
905 // else if (tagKind == kClass || tagKind == kEnum || tagKind == kVariable || tagKind == kFunction ||
906 // tagKind == kEndSelection || tagKind == kExclusion || tagKind == kEndExclusion){
907 // ROOT::TMetaUtils::Error(0,"XML at line %s. Missing </class> tag\n",lineNumCharp);
908 // out.ClearSelectionRules();
909 // return false;
910 // }
911 // // DEBUG else std::cout<<"Yes"<<std::endl;
912 // }
913 // // DEBUG else std::cout<<"No"<<std::endl;
914 
915 
916  if (!attrs.empty()){
917  // Cache the name and the value
918  std::string iAttrName;
919  std::string iAttrValue;
920  for (int i = 0, n = attrs.size(); i < n; ++i) {
921  iAttrName=attrs[i].fName;
922  iAttrValue=attrs[i].fValue;
923  // Set the class version
924  if (tagKind == kClass &&
925  csr &&
926  "ClassVersion" == iAttrName){
927  csr->SetRequestedVersionNumber(atoi(iAttrValue.c_str()));
928  continue;
929  }
930 
931  if (tagKind == kClass ||
932  tagKind == kTypedef ||
933  tagKind == kProperties ||
934  tagKind == kEnum ||
935  tagKind == kFunction ||
936  tagKind == kVariable) {
937  if (bsr->HasAttributeWithName(iAttrName)) {
938  std::string preExistingValue;
939  bsr->GetAttributeValue(iAttrName,preExistingValue);
940  if (preExistingValue!=iAttrValue){ // If different from before
942  "Line %s: assigning new value %s to attribue %s (it was %s)\n",
943  lineNumCharp,iAttrValue.c_str(),iAttrName.c_str(),preExistingValue.c_str());
944  out.ClearSelectionRules();
945  return false;
946  }
947  }
948  bsr->SetAttributeValue(iAttrName, iAttrValue);
949  if ((iAttrName == "file_name" || iAttrName == "file_pattern") && tagKind == kClass){
950  bsr->SetAttributeValue("pattern","*");
951  out.SetHasFileNameRule(true);
952  }
953  }
954  else {
955  if (bsrChild->HasAttributeWithName(iAttrName)) {
956  std::string preExistingValue;
957  bsrChild->GetAttributeValue(iAttrName,preExistingValue);
958  if (preExistingValue!=iAttrValue){ // If different from before
960  "Line %s: assigning new value %s to attribue %s (it was %s)\n",
961  lineNumCharp,iAttrValue.c_str(),iAttrName.c_str(),preExistingValue.c_str());
962  out.ClearSelectionRules();
963  return false;
964  }
965  }
966  bsrChild->SetAttributeValue(iAttrName, iAttrValue);
967  }
968  }
969  }
970  }
971 
972  // add selection rule to the SelectionRules object
973  // if field or method - add to the class selection rule object
974  // if parent class, don't add here, add when kEndClass is reached
975  switch(tagKind) {
976  case kClass:
977  if (!inClass) out.AddClassSelectionRule(*csr);
978  break;
979  case kTypedef:
980  out.AddClassSelectionRule(*csr);
981  break;
982  case kFunction:
983  out.AddFunctionSelectionRule(*fsr);
984  break;
985  case kVariable:
986  out.AddVariableSelectionRule(*vsr);
987  break;
988  case kEnum:
989  out.AddEnumSelectionRule(*esr);
990  break;
991  case kField:
992  csr->AddFieldSelectionRule(*vsr);
993  break;
994  case kMethod:
995  csr->AddMethodSelectionRule(*fsr);
996  break;
997  default:
998  break;
999  }
1000  }
1001  }
1002  // we are outside of the while cycle which means that we have read the whole XML document
1003 
1004  if (sel && !selEnd) { // if selEnd is true, it menas that we never had a closing </selection> tag
1005  ROOT::TMetaUtils::Error(0,"Error - missing </selection> tag\n");
1006  out.ClearSelectionRules();
1007  return false;
1008  }
1009  if (excl && !exclEnd ) { // if excl is true and exclEnd is false, it means that we had an opening <exclusion> tag but we
1010  // never had the closing </exclusion> tag
1011  ROOT::TMetaUtils::Error(0,"Error - missing </selection> tag\n");
1012  out.ClearSelectionRules();
1013  return false;
1014  }
1015  return true;
1016 
1017 }
bool GetAttributeValue(const std::string &attributeName, std::string &returnValue) const
VariableSelectionRule EnumSelectionRule
static const std::string comment("comment")
void Error(const char *location, const char *va_(fmt),...)
Use this function in case an error occured.
long fCount
Definition: XMLReader.h:46
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)
ClassImp(TIterator) Bool_t TIterator return false
Compare two iterator objects.
Definition: TIterator.cxx:20
static std::map< std::string, ETagNames > fgMapTagNames
Definition: XMLReader.h:84
bool HasAttributeWithName(const std::string &attributeName) const
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.
char * out
Definition: TBase64.cxx:29
void Info(const char *location, const char *va_(fmt),...)
Use this function for informational messages.
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)
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)
void Warning(const char *location, const char *va_(fmt),...)
Use this function in warning situations.
void AddClassSelectionRule(const ClassSelectionRule &classSel)
#define name(a, b)
Definition: linkTestLib0.cpp:5
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
static ETagNames GetNameOfTag(const std::string &tag, std::string &name)
Definition: XMLReader.cxx:296
cling::Interpreter & fInterp
Definition: XMLReader.h:47
bool Parse(const std::string &fileName, SelectionRules &out)
Definition: XMLReader.cxx:453
float value
Definition: math.cpp:443
const Int_t n
Definition: legend1.C:16