nitg: Use template::write_to_file in android_platform
[nit.git] / lib / opts.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Floréal Morandat <morandat@lirmm.fr>
4 # Copyright 2008 Jean Privat <jean@pryen.org>
5 #
6 # This file is free software, which comes along with NIT. This software is
7 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
9 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
10 # is kept unaltered, and a notification of the changes is added.
11 # You are allowed to redistribute it and sell it, alone or is a part of
12 # another product.
13
14 # Management of options on the command line
15 module opts
16
17 # Super class of all option's class
18 abstract class Option
19 # Names for the option (including long and short ones)
20 var names: Array[String]
21
22 # Type of the value of the option
23 type VALUE: nullable Object
24
25 # Human readable description of the option
26 var helptext: String
27
28 # Gathering errors during parsing
29 var errors: Array[String]
30
31 # Is this option mandatory?
32 var mandatory: Bool writable
33
34 # Has this option been read?
35 var read:Bool writable
36
37 # Current value of this option
38 var value: VALUE writable
39
40 # Default value of this option
41 var default_value: nullable VALUE writable
42
43 # Create a new option
44 init init_opt(help: String, default: nullable VALUE, names: nullable Array[String])
45 do
46 if names == null then
47 self.names = new Array[String]
48 else
49 self.names = names.to_a
50 end
51 helptext = help
52 mandatory = false
53 read = false
54 default_value = default
55 value = default
56 errors = new Array[String]
57 end
58
59 # Add new aliases for this option
60 fun add_aliases(names: String...) do names.add_all(names)
61
62 # An help text for this option with default settings
63 redef fun to_s do return pretty(2)
64
65 # A pretty print for this help
66 fun pretty(off: Int): String
67 do
68 var text = new FlatBuffer.from(" ")
69 text.append(names.join(", "))
70 text.append(" ")
71 var rest = off - text.length
72 if rest > 0 then text.append(" " * rest)
73 text.append(helptext)
74 #text.append(pretty_default)
75 return text.to_s
76 end
77
78 fun pretty_default: String
79 do
80 var dv = default_value
81 if dv != null then return " ({dv})"
82 return ""
83 end
84
85 # Consume parameters for this option
86 protected fun read_param(it: Iterator[String])
87 do
88 read = true
89 end
90 end
91
92 class OptionText
93 super Option
94 init(text: String) do init_opt(text, null, null)
95
96 redef fun pretty(off) do return to_s
97
98 redef fun to_s do return helptext
99 end
100
101 class OptionBool
102 super Option
103 redef type VALUE: Bool
104
105 init(help: String, names: String...) do init_opt(help, false, names)
106
107 redef fun read_param(it)
108 do
109 super
110 value = true
111 end
112 end
113
114 class OptionCount
115 super Option
116 redef type VALUE: Int
117
118 init(help: String, names: String...) do init_opt(help, 0, names)
119
120 redef fun read_param(it)
121 do
122 super
123 value += 1
124 end
125 end
126
127 # Option with one parameter (mandatory by default)
128 abstract class OptionParameter
129 super Option
130 protected fun convert(str: String): VALUE is abstract
131
132 # Is the parameter mandatory?
133 var parameter_mandatory: Bool writable
134
135 redef fun read_param(it)
136 do
137 super
138 if it.is_ok and it.item.chars.first != '-' then
139 value = convert(it.item)
140 it.next
141 else
142 if parameter_mandatory then
143 errors.add("Parameter expected for option {names.first}.")
144 end
145 end
146 end
147
148 init init_opt(h, d, n)
149 do
150 super
151 parameter_mandatory = true
152 end
153 end
154
155 class OptionString
156 super OptionParameter
157 redef type VALUE: nullable String
158
159 init(help: String, names: String...) do init_opt(help, null, names)
160
161 redef fun convert(str) do return str
162 end
163
164 class OptionEnum
165 super OptionParameter
166 redef type VALUE: Int
167 var values: Array[String]
168
169 init(values: Array[String], help: String, default: Int, names: String...)
170 do
171 assert values.length > 0
172 self.values = values.to_a
173 init_opt("{help} <{values.join(", ")}>", default, names)
174 end
175
176 redef fun convert(str)
177 do
178 var id = values.index_of(str)
179 if id == -1 then
180 var e = "Unrecognized value for option {names.join(", ")}.\n"
181 e += "Expected values are: {values.join(", ")}."
182 errors.add(e)
183 end
184 return id
185 end
186
187 fun value_name: String do return values[value]
188
189 redef fun pretty_default
190 do
191 if default_value != null then
192 return " ({values[default_value.as(not null)]})"
193 else
194 return ""
195 end
196 end
197 end
198
199 class OptionInt
200 super OptionParameter
201 redef type VALUE: Int
202
203 init(help: String, default: Int, names: String...) do init_opt(help, default, names)
204
205 redef fun convert(str) do return str.to_i
206 end
207
208 class OptionFloat
209 super OptionParameter
210 redef type VALUE: Float
211
212 init(help: String, default: Float, names: String...) do init_opt(help, default, names)
213
214 redef fun convert(str) do return str.to_f
215 end
216
217 class OptionArray
218 super OptionParameter
219 redef type VALUE: Array[String]
220
221 init(help: String, names: String...)
222 do
223 values = new Array[String]
224 init_opt(help, values, names)
225 end
226
227 private var values: Array[String]
228 redef fun convert(str)
229 do
230 values.add(str)
231 return values
232 end
233 end
234
235 class OptionContext
236 var options: Array[Option]
237 var rest: Array[String]
238 var errors: Array[String]
239
240 private var optmap: Map[String, Option]
241
242 fun usage
243 do
244 var lmax = 1
245 for i in options do
246 var l = 3
247 for n in i.names do
248 l += n.length + 2
249 end
250 if lmax < l then lmax = l
251 end
252
253 for i in options do
254 print(i.pretty(lmax))
255 end
256 end
257
258 # Parse ans assign options everywhere is the argument list
259 fun parse(argv: Collection[String])
260 do
261 var it = argv.iterator
262 parse_intern(it)
263 end
264
265 protected fun parse_intern(it: Iterator[String])
266 do
267 var parseargs = true
268 build
269 var rest = rest
270
271 while parseargs and it.is_ok do
272 var str = it.item
273 if str == "--" then
274 it.next
275 rest.add_all(it.to_a)
276 parseargs = false
277 else
278 # We're looking for packed short options
279 if str.chars.last_index_of('-') == 0 and str.length > 2 then
280 var next_called = false
281 for i in [1..str.length] do
282 var short_opt = "-" + str.chars[i].to_s
283 if optmap.has_key(short_opt) then
284 var option = optmap[short_opt]
285 if option isa OptionParameter then
286 it.next
287 next_called = true
288 end
289 option.read_param(it)
290 end
291 end
292 if not next_called then it.next
293 else
294 if optmap.has_key(str) then
295 var opt = optmap[str]
296 it.next
297 opt.read_param(it)
298 else
299 rest.add(it.item)
300 it.next
301 end
302 end
303 end
304 end
305
306 for opt in options do
307 if opt.mandatory and not opt.read then
308 errors.add("Mandatory option {opt.names.join(", ")} not found.")
309 end
310 end
311 end
312
313 fun add_option(opts: Option...)
314 do
315 for opt in opts do
316 options.add(opt)
317 end
318 end
319
320 init
321 do
322 options = new Array[Option]
323 optmap = new HashMap[String, Option]
324 rest = new Array[String]
325 errors = new Array[String]
326 end
327
328 private fun build
329 do
330 for o in options do
331 for n in o.names do
332 optmap[n] = o
333 end
334 end
335 end
336
337 fun get_errors: Array[String]
338 do
339 var errors: Array[String] = new Array[String]
340
341 errors.add_all(errors)
342
343 for o in options do
344 for e in o.errors do
345 errors.add(e)
346 end
347 end
348
349 return errors
350 end
351 end