1
# type.rb : a framework for type-to-string-to-type conversion
2
# Copyright (C) 2006-2009 Vincent Fourmond
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful, but
10
# WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19
require 'ctioga2/utils'
23
Version::register_svn_info('$Revision: 151 $', '$Date: 2010-06-19 23:45:20 +0200 (Sat, 19 Jun 2010) $')
26
# The MetaBuilder module contains a framework to perform
27
# string-to-type conversion and other stuff that can be useful for backends.
30
# An exception that must be raised when
31
# Type#string_to_type is given incorrect input.
32
class IncorrectInput < Exception
35
# An exception raised when an invalid type is given to the
36
class InvalidType < Exception
40
# A class that handles a parameter type. It has to be subclassed to
41
# actually provide a parameter. The subclasses must provide the
44
# * a #string_to_type function to convert from string to the type;
45
# * a #type_to_string to convert back from type to string
46
# * an instance #type_name that returns a really small description
47
# of the type, to be used for instance to name command-line parameters.
48
# * a #type_name statement that registers the current class to the
51
# Moerover, it is a good idea to reimplement the
52
# #qt4_create_input_widget method; the default implementation works,
53
# but you probably wish it would look better.
55
# Types are implemented using hashes: this way, additionnal
56
# parameters can easily be added. The hash *must* have a :type key
57
# that will be interpreted by the children of Type. Examples:
59
# { :type => :integer}
60
# { :type => :file, :filter => "Text Files (*.txt)}
62
# And so on. You definitely should document your type and it's
63
# attributes properly, if you ever want that someone uses it.
65
# The list of currently recognised types is here:
67
# <tt>:integer</tt> :: Types::IntegerParameter
68
# <tt>:float</tt> :: Types::FloatParameter
69
# <tt>:string</tt> :: Types::StringParameter
70
# <tt>:file</tt> :: Types::FileParameter
71
# <tt>:boolean</tt> :: Types::BooleanParameter
72
# <tt>:list</tt> :: Types::ListParameter
74
# Additionally to the parameters the given type is requiring, you can
75
# pass some other kind of information using this hash, such as option
76
# parser short argument, aliases, and so on. This has nothing to do
77
# with type conversion, but it is the best place where to put this kind
78
# of things, in my humble opinion. The currently recognized such additional
80
# * :option_parser_short: a short option name for option_parser.
81
# * :namespace: a ruby module that will be searched by #string_to_type
82
# for a constant. If one of the given name is found, its value is
84
# * :shortctus: a hash specifiying strings shortcuts for given values.
85
# Elements of this hash that are regular expressions are taken
88
# A hash that makes the :type value of the _type_ argument correspond
92
# The initial type specification that was given to the Type
95
# A hash shortcut -> value. Can be _nil_
96
attr_accessor :shortcuts
98
# A hash Regexp -> value. All elements will be looked for
99
# matches for every single string conversion, so don't dump too
101
attr_accessor :re_shortcuts
103
# If the given string matches this regular expression, it is
104
# passed through without further modification.
105
attr_accessor :passthrough
107
# A default constructor. It should be safe to use it directly for
108
# children, unless something more specific is needed. Any descendent
109
# should *always* register _type_ as @type - or, even better, call
112
if type.is_a?(Symbol)
113
type = {:type => type}
117
@shortcuts = @type[:shortcuts]
119
for k,v in @shortcuts
125
if @type[:passthrough]
126
@passthrough = @type[:passthrough]
131
# This class function actually registers the current type
132
# to the Type ancestor. _name_ should be a symbol.
133
# Moreover, if the second argument is provided, it automatically
134
# creates a #type_name instance method returning this value.
135
def self.type_name(name, public_name = nil, default_value = nil)
136
if @@types.has_key?(name)
137
warn { "Redefining type #{name} " +
138
"from #{@@types[name]} to #{self}" }
141
self.send(:define_method,:type_name) do
144
self.send(:define_method,:default_value) do
149
# This function converts a 'description' (see the Type) of the
150
# type wanted into a Type child. As a special treat, a lone
151
# symbol is converted into {:type => :symbol}
152
def self.get_param_type(type)
153
if type.is_a?(Symbol)
154
type = {:type => type}
156
raise InvalidType,"The type argument must be a Hash" unless
159
return @@types.fetch(type[:type])
161
raise InvalidType, "Type #{type[:type]} unknown to the type system"
165
# Shortcut to convert directly a string to the given type specification.
167
def self.from_string(type, string)
168
return get_type(type).string_to_type(string)
171
# Returns a Type child instance suitable for conversion
172
# of the given type specification
173
def self.get_type(type)
177
return get_param_type(type).new(type)
182
# This function converts the given string to the appropriate
183
# type. It is a wrapper around the #string_to_type_internal
184
# function that can take advantage of a few general features. It
185
# is recommanded to define a #string_to_type_internal function
186
# rather to redefine #string_to_type
187
def string_to_type(string)
189
if @passthrough && @passthrough === string
190
return stt_run_hook(string)
193
if @shortcuts and @shortcuts.key? string
194
return stt_run_hook(@shortcuts[string])
197
for k, v in @re_shortcuts
199
return stt_run_hook(v)
203
# Then, constants lookup.
204
if @type.key?(:namespace)
206
return stt_run_hook(lookup_const(string))
210
return stt_run_hook(string_to_type_internal(string))
213
# This function does the exact opposite of the #string_to_type
214
# one. It defaults to using the to_s methods of the
215
# parameter. Be careful: it is absolutely important that for any
218
# string_to_type(type_to_string(type)) == type
219
def type_to_string(type)
220
return type_to_string_internal(type)
224
# Returns a default value for the given type. This is
225
# reimplemented systematically from children, with the
226
# Type::type_name statement.
231
# Returns a type name suitable for displaying, for instance, in
232
# an option parser, or inside a dialog box, and so on. Has to be
233
# one word (not to confuse the option parser, for instance); it
234
# is better if it is lowercase.
240
# Creates an option for the OptionParser _parser_. The block is
241
# fed with the converted value. The default implementation
242
# should be fine for most classes, but this still leaves the
243
# room for reimplementation if necessary. The parameters are:
245
# * _parser_: the OptionParser;
246
# * _name_: the name of the option;
247
# * _desc_: it description,
248
# * _block_: the block used to set the data.
249
def option_parser_option(parser, name, desc, &block)
250
args = [option_parser_long_option(name), desc]
251
if @type.has_key?(:option_parser_short)
252
args.unshift(@type[:option_parser_short])
254
option_parser_raw(parser, *args, &block)
257
# Returns a value to be fed to OptionParser#on as a 'long'
258
# option. It is separated from the rest to allow easy
259
# redefinition (in special cases). _name_ is the name of the
261
def option_parser_long_option(name, param = nil)
263
param = param.gsub(/\s+/, '_')
264
return "--#{name} #{param.upcase}"
267
# Whether the type is a boolean. Booleans are special cased for
268
# their use in the command-line.
273
############################################################
274
# Part of the internal implementation of Types. This should be
275
# used/redefined in children
279
# Looks for the value as a constant specified in the :namespace
280
# modules. Raises IncorrectInput if not found.
281
def lookup_const(str)
282
for mod in [@type[:namespace]].flatten
284
return mod.const_get(str)
286
# Nothing, we still look up
289
raise IncorrectInput, "Constant #{str} not found"
292
# The internal function for converting type to a string. Used by
293
# #type_to_string, children should only reimplement this
294
# function and leave #type_to_string
295
def type_to_string_internal(type)
300
# Runs the string_to_type conversion hook
301
def stt_run_hook(val)
302
if @type.key?(:stt_hook)
303
return @type[:stt_hook].call(val)
309
# Does the actual conversion from a _string_ to the
310
# type. Redefine this in children.
311
def string_to_type_internal(string)
312
raise "The class #{self.class} should not be used by itself for conversion"
315
# Creates on option for the OptionParser _parser_. The _args_
316
# will be fed directly to OptionParser#on. The _block_ is called
317
# with the value in the target type.
318
def option_parser_raw(parser, *args, &block)
319
b = block # For safe-keeping.
321
b.call(string_to_type(str))