Class: CMF::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/cmf/parser.rb

Overview

Instances of the Parser class can parse CMF messages.

Basic usage:

p = CMF::Parser.new
p.parse("\x04\r")   # {0=>true, 1=>false}
p.parse_hex("040d") # {0=>true, 1=>false}

Using a dictionary:

p = CMF::Parser.new([:tag0, :tag1])
p.parse_hex("040d") # {:tag0=>true, :tag1=>false}

If a tag is found multiple times in the message, its values will be stored in an array in the parsed object.

CMF::Parser.new.parse_hex("040504") # {0=>[true, false, true]}

Using #next_pair:

p = CMF::Parser.new
p.message_hex = "040d"
p.next_pair # [0, true]
p.next_pair # [1, false]
p.next_pair # nil

Using #each:

p = CMF::Parser.new
p.messge_hex = "040d"
p.each { |k, v| puts "#{k}: #{v}" }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dictionary = nil) ⇒ Parser

Creates a new instance of CMF::Parser.

Parameters:

  • dictionary (Hash, Array) (defaults to: nil)

    Optional. The dictionary mapping tag names to numbers. See Dictionary.validate.



42
43
44
45
# File 'lib/cmf/parser.rb', line 42

def initialize(dictionary = nil)
  @inverted_dictionary = Dictionary.validate(dictionary).invert
  @io = StringIO.new
end

Instance Attribute Details

#inverted_dictionaryHash (readonly)

Returns The inverted dictionary, mapping numbers to tag names.

Returns:

  • (Hash)

    The inverted dictionary, mapping numbers to tag names.



36
37
38
# File 'lib/cmf/parser.rb', line 36

def inverted_dictionary
  @inverted_dictionary
end

Instance Method Details

#each {|tag, value| ... } ⇒ Enumerator

Calls the given block once for each pair found in the message.

Yield Parameters:

  • tag (Integer, Object)

    The pair's tag. An integer, or the associated value found in the dictionary.

  • value (String, Integer, Boolean, Float)

    The pair's value.

Returns:

  • (Enumerator)

    If no block is given.

See Also:



104
105
106
107
108
109
110
111
# File 'lib/cmf/parser.rb', line 104

def each
  return to_enum(:each) unless block_given?
  loop do
    pair = next_pair
    break if pair.nil?
    yield(pair[0], pair[1])
  end
end

#message=(message) ⇒ Object

Sets a new CMF message.

Parameters:

  • message (String)

    The CMF message in octet form.



50
51
52
# File 'lib/cmf/parser.rb', line 50

def message=(message)
  @io = StringIO.new(message)
end

#message_hex=(message_hex) ⇒ Object

Sets a new CMF message from a hex string.

Parameters:

  • message_hex (String)

    The hex-encoded CMF message.



57
58
59
# File 'lib/cmf/parser.rb', line 57

def message_hex=(message_hex)
  self.message = [message_hex].pack('H*')
end

#next_pairArray?

Returns the next pair in the message, or nil if the whole message has been parsed.

Returns:

  • (Array, nil)

    A (tag, value) pair. The tag will be an integer, or the associated value found in the dictionary. The value will be converted to the corresponding type defined in the message. If the value's type is Type::STRING, the value will be a string with UTF-8 encoding. If the value's type is Type::BYTE_ARRAY, the value will be a string with binary (ASCII-8BIT) encoding.

Raises:



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/cmf/parser.rb', line 125

def next_pair
  return nil if @io.eof?

  byte = @io.getbyte
  type = byte & 0x07
  tag = byte >> 3
  if tag == 31
    tag = Varint.deserialize(@io)
  end

  if @inverted_dictionary[tag]
    tag = @inverted_dictionary[tag]
  end

  case type
  when Type::POSITIVE_NUMBER
    [tag, Varint.deserialize(@io)]
  when Type::NEGATIVE_NUMBER
    [tag, -Varint.deserialize(@io)]
  when Type::STRING, Type::BYTE_ARRAY
    length = Varint.deserialize(@io)
    s = @io.read(length)
    s.bytesize == length or raise MalformedMessageError, "Unexpected end of stream"
    s = s.force_encoding(Encoding::UTF_8) if type == Type::STRING
    [tag, s]
  when Type::BOOL_TRUE
    [tag, true]
  when Type::BOOL_FALSE
    [tag, false]
  when Type::DOUBLE
    s = @io.read(8)
    s.bytesize == 8 or raise MalformedMessageError, "Unexpected end of stream"
    [tag, s.unpack('E').first]
  else
    raise MalformedMessageError, "Unknown type"
  end

end

#parse(message = nil) ⇒ Hash

Parses a CMF message into an object.

Parameters:

  • message (String) (defaults to: nil)

    The message to parse. If none provided, this parser's existing message (defined from #message= or #message_hex= will be parsed.

Returns:

  • (Hash)

    A hash mapping the messages tags to their values. For each tag, if the tag number is found in the dictionary, its associated tag name will be used as hash key. If a tag is found multiple times in the message, its values will be stored in an array in the parsed object.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/cmf/parser.rb', line 71

def parse(message = nil)
  self.message = message if message

  obj = {}
  each do |key, value|
    if obj.has_key?(key)
      obj[key] = Array(obj[key])
      obj[key] << value
    else
      obj[key] = value
    end
  end

  obj
end

#parse_hex(message_hex) ⇒ Hash

Parses a hex-encoded CMF message into an object.

Parameters:

  • message_hex (String)

    A hex-encoded CMF message.

Returns:



91
92
93
94
95
# File 'lib/cmf/parser.rb', line 91

def parse_hex(message_hex)
  self.message_hex = message_hex

  parse
end