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. |
53 | DECLARE_TEST_CASE(Foundation_Utility_CommandLineParser_ValueOptionHandler, Parse_GivenMultipleInvocations_AccumulateValues); |
54 | |
55 | namespace foundation |
56 | { |
57 | |
58 | // |
59 | // Handler for an option with one or multiple values. |
60 | // |
61 | |
62 | template <typename T> |
63 | class 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 | |
131 | template <typename T> |
132 | inline ValueOptionHandler<T>::ValueOptionHandler() |
133 | : m_min_value_count(0) |
134 | , m_max_value_count(std::numeric_limits<size_t>::max()) |
135 | { |
136 | } |
137 | |
138 | template <typename T> |
139 | inline ValueOptionHandler<T>& ValueOptionHandler<T>::add_name(const std::string& name) |
140 | { |
141 | OptionHandler::add_name(name); |
142 | return *this; |
143 | } |
144 | |
145 | template <typename T> |
146 | inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_description(const std::string& description) |
147 | { |
148 | OptionHandler::set_description(description); |
149 | return *this; |
150 | } |
151 | |
152 | template <typename T> |
153 | inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_syntax(const std::string& syntax) |
154 | { |
155 | OptionHandler::set_syntax(syntax); |
156 | return *this; |
157 | } |
158 | |
159 | template <typename T> |
160 | inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_flags(const Flags flags) |
161 | { |
162 | OptionHandler::set_flags(flags); |
163 | return *this; |
164 | } |
165 | |
166 | template <typename T> |
167 | inline 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 | |
173 | template <typename T> |
174 | inline 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 | |
180 | template <typename T> |
181 | inline 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 | |
188 | template <typename T> |
189 | inline 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 | |
195 | template <typename T> |
196 | inline ValueOptionHandler<T>& ValueOptionHandler<T>::set_default_values(const ValueVectorType& default_values) |
197 | { |
198 | m_default_values = default_values; |
199 | return *this; |
200 | } |
201 | |
202 | template <typename T> |
203 | inline const T& ValueOptionHandler<T>::value() const |
204 | { |
205 | return !m_values.empty() ? m_values.front() : m_default_values.front(); |
206 | } |
207 | |
208 | template <typename T> |
209 | inline const std::vector<T>& ValueOptionHandler<T>::values() const |
210 | { |
211 | return !m_values.empty() ? m_values : m_default_values; |
212 | } |
213 | |
214 | template <typename T> |
215 | inline bool ValueOptionHandler<T>::is_set() const |
216 | { |
217 | return !m_default_values.empty() || m_occurrence_count > 0; |
218 | } |
219 | |
220 | template <typename T> |
221 | std::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 | |
231 | template <typename T> |
232 | inline size_t ValueOptionHandler<T>::get_max_value_count() const |
233 | { |
234 | return m_max_value_count; |
235 | } |
236 | |
237 | template <typename T> |
238 | void 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 | |
307 | template <typename T> |
308 | void 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 | |