~ubuntu-branches/ubuntu/oneiric/ctioga2/oneiric

« back to all changes in this revision

Viewing changes to lib/ctioga2/metabuilder/type.rb

  • Committer: Bazaar Package Importer
  • Author(s): Vincent Fourmond
  • Date: 2011-01-24 21:36:06 UTC
  • Revision ID: james.westby@ubuntu.com-20110124213606-9ettx0ugl83z0bzp
Tags: upstream-0.1
ImportĀ upstreamĀ versionĀ 0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# type.rb : a framework for type-to-string-to-type conversion
 
2
# Copyright (C) 2006-2009 Vincent Fourmond
 
3
 
 
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.
 
8
 
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.
 
13
 
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
 
17
# USA
 
18
 
 
19
require 'ctioga2/utils'
 
20
 
 
21
module CTioga2
 
22
 
 
23
  Version::register_svn_info('$Revision: 151 $', '$Date: 2010-06-19 23:45:20 +0200 (Sat, 19 Jun 2010) $')
 
24
  
 
25
 
 
26
  # The MetaBuilder module contains a framework to perform
 
27
  # string-to-type conversion and other stuff that can be useful for backends.
 
28
  module MetaBuilder
 
29
 
 
30
    # An exception that must be raised when
 
31
    # Type#string_to_type is given incorrect input.
 
32
    class IncorrectInput < Exception
 
33
    end
 
34
    
 
35
    # An exception raised when an invalid type is given to the
 
36
    class InvalidType < Exception
 
37
    end
 
38
 
 
39
 
 
40
    # A class that handles a parameter type. It has to be subclassed to
 
41
    # actually provide a parameter. The subclasses must provide the
 
42
    # following:
 
43
    #
 
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
 
49
    #   Type system.
 
50
    #
 
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.
 
54
    #
 
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:
 
58
    #
 
59
    #  { :type => :integer}
 
60
    #  { :type => :file, :filter => "Text Files (*.txt)}
 
61
    #
 
62
    # And so on. You definitely should document your type and it's
 
63
    # attributes properly, if you ever want that someone uses it.
 
64
    #
 
65
    # The list of currently recognised types is here:
 
66
    # 
 
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
 
73
    #
 
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
 
79
    # parameters are:
 
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
 
83
    #   returned.
 
84
    # * :shortctus: a hash specifiying strings shortcuts for given values.
 
85
    #   Elements of this hash that are regular expressions are taken
 
86
    class Type
 
87
 
 
88
      # A hash that makes the :type value of the _type_ argument correspond
 
89
      # to a Type child
 
90
      @@types = { }
 
91
 
 
92
      # The initial type specification that was given to the Type
 
93
      attr_accessor :type
 
94
 
 
95
      # A hash shortcut -> value. Can be _nil_
 
96
      attr_accessor :shortcuts
 
97
 
 
98
      # A hash Regexp -> value. All elements will be looked for
 
99
      # matches for every single string conversion, so don't dump too
 
100
      # many of them here.
 
101
      attr_accessor :re_shortcuts
 
102
 
 
103
      # If the given string matches this regular expression, it is
 
104
      # passed through without further modification.
 
105
      attr_accessor :passthrough
 
106
 
 
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
 
110
      # super.
 
111
      def initialize(type)
 
112
        if type.is_a?(Symbol)
 
113
          type = {:type => type}
 
114
        end
 
115
        @type = type
 
116
        if @type[:shortcuts]
 
117
          @shortcuts = @type[:shortcuts]
 
118
          @re_shortcuts = {}
 
119
          for k,v in @shortcuts
 
120
            if k.is_a? Regexp
 
121
              @re_shortcuts[k] = v
 
122
            end
 
123
          end
 
124
        end
 
125
        if @type[:passthrough]
 
126
          @passthrough = @type[:passthrough]
 
127
        end
 
128
 
 
129
      end
 
130
 
 
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}" }
 
139
        end
 
140
        @@types[name] = self
 
141
        self.send(:define_method,:type_name) do
 
142
          public_name
 
