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
14using namespace std::string_literals;
15
16///////////////////////////////////////////////////////////////////////////////
17/// Evaluate attribute value for provided RDrawable
18
19const 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
35const 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
61bool 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}
#define R__ERROR_HERE(GROUP)
Definition: RLogger.hxx:183
char name[80]
Definition: TGX11.cxx:109
Base class for drawable entities: objects that can be painted on a RPad.
Definition: RDrawable.hxx:102
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
A set of defaults for graphics attributes, e.g.
Definition: RStyle.hxx:31
std::list< Block_t > fBlocks
Definition: RStyle.hxx:61
const RAttrMap::Value_t * Eval(const std::string &field, const RDrawable &drawable) const
Evaluate attribute value for provided RDrawable.
Definition: RStyle.cxx:19
void Clear()
Parse string with CSS code inside.
Definition: RStyle.cxx:52
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
RAttrMap & AddBlock(const std::string &selector)
Definition: RStyle.hxx:49
constexpr size_t block
Definition: BatchHelpers.h:29
static constexpr double s