bindata 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bindata might be problematic. Click here for more details.

data/lib/bindata/float.rb CHANGED
@@ -60,25 +60,21 @@ module BinData
60
60
 
61
61
  # Single precision floating point number in little endian format
62
62
  class FloatLe < BinData::BasePrimitive
63
- register_self
64
63
  FloatingPoint.define_methods(self, :single, :little)
65
64
  end
66
65
 
67
66
  # Single precision floating point number in big endian format
68
67
  class FloatBe < BinData::BasePrimitive
69
- register_self
70
68
  FloatingPoint.define_methods(self, :single, :big)
71
69
  end
72
70
 
73
71
  # Double precision floating point number in little endian format
74
72
  class DoubleLe < BinData::BasePrimitive
75
- register_self
76
73
  FloatingPoint.define_methods(self, :double, :little)
77
74
  end
78
75
 
79
76
  # Double precision floating point number in big endian format
80
77
  class DoubleBe < BinData::BasePrimitive
81
- register_self
82
78
  FloatingPoint.define_methods(self, :double, :big)
83
79
  end
84
80
  end
data/lib/bindata/int.rb CHANGED
@@ -11,7 +11,6 @@ module BinData
11
11
  unless BinData.const_defined?(name)
12
12
  BinData.module_eval <<-END
13
13
  class #{name} < BinData::BasePrimitive
