/************************************************************************* * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ #include #include #include using namespace std::string_literals; /////////////////////////////////////////////////////////////////////////////// /// Evaluate attribute value for provided RDrawable const ROOT::Experimental::RAttrMap::Value_t *ROOT::Experimental::RStyle::Eval(const std::string &field, const RDrawable &drawable) const { for (const auto &block : fBlocks) { if (drawable.MatchSelector(block.selector)) { auto res = block.map.Find(field); if (res) return res; } } return nullptr; } /////////////////////////////////////////////////////////////////////////////// /// Evaluate attribute value for provided selector - exact match is expected const ROOT::Experimental::RAttrMap::Value_t *ROOT::Experimental::RStyle::Eval(const std::string &field, const std::string &selector) const { for (const auto &block : fBlocks) { if (block.selector == selector) { auto res = block.map.Find(field); if (res) return res; } } return nullptr; } /////////////////////////////////////////////////////////////////////////////// /// Parse string with CSS code inside void ROOT::Experimental::RStyle::Clear() { fBlocks.clear(); } /////////////////////////////////////////////////////////////////////////////// /// Parse string with CSS code inside /// All data will be append to existing style records bool ROOT::Experimental::RStyle::ParseString(const std::string &css_code) { if (css_code.empty()) return true; struct RParser { int pos{0}; int nline{1}; int linebeg{0}; int len{0}; const std::string &css_code; RParser(const std::string &_code) : css_code(_code) { len = css_code.length(); } bool more_data() const { return pos < len; } char current() const { return css_code[pos]; } void shift() { ++pos; } bool check_symbol(bool isfirst = false) { auto symbol = current(); if (((symbol >= 'a') && (symbol <= 'z')) || ((symbol >= 'A') && (symbol <= 'Z')) || (symbol == '_')) return true; return (!isfirst && (symbol>='0') && (symbol<='9')); } std::string error_position() const { std::string res = "\nLine "s + std::to_string(nline) + ": "s; int p = linebeg; while ((p= len) return ""s; int pos0 = pos; // start symbols of selector if (selector && ((current() == '.') || (current() == '#'))) shift(); bool is_first = true; while ((pos < len) && check_symbol(is_first)) { shift(); is_first = false; } return css_code.substr(pos0, pos-pos0); } std::string scan_value() { if (pos >= len) return ""s; int pos0 = pos; while ((pos < len) && (current() != ';') && current() != '\n') shift(); if (pos >= len) return ""s; shift(); return css_code.substr(pos0, pos - pos0 - 1); } }; RParser parser(css_code); RStyle newstyle; while (parser.more_data()) { if (!parser.skip_empty()) return false; auto sel = parser.scan_identifier(true); if (sel.empty()) { R__ERROR_HERE("rstyle") << "Fail to find selector" << parser.error_position(); return false; } if (!parser.skip_empty()) return false; if (parser.current() != '{') { R__ERROR_HERE("rstyle") << "Fail to find starting {" << parser.error_position(); return false; } parser.shift(); if (!parser.skip_empty()) return false; auto &map = newstyle.AddBlock(sel); while (parser.current() != '}') { auto name = parser.scan_identifier(); if (name.empty()) { R__ERROR_HERE("rstyle") << "not able to extract identifier" << parser.error_position(); return false; } if (!parser.skip_empty()) return false; if (parser.current() != ':') { R__ERROR_HERE("rstyle") << "not able to find separator :" << parser.error_position(); return false; } parser.shift(); if (!parser.skip_empty()) return false; if (parser.current() == ';') { parser.shift(); map.AddNoValue(name); } else { auto value = parser.scan_value(); if (value.empty()) { R__ERROR_HERE("rstyle") << "not able to find value" << parser.error_position(); return false; } map.AddBestMatch(name, value); } if (!parser.skip_empty()) return false; } parser.shift(); parser.skip_empty(); // after closing } end of file is possible } // finally move all read blocks to this fBlocks.splice(fBlocks.end(), newstyle.fBlocks); return true; }