File: mapping.rb

package info (click to toggle)
qpid-proton 0.37.0-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 18,384 kB
  • sloc: ansic: 37,828; cpp: 37,140; python: 15,302; ruby: 6,018; xml: 477; sh: 320; pascal: 52; makefile: 18
file content (185 lines) | stat: -rw-r--r-- 5,868 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.


module Qpid::Proton
  module Codec

    # Maps between Proton types and their Ruby native language counterparts.
    #
    # @private
    class Mapping

      attr_reader :code
      attr_reader :put_method
      attr_reader :get_method

      @@by_code = {}
      @@by_class = {}

      # Creates a new mapping.
      #
      # ==== Arguments
      #
      # * code    - the AMQP code for this type
      # * name    - the AMQP name for this type
      # * klasses - native Ruby classes that are mapped to this AMQP type
      # * getter  - overrides the get method for the type
      def initialize(code, name, klasses = nil, getter = nil)

        @code = code
        @name = name

        @@by_code[code] = self

        unless klasses.nil?
          klasses.each do |klass|
            raise "entry exists for #{klass}" if @@by_class.keys.include? klass
            @@by_class[klass] = self unless klass.nil?
          end
        end

        @put_method = (name + "=").intern

        if getter.nil?
          @get_method = name.intern
        else
          @get_method = getter.intern
        end
      end

      def to_s; @name; end

      def put(data, value)
        data.__send__(@put_method, value)
      end

      def get(data)
        data.__send__(@get_method)
      end

      def self.for_class(klass)
        c = klass
        c = c.superclass while c && (x = @@by_class[c]).nil?
        raise TypeError, "#{klass} cannot be converted to AMQP" unless x
        x
      end

      def self.for_code(code)
        @@by_code[code]
      end

      # Convert x to a Mapping
      def self.[](x)
        case x
        when Mapping then x
        when Integer then @@by_code[x]
        when Types::Type then @@by_code[x.code]
        end
      end

    end

    NULL       = Mapping.new(Cproton::PN_NULL, "null", [NilClass])
    BOOL       = Mapping.new(Cproton::PN_BOOL, "bool", [TrueClass, FalseClass])
    UBYTE      = Mapping.new(Cproton::PN_UBYTE, "ubyte")
    BYTE       = Mapping.new(Cproton::PN_BYTE, "byte")
    USHORT     = Mapping.new(Cproton::PN_USHORT, "ushort")
    SHORT      = Mapping.new(Cproton::PN_SHORT, "short")
    UINT       = Mapping.new(Cproton::PN_UINT, "uint")
    INT        = Mapping.new(Cproton::PN_INT, "int")
    CHAR       = Mapping.new(Cproton::PN_CHAR, "char")
    ULONG      = Mapping.new(Cproton::PN_ULONG, "ulong")
    LONG       = Mapping.new(Cproton::PN_LONG, "long", [Integer])
    TIMESTAMP  = Mapping.new(Cproton::PN_TIMESTAMP, "timestamp", [Date, Time])
    FLOAT      = Mapping.new(Cproton::PN_FLOAT, "float")
    DOUBLE     = Mapping.new(Cproton::PN_DOUBLE, "double", [Float])
    DECIMAL32  = Mapping.new(Cproton::PN_DECIMAL32, "decimal32")
    DECIMAL64  = Mapping.new(Cproton::PN_DECIMAL64, "decimal64")
    DECIMAL128 = Mapping.new(Cproton::PN_DECIMAL128, "decimal128")
    UUID       = Mapping.new(Cproton::PN_UUID, "uuid")
    BINARY     = Mapping.new(Cproton::PN_BINARY, "binary")
    STRING     = Mapping.new(Cproton::PN_STRING, "string", [::String,
                                                            Types::UTFString,
                                                            Types::BinaryString])
    SYMBOL     = Mapping.new(Cproton::PN_SYMBOL, "symbol", [::Symbol])
    DESCRIBED  = Mapping.new(Cproton::PN_DESCRIBED, "described", [Types::Described])
    ARRAY      = Mapping.new(Cproton::PN_ARRAY, "array", [Types::UniformArray])
    LIST       = Mapping.new(Cproton::PN_LIST, "list", [::Array])
    MAP        = Mapping.new(Cproton::PN_MAP, "map", [::Hash])

    private

    class << STRING
      def put(data, value)
        # if we have a symbol then convert it to a string
        value = value.to_s if value.is_a?(Symbol)

        isutf = false

        if value.is_a?(Types::UTFString)
          isutf = true
        else
          # For Ruby 1.8 we will just treat all strings as binary.
          # For Ruby 1.9+ we can check the encoding first to see what it is
          if RUBY_VERSION >= "1.9"
            # If the string is ASCII-8BIT then treat is as binary. Otherwise,
            # try to convert it to UTF-8 and, if successful, send as that.
            if value.encoding != Encoding::ASCII_8BIT &&
                value.encode(Encoding::UTF_8).valid_encoding?
              isutf = true
            end
          end
        end

        data.string = value if isutf
        data.binary = value if !isutf
      end
    end

    class << MAP
      def put(data, map, options = {})
        data.put_map
        data.enter
        map.each_pair do |key, value|
          if options[:keys] == :SYMBOL
            SYMBOL.put(data, key)
          else
            data.object = key
          end

          if value.nil?
            data.null
          else
            data.object = value
          end
        end
        data.exit
      end
    end

    class << DESCRIBED
      def put(data, described)
        data.put_described
        data.enter
        data.object = described.descriptor
        data.object = described.value
        data.exit
      end
    end
  end
end