iso8583 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+
2
+ class ISO8583Exception < Exception
3
+ end
4
+ class ISO8583ParseException < ISO8583Exception
5
+ end
@@ -0,0 +1,178 @@
1
+ class Field
2
+ # may either be some other Field in which the length is encoded or a Fixnum for
3
+ # fixed length fields.
4
+ attr_accessor :length
5
+ attr_accessor :codec
6
+ attr_accessor :padding
7
+ attr_accessor :max
8
+
9
+ attr_writer :name
10
+ attr_accessor :bmp
11
+
12
+ def name
13
+ "BMP #{bmp}: #{@name}(#{self.class.to_s})"
14
+ end
15
+
16
+ def parse raw
17
+ len, raw = case length
18
+ when Fixnum
19
+ [length, raw]
20
+ when Field
21
+ length.parse(raw)
22
+ else
23
+ raise ISO8583Exception.new("How did you manage to fuck up configuration this bad? <-")
24
+ end
25
+
26
+ raw_value = raw[0,len]
27
+
28
+ # make sure we have enough data ...
29
+ if raw_value.length != len
30
+ mes = "Field has incorrect length! field: #{raw_value} len/expected: #{raw_value.length}/#{len}"
31
+ raise ISO8583ParseException.new(mes)
32
+ end
33
+
34
+ rest = raw[len, raw.length]
35
+ real_value = codec.decode(raw_value)
36
+ return real_value, rest
37
+ end
38
+
39
+
40
+ # Encoding needs to consider length representation, the actual encoding (such as charset or BCD)
41
+ # and padding.
42
+ # The order may be important! This impl calls codec.encode and then pads, in case you need the other
43
+ # special treatment, you may need to override this method alltogether.
44
+ def encode value
45
+ encoded_value = codec.encode(value)
46
+
47
+
48
+ if padding
49
+ if padding.arity == 1
50
+ encoded_value = padding.call(encoded_value)
51
+ elsif padding.arity == 2
52
+ encoded_value = padding.call(encoded_value, length)
53
+ end
54
+ end
55
+
56
+ len_str = case length
57
+ when Fixnum
58
+ raise ISO8583Exception.new("Too long: #{value}!") if encoded_value.length > length
59
+ raise ISO8583Exception.new("Too short: #{value}!") if encoded_value.length < length
60
+ ""
61
+ when Field
62
+ raise ISO8583Exception.new("Max lenth exceeded: #{value}, max: #{max}") if max && encoded_value.length > max
63
+ length.encode(encoded_value.length)
64
+ else
65
+ raise ISO8583Exception.new("How did you manage to fuck up configuration this bad? ->")
66
+ end
67
+ return len_str + encoded_value
68
+
69
+ end
70
+ end
71
+
72
+ #
73
+ #class Field
74
+ # attr_accessor :value
75
+ #
76
+ # def initialize value=nil
77
+ # @value = value
78
+ # end
79
+ #
80
+ # def name
81
+ # self.class._name
82
+ # end
83
+ #
84
+ # def to_b
85
+ # self.class.to_b @value
86
+ # end
87
+ #
88
+ # def to_s
89
+ # "#{name} : #{value}"
90
+ # end
91
+ #
92
+ # class << self
93
+ # def name name
94
+ # @name = name
95
+ # end
96
+ # def _name
97
+ # @name
98
+ # end
99
+ # def length len
100
+ # @len = len
101
+ # end
102
+ #
103
+ # def _len
104
+ # if (@len == nil) && (superclass.respond_to?(:_len))
105
+ # return superclass.send(:_len)
106
+ # end
107
+ # @len
108
+ # end
109
+ #
110
+ # def min min
111
+ # @min = min
112
+ # end
113
+ #
114
+ # def max max
115
+ # @max = max
116
+ # end
117
+ # #def padding pad
118
+ # # @pad = pad
119
+ # #end
120
+ # def codec codec
121
+ # codec ||= Codec
122
+ # @codec = codec
123
+ # @codec = codec.new if codec.is_a? Class
124
+ # end
125
+ #
126
+ # # tries to parse the
127
+ # def parse raw
128
+ # l, rest = _get_len raw
129
+ # #puts "len #{l}"
130
+ # #puts "rest: #{rest}"
131
+ # value = rest[0,l]
132
+ #
133
+ # unless value.length == l
134
+ # raise ISO8583ParseException.new "Wrong length for: #{value} (#{value.length}), expected #{l}"
135
+ # end
136
+ #
137
+ # check(value)
138
+ #
139
+ #
140
+ # rest = rest[l,rest.length]
141
+ # real_value = @codec.decode value
142
+ # field = self.new real_value
143
+ # return field, rest
144
+ # end
145
+ #
146
+ # def to_b value
147
+ # enc_value = @codec.encode(value)
148
+ # enc_len =
149
+ # case _len
150
+ # when Fixnum
151
+ # enc_value
152
+ # when Field
153
+ # _len.to_b(enc_value.length)
154
+ #
155
+ # end
156
+ #
157
+ # def check value
158
+ # if @min && value.length < @min
159
+ # raise ISO8583ParseException.new "Wrong length for: #{value} (#{value.length}), min length #{@min}"
160
+ # end
161
+ #
162
+ # if @max && value.length > @max
163
+ # raise ISO8583ParseException.new "Wrong length for: #{value} (#{value.length}), max length #{@max}"
164
+ # end
165
+ # end
166
+ #
167
+ # def _get_len raw
168
+ # return _len, raw if _len.is_a? Fixnum
169
+ # puts self.ancestors
170
+ # puts _len
171
+ # len_field, rest = _len.parse(raw)
172
+ # return len_field.value, rest
173
+ # end
174
+ #
175
+ #
176
+ # end
177
+ #end
178
+ #
@@ -0,0 +1,130 @@
1
+ #--
2
+ # Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
3
+ # MIT License, for details, see the LICENSE file accompaning
4
+ # this distribution
5
+ #++
6
+ # This file contains a number of preinstantiated Field definitions. You
7
+ # will probably need to create own fields in your implementation, please
8
+ # see Field and Codec for further discussion on how to do this.
9
+ # The fields currently available are those necessary to implement the
10
+ # Berlin Groups Authorization Spec.
11
+ #
12
+ # The following fields are available:
13
+ #
14
+ # [+LL+] special form to de/encode variable length indicators, two bytes ASCII numerals
15
+ # [+LLL+] special form to de/encode variable length indicators, two bytes ASCII numerals
16
+ # [+LLVAR_N+] two byte variable length ASCII numeral, payload ASCII numerals
17
+ # [+LLLVAR_N+] three byte variable length ASCII numeral, payload ASCII numerals
18
+ # [+LLVAR_Z+] two byte variable length ASCII numeral, payload Track2 data
19
+ # [+LLVAR_ANS+] two byte variable length ASCII numeral, payload ASCII+special
20
+ # [+LLLVAR_ANS+] three byte variable length ASCII numeral, payload ASCII+special
21
+ # [+LLVAR_B+] Two byte variable length binary payload
22
+ # [+LLLVAR_B+] Three byte variable length binary payload
23
+ # [+N+] fixed lengh numerals, repesented in ASCII, padding right justified using zeros
24
+ # [+AN+] fixed lengh ASCII [A-Za-z0-9], padding left justified using spaces.
25
+ # [+ANP+] fixed lengh ASCII [A-Za-z0-9] and space, padding left, spaces
26
+ # [+ANS+] fixed length ASCII [\x20-\x7E], padding left, spaces
27
+ # [+B+] binary data, padding left using nulls (0x00)
28
+ # [+MMDDhhmmss+] Date, formatted as described in ASCII numerals
29
+ # [+YYMMDDhhmmss+] Date, formatted as named in ASCII numerals
30
+ # [+YYMM+] Expiration Date, formatted as named in ASCII numerals
31
+
32
+
33
+ # Special form to de/encode variable length indicators, two bytes ASCII numerals
34
+ LL = Field.new
35
+ LL.length = 2
36
+ LL.codec = ASCII_Number
37
+ LL.padding = lambda { |value|
38
+ sprintf("%02d", value)
39
+ }
40
+ # Special form to de/encode variable length indicators, three bytes ASCII numerals
41
+ LLL = Field.new
42
+ LLL.length = 3
43
+ LLL.codec = ASCII_Number
44
+ LLL.padding = lambda { |value|
45
+ sprintf("%03d", value)
46
+ }
47
+
48
+ # Two byte variable length ASCII numeral, payload ASCII numerals
49
+ LLVAR_N = Field.new
50
+ LLVAR_N.length = LL
51
+ LLVAR_N.codec = ASCII_Number
52
+
53
+ # Three byte variable length ASCII numeral, payload ASCII numerals
54
+ LLLVAR_N = Field.new
55
+ LLLVAR_N.length = LLL
56
+ LLLVAR_N.codec = ASCII_Number
57
+
58
+ # Two byte variable length ASCII numeral, payload Track2 data
59
+ LLVAR_Z = Field.new
60
+ LLVAR_Z.length = LL
61
+ LLVAR_Z.codec = Track2
62
+
63
+ # Two byte variable length ASCII numeral, payload ASCII+special
64
+ LLVAR_ANS = Field.new
65
+ LLVAR_ANS.length= LL
66
+ LLVAR_ANS.codec = ANS_Codec
67
+
68
+ # Three byte variable length ASCII numeral, payload ASCII+special
69
+ LLLVAR_ANS = Field.new
70
+ LLLVAR_ANS.length= LLL
71
+ LLLVAR_ANS.codec = ANS_Codec
72
+
73
+ # Two byte variable length binary payload
74
+ LLVAR_B = Field.new
75
+ LLVAR_B.length= LL
76
+ LLVAR_B.codec = Null_Codec
77
+
78
+
79
+ # Three byte variable length binary payload
80
+ LLLVAR_B = Field.new
81
+ LLLVAR_B.length= LLL
82
+ LLLVAR_B.codec = Null_Codec
83
+
84
+ # Fixed lengh numerals, repesented in ASCII, padding right justified using zeros
85
+ N = Field.new
86
+ N.codec = ASCII_Number
87
+ N.padding = lambda {|val, len|
88
+ sprintf("%0#{len}d", val)
89
+ }
90
+
91
+ PADDING_LEFT_JUSTIFIED_SPACES = lambda {|val, len|
92
+ sprintf "%-#{len}s", val
93
+ }
94
+
95
+ # Fixed lengh ASCII [A-Za-z0-9], padding left justified using spaces.
96
+ AN = Field.new
97
+ AN.codec = AN_Codec
98
+ AN.padding = PADDING_LEFT_JUSTIFIED_SPACES
99
+
100
+ # Fixed lengh ASCII [A-Za-z0-9] and space, padding left, spaces
101
+ ANP = Field.new
102
+ ANP.codec = ANP_Codec
103
+ ANP.padding = PADDING_LEFT_JUSTIFIED_SPACES
104
+
105
+ # Fixed length ASCII [\x20-\x7E], padding left, spaces
106
+ ANS = Field.new
107
+ ANS.codec = ANS_Codec
108
+ ANS.padding = PADDING_LEFT_JUSTIFIED_SPACES
109
+
110
+ # Binary data, padding left using nulls (0x00)
111
+ B = Field.new
112
+ B.codec = Null_Codec
113
+ B.padding = lambda {|val, len|
114
+ while val.length < len
115
+ val = val + "\000"
116
+ end
117
+ val
118
+ }
119
+
120
+ # Date, formatted as described in ASCII numerals
121
+ MMDDhhmmss = Field.new
122
+ MMDDhhmmss.codec = MMDDhhmmssCodec
123
+
124
+ #Date, formatted as described in ASCII numerals
125
+ YYMMDDhhmmss = Field.new
126
+ YYMMDDhhmmss.codec = YYMMDDhhmmssCodec
127
+
128
+ #Date, formatted as described in ASCII numerals
129
+ YYMM = Field.new
130
+ YYMM.codec = YYMMCodec
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require "field"
5
+ require "codec"
6
+ require "fields"
7
+ require "exception"
8
+ require "bitmap"
9
+ require "message"
@@ -0,0 +1,416 @@
1
+ # Copyright 2009 by Tim Becker (tim.becker@kuriostaet.de)
2
+ # MIT License, for details, see the LICENSE file accompaning
3
+ # this distribution
4
+
5
+ # The class `Message` defines functionality to describe classes
6
+ # representing different type of messages, or message families.
7
+ # A message family consists of a number of possible message types that
8
+ # are allowed, and a way of naming and encoding the bitmaps allowed in
9
+ # the messages.
10
+ #
11
+ # To create your own message, start by subclassing Message:
12
+ #
13
+ # class MyMessage < Message
14
+ # (...)
15
+ # end
16
+ #
17
+ # the subtyped message should be told how the MTI is encoded:
18
+ #
19
+ # class MyMessage < Message
20
+ # mti_format N, :length => 4
21
+ # (...)
22
+ # end
23
+ #
24
+ # `N` above is an instance of Field which encodes numbers into their
25
+ # ASCII representations in a fixed length field. The option `length=>4`
26
+ # indicates the length of the fixed field.
27
+ #
28
+ # Next, the allowed message types are specified:
29
+ #
30
+ # class MyMessage < Message
31
+ # (...)
32
+ # mti 1100, "Authorization Request Acquirer Gateway"
33
+ # mti 1110, "Authorization Request Response Issuer Gateway"
34
+ # (...)
35
+ # end
36
+ #
37
+ # This basically defines to message types, 1100 and 1110 which may
38
+ # be accessed later either via their name or value:
39
+ #
40
+ # mes = MyMessage.new 1100
41
+ #
42
+ # or
43
+ # mes = MyMessage.new "Authorization Request Acquirer Gateway"
44
+ #
45
+ # or
46
+ # mes = MyMessage.new
47
+ # mes.mti = 1110 # or Auth. Req. Acq. Gateway ...
48
+ #
49
+ # Finally the allowed bitmaps, their names and the encoding rules are
50
+ # specified:
51
+ #
52
+ # class MyMessage < Message
53
+ # (...)
54
+ # bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
55
+ # bmp 3, "Processing Code", N, :length => 6
56
+ # bmp 4, "Amount (Transaction)", N, :length => 12
57
+ # bmp 6, "Amount, Cardholder Billing" , N, :length => 12
58
+ # (...)
59
+ # end
60
+ #
61
+ # The example above defines four bitmaps (2,3,4 and 6), and provides
62
+ # their bitmap number and description. The PAN field is variable length
63
+ # encoded (LL length indicator, ASCII, contents numeric, ASCII) and the
64
+ # maximum length of the field is limited to 19 using options.
65
+ #
66
+ # The other fields are fixed length numeric ASCII fields (the length of the fields is
67
+ # indicated by the `:length` options.)
68
+ #
69
+ # This message may be used as follows in order to interpret a received message.:
70
+ #
71
+ # mes = MyMessage.parse inputData
72
+ # puts mes[2] # prints the PAN from the message.
73
+ #
74
+ # Constructing own messages works as follows:
75
+ #
76
+ # mes = MyMessage.new 1100
77
+ # mes[2]= 474747474747
78
+ # # Alternatively
79
+ # mes["Primary Account Number (PAN)"]= 4747474747
80
+ # mes[3] = 1234 # padding is added by the Field en/decoder
81
+ # mes["Amount (Transaction)"] = 100
82
+ # mes[6] = 200
83
+ #
84
+ # the convenience method bmp_alias may be used in defining the class in
85
+ # order to provide direct access to fields using methods:
86
+ #
87
+ # class MyMessage < Message
88
+ # (...)
89
+ # bmp 2, "Primary Account Number (PAN)", LLVAR_N, :max => 19
90
+ # (...)
91
+ # bmp_alias 2, :pan
92
+ # end
93
+ #
94
+ # this allows accessing fields in the following manner:
95
+ #
96
+ # mes = MyMessage.new 1100
97
+ # mes.pan = 474747474747
98
+ # puts mes.pan
99
+ # # Identical functionality to:
100
+ # mes[2]= 474747474747
101
+ # # or:
102
+ # mes["Primary Account Number (PAN)"]= 4747474747
103
+ #
104
+ # Most of the work in implementing a new set of message type lays in
105
+ # figuring out the correct fields to use defining the Message class via
106
+ # bmp.
107
+ #
108
+
109
+
110
+ class Message
111
+
112
+ # The value of the MTI (Message Type Indicator) of this message.
113
+ attr_reader :mti
114
+
115
+ # Instantiate a new instance of this type of Message
116
+ # optionally specifying an mti.
117
+ def initialize mti=nil
118
+ # values is an internal field used to collect all the
119
+ # bmp number | bmp name | field en/decoders | values
120
+ # which are set in this message.
121
+ @values = {}
122
+ self.mti= mti if mti
123
+ end
124
+
125
+ # Set the mti of the Message using either the actual value
126
+ # or the name of the message type that was defined using
127
+ # Message.mti
128
+ #
129
+ # === Example
130
+ # class MyMessage < Message
131
+ # (...)
132
+ # mti 1100, "Authorization Request Acquirer Gateway"
133
+ # end
134
+ #
135
+ # mes = MyMessage.new
136
+ # mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
137
+
138
+ def mti= value
139
+ num, name = _get_mti_definition(value)
140
+ @mti = num
141
+ end
142
+
143
+ # Set a field in this message, `key` is either the
144
+ # bmp number or it's name.
145
+ # ===Example
146
+ #
147
+ # mes = BlaBlaMessage.new
148
+ # mes[2]=47474747 # bmp 2 is generally the PAN
149
+ # mes["Primary Account Number"]=47474747 # if thats what you called the field in Message.bmp.
150
+ def []= key, value
151
+ bmp_def = _get_definition key
152
+ bmp_def.value = value
153
+ @values[bmp_def.bmp] = bmp_def
154
+ end
155
+
156
+ # Retrieve the decoded value of the contents of a bitmap
157
+ # described either by the bitmap number or name.
158
+ #
159
+ # ===Example
160
+ #
161
+ # mes = BlaBlaMessage.parse someMessageBytes
162
+ # mes[2] # bmp 2 is generally the PAN
163
+ # mes["Primary Account Number"] # if thats what you called the field in Message.bmp.
164
+ def [] key
165
+ bmp_def = _get_definition key
166
+ bmp = @values[bmp_def.bmp]
167
+ bmp ? bmp.value : nil
168
+ end
169
+
170
+ # Retrieve the byte representation of the bitmap.
171
+ def to_b
172
+ raise ISO8583Exception.new "no MTI set!" unless mti
173
+ mti_enc = self.class._mti_format.encode(mti)
174
+ bitmap = Bitmap.new
175
+ message = ""
176
+ @values.keys.sort.each{|bmp_num|
177
+ bitmap.set(bmp_num)
178
+ enc_value = @values[bmp_num].encode
179
+ message << enc_value
180
+ }
181
+ mti_enc+bitmap.to_bytes + message
182
+ end
183
+
184
+ # Returns a nicely formatted representation of this
185
+ # message.
186
+ def to_s
187
+ _mti_name = _get_mti_definition(mti)[1]
188
+ str = "MTI:#{mti} (#{_mti_name})\n\n"
189
+ _max = @values.values.max {|a,b|
190
+ a.name.length <=> b.name.length
191
+ }
192
+ _max_name = _max.name.length
193
+
194
+ @values.keys.sort.each{|bmp_num|
195
+ _bmp = @values[bmp_num]
196
+ str += ("%03d %#{_max_name}s : %s\n" % [bmp_num, _bmp.name, _bmp.value])
197
+ }
198
+ str
199
+ end
200
+
201
+ def _get_definition key #:nodoc:
202
+ b = self.class._definitions[key]
203
+ unless b
204
+ raise ISO8583Exception.new "no definition for field: #{key}"
205
+ end
206
+ b
207
+ end
208
+
209
+ # return [mti_num, mti_value] for key being either
210
+ # mti_num or mti_value
211
+ def _get_mti_definition key #:nodoc:
212
+ num_hash,name_hash = self.class._mti_definitions
213
+ if num_hash[key]
214
+ [key, num_hash[key]]
215
+ elsif name_hash[key]
216
+ [name_hash[key], key]
217
+ else
218
+ raise ISO8583Exception.new("MTI: #{key} not allowed!")
219
+ end
220
+
221
+ end
222
+
223
+ class << self
224
+
225
+ # Defines how the message type indicator is encoded into bytes.
226
+ # ===Params:
227
+ # * field : the decoder/encoder for the MTI
228
+ # * opts : the options to pass to this field
229
+ #
230
+ # === Example
231
+ # class MyMessage < Message
232
+ # mti_format N, :length =>4
233
+ # (...)
234
+ # end
235
+ #
236
+ # encodes the mti of this message using the `N` field (fixed
237
+ # length, plain ASCII) and sets the fixed lengh to 4 bytes.
238
+ #
239
+ # See also: mti
240
+ def mti_format field, opts
241
+ f = field.dup
242
+ _handle_opts(f, opts)
243
+ @mti_format = f
244
+ end
245
+
246
+ # Defines the message types allowed for this type of message and
247
+ # gives them names
248
+ #
249
+ # === Example
250
+ # class MyMessage < Message
251
+ # (...)
252
+ # mti 1100, "Authorization Request Acquirer Gateway"
253
+ # end
254
+ #
255
+ # mes = MyMessage.new
256
+ # mes.mti = 1100 # or mes.mti = "Authorization Request Acquirer Gateway"
257
+ #
258
+ # See Also: mti_format
259
+
260
+ def mti value, name
261
+ @mtis_v ||= {}
262
+ @mtis_n ||= {}
263
+ @mtis_v[value] = name
264
+ @mtis_n[name] = value
265
+ end
266
+
267
+ #
268
+ # Define a bitmap in the message
269
+ # ===Params:
270
+ # * bmp : bitmap number
271
+ # * name : human readable form
272
+ # * field : field for encoding/decoding
273
+ # * opts : options to pass to the field, e.g. length for fxed len fields.
274
+ #
275
+ # ===Example
276
+ #
277
+ # class MyMessage < Message
278
+ # bmp 2, "PAN", LLVAR_N, :max =>19
279
+ # (...)
280
+ # end
281
+ #
282
+ # creates a class MyMessage that allows for a bitmap 2 which
283
+ # is named "PAN" and encoded by an LLVAR_N Field. The maximum
284
+ # length of the value is 19. This class may be used as follows:
285
+ #
286
+ # mes = MyMessage.new
287
+ # mes[2] = 474747474747 # or mes["PAN"] = 4747474747
288
+ #
289
+ #
290
+ def bmp bmp, name, field, opts=nil
291
+ @defs ||= {}
292
+
293
+ field = field.dup
294
+ field.name = name
295
+ field.bmp = bmp
296
+ _handle_opts(field, opts) if opts
297
+
298
+ bmp_def = BMP.new bmp, name, field
299
+
300
+ @defs[bmp] = bmp_def
301
+ @defs[name] = bmp_def
302
+ end
303
+
304
+ # Create an alias to access bitmaps directly using a method.
305
+ # Example:
306
+ # class MyMessage < Message
307
+ # (...)
308
+ # bmp 2, "PAN", LLVAR_N
309
+ # (...)
310
+ # bmp_alias 2, :pan
311
+ # end #class
312
+ #
313
+ # would allow you to access the PAN like this:
314
+ #
315
+ # mes.pan = 1234
316
+ # puts mes.pan
317
+ #
318
+ # instead of:
319
+ #
320
+ # mes[2] = 1234
321
+ #
322
+ def bmp_alias bmp, aliaz
323
+ define_method (aliaz) {
324
+ bmp_ = @values[bmp]
325
+ bmp_ ? bmp_.value : nil
326
+ }
327
+
328
+ define_method ("#{aliaz}=") { |value|
329
+ self[bmp]=value
330
+ #bmp_def = _get_definition(bmp)
331
+ #bmp_def.value= value
332
+ #@values[bmp] = bmp_def
333
+ }
334
+ end
335
+
336
+ # Parse the bytes `str` returning a message of the defined type.
337
+ def parse str
338
+ message = self.new
339
+ message.mti, rest = _mti_format.parse str
340
+ bmp,rest = Bitmap.parse(rest)
341
+ bmp.each {|bit|
342
+ bmp_def = _definitions[bit]
343
+ value, rest = bmp_def.field.parse(rest)
344
+ message[bit] = value
345
+ }
346
+ return message
347
+ end
348
+
349
+
350
+ # METHODS starting with an underscore are meant for
351
+ # internal use only ...
352
+
353
+ #
354
+ # Access the field definitions of this class, this is a
355
+ # hash containing [bmp_number, BMP] and [bitmap_name, BMP]
356
+ # pairs.
357
+ #
358
+ def _definitions #:nodoc:
359
+ @defs
360
+ end
361
+
362
+ #
363
+ # access the mti definitions applicable to the Message
364
+ #
365
+ # returns a pair of hashes containing:
366
+ #
367
+ # mti_value => mti_name
368
+ #
369
+ # mti_name => mti_value
370
+ #
371
+ def _mti_definitions #:nodoc:
372
+ [@mtis_v, @mtis_n]
373
+ end
374
+
375
+
376
+ # Returns the field definition to format the mti.
377
+ def _mti_format #:nodoc:
378
+ @mti_format
379
+ end
380
+
381
+ #
382
+ # Modifies the field definitions of the fields passed
383
+ # in through the `bmp` and `mti_format` class methods.
384
+ #
385
+ def _handle_opts field, opts #:nodoc:
386
+ opts.each_pair {|key, value|
387
+ key = (key.to_s+"=").to_sym
388
+ if field.respond_to? key
389
+ field.send(key, value)
390
+ else
391
+ warn "unknown option #{key} for #{field.name}"
392
+ end
393
+ }
394
+ end
395
+ end
396
+ end
397
+
398
+ # Internal class used to tie together name, bitmap number, field en/decoder
399
+ # and the value of the corresponding field
400
+ class BMP #:nodoc:
401
+
402
+ attr_accessor :bmp
403
+ attr_accessor :name
404
+ attr_accessor :field
405
+ attr_accessor :value
406
+
407
+ def initialize bmp, name, field
408
+ @bmp = bmp
409
+ @name = name
410
+ @field = field
411
+ end
412
+ def encode
413
+ field.encode(value)
414
+ end
415
+ end
416
+