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
51namespace renderer
52{
53
54//
55// A collection of parameters.
56//
57
58class 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
256template <typename T>
257inline ParamArray& ParamArray::insert(const char* key, const T& value)
258{
259 foundation::Dictionary::insert(key, value);
260 return *this;
261}
262
263template <>
264inline ParamArray& ParamArray::insert(const char* key, const ParamArray& value)
265{
266 dictionaries().insert(key, value);
267 return *this;
268}
269
270template <typename T>
271inline ParamArray& ParamArray::insert(const std::string& key, const T& value)
272{
273 foundation::Dictionary::insert(key, value);
274 return *this;
275}
276
277template <>
278inline ParamArray& ParamArray::insert(const std::string& key, const ParamArray& value)
279{
280 dictionaries().insert(key, value);
281 return *this;
282}
283
284template <typename T>
285inline 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
294template <typename T>
295inline 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
303template <typename T>
304inline 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
312template <typename T>
313inline 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
320template <typename T>
321inline 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
330template <typename T>
331inline 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
339template <typename T>
340inline 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
348template <typename T>
349inline 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
356template <typename T>
357inline ParamArray& ParamArray::insert_path(const char* path, const T& value)
358{
359 return insert_path(path, foundation::to_string(value).c_str());
360}
361
362template <typename T>
363inline ParamArray& ParamArray::insert_path(const std::string& path, const T& value)
364{
365 return insert_path(path.c_str(), value);
366}
367
368template <typename T>
369inline 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
378template <typename T>
379inline 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
387template <typename T>
388inline 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
395template <typename T>
396inline 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
405template <typename T>
406inline 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
414template <typename T>
415inline 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
422template <typename T>
423T 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
472template <typename T>
473T 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
517template <typename T>
518T 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
563template <typename T>
564T 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
604template <typename T>
605bool 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