14
- register_self
15
14
  Int.define_methods(self, #{nbits}, :#{endian}, :#{signed})
16
15
  end
17
16
  END
@@ -81,9 +80,7 @@ module BinData
81
80
  end
82
81
 
83
82
  def create_uint2int_code(nbits)
84
- mask = (1 << (nbits - 1)) - 1
85
-
86
- "val = -(((~val) & #{mask}) + 1) if (val >= #{1 << (nbits - 1)})"
83
+ "val = val - #{1 << nbits} if (val >= #{1 << (nbits - 1)})"
87
84
  end
88
85
 
89
86
  def create_read_code(nbits, endian)
@@ -151,13 +148,11 @@ module BinData
151
148
 
152
149
  # Unsigned 1 byte integer.
153
150
  class Uint8 < BinData::BasePrimitive
154
- register_self
155
151
  Int.define_methods(self, 8, :little, :unsigned)
156
152
  end
157
153
 
158
154
  # Signed 1 byte integer.
159
155
  class Int8 < BinData::BasePrimitive
160
- register_self
161
156
  Int.define_methods(self, 8, :little, :signed)
162
157
  end
163
158
 
data/lib/bindata/lazy.rb CHANGED
@@ -22,10 +22,10 @@ module BinData
22
22
  class << self
23
23
  # Lazily evaluates +val+ in the context of +obj+, with possibility of
24
24
  # +overrides+.
25
- def eval(obj, val, overrides = {})
25
+ def eval(obj, val, overrides = nil)
26
26
  if can_eval?(val)
27
- env = self.new(obj, overrides)
28
- env.lazy_eval(val)
27
+ env = self.new(obj)
28
+ env.lazy_eval(val, overrides)
29
29
  else
30
30
  val
31
31
  end
@@ -41,13 +41,12 @@ module BinData
41
41
 
42
42
  # Creates a new evaluator. All lazy evaluation is performed in the
43
43
  # context of +obj+.
44
- # +overrides+ is an optional +obj.parameters+ like hash.
45
- def initialize(obj, overrides = {})
44
+ def initialize(obj)
46
45
  @obj = obj
47
- @overrides = overrides
48
46
  end
49
47
 
50
- def lazy_eval(val)
48
+ def lazy_eval(val, overrides = nil)
49
+ @overrides = overrides if overrides
51
50
  if val.is_a? Symbol
52
51
  __send__(val)
53
52
  elsif val.respond_to? :arity
@@ -69,7 +68,7 @@ module BinData
69
68
  # Returns the index of this data object inside it's nearest container
70
69
  # array.
71
70
  def index
72
- return @overrides[:index] if @overrides.has_key?(:index)
71
+ return @overrides[:index] if @overrides and @overrides.has_key?(:index)
73
72
 
74
73
  child = @obj
75
74
  parent = @obj.parent
@@ -84,7 +83,7 @@ module BinData
84
83
  end
85
84
 
86
85
  def method_missing(symbol, *args)
87
- return @overrides[symbol] if @overrides.has_key?(symbol)
86
+ return @overrides[symbol] if @overrides and @overrides.has_key?(symbol)
88
87
 
89
88
  if @obj.parent
90
89
  eval_symbol_in_parent_context(symbol, args)
@@ -62,12 +62,12 @@ module BinData
62
62
  class Primitive < BasePrimitive
63
63
  include DSLMixin
64
64
 
65
- register_subclasses
66
- dsl_parser :multiple_fields, :optional_fieldnames, :sanitize_fields
65
+ unregister_self
66
+ dsl_parser :primitive
67
67
 
68
68
  class << self
69
- def sanitize_parameters!(params, sanitizer) #:nodoc:
70
- params[:struct_params] = sanitizer.create_sanitized_params(to_struct_params, BinData::Struct)
69
+ def sanitize_parameters!(params) #:nodoc:
70
+ params[:struct_params] = params.create_sanitized_params(dsl_params, BinData::Struct)
71
71
  end
72
72
  end
73
73
 
@@ -3,25 +3,39 @@ require 'bindata/sanitize'
3
3
  require 'bindata/struct'
4
4
 
5
5
  module BinData
6
+ # Extracts args for Records.
7
+ #
8
+ # Foo.new(:bar => "baz) is ambiguous as to whether :bar is a value or parameter.
9
+ #
10
+ # BaseArgExtractor always assumes :bar is parameter. This extractor correctly
11
+ # identifies it as value or parameter.
6
12
  class RecordArgExtractor
7
- def self.extract(the_class, the_args)
8
- value, parameters, parent = BaseArgExtractor.extract(the_class, the_args)
13
+ class << self
14
+ def extract(the_class, the_args)
15
+ value, parameters, parent = BaseArgExtractor.extract(the_class, the_args)
9
16
 
10
- if value.nil? and parameters.length > 0
11
- if field_names_in_parameters(the_class, parameters).length > 0
17
+ if parameters_is_value?(the_class, value, parameters)
12
18
  value = parameters
13
- parameters = nil
19
+ parameters = {}
14
20
  end
21
+
22
+ [value, parameters, parent]
15
23
  end
16
24
 
17
- [value, parameters, parent]
18
- end
25
+ def parameters_is_value?(the_class, value, parameters)
26
+ if value.nil? and parameters.length > 0
27
+ field_names_in_parameters?(the_class, parameters)
28
+ else
29
+ false
30
+ end
31
+ end
19
32
 
20
- def self.field_names_in_parameters(the_class, parameters)
21
- field_names = the_class.fields.field_names.collect { |k| k.to_s }
22
- param_keys = parameters.keys.collect { |k| k.to_s }
33
+ def field_names_in_parameters?(the_class, parameters)
34
+ field_names = the_class.fields.field_names
35
+ param_keys = parameters.keys
23
36
 
24
- (field_names & param_keys)
37
+ (field_names & param_keys).length > 0
38
+ end
25
39
  end
26
40
  end
27
41
 
@@ -30,7 +44,7 @@ module BinData
30
44
  # require 'bindata'
31
45
  #
32
46
  # class SomeDataType < BinData::Record
33
- # hide 'a'
47
+ # hide :a
34
48
  #
35
49
  # int32le :a
36
50
  # int16le :b
@@ -45,30 +59,11 @@ module BinData
45
59
  # obj.field_names =># ["b", "s"]
46
60
  # obj.s.field_names =># ["x", "y", "z"]
47
61
  #
48
- #
49
- # == Parameters
50
- #
51
- # Parameters may be provided at initialisation to control the behaviour of
52
- # an object. These params are:
53
- #
54
- # <tt>:fields</tt>:: An array specifying the fields for this struct.
55
- # Each element of the array is of the form [type, name,
56
- # params]. Type is a symbol representing a registered
57
- # type. Name is the name of this field. Params is an
58
- # optional hash of parameters to pass to this field
59
- # when instantiating it.
60
- # <tt>:hide</tt>:: A list of the names of fields that are to be hidden
61
- # from the outside world. Hidden fields don't appear
62
- # in #snapshot or #field_names but are still accessible
63
- # by name.
64
- # <tt>:endian</tt>:: Either :little or :big. This specifies the default
65
- # endian of any numerics in this struct, or in any
66
- # nested data objects.
67
62
  class Record < BinData::Struct
68
63
  include DSLMixin
69
64
 
70
- register_subclasses
71
- dsl_parser :multiple_fields, :optional_fieldnames, :sanitize_fields, :hidden_fields
65
+ unregister_self
66
+ dsl_parser :struct
72
67
 
73
68
  class << self
74
69
 
@@ -76,12 +71,10 @@ module BinData
76
71
  RecordArgExtractor
77
72
  end
78
73
 
79
- def sanitize_parameters!(params, sanitizer) #:nodoc:
80
- params[:fields] = fields
81
- params[:endian] = endian unless endian.nil?
82
- params[:hide] = hide unless hide.empty?
74
+ def sanitize_parameters!(params) #:nodoc:
75
+ params.merge!(dsl_params)
83
76
 
84
- super(params, sanitizer)
77
+ super(params)
85
78
 
86
79
  define_field_accessors(params[:fields].fields)
87
80
  end
@@ -92,8 +85,9 @@ module BinData
92
85
  def define_field_accessors(fields) #:nodoc:
93
86
  unless method_defined?(:bindata_defined_accessors_for_fields?)
94
87
  fields.each_with_index do |field, i|
95
- if field.name
96
- define_field_accessors_for(field.name, i)
88
+ name = field.name_as_sym
89
+ if name
90
+ define_field_accessors_for(name, i)
97
91
  end
98
92
  end
99
93
 
@@ -102,11 +96,11 @@ module BinData
102
96
  end
103
97
 
104
98
  def define_field_accessors_for(name, index)
105
- define_method(name.to_sym) do
99
+ define_method(name) do
106
100
  instantiate_obj_at(index) unless @field_objs[index]
107
101
  @field_objs[index]
108
102
  end
109
- define_method((name + "=").to_sym) do |*vals|
103
+ define_method(name.to_s + "=") do |*vals|
110
104
  instantiate_obj_at(index) unless @field_objs[index]
111
105
  @field_objs[index].assign(*vals)
112
106
  end
@@ -24,6 +24,11 @@ module BinData
24
24
  @registry[formatted_name] = class_to_register
25
25
  end
26
26
 
27
+ def unregister(name)
28
+ formatted_name = lookup_key(name)
29
+ @registry.delete(formatted_name)
30
+ end
31
+
27
32
  def lookup(name, endian = nil)
28
33
  key = lookup_key(name, endian)
29
34
  try_registering_key(key) unless @registry.has_key?(key)
@@ -37,10 +42,6 @@ module BinData
37
42
  end
38
43
  end
39
44
 
40
- def is_registered?(name, endian = nil)
41
- lookup(name, endian) != nil
42
- end
43
-
44
45
  # Convert CamelCase +name+ to underscore style.
45
46
  def underscore_name(name)
46
47
  name.to_s.sub(/.*::/, "").
data/lib/bindata/rest.rb CHANGED
@@ -17,8 +17,6 @@ module BinData
17
17
  #
18
18
  class Rest < BinData::BasePrimitive
19
19
 
20
- register_self
21
-
22
20
  #---------------
23
21
  private
24
22
 
@@ -2,281 +2,267 @@ require 'bindata/registry'
2
2
 
3
3
  module BinData
4
4
 
5
- # When a BinData object is instantiated, it can be supplied parameters to
6
- # determine its behaviour. These parameters must be sanitized to ensure
7
- # their values are valid. When instantiating many objects, such as an array
8
- # of records, there is much duplicated validation.
9
- #
10
- # The purpose of the sanitizing code is to eliminate the duplicated
11
- # validation.
12
- #
13
- # SanitizedParameters is a hash-like collection of parameters. Its purpose
14
- # it to recursively sanitize the parameters of an entire BinData object chain
15
- # at a single time.
16
- class SanitizedParameters < Hash
5
+ # Subclasses of this are sanitized
6
+ class SanitizedParameter; end
17
7
 
18
- def initialize(parameters, the_class)
19
- parameters.each_pair { |key, value| self[key.to_sym] = value }
8
+ class SanitizedPrototype < SanitizedParameter
9
+ def initialize(obj_type, obj_params, endian)
10
+ endian = endian.endian if endian.respond_to? :endian
11
+ obj_params ||= {}
20
12
 
21
- @all_sanitized = false
22
- @the_class = the_class
23
- ensure_no_nil_values
13
+ @obj_class = RegisteredClasses.lookup(obj_type, endian)
14
+ @obj_params = SanitizedParameters.new(obj_params, @obj_class, endian)
24
15
  end
25
16
 
26
- alias_method :has_parameter?, :has_key?
17
+ def instantiate(value = nil, parent = nil)
18
+ @factory ||= @obj_class.new(@obj_params)
27
19
 
28
- def needs_sanitizing?(key)
29
- parameter = self[key]
20
+ @factory.new(value, parent)
21
+ end
22
+ end
23
+ #----------------------------------------------------------------------------
30
24
 
31
- parameter and not parameter.is_a?(SanitizedParameter)
25
+ class SanitizedField < SanitizedParameter
26
+ def initialize(name, field_type, field_params, endian)
27
+ @name = name
28
+ @prototype = SanitizedPrototype.new(field_type, field_params, endian)
32
29
  end
33
30
 
34
- def all_sanitized?
35
- @all_sanitized
31
+ attr_reader :prototype
32
+
33
+ def name_as_sym
34
+ @name.nil? ? nil : @name.to_sym
36
35
  end
37
36
 
38
- def sanitize!(sanitizer)
39
- unless @all_sanitized
40
- merge_default_parameters!
37
+ def name
38
+ @name
39
+ end
41
40
 
42
- @the_class.sanitize_parameters!(self, sanitizer)
41
+ def instantiate(value = nil, parent = nil)
42
+ @prototype.instantiate(value, parent)
43
+ end
44
+ end
45
+ #----------------------------------------------------------------------------
43
46
 
44
- ensure_mandatory_parameters_exist
45
- ensure_mutual_exclusion_of_parameters
47
+ class SanitizedFields < SanitizedParameter
48
+ def initialize(endian)
49
+ @fields = []
50
+ @endian = endian
51
+ end
52
+ attr_reader :fields
46
53
 
47
- @all_sanitized = true
48
- end
54
+ def add_field(type, name, params)
55
+ name = nil if name == ""
56
+
57
+ @fields << SanitizedField.new(name, type, params, @endian)
49
58
  end
50
59
 
51
- def move_unknown_parameters_to(dest)
52
- unless @all_sanitized
53
- unused_keys = keys - @the_class.accepted_parameters.all
54
- unused_keys.each do |key|
55
- dest[key] = delete(key)
56
- end
57
- end
60
+ def [](idx)
61
+ @fields[idx]
58
62
  end
59
63
 
60
- def warn_replacement_parameter(bad_key, suggested_key)
61
- if has_parameter?(bad_key)
62
- warn ":#{bad_key} is not used with #{@the_class}. " +
63
- "You probably want to change this to :#{suggested_key}"
64
- end
64
+ def empty?
65
+ @fields.empty?
65
66
  end
66
67
 
67
- #---------------
68
- private
68
+ def length
69
+ @fields.length
70
+ end
69
71
 
70
- def ensure_no_nil_values
71
- each do |key, value|
72
- if value.nil?
73
- raise ArgumentError,
74
- "parameter '#{key}' has nil value in #{@the_class}"
75
- end
76
- end
72
+ def each(&block)
73
+ @fields.each(&block)
77
74
  end
78
75
 
79
- def merge_default_parameters!
80
- @the_class.default_parameters.each do |key, value|
81
- self[key] ||= value
82
- end
76
+ def collect(&block)
77
+ @fields.collect(&block)
83
78
  end
84
79
 
85
- def ensure_mandatory_parameters_exist
86
- @the_class.mandatory_parameters.each do |key|
87
- unless has_parameter?(key)
88
- raise ArgumentError,
89
- "parameter '#{key}' must be specified in #{@the_class}"
90
- end
91
- end
80
+ def field_names
81
+ @fields.collect { |field| field.name_as_sym }
92
82
  end
93
83
 
94
- def ensure_mutual_exclusion_of_parameters
95
- return if length < 2
84
+ def has_field_name?(name)
85
+ @fields.detect { |f| f.name_as_sym == name.to_sym }
86
+ end
96
87
 
97
- @the_class.mutually_exclusive_parameters.each do |key1, key2|
98
- if has_parameter?(key1) and has_parameter?(key2)
99
- raise ArgumentError, "params '#{key1}' and '#{key2}' " +
100
- "are mutually exclusive in #{@the_class}"
101
- end
102
- end
88
+ def all_field_names_blank?
89
+ @fields.all? { |f| f.name == nil }
103
90
  end
104
91
 
92
+ def no_field_names_blank?
93
+ @fields.all? { |f| f.name != nil }
94
+ end
95
+
96
+ def copy_fields(other)
97
+ @fields.concat(other.fields)
98
+ end
105
99
  end
106
100
  #----------------------------------------------------------------------------
107
101
 
108
- # The Sanitizer sanitizes the parameters that are passed when creating a
109
- # BinData object. Sanitizing consists of checking for mandatory, optional
110
- # and default parameters and ensuring the values of known parameters are
111
- # valid.
112
- class Sanitizer
113
-
114
- class << self
115
- # Sanitize +params+ for +the_class+.
116
- # Returns sanitized parameters.
117
- def sanitize(params, the_class)
118
- if params.is_a?(SanitizedParameters) and params.all_sanitized?
119
- params
102
+ class SanitizedChoices < SanitizedParameter
103
+ def initialize(choices, endian)
104
+ @choices = {}
105
+ choices.each_pair do |key, val|
106
+ if SanitizedParameter === val
107
+ prototype = val
120
108
  else
121
- sanitizer = self.new
122
- sanitizer.create_sanitized_params(params, the_class)
109
+ type, param = val
110
+ prototype = SanitizedPrototype.new(type, param, endian)
123
111
  end
112
+
113
+ @choices[key] = prototype
124
114
  end
125
115
  end
126
116
 
127
- def initialize
128
- @endian = nil
117
+ def [](key)
118
+ @choices[key]
129
119
  end
120
+ end
121
+ #----------------------------------------------------------------------------
130
122
 
131
- def create_sanitized_params(params, the_class)
132
- sanitized_params = as_sanitized_params(params, the_class)
133
- sanitized_params.sanitize!(self)
123
+ class SanitizedBigEndian < SanitizedParameter
124
+ def endian
125
+ :big
126
+ end
127
+ end
134
128
 
135
- sanitized_params
129
+ class SanitizedLittleEndian < SanitizedParameter
130
+ def endian
131
+ :little
136
132
  end
133
+ end
134
+ #----------------------------------------------------------------------------
137
135
 
138
- def create_sanitized_endian(endian)
139
- # memoize return value to reduce memory usage
140
- if endian == :big
141
- @@sbe ||= SanitizedBigEndian.new
142
- elsif endian == :little
143
- @@sle ||= SanitizedLittleEndian.new
144
- else
145
- raise ArgumentError, "unknown value for endian '#{endian}'"
136
+ # BinData objects are instantiated with parameters to determine their
137
+ # behaviour. These parameters must be sanitized to ensure their values
138
+ # are valid. When instantiating many objects with identical parameters,
139
+ # such as an array of records, there is much duplicated sanitizing.
140
+ #
141
+ # The purpose of the sanitizing code is to eliminate the duplicated
142
+ # validation.
143
+ #
144
+ # SanitizedParameters is a hash-like collection of parameters. Its purpose
145
+ # is to recursively sanitize the parameters of an entire BinData object chain
146
+ # at a single time.
147
+ class SanitizedParameters < Hash
148
+
149
+ # Memoized constants
150
+ BIG_ENDIAN = SanitizedBigEndian.new
151
+ LITTLE_ENDIAN = SanitizedLittleEndian.new
152
+
153
+ class << self
154
+ def sanitize(parameters, the_class)
155
+ if SanitizedParameters === parameters
156
+ parameters
157
+ else
158
+ SanitizedParameters.new(parameters, the_class, nil)
159
+ end
146
160
  end
147
161
  end
148
162
 
149
- def create_sanitized_choices(choices)
150
- SanitizedChoices.new(self, choices)
151
- end
163
+ def initialize(parameters, the_class, endian)
164
+ parameters.each_pair { |key, value| self[key.to_sym] = value }
152
165
 
153
- def create_sanitized_fields(fields = nil)
154
- new_fields = SanitizedFields.new(self)
155
- new_fields.copy_fields(fields) if fields
156
- new_fields
166
+ @the_class = the_class
167
+ @endian = endian
168
+
169
+ sanitize!
157
170
  end
158
171
 
159
- def create_sanitized_object_prototype(obj_type, obj_params, endian = nil)
160
- SanitizedPrototype.new(self, obj_type, obj_params, endian)
172
+ alias_method :has_parameter?, :has_key?
173
+
174
+ def needs_sanitizing?(key)
175
+ parameter = self[key]
176
+
177
+ parameter and not parameter.is_a?(SanitizedParameter)
161
178
  end
162
179
 
163
- def with_endian(endian, &block)
164
- if endian != nil
165
- saved_endian = @endian
166
- @endian = endian.respond_to?(:endian) ? endian.endian : endian
167
- yield
168
- @endian = saved_endian
169
- else
170
- yield
180
+ def warn_replacement_parameter(bad_key, suggested_key)
181
+ if has_parameter?(bad_key)
182
+ warn ":#{bad_key} is not used with #{@the_class}. " +
183
+ "You probably want to change this to :#{suggested_key}"
171
184
  end
172
185
  end
173
186
 
174
- def lookup_class(type)
175
- RegisteredClasses.lookup(type, @endian)
187
+ def endian
188
+ @endian || self[:endian]
176
189
  end
190
+ attr_writer :endian
177
191
 
178
- #---------------
179
- private
180
-
181
- def as_sanitized_params(params, the_class)
182
- if SanitizedParameters === params
183
- params
192
+ def create_sanitized_endian(endian)
193
+ if endian == :big
194
+ BIG_ENDIAN
195
+ elsif endian == :little
196
+ LITTLE_ENDIAN
184
197
  else
185
- SanitizedParameters.new(params || {}, the_class)
198
+ raise ArgumentError, "unknown value for endian '#{endian}'"
186
199
  end
187
200
  end
188
- end
189
- #----------------------------------------------------------------------------
190
-
191
- class SanitizedParameter; end
192
201
 
193
- class SanitizedPrototype < SanitizedParameter
194
- def initialize(sanitizer, obj_type, obj_params, endian)
195
- sanitizer.with_endian(endian) do
196
- @obj_class = sanitizer.lookup_class(obj_type)
197
- @obj_params = sanitizer.create_sanitized_params(obj_params, @obj_class)
198
- end
202
+ def create_sanitized_params(params, the_class)
203
+ SanitizedParameters.new(params, the_class, self.endian)
199
204
  end
200
205
 
201
- def instantiate(value = nil, parent = nil)
202
- @factory ||= @obj_class.new(@obj_params)
203
-
204
- @factory.new(value, parent)
206
+ def create_sanitized_choices(choices)
207
+ SanitizedChoices.new(choices, self.endian)
205
208
  end
206
- end
207
- #----------------------------------------------------------------------------
208
209
 
209
- class SanitizedField < SanitizedParameter
210
- def initialize(sanitizer, name, field_type, field_params, endian)
211
- @name = (name != nil and name != "") ? name.to_s : nil
212
- @prototype = sanitizer.create_sanitized_object_prototype(field_type, field_params, endian)
210
+ def create_sanitized_fields
211
+ SanitizedFields.new(self.endian)
213
212
  end
214
- attr_reader :name
215
213
 
216
- def instantiate(value = nil, parent = nil)
217
- @prototype.instantiate(value, parent)
214
+ def create_sanitized_object_prototype(obj_type, obj_params)
215
+ SanitizedPrototype.new(obj_type, obj_params, self.endian)
218
216
  end
219
- end
220
- #----------------------------------------------------------------------------
221
217
 
222
- class SanitizedFields < SanitizedParameter
223
- def initialize(sanitizer)
224
- @sanitizer = sanitizer
225
- @fields = []
226
- @field_names = nil
227
- end
228
- attr_reader :fields
218
+ #---------------
219
+ private
229
220
 
230
- def add_field(type, name, params, endian)
231
- @field_names = nil
232
- @fields << SanitizedField.new(@sanitizer, name, type, params, endian)
233
- end
221
+ def sanitize!
222
+ ensure_no_nil_values
223
+ merge_default_parameters!
234
224
 
235
- def [](idx)
236
- @fields[idx]
237
- end
225
+ @the_class.sanitize_parameters!(self)
238
226
 
239
- def empty?
240
- @fields.empty?
227
+ ensure_mandatory_parameters_exist
228
+ ensure_mutual_exclusion_of_parameters
241
229
  end
242
230
 
243
- def field_names
244
- # memoize field names to reduce duplicate copies
245
- @field_names ||= @fields.collect { |field| field.name }
231
+ def ensure_no_nil_values
232
+ each do |key, value|
233
+ if value.nil?
234
+ raise ArgumentError,
235
+ "parameter '#{key}' has nil value in #{@the_class}"
236
+ end
237
+ end
246
238
  end
247
239
 
248
- def copy_fields(other)
249
- @field_names = nil
250
- @fields.concat(other.fields)
240
+ def merge_default_parameters!
241
+ @the_class.default_parameters.each do |key, value|
242
+ self[key] ||= value
243
+ end
251
244
  end
252
- end
253
- #----------------------------------------------------------------------------
254
245
 
255
- class SanitizedChoices < SanitizedParameter
256
- def initialize(sanitizer, choices)
257
- @choices = {}
258
- choices.each_pair do |key, val|
259
- type, param = val
260
- prototype = sanitizer.create_sanitized_object_prototype(type, param)
261
- @choices[key] = prototype
246
+ def ensure_mandatory_parameters_exist
247
+ @the_class.mandatory_parameters.each do |key|
248
+ unless has_parameter?(key)
249
+ raise ArgumentError,
250
+ "parameter '#{key}' must be specified in #{@the_class}"
251
+ end
262
252
  end
263
253
  end
264
254
 
265
- def [](key)
266
- @choices[key]
267
- end
268
- end
269
- #----------------------------------------------------------------------------
255
+ def ensure_mutual_exclusion_of_parameters
256
+ return if length < 2
270
257
 
271
- class SanitizedBigEndian < SanitizedParameter
272
- def endian
273
- :big
258
+ @the_class.mutually_exclusive_parameters.each do |key1, key2|
259
+ if has_parameter?(key1) and has_parameter?(key2)
260
+ raise ArgumentError, "params '#{key1}' and '#{key2}' " +
261
+ "are mutually exclusive in #{@the_class}"
262
+ end
263
+ end
274
264
  end
275
265
  end
266
+ #----------------------------------------------------------------------------
276
267
 
277
- class SanitizedLittleEndian < SanitizedParameter
278
- def endian
279
- :little
280
- end
281
- end
282
268
  end