// @(#)root/core/utils:$Id: XMLReader.cxx 35213 2010-09-08 16:39:04Z axel $ // Author: Velislava Spasova, 2010-09-16 /************************************************************************* * Copyright (C) 1995-2011, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ ////////////////////////////////////////////////////////////////////////// // // // This class reads selection.xml files. // // // ////////////////////////////////////////////////////////////////////////// #include "XMLReader.h" #include "SelectionRules.h" std::map XMLReader::fgMapTagNames; /* This is a static function - which in our context means it is populated only ones */ void XMLReader::PopulateMap(){ if (!(fgMapTagNames.empty())) return; // if the map has already been populated, return, else populate it XMLReader::fgMapTagNames["class"] = kClass; XMLReader::fgMapTagNames["/class"] = kEndClass; XMLReader::fgMapTagNames["struct"] = kClass; XMLReader::fgMapTagNames["/struct"] = kEndClass; XMLReader::fgMapTagNames["function"] = kFunction; XMLReader::fgMapTagNames["variable"] = kVariable; XMLReader::fgMapTagNames["enum"] = kEnum; XMLReader::fgMapTagNames["method"] = kMethod; XMLReader::fgMapTagNames["field"] = kField; XMLReader::fgMapTagNames["lcgdict"] = kLcgdict; XMLReader::fgMapTagNames["/lcgdict"] = kEndLcgdict; XMLReader::fgMapTagNames["selection"] = kSelection; XMLReader::fgMapTagNames["/selection"] = kEndSelection; XMLReader::fgMapTagNames["exclusion"] = kExclusion; XMLReader::fgMapTagNames["/exclusion"] = kEndExclusion; XMLReader::fgMapTagNames["properties"] = kProperties; } /* This function Gets the next tag from teh input file stream file - the open input stream out - we return the tag through that parameter lineCount - we are counting the line numbers here in order to print error messages in case of an error */ bool XMLReader::GetNextTag(std::ifstream& file, std::string& out, int& lineCount) { char c; std::string str; bool angleBraceLevel = false; bool quotes = false; while(file.good()) { c = file.get(); if (file.good()){ bool br = false; // break - we Set it when we have found the end of the tag //count quotes - we don't want to count < and > inside quotes as opening/closing brackets switch (c) { case '\n': ++lineCount; // if new line increment lineCount break; case '"': quotes = !quotes; // we are allowed to have only pair number of quotes per tag - for the attr. values break; case '<': if (!quotes) angleBraceLevel = !angleBraceLevel; // we count < only outside quotes (i.e. quotes = false) if (!angleBraceLevel) return false; // if angleBraceLevel = true, we have < outside quotes - this is error break; case '>': if (!quotes) angleBraceLevel = !angleBraceLevel; // we count > only outside quotes (i.e. quotes = false) if (!angleBraceLevel) br = true; // if angleBraceLevel = true, we have > outside quotes - this is end of tag => break break; } out += c; // if c != {<,>,"}, add it to the tag if (br) break; // if br = true, we have reached the end of the tag and we stop reading from the input stream } } // Trim Both leading and trailing spaces int startpos = out.find_first_not_of(" \t\n"); // Find the first character position after excluding leading blank spaces int endpos = out.find_last_not_of(" \t\n"); // Find the first character position from reverse af // if all spaces or empty return an empty string if (((int) std::string::npos == startpos ) || ((int) std::string::npos == endpos)) { out = ""; } else out = out.substr( startpos, endpos-startpos+1 ); // if tag isn't empty, check if everything is OK with the tag format if (!out.empty()) return CheckIsTagOK(out); else return true; } ////////////////////////////////////////////////////////////////////////////////////////// /* Checks if the tag is OK with respect to the opening and closing <> */ bool XMLReader::CheckIsTagOK(const std::string& tag) { if (tag.length()<3){ std::cout<<"This is not a tag!"< int countWSp = 0; for (std::string::size_type i = tag.length()-2; true /*see break below*/; --i) { char c = tag[i]; if (isspace(c)) { ++countWSp; } else { if (c == '/' && countWSp>0) { std::cout<<"Malformed tag (there should be no white-spaces between / and >)!"<). // NOTE: this will only work if tags like arent valid because in any case they will // be processed as invalid tags int pos1 = tag.find(">"); if (pos1>-1) { for (std::string::size_type i = pos1+1, e = tag.length(); i < e; ++i) { char c = tag[i]; if (isspace(c)){ continue; } if (c == '<'){ return false; } else{ break; } } } return true; } ////////////////////////////////////////////////////////////////////////////////////////// /* Returns true if the tag is standalone. By standlone I mean */ bool XMLReader::IsStandaloneTag(const std::string& tag) { std::string tagEnd = tag.substr(tag.length()-2, 2); return (tagEnd == "/>"); } ////////////////////////////////////////////////////////////////////////////////////////// /* Returns true if the tag is closing tag, t.e. */ bool XMLReader::IsClosingTag(const std::string& tag) { std::string tagBegin = tag.substr(0, 2); return (tagBegin == "')) name += c; } std::map::iterator it; it = XMLReader::fgMapTagNames.find(name); if (it != XMLReader::fgMapTagNames.end()) return XMLReader::fgMapTagNames[name]; else return kInvalid; } ///////////////////////////////////////////////////////////////////////////////////////// /* We Get the attributes (if any) of the tag as {attribute_name, attribute_value} couples If there are no attributes, I don't fill the out vector and after that in the Parse() method check if out is empty. All the error handling conserning attributes is done here and this is the reason why the logic is somtimes a bit obscure. */ bool XMLReader::GetAttributes(const std::string& tag, std::vector& out) { // Get position of first symbol of the name of the tag std::string name; GetNameOfTag(tag,name); bool standalone = IsStandaloneTag(tag); // cut off the name of the tag and the trailing /> or > std::string::size_type cutend = tag.length() - 1 - name.length(); if (standalone) --cutend; std::string attrstr = tag.substr(1 /*for '<'*/ + name.length(), cutend); if (attrstr.length() > 4) { //ELSE ERROR HANDLING; - no need for it - I check in Parse() //cut off any last spaces, tabs or end of lines int pos = attrstr.find_last_not_of(" \t\n"); attrstr = attrstr.substr(1, pos+1); /* The logic here is the following - we have bool name - it shows if we have read (or are reading) an attribute name bool equalfound - shows if we have found the = symbol after the name bool value - shows if we have found or are reading the attribute value bool newattr - do we have other attributes to read char lastsymbol - I use it to detect a situation like name = xx"value" */ std::string attrtemp; bool namefound = false; bool equalfound = false; bool value = false; bool newattr = true; std::string attr_name; std::string attr_value; char lastsymbol; for (std::string::size_type i = 0, e = attrstr.length()-1; i < e; ++i) { char c = attrstr[i]; if (c == '=') { if (!namefound){ // if no name was read, report error (i.e. ) std::cout<<"Error - no name of attribute"< // this is an error std::cout<<"Error - wrong quotes placement or lack of quotes"< std::cout<<"Attribute error - missing attribute value"< attr; std::string name; ETagNames tagKind = GetNameOfTag(tagStr, name); bool attrError = GetAttributes(tagStr, attr); if (!attrError) { std::cout<<"Attribute error at line "< tag at line "< tag) selection = true; exclusion = false; break; case kEndSelection: if (selection) { // if we had opening selection tag, everything is OK selection = false; selEnd = true; } else { // if not, this is a closing tag without an opening such std::cout<<"Error at line "< tag"< tag) if (selection) { // if selection is true, we didn't have fEndSelection type of tag std::cout<<"Error at line "< tag"< tag without an opening tag std::cout<<"Error at line "< tag"<, or tag outside a parent s tag, //this is an error std::cout<<"Error at line "< element!"<, or tag outside a parent s tag, //this is an error std::cout<<"Error at line "< element!"<, or tag outside a parent s tag, //this is an error std::cout<<"Error at line "< element!"< and if (tagKind == kLcgdict || tagKind == kSelection) ;// DEBUG std::cout<<"Don't care (don't create sel rule)"<SetSelected(BaseSelectionRule::kYes); // if kMethod or kField - add to child else bsr->SetSelected(BaseSelectionRule::kYes); } } else { // if exclusion = true if (IsStandaloneTag(tagStr)){ // DEBUG std::cout<<"No"<SetSelected(BaseSelectionRule::kNo); else bsr->SetSelected(BaseSelectionRule::kNo); } else if (tagKind == kClass) { // DEBUG std::cout<<"Don't care (create sel rule)"<SetSelected(BaseSelectionRule::kDontCare); // this is for the parent } // DEBUG else std::cout<<"Don't care (don't create sel rule)"< tag // because these tag kinds cannot be children for a parent tag else if (tagKind == kClass || tagKind == kEnum || tagKind == kVariable || tagKind == kFunction || tagKind == kEndSelection || tagKind == kExclusion || tagKind == kEndExclusion){ std::cout<<"XML error at line "< tag"<HasAttributeWithName(attr[i].fName)) { std::cout<<"Error - duplicating attribute at line "<SetAttributeValue(attr[i].fName, attr[i].fValue); if ((attr[i].fName == "file_name" || attr[i].fName == "file_pattern") && tagKind == kClass){ bsr->SetAttributeValue("pattern","*"); out.SetHasFileNameRule(true); } } else { if (bsrChild->HasAttributeWithName(attr[i].fName)) { std::cout<<"Error - duplicating attribute at line "<SetAttributeValue(attr[i].fName, attr[i].fValue); } } // DEBUG std::cout<AddFieldSelectionRule(*vsr); break; case kMethod: csr->AddMethodSelectionRule(*fsr); break; default: break; } } } // we are outside of the while cycle which means that we have read the whole XML document if (sel && !selEnd) { // if selEnd is true, it menas that we never had a closing tag std::cout<<"Error - missing tag"< tag but we // never had the closing tag std::cout<<"Error - missing tag"<