diff options
Diffstat (limited to 'cpp/rubygen/amqpgen.rb')
-rwxr-xr-x | cpp/rubygen/amqpgen.rb | 296 |
1 files changed, 145 insertions, 151 deletions
diff --git a/cpp/rubygen/amqpgen.rb b/cpp/rubygen/amqpgen.rb index ceaf04090b..068ecf7aae 100755 --- a/cpp/rubygen/amqpgen.rb +++ b/cpp/rubygen/amqpgen.rb @@ -17,220 +17,216 @@ class String # Convert to lowerCaseCapitalizedForm def lcaps() gsub( /\W(\w)/ ) { |m| $1.upcase } end + + def plural() self + (/[xs]$/ === self ? 'es' : 's'); end end # Sort an array by name. -class Array - def sort_by_name() - sort() { |a,b| a.name <=> b.name } - end +module Enumerable + def sort_by_name() sort { |a,b| a.name <=> b.name }; end end -# Add collect to Elements -class Elements - def collect(xpath, &block) - result=[] - each(xpath) { |el| result << yield(el) } - result +# Add functions similar to attr_reader for AMQP attributes/children. +# Symbols that are ruby Object function names (e.g. class) get +# an "_" suffix. +class Module + # Add trailing _ to avoid conflict with Object methods. + def mangle(sym) + (Object.method_defined? sym) ? (sym.to_s+"_").intern : sym + end + + # Add attribute reader for XML attribute. + def amqp_attr_reader(*attrs) + attrs.each { |a| + define_method(mangle(a)) { + @amqp_attr_reader||={ } + @amqp_attr_reader[a] ||= xml.attributes[a.to_s] + } + } end -end - -# An AmqpElement extends (delegates to) a REXML::Element -# -# NB: AmqpElements cache various values, they assume that -# the XML model does not change after the AmqpElement has -# been created. -# -class AmqpElement < DelegateClass(Element) - def initialize(xml, amqp) super(xml); @amqp_parent=amqp; end - attr_reader :amqp_parent - - # Return the name attribute, not the element name. - def name() attributes["name"]; end + # Add 2 child readers: + # elname(name) == child('elname',name) + # elnames() == children('elname') + def amqp_child_reader(*element_names) + element_names.each { |e| + define_method(mangle(e)) { |name| child(e.to_s, name) } + define_method(mangle(e.to_s.plural)) { children(e.to_s) } } + end - def amqp_root() - amqp_parent ? amqp_parent.amqp_root : self; + # When there can only be one child instance + def amqp_single_child_reader(*element_names) + element_names.each { |e| + define_method(mangle(e)) { children(e.to_s)[0] } } end end -# AMQP field element -class AmqpField < AmqpElement - def initialize(xml, amqp) super; end; - # Get AMQP type for a domain name. - def domain_type(name) - @cache_domain_type ||= domain_type_impl(name) - end +# An AmqpElement contains an XML element and provides a convenient +# API to access AMQP data. +# +# NB: AmqpElements cache values from XML, they assume that +# the XML model does not change after the AmqpElement has +# been created. +class AmqpElement - # Get the AMQP type of this field. - def field_type() - d=attributes["domain"] - dt=domain_type d if d - (dt or attributes["type"]) + def wrap(xml) + return nil if ["doc","assert","rule"].include? xml.name + eval("Amqp"+xml.name.caps).new(xml, self) or raise "nil wrapper" end - # determine whether this type is defined as a struct in the spec - def defined_as_struct() - @cache_defined_as_struct ||= defined_as_struct_impl() + public + + def initialize(xml, parent) + @xml, @parent=xml, parent + @children=xml.elements.map { |e| wrap e }.compact + @cache_child={} + @cache_children={} + @cache_children[nil]=@children end -private - def domain_type_impl(name) - domain=elements["/amqp/domain[@name='#{name}']"] - (domain and domain.attributes["type"] or name) - end + attr_reader :parent, :xml, :children + amqp_attr_reader :name, :label - def defined_as_struct_impl() - domain_name = attributes["domain"] - elements["/amqp/domain[@name='#{domain_name}']/struct"] + # List of children of type elname, or all children if elname + # not specified. + def children(elname=nil) + @cache_children[elname] ||= @children.select { |c| elname==c.xml.name } end -end - -# AMQP method element -class AmqpMethod < AmqpElement - def initialize(xml, amqp) super; end - def content() - attributes["content"] + # Look up child of type elname with attribute name. + def child(elname, name) + @cache_child[[elname,name]] ||= children(elname).find { |c| c.name==name } end - def index() attributes["index"]; end + # The root <amqp> element. + def root() @root ||=parent ? parent.root : self; end - def fields() - @cache_fields ||= elements.collect("field") { |f| AmqpField.new(f,self); } - end + def to_s() "#<#{self.class}(#{name})>"; end + def inspect() to_s; end +end - # Responses to this method (0-9) - def responses() - @cache_responses ||= elements.collect("response") { |el| AmqpMethod.new(el,self) } - end +AmqpResponse = AmqpElement - # Methods this method responds to (0-9) - def responds_to() - @cache_responds_to ||= elements.collect("../method/response[@name='#{attributes['name']}']") { |el| - AmqpMethod.new(el.parent, amqp_parent) - } - end +class AmqpDomain < AmqpElement + def initialize(xml, parent) super; end + amqp_attr_reader :type + amqp_single_child_reader :struct - def has_result?() - @cache_has_result ||= elements["result/struct"] + def unalias() + d=self + while (d.type_ != d.name and root.domain(d.type_)) + d=root.domain(d.type_) + end + return d end +end - def result_struct() - @cache_result_struct ||= has_result? ? AmqpStruct.new(elements["result/struct"], self) : nil - end +class AmqpField < AmqpElement + def initialize(xml, amqp) super; end; + def domain() root.domain(xml.attributes["domain"]); end + amqp_single_child_reader :struct +end - def is_server_method?() - @cache_is_server_method ||= elements["chassis[@name='server']"] - end +class AmqpChassis < AmqpElement + def initialize(xml, parent) super; end + amqp_attr_reader :implement +end - def request?() responds_to().empty?; end - def response?() not request?; end +class AmqpConstant < AmqpElement + def initialize(xml, parent) super; end + amqp_attr_reader :value, :datatype end -# AMQP class element. -class AmqpClass < AmqpElement - def initialize(xml,amqp) super; end - def index() attributes["index"]; end - def amqp_methods() - @cache_amqp_methods ||= elements.collect("method") { |el| - AmqpMethod.new(el,self) - }.sort_by_name - end +class AmqpResult < AmqpElement + def initialize(xml, parent) super; end + amqp_single_child_reader :struct +end - # chassis should be "client" or "server" - def amqp_methods_on(chassis) - @cache_amqp_methods_on ||= { } - @cache_amqp_methods_on[chassis] ||= elements.collect("method/chassis[@name='#{chassis}']/..") { |m| AmqpMethod.new(m,self) }.sort_by_name - end +class AmqpStruct < AmqpElement + def initialize(xml, parent) super; end + amqp_attr_reader :size, :type + amqp_child_reader :field + + def result?() parent.xml.name == "result"; end + def domain?() parent.xml.name == "domain"; end end +class AmqpMethod < AmqpElement + def initialize(xml, parent) super; end -# AMQP struct element. -class AmqpStruct < AmqpElement - def initialize(xml,amqp) super; end + amqp_attr_reader :content, :index, :synchronous + amqp_child_reader :field, :chassis,:response + amqp_single_child_reader :result - def type() attributes["type"]; end + def on_chassis?(chassis) child("chassis", chassis); end + def on_client?() on_chassis? "client"; end + def on_server?() on_chassis? "server"; end +end - def size() attributes["size"]; end +class AmqpClass < AmqpElement + def initialize(xml,amqp) super; end + amqp_attr_reader :index - def result?() parent.name == "result"; end + amqp_child_reader :method - def domain?() parent.name == "domain"; end + # FIXME aconway 2007-08-27: REMOVE + def methods_() children("method").sort_by_name; end - def fields() - @cache_fields ||= elements.collect("field") { |f| AmqpField.new(f,self); } + # chassis should be "client" or "server" + def methods_on(chassis) + @methods_on ||= { } + @methods_on[chassis] ||= methods_.select { |m| m.on_chassis? chassis } end end + + # AMQP root element. class AmqpRoot < AmqpElement - - # FIXME aconway - something namespace-related in ruby 1.8.6 - # breaks all the xpath expressions with [@attr] tests. - # Not clear if this is a ruby bug or error in my xpath, - # current workaround is to simply delete the namespace node. - def newDoc(xmlFile) - root=Document.new(File.new(xmlFile)).root - root.delete_namespace - throw "Internal error, FIXME comment in aqmpgen.rb." unless (root.namespaces.empty?) - root - end + def parse(filename) Document.new(File.new(filename)).root; end # Initialize with output directory and spec files from ARGV. def initialize(*specs) - specs.size or raise "No XML spec files." - specs.each { |f| File.exists?(f) or raise "Invalid XML file: #{f}"} - super newDoc(specs.shift), nil - specs.each { |s| # Merge in additional specs - root=newDoc s - merge(self,root) - } + raise "No XML spec files." if specs.empty? + xml=parse(specs.shift) + specs.each { |s| xml_merge(xml, parse(s)) } + super(xml, nil) end - def version() - attributes["major"]+"-"+attributes["minor"] - end + amqp_attr_reader :major, :minor + amqp_child_reader :class, :domain - def amqp_classes() - @cache_amqp_classes ||= elements.collect("class") { |c| - AmqpClass.new(c,self) }.sort_by_name - end + # FIXME aconway 2007-08-27: REMOVE + def classes() children("class").sort_by_name; end + + def version() major + "-" + minor; end - def amqp_structs() - @cache_amqp_structs ||= amqp_result_structs + amqp_domain_structs - end + def domain_structs() domains.map{ |d| d.struct }.compact; end - def amqp_domain_structs() - @cache_amqp_domain_structs ||= elements.collect("domain/struct") { |s| AmqpStruct.new(s, self) } + def result_structs() + methods_.map { |m| m.result and m.result.struct }.compact end - def amqp_result_structs() - @cache_amqp_result_structs ||= amqp_methods.collect { |m| m.result_struct }.compact - end - - # Return all methods on all classes. - def amqp_methods() - @cache_amqp_methods ||= amqp_classes.collect { |c| c.amqp_methods }.flatten; - end + def structs() result_structs+domain_structs; end + def methods_() classes.map { |c| c.methods_ }.flatten; end + # Return all methods on chassis for all classes. - def amqp_methods_on(chassis) - @cache_amqp_methods_on ||= { } - @cache_amqp_methods_on[chassis] ||= amqp_classes.collect { |c| c.amqp_methods_on(chassis) }.flatten + def methods_on(chassis) + @methods_on ||= { } + @methods_on[chassis] ||= classes.map { |c| c.methods_on(chassis) }.flatten end + private + # Merge contents of elements. - def merge(to,from) + def xml_merge(to,from) from.elements.each { |from_child| tag,name = from_child.name, from_child.attributes["name"] to_child=to.elements["./#{tag}[@name='#{name}']"] - to_child ? merge(to_child, from_child) : to.add(from_child.deep_clone) } + to_child ? xml_merge(to_child, from_child) : to.add(from_child.deep_clone) } end - - private :merge - end # Collect information about generated files. @@ -304,5 +300,3 @@ class Generator attr_accessor :out end - - |