diff options
| author | Alan Conway <aconway@apache.org> | 2007-08-27 12:56:29 +0000 |
|---|---|---|
| committer | Alan Conway <aconway@apache.org> | 2007-08-27 12:56:29 +0000 |
| commit | 54b9b8aeb902851cd52825764938e1b012e06a43 (patch) | |
| tree | b48f08f56570a857091dcf6b3276372bb0c0e5a4 /cpp/rubygen/amqpgen.rb | |
| parent | 91b71b5e76868d482a0889758acf0675c871d871 (diff) | |
| download | qpid-python-54b9b8aeb902851cd52825764938e1b012e06a43.tar.gz | |
* rubygen/amqpgen.rb: Performance and API improvements.
Added nodes for all amqp.xml elements except doc, assert and
rule. (They can easily be added.) In particular AmqpDomain
is a proper node, providing a place to do type mapping.
Every node has reader methods for AMQP attributes/elements:
- attr() for each AMQP attribute "attr" returns the string value.
- foos() returns AmqpElements for all the "foo" children.
- foo(name) returns AmqpElements for the named "foo" child
domain() returns an AmqpDomain rather than the string name.
Method names that would clash with Object methods get a trailing "_"
So: class_/classes, method_/methods_, type_/types
Notes:
- no amqp_ prefixes.
- AmqpElement does not inherit REXML::Element,
AmqpElement#xml() to get the REXML element.
Performance: all templates run in 2.8 seconds on my laptop,
compared to almost two minutes previously. Main change was to
replace xpath searches with simple descent of the Amqp model
and cache values selectively based on profiling.
* rubygen/cppgen.rb:
- Updated for amqpgen changes.
- Introduced CppType to manage C++ type information
- Moved all type mapping to CppType/AmqpDomain
Some templates still do their own type calculations, these
should be centralized in CppType so they can be re-used.
* rubygen/templates/*: Updated for new API
* src/qpid/framing/amqp_types_full.h: Added Uuid.h
* xml/cluster.xml: change "type" attribute to "domain"
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@570096 13f79535-47bb-0310-9956-ffa450edef68
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 - - |
