1 | |
2 | // |
3 | // This source file is part of appleseed. |
4 | // Visit http://appleseedhq.net/ for additional information and resources. |
5 | // |
6 | // This software is released under the MIT license. |
7 | // |
8 | // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited |
9 | // Copyright (c) 2014-2017 Francois Beaune, The appleseedhq Organization |
10 | // |
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
12 | // of this software and associated documentation files (the "Software"), to deal |
13 | // in the Software without restriction, including without limitation the rights |
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
15 | // copies of the Software, and to permit persons to whom the Software is |
16 | // furnished to do so, subject to the following conditions: |
17 | // |
18 | // The above copyright notice and this permission notice shall be included in |
19 | // all copies or substantial portions of the Software. |
20 | // |
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
27 | // THE SOFTWARE. |
28 | // |
29 | |
30 | #ifndef APPLESEED_RENDERER_UTILITY_PARAMARRAY_H |
31 | #define APPLESEED_RENDERER_UTILITY_PARAMARRAY_H |
32 | |
33 | // appleseed.renderer headers. |
34 | #include "renderer/global/globallogger.h" |
35 | #include "renderer/utility/messagecontext.h" |
36 | #include "renderer/utility/paramarray.h" |
37 | |
38 | // appleseed.foundation headers. |
39 | #include "foundation/utility/containers/dictionary.h" |
40 | #include "foundation/utility/foreach.h" |
41 | #include "foundation/utility/string.h" |
42 | |
43 | // appleseed.main headers. |
44 | #include "main/dllsymbol.h" |
45 | |
46 | // Standard headers. |
47 | #include <cassert> |
48 | #include <string> |
49 | #include <vector> |
50 | |
51 | namespace renderer |
52 | { |
53 | |
54 | // |
55 | // A collection of parameters. |
56 | // |
57 | |
58 | class APPLESEED_DLLSYMBOL ParamArray |
59 | : public foundation::Dictionary |
60 | { |
61 | public: |
62 | typedef std::vector<std::string> StringVec; |
63 | |
64 | // Constructors. |
65 | ParamArray(); |
66 | ParamArray(const ParamArray& rhs); |
67 | ParamArray(const foundation::Dictionary& dictionary); |
68 | |
69 | // Assignment operator. |
70 | ParamArray& operator=(const ParamArray& rhs); |
71 | |
72 | // Insert an item into the dictionary. |
73 | template <typename T> ParamArray& insert(const char* key, const T& value); |
74 | template <typename T> ParamArray& insert(const std::string& key, const T& value); |
75 | |
76 | // |
77 | // Retrieve the value of a required parameter. |
78 | // |
79 | // If the parameter is missing, an error message is emitted and the default |
80 | // value is returned. |
81 | // |
82 | // If the parameter is present but its value is invalid, an error message |
83 | // is emitted and the default value is returned. |
84 | // |
85 | // If the parameter is present and a list of allowed values is given, but |
86 | // the value of the parameter does not appear to be allowed, an error |
87 | // message is emitted and the default value is returned. |
88 | // |
89 | |
90 | template <typename T> |
91 | T get_required( |
92 | const char* name, |
93 | const T& default_value, |
94 | const StringVec& allowed_values, |
95 | const MessageContext& message_context) const; |
96 | |
97 | template <typename T> |
98 | T get_required( |
99 | const char* name, |
100 | const T& default_value, |
101 | const StringVec& allowed_values) const; |
102 | |
103 | template <typename T> |
104 | T get_required( |
105 | const char* name, |
106 | const T& default_value, |
107 | const MessageContext& message_context) const; |
108 | |
109 | template <typename T> |
110 | T get_required( |
111 | const char* name, |
112 | const T& default_value) const; |
113 | |
114 | // |
115 | // Retrieve the value of an optional parameter. |
116 | // |
117 | // If the parameter is missing, the default value is silently returned. |
118 | // |
119 | // If the parameter is present but its value is invalid, an error message |
120 | // is emitted and the default value is returned. |
121 | // |
122 | // If the parameter is present and a list of allowed values is given, but |
123 | // the value of the parameter does not appear to be allowed, an error |
124 | // message is emitted and the default value is returned. |
125 | // |
126 | |
127 | template <typename T> |
128 | T get_optional( |
129 | const char* name, |
130 | const T& default_value, |
131 | const StringVec& allowed_values, |
132 | const MessageContext& message_context) const; |
133 | |
134 | template <typename T> |
135 | T get_optional( |
136 | const char* name, |
137 | const T& default_value, |
138 | const StringVec& allowed_values) const; |
139 | |
140 | template <typename T> |
141 | T get_optional( |
142 | const char* name, |
143 | const T& default_value, |
144 | const MessageContext& message_context) const; |
145 | |
146 | template <typename T> |
147 | T get_optional( |
148 | const char* name, |
149 | const T& default_value = T()) const; |
150 | |
151 | // |
152 | // Insert an item through a given hierarchy, creating branches as needed. |
153 | // |
154 | // For instance, insert_path("a.b.c", 12) will insert the value 12 with key 'c' |
155 | // inside a dictionary named 'b' itself contained inside a dictionary named 'a'. |
156 | // |
157 | |
158 | ParamArray& insert_path(const char* path, const char* value); |
159 | template <typename T> ParamArray& insert_path(const char* path, const T& value); |
160 | template <typename T> ParamArray& insert_path(const std::string& path, const T& value); |
161 | |
162 | // Return true if an item at a given path exists. |
163 | bool exist_path(const char* path) const; |
164 | |
165 | // Retrieve an item in a given hierarchy. |
166 | const char* get_path(const char* path) const; |
167 | |
168 | // Like get_required() but given a path instead of a key. |
169 | template <typename T> |
170 | T get_path_required( |
171 | const char* path, |
172 | const T& default_value, |
173 | const StringVec& allowed_values, |
174 | const MessageContext& message_context) const; |
175 | template <typename T> |
176 | T get_path_required( |
177 | const char* path, |
178 | const T& default_value, |
179 | const StringVec& allowed_values) const; |
180 | template <typename T> |
181 | T get_path_required( |
182 | const char* path, |
183 | const T& default_value) const; |
184 | |
185 | // Like get_optional() but given a path instead of a key. |
186 | template <typename T> |
187 | T get_path_optional( |
188 | const char* path, |
189 | const T& default_value, |
190 | const StringVec& allowed_values, |
191 | const MessageContext& message_context) const; |
192 | template <typename T> |
193 | T get_path_optional( |
194 | const char* path, |
195 | const T& default_value, |
196 | const StringVec& allowed_values) const; |
197 | template <typename T> |
198 | T get_path_optional( |
199 | const char* path, |
200 | const T& default_value = T()) const; |
201 | |
202 | // Remove an item at a given path, if it exists. |
203 | ParamArray& remove_path(const char* path); |
204 | |
205 | // Return a child set of parameters, or create it if it doesn't exist. |
206 | ParamArray& push(const char* name); |
207 | |
208 | // Retrieve a child set of parameters. |
209 | // Returns an empty parameter set if the specified set cannot be found. |
210 | const ParamArray& child(const char* name) const; |
211 | |
212 | private: |
213 | template <typename T> |
214 | T get_helper( |
215 | const char* name, |
216 | const bool is_path, |
217 | const bool is_required, |
218 | const T& default_value, |
219 | const StringVec& allowed_values, |
220 | const MessageContext& message_context) const; |
221 | |
222 | template <typename T> |
223 | T get_helper( |
224 | const char* name, |
225 | const bool is_path, |
226 | const bool is_required, |
227 | const T& default_value, |
228 | const StringVec& allowed_values) const; |
229 | |
230 | template <typename T> |
231 | T get_helper( |
232 | const char* name, |
233 | const bool is_path, |
234 | const bool is_required, |
235 | const T& default_value, |
236 | const MessageContext& message_context) const; |
237 | |
238 | template <typename T> |
239 | T get_helper( |
240 | const char* name, |
241 | const bool is_path, |
242 | const bool is_required, |
243 | const T& default_value) const; |
244 | |
245 | template <typename T> |
246 | static bool contains( |
247 | const StringVec& allowed_values, |
248 | const T& value); |
249 | }; |
250 | |
251 | |
252 | // |
253 | // ParamArray class implementation. |
254 | // |
255 | |
256 | template <typename T> |
257 | inline ParamArray& ParamArray::insert(const char* key, const T& value) |
258 | { |
259 | foundation::Dictionary::insert(key, value); |
260 | return *this; |
261 | } |
262 | |
263 | template <> |
264 | inline ParamArray& ParamArray::insert(const char* key, const ParamArray& value) |
265 | { |
266 | dictionaries().insert(key, value); |
267 | return *this; |
268 | } |
269 | |
270 | template <typename T> |
271 | inline ParamArray& ParamArray::insert(const std::string& key, const T& value) |
272 | { |
273 | foundation::Dictionary::insert(key, value); |
274 | return *this; |
275 | } |
276 | |
277 | template <> |
278 | inline ParamArray& ParamArray::insert(const std::string& key, const ParamArray& value) |
279 | { |
280 | dictionaries().insert(key, value); |
281 | return *this; |
282 | } |
283 | |
284 | template <typename T> |
285 | inline T ParamArray::get_required( |
286 | const char* name, |
287 | const T& default_value, |
288 | const StringVec& allowed_values, |
289 | const MessageContext& message_context) const |
290 | { |
291 | return get_helper(name, false, true, default_value, allowed_values, message_context); |
292 | } |
293 | |
294 | template <typename T> |
295 | inline T ParamArray::get_required( |
296 | const char* name, |
297 | const T& default_value, |
298 | const StringVec& allowed_values) const |
299 | { |
300 | return get_helper(name, false, true, default_value, allowed_values); |
301 | } |
302 | |
303 | template <typename T> |
304 | inline T ParamArray::get_required( |
305 | const char* name, |
306 | const T& default_value, |
307 | const MessageContext& message_context) const |
308 | { |
309 | return get_helper(name, false, true, default_value, message_context); |
310 | } |
311 | |
312 | template <typename T> |
313 | inline T ParamArray::get_required( |
314 | const char* name, |
315 | const T& default_value) const |
316 | { |
317 | return get_helper(name, false, true, default_value); |
318 | } |
319 | |
320 | template <typename T> |
321 | inline T ParamArray::get_optional( |
322 | const char* name, |
323 | const T& default_value, |
324 | const StringVec& allowed_values, |
325 | const MessageContext& message_context) const |
326 | { |
327 | return get_helper(name, false, false, default_value, allowed_values, message_context); |
328 | } |
329 | |
330 | template <typename T> |
331 | inline T ParamArray::get_optional( |
332 | const char* name, |
333 | const T& default_value, |
334 | const StringVec& allowed_values) const |
335 | { |
336 | return get_helper(name, false, false, default_value, allowed_values); |
337 | } |
338 | |
339 | template <typename T> |
340 | inline T ParamArray::get_optional( |
341 | const char* name, |
342 | const T& default_value, |
343 | const MessageContext& message_context) const |
344 | { |
345 | return get_helper(name, false, false, default_value, message_context); |
346 | } |
347 | |
348 | template <typename T> |
349 | inline T ParamArray::get_optional( |
350 | const char* name, |
351 | const T& default_value) const |
352 | { |
353 | return get_helper(name, false, false, default_value); |
354 | } |
355 | |
356 | template <typename T> |
357 | inline ParamArray& ParamArray::insert_path(const char* path, const T& value) |
358 | { |
359 | return insert_path(path, foundation::to_string(value).c_str()); |
360 | } |
361 | |
362 | template <typename T> |
363 | inline ParamArray& ParamArray::insert_path(const std::string& path, const T& value) |
364 | { |
365 | return insert_path(path.c_str(), value); |
366 | } |
367 | |
368 | template <typename T> |
369 | inline T ParamArray::get_path_required( |
370 | const char* path, |
371 | const T& default_value, |
372 | const StringVec& allowed_values, |
373 | const MessageContext& message_context) const |
374 | { |
375 | return get_helper(path, true, true, default_value, allowed_values, message_context); |
376 | } |
377 | |
378 | template <typename T> |
379 | inline T ParamArray::get_path_required( |
380 | const char* path, |
381 | const T& default_value, |
382 | const StringVec& allowed_values) const |
383 | { |
384 | return get_helper(path, true, true, default_value, allowed_values); |
385 | } |
386 | |
387 | template <typename T> |
388 | inline T ParamArray::get_path_required( |
389 | const char* path, |
390 | const T& default_value) const |
391 | { |
392 | return get_helper(path, true, true, default_value); |
393 | } |
394 | |
395 | template <typename T> |
396 | inline T ParamArray::get_path_optional( |
397 | const char* path, |
398 | const T& default_value, |
399 | const StringVec& allowed_values, |
400 | const MessageContext& message_context) const |
401 | { |
402 | return get_helper(path, true, false, default_value, allowed_values, message_context); |
403 | } |
404 | |
405 | template <typename T> |
406 | inline T ParamArray::get_path_optional( |
407 | const char* path, |
408 | const T& default_value, |
409 | const StringVec& allowed_values) const |
410 | { |
411 | return get_helper(path, true, false, default_value, allowed_values); |
412 | } |
413 | |
414 | template <typename T> |
415 | inline T ParamArray::get_path_optional( |
416 | const char* path, |
417 | const T& default_value) const |
418 | { |
419 | return get_helper(path, true, false, default_value); |
420 | } |
421 | |
422 | template <typename T> |
423 | T ParamArray::get_helper( |
424 | const char* name, |
425 | const bool is_path, |
426 | const bool is_required, |
427 | const T& default_value, |
428 | const StringVec& allowed_values, |
429 | const MessageContext& message_context) const |
430 | { |
431 | assert(name); |
432 | |
433 | try |
434 | { |
435 | if (is_path ? exist_path(name) : strings().exist(name)) |
436 | { |
437 | const T value = is_path ? foundation::from_string<T>(get_path(name)) : get<T>(name); |
438 | |
439 | if (allowed_values.empty() || contains(allowed_values, value)) |
440 | return value; |
441 | } |
442 | else |
443 | { |
444 | if (is_required) |
445 | { |
446 | RENDERER_LOG_ERROR( |
447 | "%s%srequired parameter \"%s\" not found; continuing using value \"%s\"." , |
448 | message_context.get(), |
449 | message_context.empty() ? "" : ": " , |
450 | name, |
451 | foundation::to_string(default_value).c_str()); |
452 | } |
453 | |
454 | return default_value; |
455 | } |
456 | } |
457 | catch (const foundation::ExceptionStringConversionError&) |
458 | { |
459 | } |
460 | |
461 | RENDERER_LOG_ERROR( |
462 | "%s%sinvalid value \"%s\" for parameter \"%s\"; continuing using value \"%s\"." , |
463 | message_context.get(), |
464 | message_context.empty() ? "" : ": " , |
465 | is_path ? get_path(name) : get(name), |
466 | name, |
467 | foundation::to_string(default_value).c_str()); |
468 | |
469 | return default_value; |
470 | } |
471 | |
472 | template <typename T> |
473 | T ParamArray::get_helper( |
474 | const char* name, |
475 | const bool is_path, |
476 | const bool is_required, |
477 | const T& default_value, |
478 | const StringVec& allowed_values) const |
479 | { |
480 | assert(name); |
481 | |
482 | try |
483 | { |
484 | if (is_path ? exist_path(name) : strings().exist(name)) |
485 | { |
486 | const T value = is_path ? foundation::from_string<T>(get_path(name)) : get<T>(name); |
487 | |
488 | if (allowed_values.empty() || contains(allowed_values, value)) |
489 | return value; |
490 | } |
491 | else |
492 | { |
493 | if (is_required) |
494 | { |
495 | RENDERER_LOG_ERROR( |
496 | "required parameter \"%s\" not found; continuing using value \"%s\"." , |
497 | name, |
498 | foundation::to_string(default_value).c_str()); |
499 | } |
500 | |
501 | return default_value; |
502 | } |
503 | } |
504 | catch (const foundation::ExceptionStringConversionError&) |
505 | { |
506 | } |
507 | |
508 | RENDERER_LOG_ERROR( |
509 | "invalid value \"%s\" for parameter \"%s\"; continuing using value \"%s\"." , |
510 | is_path ? get_path(name) : get(name), |
511 | name, |
512 | foundation::to_string(default_value).c_str()); |
513 | |
514 | return default_value; |
515 | } |
516 | |
517 | template <typename T> |
518 | T ParamArray::get_helper( |
519 | const char* name, |
520 | const bool is_path, |
521 | const bool is_required, |
522 | const T& default_value, |
523 | const MessageContext& message_context) const |
524 | { |
525 | assert(name); |
526 | |
527 | try |
528 | { |
529 | if (is_path ? exist_path(name) : strings().exist(name)) |
530 | { |
531 | return is_path ? foundation::from_string<T>(get_path(name)) : get<T>(name); |
532 | } |
533 | else |
534 | { |
535 | if (is_required) |
536 | { |
537 | RENDERER_LOG_ERROR( |
538 | "%s%srequired parameter \"%s\" not found; continuing using value \"%s\"." , |
539 | message_context.get(), |
540 | message_context.empty() ? "" : ": " , |
541 | name, |
542 | foundation::to_string(default_value).c_str()); |
543 | } |
544 | |
545 | return default_value; |
546 | } |
547 | } |
548 | catch (const foundation::ExceptionStringConversionError&) |
549 | { |
550 | } |
551 | |
552 | RENDERER_LOG_ERROR( |
553 | "%s%sinvalid value \"%s\" for parameter \"%s\"; continuing using value \"%s\"." , |
554 | message_context.get(), |
555 | message_context.empty() ? "" : ": " , |
556 | is_path ? get_path(name) : get(name), |
557 | name, |
558 | foundation::to_string(default_value).c_str()); |
559 | |
560 | return default_value; |
561 | } |
562 | |
563 | template <typename T> |
564 | T ParamArray::get_helper( |
565 | const char* name, |
566 | const bool is_path, |
567 | const bool is_required, |
568 | const T& default_value) const |
569 | { |
570 | assert(name); |
571 | |
572 | try |
573 | { |
574 | if (is_path ? exist_path(name) : strings().exist(name)) |
575 | { |
576 | return is_path ? foundation::from_string<T>(get_path(name)) : get<T>(name); |
577 | } |
578 | else |
579 | { |
580 | if (is_required) |
581 | { |
582 | RENDERER_LOG_ERROR( |
583 | "required parameter \"%s\" not found; continuing using value \"%s\"." , |
584 | name, |
585 | foundation::to_string(default_value).c_str()); |
586 | } |
587 | |
588 | return default_value; |
589 | } |
590 | } |
591 | catch (const foundation::ExceptionStringConversionError&) |
592 | { |
593 | } |
594 | |
595 | RENDERER_LOG_ERROR( |
596 | "invalid value \"%s\" for parameter \"%s\"; continuing using value \"%s\"." , |
597 | is_path ? get_path(name) : get(name), |
598 | name, |
599 | foundation::to_string(default_value).c_str()); |
600 | |
601 | return default_value; |
602 | } |
603 | |
604 | template <typename T> |
605 | bool ParamArray::contains( |
606 | const StringVec& allowed_values, |
607 | const T& value) |
608 | { |
609 | for (foundation::const_each<StringVec> i = allowed_values; i; ++i) |
610 | { |
611 | if (value == foundation::from_string<T>(*i)) |
612 | return true; |
613 | } |
614 | |
615 | return false; |
616 | } |
617 | |
618 | } // namespace renderer |
619 | |
620 | #endif // !APPLESEED_RENDERER_UTILITY_PARAMARRAY_H |
621 | |