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_FOUNDATION_UTILITY_COMMANDLINEPARSER_VALUEOPTIONHANDLER_H
31#define APPLESEED_FOUNDATION_UTILITY_COMMANDLINEPARSER_VALUEOPTIONHANDLER_H
32
33// appleseed.foundation headers.
34#include "foundation/platform/compiler.h"
35#include "foundation/utility/commandlineparser/messagelist.h"
36#include "foundation/utility/commandlineparser/optionhandler.h"
37#include "foundation/utility/commandlineparser/parseresults.h"
38#include "foundation/utility/foreach.h"
39#include "foundation/utility/iostreamop.h"
40#include "foundation/utility/log.h"
41#include "foundation/utility/makevector.h"
42#include "foundation/utility/string.h"
43#include "foundation/utility/test.h"
44
45// Standard headers.
46#include <cassert>
47#include <cstddef>
48#include <limits>
49#include <string>
50#include <vector>
51
52// Unit test case declarations.
53DECLARE_TEST_CASE(Foundation_Utility_CommandLineParser_ValueOptionHandler, Parse_GivenMultipleInvocations_AccumulateValues);
54
55namespace foundation
56{
57
58//
59// Handler for an option with one or multiple values.
60//
61
62template <typename T>
63class ValueOptionHandler
64 : public OptionHandler
65{
66 public:
67 // Value and value vector types.
68 typedef T ValueType;
69 typedef std::vector<ValueType> ValueVectorType;
70
71 // Constructor.
72 ValueOptionHandler();
73
74 // Add a name for this option.
75 ValueOptionHandler<T>& add_name(const std::string& name);
76
77 // Set a description of this option.
78 ValueOptionHandler<T>& set_description(const std::string& description);
79
80 // Set the syntax for this option.
81 ValueOptionHandler<T>& set_syntax(const std::string& syntax);
82
83 // Set the flags for this option.
84 ValueOptionHandler<T>& set_flags(const Flags flags);
85
86 // Set minimum/maximum number of values.
87 ValueOptionHandler<T>& set_min_value_count(const size_t min_count);
88 ValueOptionHandler<T>& set_max_value_count(const size_t max_count);
89 ValueOptionHandler<T>& set_exact_value_count(const size_t count);
90
91 // Set default values.
92 ValueOptionHandler<T>& set_default_value(const ValueType& default_value);
93 ValueOptionHandler<T>& set_default_values(const ValueVectorType& default_values);
94
95 // Return the values of this option.
96 const ValueType& value() const;
97 const ValueVectorType& values() const;
98
99 // Return true if this option is set.
100 virtual bool is_set() const APPLESEED_OVERRIDE;
101
102 private:
103 GRANT_ACCESS_TO_TEST_CASE(Foundation_Utility_CommandLineParser_ValueOptionHandler, Parse_GivenMultipleInvocations_AccumulateValues);
104
105 size_t m_min_value_count;
106 size_t m_max_value_count;
107 ValueVectorType m_default_values;
108 ValueVectorType m_values;
109
110 // Return a description of this option.
111 virtual std::string get_description() const APPLESEED_OVERRIDE;
112
113 // Return the maximum number of values this option can handle.
114 virtual size_t get_max_value_count() const APPLESEED_OVERRIDE;
115
116 // Parse a vector of values.
117 virtual void parse(
118 const std::string& name,
119 const StringVector& vals,
120 ParseResults& results) APPLESEED_OVERRIDE;
121
122 // Print this option to a string.
123 virtual void print(std::string& s) const APPLESEED_OVERRIDE;
124};
125
126
127//
128// ValueOptionHandler class implementation.
129//
130
131template <typename T>
132inline ValueOptionHandler<T>::ValueOptionHandler()
133 : m_min_value_count(0)
134 , m_max_value_count(std::numeric_limits<size_t>::max())
135{
136}
137
138template <typename T>
139inline ValueOptionHandler<T>& ValueOptionHandler<T>::add_name(const std::string& name)
140{
141 OptionHandler::add_name(name);
142 return *this;
143}
144
145template <typename T>
146inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_description(const std::string& description)
147{
148 OptionHandler::set_description(description);
149 return *this;
150}
151
152template <typename T>
153inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_syntax(const std::string& syntax)
154{
155 OptionHandler::set_syntax(syntax);
156 return *this;
157}
158
159template <typename T>
160inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_flags(const Flags flags)
161{
162 OptionHandler::set_flags(flags);
163 return *this;
164}
165
166template <typename T>
167inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_min_value_count(const size_t min_count)
168{
169 m_min_value_count = min_count;
170 return *this;
171}
172
173template <typename T>
174inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_max_value_count(const size_t max_count)
175{
176 m_max_value_count = max_count;
177 return *this;
178}
179
180template <typename T>
181inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_exact_value_count(const size_t count)
182{
183 m_min_value_count = count;
184 m_max_value_count = count;
185 return *this;
186}
187
188template <typename T>
189inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_default_value(const ValueType& default_value)
190{
191 m_default_values = make_vector(default_value);
192 return *this;
193}
194
195template <typename T>
196inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_default_values(const ValueVectorType& default_values)
197{
198 m_default_values = default_values;
199 return *this;
200}
201
202template <typename T>
203inline const T& ValueOptionHandler<T>::value() const
204{
205 return !m_values.empty() ? m_values.front() : m_default_values.front();
206}
207
208template <typename T>
209inline const std::vector<T>& ValueOptionHandler<T>::values() const
210{
211 return !m_values.empty() ? m_values : m_default_values;
212}
213
214template <typename T>
215inline bool ValueOptionHandler<T>::is_set() const
216{
217 return !m_default_values.empty() || m_occurrence_count > 0;
218}
219
220template <typename T>
221std::string ValueOptionHandler<T>::get_description() const
222{
223 std::string result = m_description;
224
225 if (!m_default_values.empty())
226 result += " (default: " + to_string(m_default_values) + ")";
227
228 return result;
229}
230
231template <typename T>
232inline size_t ValueOptionHandler<T>::get_max_value_count() const
233{
234 return m_max_value_count;
235}
236
237template <typename T>
238void ValueOptionHandler<T>::parse(
239 const std::string& name,
240 const StringVector& vals,
241 ParseResults& results)
242{
243 assert(vals.size() <= m_max_value_count);
244
245 if ((m_flags & OptionHandler::Repeatable) == 0 && m_occurrence_count == 1)
246 {
247 // Error: option already specified.
248 results.m_messages.add(
249 LogMessage::Warning,
250 "option '%s' already specified, ignoring all extra occurrences.",
251 name.c_str());
252 ++results.m_warnings;
253 ++m_occurrence_count;
254 return;
255 }
256
257 if (vals.size() < m_min_value_count)
258 {
259 // Error: wrong number of option values.
260 std::string error =
261 name.empty()
262 ? "wrong number of positional arguments"
263 : "option '" + name + "': wrong number of values";
264 error += ", expected ";
265 error += m_min_value_count == m_max_value_count ? "exactly " : "at least ";
266 error += to_string(m_min_value_count) + " but ";
267 error += to_string(vals.size()) + " given";
268 if (!m_syntax.empty())
269 error += " (syntax: " + name + " " + m_syntax + ")";
270 error += ".";
271 results.m_messages.add(LogMessage::Error, "%s", error.c_str());
272 ++results.m_errors;
273 return;
274 }
275
276 // Parse option values.
277 ValueVectorType parsed_values;
278 for (const_each<StringVector> i = vals; i; ++i)
279 {
280 try
281 {
282 parsed_values.push_back(from_string<T>(*i));
283 }
284 catch (const ExceptionStringConversionError&)
285 {
286 // Error: value type mismatch.
287 std::string error =
288 name.empty()
289 ? "positional arguments: "
290 : "option '" + name + "': ";
291 error += "type mismatch.";
292 if (!m_syntax.empty())
293 error += " syntax: " + name + " " + m_syntax + ".";
294 results.m_messages.add(LogMessage::Error, "%s", error.c_str());
295 ++results.m_errors;
296 return;
297 }
298 }
299
300 // Store values.
301 m_values.insert(m_values.end(), parsed_values.begin(), parsed_values.end());
302
303 // The option was successfully parsed.
304 ++m_occurrence_count;
305}
306
307template <typename T>
308void ValueOptionHandler<T>::print(std::string& s) const
309{
310 // Print the first name of the option.
311 s += m_names.front() + " ";
312
313 // Print the values of the option.
314 for (const_each<ValueVectorType> i = m_values; i; ++i)
315 {
316 if (i > m_values.begin())
317 s += " ";
318
319 // Print this value.
320 s += to_string(*i);
321 }
322}
323
324} // namespace foundation
325
326#endif // !APPLESEED_FOUNDATION_UTILITY_COMMANDLINEPARSER_VALUEOPTIONHANDLER_H
327