awips2/cotsSource/org.apache.qpid/ruby/lib/qpid/spec010.rb
root 9f19e3f712 Initial revision of AWIPS2 11.9.0-7p5
Former-commit-id: 64fa9254b946eae7e61bbc3f513b7c3696c4f54f
2012-01-06 08:55:05 -06:00

485 lines
9.8 KiB
Ruby

#
# 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.
#
require "qpid/spec"
require 'pathname'
require 'fileutils'
module Qpid::Spec010
include Qpid::Spec
# XXX: workaround for ruby bug/missfeature
Reference = Reference
Loader = Loader
class Spec
ENCODINGS = {
String => "str16",
Fixnum => "int64",
Bignum => "int64",
Float => "float",
NilClass => "void",
Array => "list",
Hash => "map"
}
fields(:major, :minor, :port, :children)
def init()
@controls = {}
@commands = {}
@structs = {}
@types = {}
children.each {|c|
case c
when Control
@controls[c.code] = c
when Command
@commands[c.code] = c
when Struct
@structs[c.code] = c
when Type
@types[c.code] = c unless c.code.nil?
end
}
end
attr_reader :controls, :commands, :structs, :types
def [](key)
return @children[key]
end
def encoding(klass)
if ENCODINGS.has_key?(klass)
return self[ENCODINGS[klass]]
end
for base in klass.__bases__
result = encoding(base)
return result unless result.nil?
end
end
def inspect; "spec"; end
end
class Constant
fields(:name, :value)
attr :parent, true
end
class Type
fields(:name, :code, :fixed, :variable)
attr :parent, true
def present?(value)
if @fixed == 0
return value
else
return !value.nil?
end
end
def encode(codec, value)
codec.send("write_#{name}", value)
end
def decode(codec)
return codec.send("read_#{name}")
end
def inspect; name; end
end
class Domain < Type
fields(:name, :type, :enum)
attr :parent, true
def encode(codec, value)
@type.encode(codec, value)
end
def decode(codec)
return @type.decode(codec)
end
end
class Enum
fields(:choices)
def [](choice)
case choice
when String
choice = choice.to_sym
return choices.find { |c| c.name == choice }
when Symbol
return choices.find { |c| c.name == choice }
else
return choices.find { |c| c.value == choice }
end
end
def method_missing(name, *args)
raise ArgumentError.new("wrong number of arguments") unless args.empty?
return self[name].value
end
end
class Choice
fields(:name, :value)
end
class Composite
fields(:name, :code, :size, :pack, :fields)
attr :parent, true
# Python calls this 'new', but that has special meaning in Ruby
def create(*args)
return Qpid::struct(self, *args)
end
def decode(codec)
codec.read_size(@size)
codec.read_uint16() unless @code.nil?
return Qpid::struct(self, self.decode_fields(codec))
end
def decode_fields(codec)
flags = 0
pack.times {|i| flags |= (codec.read_uint8() << 8*i)}
result = {}
fields.each_index {|i|
f = @fields[i]
if flags & (0x1 << i) != 0
result[f.name] = f.type.decode(codec)
else
result[f.name] = nil
end
}
return result
end
def encode(codec, value)
sc = Qpid::StringCodec.new(@spec)
sc.write_uint16(@code) unless @code.nil?
encode_fields(sc, value)
codec.write_size(@size, sc.encoded.size)
codec.write(sc.encoded)
end
def encode_fields(codec, values)
# FIXME: This could be written cleaner using select
# instead of flags
flags = 0
fields.each_index do |i|
f = fields[i]
flags |= (0x1 << i) if f.type.present?(values[f.name])
end
pack.times { |i| codec.write_uint8((flags >> 8*i) & 0xFF) }
fields.each_index do |i|
f = fields[i]
f.type.encode(codec, values[f.name]) if flags & (0x1 << i) != 0
end
end
def inspect; name; end
end
class Field
fields(:name, :type, :exceptions)
def default()
return nil
end
end
class Struct < Composite
def present?(value)
return !value.nil?
end
end
class Action < Composite; end
class Control < Action
def segment_type
@parent[:segment_type].enum[:control].value
end
def track
@parent[:track].enum[:control].value
end
end
class Command < Action
attr_accessor :payload, :result
def segment_type
@parent["segment_type"].enum["command"].value
end
def track
@parent["track"].enum["command"].value
end
end
class Doc
fields(:type, :title, :text)
end
class Loader010 < Loader
def initialize()
super()
end
def klass
cls = element
until cls.nil?
break if cls.name == "class"
cls = cls.parent
end
return cls
end
def scope
if element.name == "struct"
return nil
else
return class_name
end
end
def class_name
cls = klass
if cls.nil?
return nil
else
return parse_name(cls.attributes["name"].strip)
end
end
def class_code
cls = klass
if cls.nil?
return 0
else
return parse_int(cls.attributes["code"].strip)
end
end
def parse_decl(value)
name = parse_name(value)
s = scope
if s.nil?
return name
else
return :"#{s}_#{name}"
end
end
def parse_code(value)
c = parse_int(value)
if c.nil?
return nil
else
return c | (class_code << 8)
end
end
def parse_type(value)
name = parse_name(value.sub(".", "_"))
cls = class_name
return Reference.new {|spec|
candidates = [name]
candidates << :"#{cls}_#{name}" unless cls.nil?
for c in candidates
child = spec[c]
break unless child.nil?
end
if child.nil?
raise Exception.new("unresolved type: #{name}")
else
child
end
}
end
def load_amqp()
children = nil
for s in ["constant", "type", "domain", "struct", "control",
"command"]
ch = load(s)
if children.nil?
children = ch
else
children += ch
end
children += load("class/#{s}")
end
children += load("class/command/result/struct")
Spec.new(attr("major", :int), attr("minor", :int), attr("port", :int),
children)
end
def load_constant()
Constant.new(attr("name", :decl), attr("value", :int))
end
def load_type()
Type.new(attr("name", :decl), attr("code", :code),
attr("fixed-width", :int), attr("variable-width", :int))
end
def load_domain()
Domain.new(attr("name", :decl), attr("type", :type), load("enum").first)
end
def load_enum()
Enum.new(load("choice"))
end
def load_choice()
Choice.new(attr("name", :name), attr("value", :int))
end
def load_field()
Field.new(attr("name", :name), attr("type", :type))
end
def load_struct()
Struct.new(attr("name", :decl), attr("code", :code), attr("size", :int),
attr("pack", :int), load("field"))
end
def load_action(cls)
cls.new(attr("name", :decl), attr("code", :code), 0, 2, load("field"))
end
def load_control()
load_action(Control)
end
def load_command()
result = attr("type", :type, nil, "result")
result = attr("name", :type, nil, "result/struct") if result.nil?
segs = load("segments")
cmd = load_action(Command)
cmd.result = result
cmd.payload = !segs.empty?
return cmd
end
def load_result()
true
end
def load_segments()
true
end
end
def self.spec_cache(specfile)
File::join(File::dirname(__FILE__), "spec_cache",
File::basename(specfile, ".xml") + ".rb_marshal")
end
# XXX: could be shared
def self.load(spec = nil)
return spec if spec.is_a?(Qpid::Spec010::Spec)
if spec.nil?
# FIXME: Need to add a packaging setup in here so we know where
# the installed spec is going to be.
specfile = nil
if ENV['AMQP_SPEC']
specfile = ENV['AMQP_SPEC']
else
require "qpid/config"
specfile = Qpid::Config.amqp_spec
end
else
specfile = spec
end
specfile_cache = spec_cache(specfile)
# FIXME: Check that cache is newer than specfile
if File::exist?(specfile_cache)
begin
spec = File::open(specfile_cache, "r") do |f|
Marshal::load(f)
end
return spec
rescue
# Ignore, will load from XML
end
end
doc = File::open(specfile, "r") { |f| Document.new(f) }
spec = Loader010.new().load(doc.root)
spec.traverse! do |o|
if o.is_a?(Reference)
o.resolve(spec)
else
o
end
end
spec.children.each { |c| c.parent = spec }
begin
FileUtils::mkdir_p(File::dirname(specfile_cache))
File::open(specfile_cache, "w") { |f| Marshal::dump(spec, f) }
rescue
# Ignore, we are fine without the cached spec
end
return spec
end
end