Logo ROOT  
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 "TClingUtils.h"
23
24std::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
36 XMLReader::fgMapTagNames["namespace"] = kClass;
37 XMLReader::fgMapTagNames["/namespace"] = kEndClass;
45 XMLReader::fgMapTagNames["member"] = kField; // field and member treated identically
46 XMLReader::fgMapTagNames["/member"] = kEndField; // field and member treated identically
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 */
72bool 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
209bool XMLReader::CheckIsTagOK(const std::string& tag)
210{
211 if (tag.length()<3){
212 ROOT::TMetaUtils::Error(nullptr,"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(nullptr,"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(nullptr,"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(nullptr,"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 */
275bool 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 */
285bool 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 */
296XMLReader::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;
307 if (it != XMLReader::fgMapTagNames.end())
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 */
321bool 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(nullptr,"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(nullptr,"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(nullptr, "*** 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(nullptr,"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(nullptr,"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(nullptr,"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(nullptr,"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 */
453bool 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 = nullptr; // Pointer to the base class, in it is written information about the current sel. rule
473 BaseSelectionRule *bsrChild = nullptr; // 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(nullptr,"At line %s. Bad tag: %s\n", lineNumCharp, tagStrCharp);
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(nullptr,"Attribute at line %s. Bad tag: %s\n", lineNumCharp, tagStrCharp);
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(nullptr,"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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"Single </class> tag at line %s",lineNumCharp);
541 return false;
542 }
543 break;
544 }
545 case kVersion:
546 {
547 if (!inClass){
548 ROOT::TMetaUtils::Error(nullptr,"Version tag not within class element at line %s",lineNumCharp);
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(nullptr,"Single </ioread> at line %s",lineNumCharp);
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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Missing <selection> tag", lineNumCharp);
641 return false;
642 }
643 break;
644 }
645 case kExclusion:
646 {
647 if (inClass){
648 //this is an error
649 ROOT::TMetaUtils::Error(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Missing </selection> tag", lineNumCharp);
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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Missing <exclusion> tag", lineNumCharp);
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(nullptr,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Closing field tag which was not opened\n", lineNumCharp);
702 return false;
703 }
704 inField=false;
705 ROOT::TMetaUtils::Info(nullptr,"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(nullptr,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Closing method tag which was not opened\n", lineNumCharp);
728 return false;
729 }
730 inMethod=false;
731 ROOT::TMetaUtils::Info(nullptr,"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(nullptr,"At line %s. Tag %s not inside a <class> element\n", lineNumCharp,tagStrCharp);
740 return false;
741 }
742 if (!IsStandaloneTag(tagStr)) {
743 ROOT::TMetaUtils::Error(nullptr,"At line %s. Tag should be standalone\n", lineNumCharp);
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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
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(nullptr,"At line %s. Tag %s inside a <class> element\n", lineNumCharp,tagStrCharp);
807 return false;
808 }
809 break;
810 }
811 default: ROOT::TMetaUtils::Error(nullptr,"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(nullptr,"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(nullptr,"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(nullptr, "%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)
883 bsrChild->SetSelected(BaseSelectionRule::kYes); // if kMethod or kField - add to child
884 else if (bsr)
886 }
887 }
888 else { // if exclusion = true
889 if (IsStandaloneTag(tagStr)) {
890 // DEBUG std::cout<<"No"<<std::endl;
891 if (tagKind == kField || tagKind == kMethod)
893 else if (bsr)
895 }
896 else if (tagKind == kClass) {
897 // DEBUG std::cout<<"Don't care (create sel rule)"<<std::endl; // if it is not a standalone tag,
898 //this means it is a parent class tag
899 // In that case we don't care about the class, but we do care about the children, for which the selection
900 // rule should be No. So for the parent class it is - Don't care; for the children it is No
901 bsr->SetSelected(BaseSelectionRule::kDontCare); // this is for the parent
902 }
903 // DEBUG else std::cout<<"Don't care (don't create sel rule)"<<std::endl;
904 }
905
906// // DEBUG std::cout<<"Is child: ";
907// if (inClass){
908// if (((tagKind == kClass)) || tagKind == kEndClass) // if this is the same tag as the parent
909// // or it is a closing tag, the tag is not a child
910// ;// DEBUG std::cout<<"No"<<std::endl;
911// // else if tagKind is one of the following, it means that we have a missing </class> tag
912// // because these tag kinds cannot be children for a parent <class> tag
913// else if (tagKind == kClass || tagKind == kEnum || tagKind == kVariable || tagKind == kFunction ||
914// tagKind == kEndSelection || tagKind == kExclusion || tagKind == kEndExclusion){
915// ROOT::TMetaUtils::Error(0,"XML at line %s. Missing </class> tag\n",lineNumCharp);
916// out.ClearSelectionRules();
917// return false;
918// }
919// // DEBUG else std::cout<<"Yes"<<std::endl;
920// }
921// // DEBUG else std::cout<<"No"<<std::endl;
922
923
924 if (!attrs.empty()){
925 // Cache the name and the value
926 std::string iAttrName;
927 std::string iAttrValue;
928 for (int i = 0, n = attrs.size(); i < n; ++i) {
929 iAttrName=attrs[i].fName;
930 iAttrValue=attrs[i].fValue;
931
932 // request no streamer
933 if (tagKind == kClass && csr && "noStreamer" == iAttrName){
934 if (iAttrValue == "true") {
935 csr->SetRequestNoStreamer(true);
936 } else if (iAttrValue != "false") {
938 "XML at line %s: class attribute 'noStreamer' must be 'true' or 'false' (it was %s)\n",
939 lineNumCharp, iAttrValue.c_str());
940 }
941 }
942
943 // request no input operator
944 if (tagKind == kClass && csr && "noInputOperator" == iAttrName){
945 if (iAttrValue == "true") {
946 csr->SetRequestNoInputOperator(true);
947 } else if (iAttrValue != "false") {
949 "XML at line %s: class attribute 'noInputOperator' must be 'true' or 'false' (it was %s)\n",
950 lineNumCharp, iAttrValue.c_str());
951 }
952 }
953
954 // Set the class version
955 if (tagKind == kClass &&
956 csr &&
957 "ClassVersion" == iAttrName){
958 csr->SetRequestedVersionNumber(atoi(iAttrValue.c_str()));
959 continue;
960 }
961
962 if (tagKind == kClass ||
963 tagKind == kTypedef ||
964 tagKind == kProperties ||
965 tagKind == kEnum ||
966 tagKind == kFunction ||
967 tagKind == kVariable) {
968 if (bsr->HasAttributeWithName(iAttrName)) {
969 std::string preExistingValue;
970 bsr->GetAttributeValue(iAttrName,preExistingValue);
971 if (preExistingValue!=iAttrValue){ // If different from before
973 "Line %s: assigning new value %s to attribue %s (it was %s)\n",
974 lineNumCharp,iAttrValue.c_str(),iAttrName.c_str(),preExistingValue.c_str());
976 return false;
977 }
978 }
979 bsr->SetAttributeValue(iAttrName, iAttrValue);
980 if ((iAttrName == "file_name" || iAttrName == "file_pattern") && tagKind == kClass){
981 bsr->SetAttributeValue("pattern","*");
982 out.SetHasFileNameRule(true);
983 }
984 }
985 else if (bsrChild) {
986 if (bsrChild->HasAttributeWithName(iAttrName)) {
987 std::string preExistingValue;
988 bsrChild->GetAttributeValue(iAttrName,preExistingValue);
989 if (preExistingValue!=iAttrValue){ // If different from before
991 "Line %s: assigning new value %s to attribue %s (it was %s)\n",
992 lineNumCharp,iAttrValue.c_str(),iAttrName.c_str(),preExistingValue.c_str());
994 return false;
995 }
996 }
997 bsrChild->SetAttributeValue(iAttrName, iAttrValue);
998 }
999 }
1000 }
1001 }
1002
1003 // add selection rule to the SelectionRules object
1004 // if field or method - add to the class selection rule object
1005 // if parent class, don't add here, add when kEndClass is reached
1006 switch(tagKind) {
1007 case kClass:
1008 if (!inClass) out.AddClassSelectionRule(*csr);
1009 break;
1010 case kTypedef:
1011 out.AddClassSelectionRule(*csr);
1012 break;
1013 case kFunction:
1014 out.AddFunctionSelectionRule(*fsr);
1015 break;
1016 case kVariable:
1017 out.AddVariableSelectionRule(*vsr);
1018 break;
1019 case kEnum:
1020 out.AddEnumSelectionRule(*esr);
1021 break;
1022 case kField:
1023 csr->AddFieldSelectionRule(*vsr);
1024 break;
1025 case kMethod:
1026 csr->AddMethodSelectionRule(*fsr);
1027 break;
1028 default:
1029 break;
1030 }
1031 }
1032 }
1033 // we are outside of the while cycle which means that we have read the whole XML document
1034
1035 if (sel && !selEnd) { // if selEnd is true, it menas that we never had a closing </selection> tag
1036 ROOT::TMetaUtils::Error(nullptr,"Error - missing </selection> tag\n");
1037 out.ClearSelectionRules();
1038 return false;
1039 }
1040 if (excl && !exclEnd ) { // if excl is true and exclEnd is false, it means that we had an opening <exclusion> tag but we
1041 // never had the closing </exclusion> tag
1042 ROOT::TMetaUtils::Error(nullptr,"Error - missing </selection> tag\n");
1043 out.ClearSelectionRules();
1044 return false;
1045 }
1046 return true;
1047
1048}
#define c(i)
Definition: RSha256.hxx:101
#define e(i)
Definition: RSha256.hxx:103
char name[80]
Definition: TGX11.cxx:110
VariableSelectionRule EnumSelectionRule
VariableSelectionRule FunctionSelectionRule
void SetAttributeValue(const std::string &attributeName, const std::string &attributeValue)
bool GetAttributeValue(const std::string &attributeName, std::string &returnValue) const
void SetSelected(ESelect sel)
bool HasAttributeWithName(const std::string &attributeName) const
The class representing the collection of selection rules.
void AddVariableSelectionRule(const VariableSelectionRule &varSel)
void AddClassSelectionRule(const ClassSelectionRule &classSel)
void AddEnumSelectionRule(const EnumSelectionRule &enumSel)
void ClearSelectionRules()
void AddFunctionSelectionRule(const FunctionSelectionRule &funcSel)
void SetHasFileNameRule(bool file_rule)
cling::Interpreter & fInterp
Definition: XMLReader.h:48
static bool IsClosingTag(const std::string &tag)
Definition: XMLReader.cxx:285
@ kLcgdict
Definition: XMLReader.h:67
@ kEndField
Definition: XMLReader.h:63
@ kEndMethod
Definition: XMLReader.h:61
@ kBeginIoreadRaw
Definition: XMLReader.h:77
@ kEndIoreadRaw
Definition: XMLReader.h:78
@ kExclusion
Definition: XMLReader.h:69
@ kVariable
Definition: XMLReader.h:58
@ kEndSelection
Definition: XMLReader.h:72
@ kEndIoread
Definition: XMLReader.h:76
@ kEndLcgdict
Definition: XMLReader.h:74
@ kTypedef
Definition: XMLReader.h:81
@ kBeginIoread
Definition: XMLReader.h:75
@ kInvalid
Definition: XMLReader.h:79
@ kEndClass
Definition: XMLReader.h:70
@ kEndExclusion
Definition: XMLReader.h:73
@ kProperties
Definition: XMLReader.h:64
@ kFunction
Definition: XMLReader.h:57
@ kSelection
Definition: XMLReader.h:68
@ kVersion
Definition: XMLReader.h:65
static void PopulateMap()
Definition: XMLReader.cxx:29
long fCount
Definition: XMLReader.h:47
static std::map< std::string, ETagNames > fgMapTagNames
Definition: XMLReader.h:85
static bool CheckIsTagOK(const std::string &tag)
Definition: XMLReader.cxx:209
bool Parse(const std::string &fileName, SelectionRules &out)
Definition: XMLReader.cxx:453
static bool GetNextTag(std::ifstream &file, std::string &out, int &lineCount)
Definition: XMLReader.cxx:72
static bool GetAttributes(const std::string &tag, std::vector< Attributes > &out, const char *lineNum)
Definition: XMLReader.cxx:321
static ETagNames GetNameOfTag(const std::string &tag, std::string &name)
Definition: XMLReader.cxx:296
static bool IsStandaloneTag(const std::string &tag)
Definition: XMLReader.cxx:275
const Int_t n
Definition: legend1.C:16
static const std::string pattern("pattern")
static const std::string comment("comment")
void Info(const char *location, const char *va_(fmt),...)
Definition: TClingUtils.h:809
void Error(const char *location, const char *va_(fmt),...)
Definition: TClingUtils.h:789
void Warning(const char *location, const char *va_(fmt),...)
Definition: TClingUtils.h:819
void ProcessReadPragma(const char *args, std::string &error_string)
I am being called when a read pragma is encountered.
void ProcessReadRawPragma(const char *args, std::string &error_string)
I am being called then a readraw pragma is encountered.
Definition: file.py:1