143
        end
 
144
        self.send(:define_method,:default_value) do
 
145
          default_value
 
146
        end
 
147
      end      
 
148
 
 
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}
 
155
        end
 
156
        raise InvalidType,"The type argument must be a Hash" unless 
 
157
          type.is_a?(Hash)
 
158
        begin
 
159
          return @@types.fetch(type[:type])
 
160
        rescue
 
161
          raise InvalidType, "Type #{type[:type]} unknown to the type system"
 
162
        end
 
163
      end
 
164
 
 
165
      # Shortcut to convert directly a string to the given type specification.
 
166
      # Handy shortcut.
 
167
      def self.from_string(type, string)
 
168
        return get_type(type).string_to_type(string)
 
169
      end
 
170
 
 
171
      # Returns a Type child instance suitable for conversion
 
172
      # of the given type specification
 
173
      def self.get_type(type)
 
174
        if type.is_a? Type
 
175
          return type
 
176
        end
 
177
        return get_param_type(type).new(type)
 
178
      end
 
179
 
 
180
      
 
181
 
 
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)
 
188
        # First, passthrough
 
189
        if @passthrough && @passthrough === string
 
190
          return stt_run_hook(string)
 
191
        end
 
192
        # First, shortcuts:
 
193
        if @shortcuts and @shortcuts.key? string
 
194
          return stt_run_hook(@shortcuts[string])
 
195
        end
 
196
        if @re_shortcuts
 
197
          for k, v in @re_shortcuts
 
198
            if string =~ k
 
199
              return stt_run_hook(v)
 
200
            end
 
201
          end
 
202
        end
 
203
        # Then, constants lookup.
 
204
        if @type.key?(:namespace)
 
205
          begin
 
206
            return stt_run_hook(lookup_const(string))
 
207
          rescue Exception
 
208
          end
 
209
        end
 
210
        return stt_run_hook(string_to_type_internal(string))
 
211
      end
 
212
 
 
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
 
216
      # valid type,
 
217
      #
 
218
      #   string_to_type(type_to_string(type)) == type
 
219
      def type_to_string(type)
 
220
        return type_to_string_internal(type)
 
221
      end
 
222
 
 
223
 
 
224
      # Returns a default value for the given type. This is
 
225
      # reimplemented systematically from children, with the
 
226
      # Type::type_name statement.
 
227
      def default_value
 
228
      end
 
229
 
 
230
 
 
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.
 
235
      def type_name
 
236
        return 'notype'
 
237
      end
 
238
 
 
239
 
 
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:
 
244
      # 
 
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])
 
253
        end
 
254
        option_parser_raw(parser, *args, &block)
 
255
      end
 
256
 
 
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
 
260
      # option.
 
261
      def option_parser_long_option(name, param = nil)
 
262
        param ||= type_name
 
263
        param = param.gsub(/\s+/, '_')
 
264
        return "--#{name} #{param.upcase}"
 
265
      end
 
266
 
 
267
      # Whether the type is a boolean. Booleans are special cased for
 
268
      # their use in the command-line.
 
269
      def boolean?
 
270
        return false
 
271
      end
 
272
 
 
273
      ############################################################
 
274
      # Part of the internal implementation of Types. This should be
 
275
      # used/redefined in children
 
276
      
 
277
      protected
 
278
 
 
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
 
283
          begin
 
284
            return mod.const_get(str)
 
285
          rescue
 
286
            # Nothing, we still look up
 
287
          end
 
288
        end
 
289
        raise IncorrectInput, "Constant #{str} not found"
 
290
      end
 
291
      
 
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)
 
296
        return type.to_s
 
297
      end
 
298
      
 
299
 
 
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)
 
304
        else
 
305
          val
 
306
        end
 
307
      end
 
308
 
 
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"
 
313
      end
 
314
 
 
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.
 
320
        c = proc do |str|
 
321
          b.call(string_to_type(str))
 
322
        end
 
323
        parser.on(*args, &c)
 
324
      end
 
325
 
 
326
    end
 
327
  end
 
328
end