blob: 1b8df6ed49155297f72a8c79c15900051697e877 [file] [log] [blame]
# 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