| # 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 |