Logo ROOT  
Reference Guide
RStyle.cxx
Go to the documentation of this file.
1 /*************************************************************************
2  * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. *
3  * All rights reserved. *
4  * *
5  * For the licensing terms see $ROOTSYS/LICENSE. *
6  * For the list of contributors see $ROOTSYS/README/CREDITS. *
7  *************************************************************************/
8 
9 #include <ROOT/RStyle.hxx>
10 
11 #include <ROOT/RDrawable.hxx>
12 #include <ROOT/RLogger.hxx>
13 
14 using namespace std::string_literals;
15 
16 ///////////////////////////////////////////////////////////////////////////////
17 /// Evaluate attribute value for provided RDrawable
18 
19 const ROOT::Experimental::RAttrMap::Value_t *ROOT::Experimental::RStyle::Eval(const std::string &field, const RDrawable &drawable) const
20 {
21  for (const auto &block : fBlocks) {
22  if (drawable.MatchSelector(block.selector)) {
23  auto res = block.map.Find(field);
24  if (res)
25  return res;
26  }
27  }
28 
29  return nullptr;
30 }
31 
32 ///////////////////////////////////////////////////////////////////////////////
33 /// Evaluate attribute value for provided selector - exact match is expected
34 
35 const ROOT::Experimental::RAttrMap::Value_t *ROOT::Experimental::RStyle::Eval(const std::string &field, const std::string &selector) const
36 {
37  for (const auto &block : fBlocks) {
38  if (block.selector == selector) {
39  auto res = block.map.Find(field);
40  if (res)
41  return res;
42  }
43  }
44 
45  return nullptr;
46 }
47 
48 
49 ///////////////////////////////////////////////////////////////////////////////
50 /// Parse string with CSS code inside
51 
53 {
54  fBlocks.clear();
55 }
56 
57 ///////////////////////////////////////////////////////////////////////////////
58 /// Parse string with CSS code inside
59 /// All data will be append to existing style records
60 
61 bool ROOT::Experimental::RStyle::ParseString(const std::string &css_code)
62 {
63  if (css_code.empty())
64  return true;
65 
66  struct RParser {
67  int pos{0};
68  int nline{1};
69  int linebeg{0};
70  int len{0};
71  const std::string &css_code;
72 
73  RParser(const std::string &_code) : css_code(_code)
74  {
75  len = css_code.length();
76  }
77 
78  bool more_data() const { return pos < len; }
79 
80  char current() const { return css_code[pos]; }
81 
82  void shift() { ++pos; }
83 
84  bool check_symbol(bool isfirst = false)
85  {
86  auto symbol = current();
87  if (((symbol >= 'a') && (symbol <= 'z')) ||
88  ((symbol >= 'A') && (symbol <= 'Z')) || (symbol == '_')) return true;
89  return (!isfirst && (symbol>='0') && (symbol<='9'));
90  }
91 
92  std::string error_position() const
93  {
94  std::string res = "\nLine "s + std::to_string(nline) + ": "s;
95 
96  int p = linebeg;
97  while ((p<len) && (p < linebeg+100) && (css_code[p] != '\n')) ++p;
98 
99  return res + css_code.substr(linebeg, p-linebeg);
100  }
101 
102  bool skip_empty()
103  {
104  bool skip_until_newline = false, skip_until_endblock = false;
105 
106  while (pos < len) {
107  if (current() == '\n') {
108  skip_until_newline = false;
109  linebeg = ++pos;
110  ++nline;
111  continue;
112  }
113 
114  if (skip_until_endblock && (current() == '*') && (pos+1 < len) && (css_code[pos+1] == '/')) {
115  pos+=2;
116  skip_until_endblock = false;
117  continue;
118  }
119 
120  if (skip_until_newline || skip_until_endblock || (current() == ' ') || (current() == '\t')) {
121  shift();
122  continue;
123  }
124 
125  if ((current() == '/') && (pos+1 < len)) {
126  if (css_code[pos+1] == '/') {
127  pos+=2;
128  skip_until_newline = true;
129  continue;
130  } else if (css_code[pos+1] == '*') {
131  pos+=2;
132  skip_until_endblock = true;
133  continue;
134  }
135  }
136 
137  return true;
138  }
139 
140  return false;
141  }
142 
143  std::string scan_identifier(bool selector = false)
144  {
145  if (pos >= len) return ""s;
146 
147  int pos0 = pos;
148 
149  // start symbols of selector
150  if (selector && ((current() == '.') || (current() == '#'))) shift();
151 
152  bool is_first = true;
153 
154  while ((pos < len) && check_symbol(is_first)) { shift(); is_first = false; }
155 
156  return css_code.substr(pos0, pos-pos0);
157  }
158 
159  std::string scan_value()
160  {
161  if (pos >= len) return ""s;
162 
163  int pos0 = pos;
164 
165  while ((pos < len) && (current() != ';') && current() != '\n') shift();
166 
167  if (pos >= len)
168  return ""s;
169 
170  shift();
171 
172  return css_code.substr(pos0, pos - pos0 - 1);
173  }
174 
175  };
176 
177  RParser parser(css_code);
178 
179  RStyle newstyle;
180 
181  while (parser.more_data()) {
182 
183  if (!parser.skip_empty())
184  return false;
185 
186  auto sel = parser.scan_identifier(true);
187  if (sel.empty()) {
188  R__ERROR_HERE("rstyle") << "Fail to find selector" << parser.error_position();
189  return false;
190  }
191 
192  if (!parser.skip_empty())
193  return false;
194 
195  if (parser.current() != '{') {
196  R__ERROR_HERE("rstyle") << "Fail to find starting {" << parser.error_position();
197  return false;
198  }
199 
200  parser.shift();
201 
202  if (!parser.skip_empty())
203  return false;
204 
205  auto &map = newstyle.AddBlock(sel);
206 
207  while (parser.current() != '}') {
208  auto name = parser.scan_identifier();
209  if (name.empty()) {
210  R__ERROR_HERE("rstyle") << "not able to extract identifier" << parser.error_position();
211  return false;
212  }
213 
214  if (!parser.skip_empty())
215  return false;
216 
217  if (parser.current() != ':') {
218  R__ERROR_HERE("rstyle") << "not able to find separator :" << parser.error_position();
219  return false;
220  }
221 
222  parser.shift();
223 
224  if (!parser.skip_empty())
225  return false;
226 
227  if (parser.current() == ';') {
228  parser.shift();
229  map.AddNoValue(name);
230  } else {
231  auto value = parser.scan_value();
232  if (value.empty()) {
233  R__ERROR_HERE("rstyle") << "not able to find value" << parser.error_position();
234  return false;
235  }
236 
237  map.AddBestMatch(name, value);
238  }
239 
240  if (!parser.skip_empty())
241  return false;
242  }
243 
244  parser.shift();
245 
246  parser.skip_empty(); // after closing } end of file is possible
247  }
248 
249  // finally move all read blocks to this
250  fBlocks.splice(fBlocks.end(), newstyle.fBlocks);
251 
252  return true;
253 }
254 
255 
256 ///////////////////////////////////////////////////////////////////////////////
257 /// Parse CSS code and returns std::shared_ptr<RStyle> when successful
258 
259 std::shared_ptr<ROOT::Experimental::RStyle> ROOT::Experimental::RStyle::Parse(const std::string &css_code)
260 {
261  auto style = std::make_shared<RStyle>();
262  if (!style->ParseString(css_code)) return nullptr;
263  return style;
264 }
ROOT::Experimental::RAttrMap::Value_t
Definition: RAttrMap.hxx:42
TGeant4Unit::s
static constexpr double s
Definition: TGeant4SystemOfUnits.h:162
ROOT::Experimental::RDrawable::MatchSelector
bool MatchSelector(const std::string &selector) const
Preliminary method which checks if drawable matches with given selector Following selector are allowe...
Definition: RDrawable.cxx:59
BatchHelpers::block
constexpr size_t block
Definition: BatchHelpers.h:29
style
TCanvas * style()
Definition: style.C:1
RLogger.hxx
R__ERROR_HERE
#define R__ERROR_HERE(GROUP)
Definition: RLogger.hxx:183
ROOT::Experimental::RStyle::Parse
static std::shared_ptr< RStyle > Parse(const std::string &css_code)
Parse CSS code and returns std::shared_ptr<RStyle> when successful.
Definition: RStyle.cxx:259
RDrawable.hxx
ROOT::Experimental::RDrawable
Base class for drawable entities: objects that can be painted on a RPad.
Definition: RDrawable.hxx:109
ROOT::Experimental::RStyle
A set of defaults for graphics attributes, e.g.
Definition: RStyle.hxx:32
ROOT::Experimental::RStyle::ParseString
bool ParseString(const std::string &css_code)
Parse string with CSS code inside All data will be append to existing style records.
Definition: RStyle.cxx:61
name
char name[80]
Definition: TGX11.cxx:110
ROOT::Experimental::RStyle::Eval
const RAttrMap::Value_t * Eval(const std::string &field, const RDrawable &drawable) const
Evaluate attribute value for provided RDrawable.
Definition: RStyle.cxx:19
ROOT::Experimental::RStyle::AddBlock
RAttrMap & AddBlock(const std::string &selector)
Definition: RStyle.hxx:50
ROOT::Experimental::RStyle::fBlocks
std::list< Block_t > fBlocks
Definition: RStyle.hxx:64
RStyle.hxx
ROOT::Experimental::RStyle::Clear
void Clear()
Parse string with CSS code inside.
Definition: RStyle.cxx:52