summaryrefslogtreecommitdiff
path: root/qpid/cpp/rubygen
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/rubygen')
-rwxr-xr-xqpid/cpp/rubygen/0-10/allsegmenttypes.rb52
-rwxr-xr-xqpid/cpp/rubygen/0-10/exceptions.rb73
-rwxr-xr-xqpid/cpp/rubygen/0-10/handlers.rb47
-rwxr-xr-xqpid/cpp/rubygen/0-10/specification.rb389
-rwxr-xr-xqpid/cpp/rubygen/0-10/typecode.rb117
-rwxr-xr-xqpid/cpp/rubygen/MethodBodyDefaultVisitor.rb53
-rw-r--r--qpid/cpp/rubygen/README.txt17
-rwxr-xr-xqpid/cpp/rubygen/amqpgen.rb550
-rwxr-xr-xqpid/cpp/rubygen/cppgen.rb452
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb45
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb54
-rw-r--r--qpid/cpp/rubygen/framing.0-10/MethodBodyFactory.rb59
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/Operations.rb121
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/OperationsInvoker.rb117
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/Proxy.rb109
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/Session.rb417
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/all_method_bodies.rb39
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/constants.rb208
-rw-r--r--qpid/cpp/rubygen/framing.0-10/frame_body_lists.rb49
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/structs.rb615
-rwxr-xr-xqpid/cpp/rubygen/generate160
21 files changed, 3743 insertions, 0 deletions
diff --git a/qpid/cpp/rubygen/0-10/allsegmenttypes.rb b/qpid/cpp/rubygen/0-10/allsegmenttypes.rb
new file mode 100755
index 0000000000..26363d6a1f
--- /dev/null
+++ b/qpid/cpp/rubygen/0-10/allsegmenttypes.rb
@@ -0,0 +1,52 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class GenAllSegmentTypes < CppGen
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ end
+
+ def generate
+ h_file("tests/allSegmentTypes.h") {
+ include "qpid/amqp_0_10/specification.h"
+ include "qpid/amqp_0_10/Header.h"
+ include "qpid/amqp_0_10/Body.h"
+ genl
+ genl "using namespace qpid::amqp_0_10;"
+ genl
+ scope("template <class Op> size_t allSegmentTypes(Op& op) {"){
+ genl "op(Header());"
+ genl "op(Body());"
+ n = 2;
+ @amqp.classes.each { |c|
+ c.commands.each { |s| genl "op(CommandHolder(#{c.nsname}::#{s.classname}()));" }
+ c.controls.each { |s| genl "op(ControlHolder(#{c.nsname}::#{s.classname}()));" }
+ n += 2
+ }
+ genl "return #{n};"
+ }
+ }
+ end
+end
+
+GenAllSegmentTypes.new($outdir, $amqp).generate();
+
diff --git a/qpid/cpp/rubygen/0-10/exceptions.rb b/qpid/cpp/rubygen/0-10/exceptions.rb
new file mode 100755
index 0000000000..02e3a5d547
--- /dev/null
+++ b/qpid/cpp/rubygen/0-10/exceptions.rb
@@ -0,0 +1,73 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class GenExceptions < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @ns="qpid::amqp_#{@amqp.version.bars}"
+ @dir="qpid/amqp_#{@amqp.version.bars}"
+ end
+
+ def exceptions_for_enum(enum, base, ns, suffix="")
+ enum.choices.each { |c|
+ name=c.name.typename+suffix+"Exception"
+ genl
+ doxygen_comment { genl c.doc }
+ struct(name, "public #{base}") {
+ genl "#{name}(const std::string& msg=std::string())"
+ genl " : #{base}(#{ns}::#{c.name.shout}, msg) {}"
+ protected
+ genl "std::string getPrefix() const { return \"#{name}\"; }"
+ }
+ }
+ end
+
+ def gen_exceptions()
+ h_file("#{@dir}/exceptions") {
+ include "qpid/amqp_0_10/Exception"
+ namespace("#{@ns}") {
+ error_code = @amqp.class_("execution").domain("error-code").enum
+ exceptions_for_enum(error_code, "SessionAbortedException", "execution")
+ genl
+
+ detach_code = @amqp.class_("session").domain("detach-code").enum
+ exceptions_for_enum(detach_code, "SessionDetachedException", "session", "Detached")
+
+ genl
+ exceptions_for_enum(detach_code, "SessionExpiredException", "session", "Expired")
+ genl
+
+ close_code = @amqp.class_("connection").domain("close-code").enum
+ exceptions_for_enum(close_code, "ConnectionException", "connection")
+ }
+ }
+ end
+
+ def generate()
+ gen_exceptions
+ end
+end
+
+GenExceptions.new($outdir, $amqp).generate();
+
+
diff --git a/qpid/cpp/rubygen/0-10/handlers.rb b/qpid/cpp/rubygen/0-10/handlers.rb
new file mode 100755
index 0000000000..981ea890e6
--- /dev/null
+++ b/qpid/cpp/rubygen/0-10/handlers.rb
@@ -0,0 +1,47 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class GenHandlers < CppGen
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @ns="qpid::amqp_#{@amqp.version.bars}"
+ @dir="qpid/amqp_#{@amqp.version.bars}"
+ end
+
+ def action_handler(type, actions)
+ genl
+ bases=actions.map { |a| "public #{a.fqclassname}::Handler" }
+ struct("#{type}Handler", *bases) { }
+ end
+
+ def generate()
+ h_file("#{@dir}/handlers.h") {
+ include "#{@dir}/specification"
+ namespace("#{@ns}") {
+ action_handler "Command", @amqp.collect_all(AmqpCommand)
+ action_handler "Control", @amqp.collect_all(AmqpControl)
+ }
+ }
+ end
+end
+
+GenHandlers.new($outdir, $amqp).generate()
diff --git a/qpid/cpp/rubygen/0-10/specification.rb b/qpid/cpp/rubygen/0-10/specification.rb
new file mode 100755
index 0000000000..ef193f5fd0
--- /dev/null
+++ b/qpid/cpp/rubygen/0-10/specification.rb
@@ -0,0 +1,389 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+
+# Dummy element representing an unknown struct type.
+class UnknownStruct
+ def visitable?() true end
+ def fqclassname() "UnknownStruct" end
+end
+
+# Dummy element representing a session.header field
+class SessionHeaderField
+ def amqp2cpp() "session::Header" end
+ def cppname() "sessionHeader" end
+ def name() "session-header" end
+end
+
+class Specification < CppGen
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @ns="qpid::amqp_#{@amqp.version.bars}"
+ @dir="qpid/amqp_#{@amqp.version.bars}"
+ end
+
+ # domains
+
+ def domain_h(d)
+ genl
+ typename=d.name.typename
+ if d.enum
+ scope("enum #{typename} {", "};") {
+ genl d.enum.choices.map { |c|
+ "#{c.name.constname} = #{c.value}" }.join(",\n")
+ }
+ scope("inline SerializeAs<#{typename}, uint8_t> serializable(#{typename}& e) {") {
+ genl "return SerializeAs<#{typename}, uint8_t>(e);"
+ }
+ else
+ genl "typedef #{d.amqp2cpp} #{typename};"
+ end
+ end
+
+ def visitable?(x) x.code and x.size=="4" end
+
+ # Used by structs, commands and controls.
+ def action_struct_h(x, base, consts, &block)
+ genl
+ base = visitable?(x) ? ["public #{base}"] : []
+ struct(x.classname, *base) {
+ x.fields.each { |f| genl "#{f.amqp2cpp} #{f.cppname};" }
+ genl
+ genl "static const char* NAME;"
+ consts.each {
+ |c| genl "static const uint8_t #{c.upcase}=#{(x.send c) or 0};"
+ }
+ genl "static const uint8_t CLASS_CODE=#{x.containing_class.nsname}::CODE;"
+ genl "static const char* CLASS_NAME;"
+ ctor_decl("explicit #{x.classname}", x.parameters(true))
+
+ if visitable? x
+ genl "void accept(Visitor&);"
+ genl "void accept(ConstVisitor&) const;"
+ end
+
+ if (x.fields.empty?)
+ genl "template <class S> void serialize(S&) {}"
+ else
+ scope("template <class S> void serialize(S& s) {") {
+ gen "s"; x.fields.each { |f| gen "(#{f.cppname})"}; genl ";"
+ }
+ end
+ genl
+ yield if block
+ }
+ case x
+ when AmqpCommand then packer = "CommandPacker"
+ when AmqpControl then packer = "Packer"
+ when AmqpStruct then packer = "SizedPacker"
+ end
+ genl "inline #{packer}<#{x.classname}> serializable(#{x.classname}& x) { return #{packer}<#{x.classname}>(x); }" unless x.respond_to? :pack and x.pack == "0"
+ genl "std::ostream& operator << (std::ostream&, const #{x.classname}&);"
+ genl "bool operator==(const #{x.classname}&, const #{x.classname}&);"
+ end
+
+ def action_struct_cpp(x, &block)
+ genl
+ genl "const char* #{x.classname}::NAME=\"#{x.fqname}\";"
+ genl "const char* #{x.classname}::CLASS_NAME=#{x.containing_class.nsname}::NAME;"
+ genl
+ ctor=x.classname+"::"+x.classname
+ ctor_defn(ctor, x.parameters, x.initializers) {}
+
+ if visitable? x
+ genl "void #{x.classname}::accept(Visitor& v) { v.visit(*this); }"
+ genl "void #{x.classname}::accept(ConstVisitor& v) const { v.visit(*this); }"
+ end
+ genl
+ scope("std::ostream& operator << (std::ostream& o, const #{x.classname}&#{"x" unless x.fields.empty?}) {") {
+ genl "o << \"#{x.fqname}[\";";
+ x.fields.each{ |f| genl "o << \" #{f.name}=\" << x.#{f.cppname};" }
+ genl "o << \"]\";"
+ genl "return o;"
+ }
+ yield if block
+ end
+
+ # structs
+
+ def struct_h(s) action_struct_h(s, "Struct", ["size","pack","code"]); end
+ def struct_cpp(s) action_struct_cpp(s) end
+
+ # command and control
+
+ def action_h(a)
+ action_struct_h(a, a.base, ["code"]) {
+ struct("Handler") {
+ scope("void #{a.funcname}(", ");") {
+ genl a.parameters.join(",\n")
+ }
+ }
+ function_defn("template <class T> void invoke", ["T& target"], "const") {
+ genl "target.#{a.funcname}(#{a.values.join(', ')} );"
+ }
+ }
+ end
+
+ def action_cpp(a)
+ action_struct_cpp(a) {
+ scope("void #{a.classname}::Handler::#{a.funcname}(", ")") {
+ genl a.unused_parameters.join(",\n")
+ }
+ scope {
+ genl "assert(0);"
+ genl "throw NotImplementedException(QPID_MSG(\"#{a.fqname} not implemented.\"));"
+ }
+ }
+ end
+
+ # Types that must be generated early because they are used by other types.
+ def pregenerate?(x) not @amqp.used_by[x.fqname].empty?; end
+
+ def pregenerate_class?(c)
+ c.children.select{ |t| (t.is_a? AmqpStruct or t.is_a? AmqpDomain) and pregenerate? t}
+ end
+
+ # Typedefs, enums and forward declarations for classes.
+ def gen_specification_fwd()
+ h_file("#{@dir}/specification_fwd") {
+ include "#{@dir}/built_in_types"
+ namespace(@ns) {
+ # Top level
+ @amqp.domains.each { |d|
+ # segment-type and track are are built in
+ domain_h d unless ["track","segment-type"].include?(d.name)
+ }
+ each_class_ns { |c|
+ genl "const uint8_t CODE=#{c.code};" # class code
+ genl "extern const char* NAME;"
+ c.each_descendant { |x|
+ case x
+ when AmqpDomain then domain_h x
+ when AmqpStruct then genl "class #{x.classname};"
+ when AmqpAction then genl "class #{x.classname};"
+ end
+ }
+ }
+ }
+ }
+ end
+
+ # Generate struct definitions into a separate header file so the
+ # can be included by StructHolder.h without circularity.
+ def gen_structs()
+ h_file("#{@dir}/structs") {
+ include "#{@dir}/specification_fwd"
+ include "#{@dir}/Map.h"
+ include "#{@dir}/Array.h"
+ include "#{@dir}/Struct.h"
+ include "#{@dir}/UnknownStruct.h"
+ include "#{@dir}/Packer.h"
+ namespace(@ns) {
+ each_class_ns { |c|
+ c.collect_all(AmqpStruct).each { |s| struct_h s }
+ }
+ }
+ }
+
+ cpp_file("#{@dir}/structs") {
+ include "#{@dir}/structs"
+ include "#{@dir}/StructHolder"
+ namespace(@ns) {
+ each_class_ns { |c|
+ c.collect_all(AmqpStruct).each { |s| struct_cpp(s) }
+ }
+ }
+ }
+ end
+
+ # Generate the specification files
+ def gen_specification()
+ h_file("#{@dir}/specification") {
+ include "#{@dir}/specification_fwd.h"
+ include "#{@dir}/Map.h"
+ include "#{@dir}/Array.h"
+ include "#{@dir}/UnknownType.h"
+ include "#{@dir}/Struct32"
+ include "#{@dir}/Control.h"
+ include "#{@dir}/Command.h"
+ include "#{@dir}/Packer.h"
+ include "<iosfwd>"
+ namespace(@ns) {
+ each_class_ns { |c|
+ c.collect_all(AmqpAction).each { |a| action_h a }
+ }
+ }}
+
+ cpp_file("#{@dir}/specification") {
+ include "#{@dir}/specification"
+ include "#{@dir}/exceptions"
+ include "<iostream>"
+ ["Command","Control", "Struct"].each { |x| include "#{@dir}/Apply#{x}" }
+ namespace(@ns) {
+ each_class_ns { |c|
+ genl "const char* NAME=\"#{c.fqname}\";"
+ c.actions.each { |a| action_cpp a}
+ }
+ }
+ }
+ end
+
+ def gen_proxy()
+ h_file("#{@dir}/ProxyTemplate.h") {
+ include "#{@dir}/specification"
+ namespace(@ns) {
+ genl "template <class F, class R=typename F::result_type>"
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", "ProxyTemplate") {
+ public
+ genl "ProxyTemplate(F f=F()) : functor(f) {}"
+ @amqp.classes.each { |c|
+ c.actions.each { |a|
+ genl
+ function_defn("R #{a.funcname}", a.parameters) {
+ var=a.name.funcname
+ args = a.arguments.empty? ? "" : "("+a.arguments.join(", ")+")"
+ genl("#{a.fqclassname} #{var}#{args};")
+ genl "return functor(#{var});"
+ }
+ }
+ }
+ private
+ genl "F functor;"
+ }
+ }
+ }
+ end
+
+ def visitor_interface_h(base, subs, is_const)
+ name="#{is_const ? 'Const' : ''}#{base}Visitor"
+ const=is_const ? "const " : ""
+ struct(name) {
+ genl "virtual ~#{name}() {}"
+ genl "typedef #{const}#{base} BaseType;"
+ subs.each{ |s|
+ genl "virtual void visit(#{const}#{s.fqclassname}&) = 0;"
+ }}
+ end
+
+ def visitor_impl(base, subs, is_const)
+ name="#{is_const ? 'Const' : ''}#{base}Visitor"
+ const=is_const ? "const " : ""
+ genl "template <class F>"
+ struct("ApplyVisitor<#{name}, F>", "public ApplyVisitorBase<#{name}, F>") {
+ subs.each{ |s|
+ genl "virtual void visit(#{const}#{s.fqclassname}& x) { this->invoke(x); }"
+ }}
+ end
+
+ def gen_visitor(base, subs)
+ h_file("#{@dir}/#{base}Visitor.h") {
+ include base=="Struct" ? "#{@dir}/structs" : "#{@dir}/specification"
+ namespace("#{@ns}") {
+ visitor_interface_h(base, subs, false)
+ visitor_interface_h(base, subs, true)
+ }}
+
+ h_file("#{@dir}/Apply#{base}.h") {
+ include "#{@dir}/#{base}Visitor.h"
+ include "#{@dir}/apply.h"
+ namespace("#{@ns}") {
+ visitor_impl(base, subs, false)
+ visitor_impl(base, subs, true)
+ }
+ }
+ end
+
+ def gen_holder(base, subs)
+ name=base+"Holder"
+ h_file("#{@dir}/#{name}") {
+ include "#{@dir}/Apply#{base}"
+ include "#{@dir}/Holder"
+ include base=="Struct" ? "#{@dir}/structs" : "#{@dir}/specification"
+ namespace(@ns){
+ namespace("#{base.downcase}_max") {
+ genl "static const size_t MAX000=0;"
+ last="MAX000"
+ subs.each { |s|
+ sizeof="sizeof(#{s.fqclassname})"
+ genl "static const size_t #{last.succ} = #{sizeof} > #{last} ? #{sizeof} : #{last};"
+ last.succ!
+ }
+ genl "static const int MAX=#{last};"
+ }
+ holder_base="amqp_0_10::Holder<#{name}, #{base}, #{base.downcase}_max::MAX>"
+ struct("#{name}", "public #{holder_base}") {
+ genl "#{name}() {}"
+ genl "template <class T> explicit #{name}(const T& t) : #{holder_base}(t) {}"
+ genl "using #{holder_base}::operator=;"
+ genl "void set(uint8_t classCode, uint8_t code);"
+ }
+ genl
+ genl "std::ostream& operator<<(std::ostream& o, const #{name}& h);"
+ }
+ }
+
+ cpp_file("#{@dir}/#{name}") {
+ include "#{@dir}/#{name}"
+ include "#{@dir}/exceptions.h"
+ namespace(@ns) {
+ genl "using framing::in_place;"
+ genl
+ scope("void #{name}::set(uint8_t classCode, uint8_t code) {") {
+ genl "uint16_t key=(classCode<<8)+code;"
+ scope ("switch(key) {") {
+ subs.each { |s|
+ genl "case 0x#{s.full_code.to_s(16)}: *this=in_place<#{s.fqclassname}>(); break;" unless (s.is_a? UnknownStruct)
+ }
+ genl "default: "
+ indent {
+ if (base=="Struct")
+ genl "*this=in_place<UnknownStruct>(classCode, code);"
+ else
+ genl "throw CommandInvalidException(QPID_MSG(\"Invalid class-#{base.downcase} key \" << std::hex << key));"
+ end
+ }
+ }
+ }
+ genl
+ genl "std::ostream& operator<<(std::ostream& o, const #{name}& h) { return h.get() ? (o << *h.get()) : (o << \"<empty #{name}>\"); }"
+ }
+ }
+ end
+
+ def gen_visitable(base, subs)
+ subs << UnknownStruct.new if base=="Struct" # Extra case for unknown structs.
+ gen_holder(base, subs)
+ gen_visitor(base, subs)
+ end
+
+ def generate
+ gen_specification_fwd
+ gen_specification
+ gen_proxy
+ gen_structs
+ gen_visitable("Command", @amqp.collect_all(AmqpCommand))
+ gen_visitable("Control", @amqp.collect_all(AmqpControl))
+ gen_visitable("Struct", @amqp.collect_all(AmqpStruct).select { |s| s.code})
+ end
+end
+
+Specification.new($outdir, $amqp).generate();
diff --git a/qpid/cpp/rubygen/0-10/typecode.rb b/qpid/cpp/rubygen/0-10/typecode.rb
new file mode 100755
index 0000000000..0ab9c4be5d
--- /dev/null
+++ b/qpid/cpp/rubygen/0-10/typecode.rb
@@ -0,0 +1,117 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class TypeCode < CppGen
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @ns="qpid::amqp_#{@amqp.version.bars}"
+ @dir="qpid/amqp_#{@amqp.version.bars}"
+ @types = @amqp.collect_all(AmqpType).select { |t| t.code }
+
+ end
+
+ def type_for_code_h()
+ h_file("#{@dir}/TypeForCode") {
+ include "#{@dir}/built_in_types.h"
+ include "#{@dir}/UnknownType.h"
+ namespace(@ns) {
+ genl
+ genl "template <uint8_t Code> struct TypeForCode;"
+ genl
+ @types.each { |t|
+ genl "template <> struct TypeForCode<#{t.code}> { typedef #{t.typename} type; };"
+ }
+ genl
+ genl "template <class V> typename V::result_type"
+ scope("apply_visitor(V& visitor, uint8_t code) {") {
+ scope("switch (code) {", "}") {
+ @types.each { |t|
+ genl "case #{t.code}: return visitor((#{t.typename}*)0);"
+ }
+ genl "default: return visitor((UnknownType*)0);"
+ }
+ }
+ genl
+ genl "std::string typeName(uint8_t code);"
+ }
+ }
+ end
+
+ def type_for_code_cpp()
+ cpp_file("#{@dir}/TypeForCode") {
+ include "<string>"
+ include "<sstream>"
+ namespace(@ns) {
+ namespace("") {
+ struct("Names") {
+ scope("Names() {") {
+ scope("for (int i =0; i < 256; ++i) {") {
+ genl "std::ostringstream os;"
+ genl "os << \"UnknownType<\" << i << \">\";"
+ genl "names[i] = os.str();"
+ }
+ @types.each { |t| genl "names[#{t.code}] = \"#{t.name}\";" }
+ }
+ genl "std::string names[256];"
+ }
+ genl "Names names;"
+ }
+ genl "std::string typeName(uint8_t code) { return names.names[code]; }"
+ }}
+ end
+
+ def code_for_type_h()
+ name="#{@dir}/CodeForType"
+ h_file(name) {
+ include "#{@dir}/built_in_types.h"
+
+ namespace(@ns) {
+ genl
+ genl "template <class T> struct CodeForType;"
+ genl
+ @types.each { |t|
+ genl "template <> struct CodeForType<#{t.typename}> { static const uint8_t value; };"
+ }
+ genl
+ genl "template <class T> uint8_t codeFor(const T&) { return CodeForType<T>::value; }"
+ }
+ }
+
+ cpp_file(name) {
+ include name
+ namespace(@ns) {
+ @types.each { |t|
+ genl "const uint8_t CodeForType<#{t.typename}>::value=#{t.code};"
+ }
+ }
+ }
+ end
+
+ def generate
+ type_for_code_h
+ type_for_code_cpp
+ code_for_type_h
+ end
+end
+
+TypeCode.new($outdir, $amqp).generate();
+
diff --git a/qpid/cpp/rubygen/MethodBodyDefaultVisitor.rb b/qpid/cpp/rubygen/MethodBodyDefaultVisitor.rb
new file mode 100755
index 0000000000..11dbcb8f83
--- /dev/null
+++ b/qpid/cpp/rubygen/MethodBodyDefaultVisitor.rb
@@ -0,0 +1,53 @@
+#!/usr/bin/env 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.
+#
+
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class MethodBodyDefaultVisitorGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ set_classname("qpid::framing::MethodBodyDefaultVisitor")
+ end
+
+ def generate()
+ h_file(@filename) {
+ include "qpid/framing/MethodBodyConstVisitor"
+ namespace(@namespace) {
+ genl
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", @classname, "public MethodBodyConstVisitor") {
+ genl "public:"
+ genl "virtual void defaultVisit() = 0;"
+ @amqp.methods_.each { |m|
+ genl "virtual void visit(const #{m.body_name}&);" }
+ }}}
+
+ cpp_file(@filename) {
+ include(@filename)
+ namespace(@namespace) {
+ @amqp.methods_.each { |m|
+ genl "void #{@classname}::visit(const #{m.body_name}&) { defaultVisit(); }"
+ }}}
+ end
+end
+
+MethodBodyDefaultVisitorGen.new($outdir, $amqp).generate();
+
diff --git a/qpid/cpp/rubygen/README.txt b/qpid/cpp/rubygen/README.txt
new file mode 100644
index 0000000000..a1fd6cfec8
--- /dev/null
+++ b/qpid/cpp/rubygen/README.txt
@@ -0,0 +1,17 @@
+RUBY CODE GENERATOR
+
+Run ./generate for usage.
+Examples in samples/
+
+For example:
+ ./generate . ../../specs/amqp.0-9.xml samples/Proxy.rb
+will generate
+
+
+
+
+
+
+
+
+
diff --git a/qpid/cpp/rubygen/amqpgen.rb b/qpid/cpp/rubygen/amqpgen.rb
new file mode 100755
index 0000000000..20aac35194
--- /dev/null
+++ b/qpid/cpp/rubygen/amqpgen.rb
@@ -0,0 +1,550 @@
+# 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.
+#
+# Generic AMQP code generation library.
+#
+# TODO aconway 2008-02-21:
+#
+# The amqp_attr_reader and amqp_child_reader for each Amqp* class
+# should correspond exactly to ampq.dtd. Currently they are more
+# permissive so we can parse 0-10 preview and 0-10 final XML.
+#
+# Code marked with "# preview" should be removed/modified when final 0-10
+# is complete and we are ready to remove preview-related code.
+#
+
+require 'delegate'
+require 'rexml/document'
+require 'pathname'
+require 'set'
+include REXML
+
+# Handy String functions for converting names.
+class String
+ # Convert to CapitalizedForm.
+ def caps() gsub( /(^|\W)(\w)/ ) { |m| $2.upcase } end
+
+ # Convert to underbar_separated_form.
+ def bars() tr('- .','_'); end
+
+ # Convert to ALL_UPPERCASE_FORM
+ def shout() bars.upcase; end
+
+ # 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.
+module Enumerable
+ def sort_by_name() sort { |a,b| a.name <=> b.name }; end
+end
+
+# 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)
+ sym = (sym.to_s+"_").to_sym if (Object.method_defined?(sym) or sym == :type)
+ sym
+ end
+
+ # Add attribute reader for XML attribute.
+ def amqp_attr_reader(*attrs)
+ attrs.each { |a|
+ case a
+ when Symbol
+ define_method(mangle(a)) {
+ @amqp_attr_reader||={ }
+ @amqp_attr_reader[a] ||= xml.attributes[a.to_s]
+ }
+ when Hash
+ a.each { |attr, default|
+ define_method(mangle(attr)) {
+ @amqp_attr_reader||={ }
+ value = xml.attributes[attr.to_s]
+ if value
+ @amqp_attr_reader[attr] ||= value
+ else
+ @amqp_attr_reader[attr] ||= default
+ end
+ }
+ }
+ end
+ }
+ 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
+
+ # 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
+
+# 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
+
+ def wrap(xml)
+ return nil if ["assert","rule"].include? xml.name
+ eval("Amqp"+xml.name.caps).new(xml, self) or raise "nil wrapper"
+ end
+
+ public
+
+ def initialize(xml, parent)
+ @xml, @parent=xml, parent
+ @children=xml.elements.map { |e| wrap e }.compact
+ @cache_child={}
+ @cache_child_named={}
+ @cache_children={}
+ @cache_children[nil]=@children
+ end
+
+ attr_reader :parent, :xml, :children, :doc
+ amqp_attr_reader :name, :label
+
+ # List of children of type elname, or all children if elname
+ # not specified.
+ def children(elname=nil)
+ if elname
+ @cache_children[elname] ||= @children.select { |c| elname==c.xml.name }
+ else
+ @children
+ end
+ end
+
+ def each_descendant(&block)
+ yield self
+ @children.each { |c| c.each_descendant(&block) }
+ end
+
+ def collect_all(amqp_type)
+ collect=[]
+ each_descendant { |d| collect << d if d.is_a? amqp_type }
+ collect
+ end
+
+ # 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
+
+ # Look up any child with name
+ def child_named(name)
+ @cache_child_named[name] ||= @children.find { |c| c.name==name }
+ end
+
+ # The root <amqp> element.
+ def root() @root ||=parent ? parent.root : self; end
+
+ def to_s() "#<#{self.class}(#{fqname})>"; end
+ def inspect() to_s; end
+
+ # Text of doc child if there is one.
+ def doc() d=xml.elements["doc"]; d and d.text; end
+
+ def fqname()
+ throw "fqname: #{self} #{parent.fqname} has no name" unless name
+ p=parent && parent.fqname
+ p ? p+"."+name : name;
+ end
+
+ def containing_class()
+ return self if is_a? AmqpClass
+ return parent && parent.containing_class
+ end
+
+ # 0-10 array domains are missing element type information, add it here.
+ ArrayTypes={
+ "str16-array" => "str-16",
+ "amqp-host-array" => "connection.amqp-host-url",
+ "command-fragments" => "session.command-fragment",
+ "in-doubt" => "dtx.xid",
+ "tx-publish" => "str-8",
+ "queues" => "str-8"
+ }
+
+ def array_type(name)
+ return ArrayTypes[name] if ArrayTypes[name]
+ raise "Missing ArrayType entry for " + name
+ end
+
+end
+
+class AmqpResponse < AmqpElement
+ def initialize(xml, parent) super; end
+ def fqname() (parent ? parent.dotted_name+"." : "") + "response"; end
+end
+
+class AmqpDoc < AmqpElement
+ def initialize(xml,parent) super; end
+ def text() @xml.text end
+end
+
+class AmqpChoice < AmqpElement
+ def initialize(xml,parent) super; end
+ amqp_attr_reader :name, :value
+end
+
+class AmqpEnum < AmqpElement
+ def initialize(xml,parent) super; end
+ amqp_child_reader :choice
+end
+
+class AmqpDomain < AmqpElement
+ def initialize(xml, parent)
+ super
+ root.used_by[uses].push(fqname) if uses and uses.index('.')
+ end
+
+ amqp_attr_reader :type
+ amqp_single_child_reader :struct # preview
+ amqp_single_child_reader :enum
+
+ def uses() type_=="array" ? ArrayTypes[name] : type_; end
+end
+
+class AmqpException < AmqpElement
+ def initialize(xml, amqp) super; end;
+ amqp_attr_reader :error_code
+end
+
+class AmqpField < AmqpElement
+ def initialize(xml, amqp)
+ super;
+ root.used_by[type_].push(parent.fqname) if type_ and type_.index('.')
+ end
+ amqp_single_child_reader :struct # preview
+ amqp_child_reader :exception
+ amqp_attr_reader :type, :default, :code, :required
+end
+
+class AmqpChassis < AmqpElement # preview
+ def initialize(xml, parent) super; end
+ amqp_attr_reader :implement
+end
+
+class AmqpConstant < AmqpElement
+ def initialize(xml, parent) super; end
+ amqp_attr_reader :value, :class
+end
+
+class AmqpResult < AmqpElement
+ def initialize(xml, parent) super; end
+ amqp_single_child_reader :struct # preview
+ amqp_attr_reader :type
+ def name() "result"; end
+end
+
+class AmqpEntry < AmqpElement
+ def initialize(xml,parent) super; end
+ amqp_attr_reader :type
+end
+
+class AmqpHeader < AmqpElement
+ def initialize(xml,parent) super; end
+ amqp_child_reader :entry
+ amqp_attr_reader :required
+end
+
+class AmqpBody < AmqpElement
+ def initialize(xml,parent) super; end
+ amqp_attr_reader :required
+end
+
+class AmqpSegments < AmqpElement
+ def initialize(xml,parent) super; end
+ amqp_child_reader :header, :body
+end
+
+class AmqpStruct < AmqpElement
+ def initialize(xml, parent) super; end
+ amqp_attr_reader :type # preview
+ amqp_attr_reader :size, :code, :pack
+ 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_attr_reader :content, :index, :synchronous
+ amqp_child_reader :field, :chassis,:response
+ amqp_single_child_reader :result
+
+ def on_chassis?(chassis) child("chassis", chassis); end
+ def on_client?() on_chassis? "client"; end
+ def on_server?() on_chassis? "server"; end
+end
+
+# preview: Map command/control to preview method.
+class AmqpFakeMethod < AmqpMethod
+ def initialize(action)
+ super(action.xml, action.parent);
+ @action=action
+ end
+
+ def content() return "1" if @action.is_a? AmqpCommand and @action.segments end
+ def index() @action.code end
+ def code() @action.code end
+ def synchronous() end
+ def on_chassis?(chassis)
+ @action.received_by?(chassis)
+ end
+ def pack() "2" end # Encode pack=2, size=4 struct
+ def size() "4" end
+end
+
+class AmqpImplement < AmqpElement
+ def initialize(xml,amqp) super; end
+ amqp_attr_reader :handle, :send
+end
+
+class AmqpRole < AmqpElement
+ def initialize(xml,amqp) super; end
+ amqp_attr_reader :implement
+end
+
+# Base class for command and control.
+class AmqpAction < AmqpElement
+ def initialize(xml,amqp) super; end
+ amqp_child_reader :implement, :field, :response
+ amqp_attr_reader :code
+ def implement?(role)
+ # we can't use xpath for this because it triggers a bug in some
+ # versions of ruby, including version 1.8.6.110
+ xml.elements.each {|el|
+ return true if el.name == "implement" and el.attributes["role"] == role
+ }
+ return false
+ end
+ def received_by?(client_or_server)
+ return (implement?(client_or_server) or implement?("sender") or implement?("receiver"))
+ end
+ def pack() "2" end
+ def size() "4" end # Encoded as a size 4 Struct
+end
+
+class AmqpControl < AmqpAction
+ def initialize(xml,amqp) super; end
+end
+
+class AmqpCommand < AmqpAction
+ def initialize(xml,amqp) super; end
+ amqp_child_reader :exception
+ amqp_single_child_reader :result, :segments
+end
+
+class AmqpClass < AmqpElement
+ def initialize(xml,amqp) super; end
+
+ amqp_attr_reader :index # preview
+
+ amqp_child_reader :struct, :domain, :control, :command, :role, :method
+ amqp_attr_reader :code
+
+ def actions() controls+commands; end
+
+ # preview - command/control as methods
+ def methods_()
+ return (controls + commands).map { |a| AmqpFakeMethod.new(a) }
+ end
+
+ def method(name)
+ a = (command(name) or control(name))
+ return AmqpFakeMethod.new(a)
+ end
+
+ # chassis should be "client" or "server"
+ def methods_on(chassis) # preview
+ @methods_on ||= { }
+ @methods_on[chassis] ||= methods_.select { |m| m.on_chassis? chassis }
+ end
+
+ # FIXME aconway 2008-04-11:
+ def l4?() # preview
+ !["connection", "session", "execution"].include?(name) && !control?
+ end
+
+ # FIXME aconway 2008-04-11:
+ def control?()
+ ["connection", "session"].include?(name)
+ end
+end
+
+class AmqpType < AmqpElement
+ def initialize(xml,amqp) super; end
+ amqp_attr_reader :code, :fixed_width, :variable_width
+end
+
+class AmqpXref < AmqpElement
+ def initialize(xml,amqp) super; end
+end
+
+# AMQP root element.
+class AmqpRoot < AmqpElement
+ amqp_attr_reader :major, :minor, :port, :comment
+ amqp_child_reader :doc, :type, :struct, :domain, :constant, :class
+
+ def get_root(x)
+ case x
+ when Element then x
+ when Document then x.root
+ else Document.new(x).root
+ end
+ end
+
+ # Initialize with output directory and spec files from ARGV.
+ def initialize(*specs)
+ raise "No XML spec files." if specs.empty?
+ xml=get_root(specs.shift)
+ specs.each { |s| xml_merge(xml, get_root(s)) }
+ @used_by=Hash.new{ |h,k| h[k]=[] }
+ super(xml, nil)
+ end
+
+ attr_reader :used_by
+
+ def merge(root) xml_merge(xml, root.xml); end
+
+ def version() major + "-" + minor; end
+
+ def methods_() classes.map { |c| c.methods_ }.flatten; end
+
+ #preview
+ # Return all methods on chassis for all classes.
+ def methods_on(chassis)
+ @methods_on ||= { }
+ @methods_on[chassis] ||= classes.map { |c| c.methods_on(chassis) }.flatten
+ end
+
+ def fqname() nil; end
+
+ private
+
+ # Merge contents of elements.
+ 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 ? xml_merge(to_child, from_child) : to.add(from_child.deep_clone) }
+ end
+end
+
+# Collect information about generated files.
+class GenFiles
+ @@files = Set.new
+ @@public_api = []
+ def GenFiles.add(f) @@files.add(f); end
+ def GenFiles.get() @@files; end
+ def GenFiles.public_api(file) @@public_api << file; end
+ def GenFiles.public_api?(file) @@public_api.find { |f| f == file }; end
+end
+
+# Base class for code generators.
+# Supports setting a per-line prefix, useful for e.g. indenting code.
+#
+class Generator
+ # Takes directory for output or "-", meaning print file names that
+ # would be generated.
+ def initialize (outdir, amqp)
+ @outdir=outdir[0]
+ @apidir=outdir[1]
+ @amqp=amqp
+ raise "outdir is not an array" unless outdir.class == Array
+ @prefix=[''] # For indentation or comments.
+ @indentstr=' ' # One indent level.
+ @outdent=2
+ end
+
+ # Declare next file to be public API
+ def public_api(file) GenFiles.public_api(file); end
+
+ # Create a new file, set @out.
+ def file(file, &block)
+ GenFiles.add(file)
+ dir = GenFiles.public_api?(file) ? @apidir : @outdir
+ if (dir != "-")
+ @path=Pathname.new "#{dir}/#{file}"
+ @path.parent.mkpath
+ @out=String.new # Generate in memory first
+ yield if block
+ if @path.exist? and @path.read == @out
+ puts "Skipped #{@path} - unchanged" # Dont generate if unchanged
+ else
+ @path.open('w') { |f| f << @out }
+ puts "Generated #{@path}"
+ end
+ end
+ end
+
+ # Append multi-line string to generated code, prefixing each line.
+ def gen(str)
+ str.each_line { |line|
+ @out << @prefix.last unless @midline
+ @out << line
+ @midline = nil
+ }
+ # Note if we stopped mid-line
+ @midline = /[^\n]\z/ === str
+ end
+
+ # Append str + '\n' to generated code.
+ def genl(str="") gen str+"\n"; end
+
+ # Generate code with added prefix.
+ def prefix(add, &block)
+ @prefix.push @prefix.last+add
+ if block then yield; endprefix; end
+ end
+
+ def endprefix()
+ @prefix.pop
+ end
+
+ # Generate indented code
+ def indent(n=1,&block) prefix(@indentstr * n,&block); end
+ alias :endindent :endprefix
+
+ # Generate outdented code
+ def outdent(&block)
+ @prefix.push @prefix.last[0...-2]
+ if block then yield; endprefix; end
+ end
+ alias :endoutdent :endprefix
+
+ attr_accessor :out
+end
+
diff --git a/qpid/cpp/rubygen/cppgen.rb b/qpid/cpp/rubygen/cppgen.rb
new file mode 100755
index 0000000000..7dc21fe1bc
--- /dev/null
+++ b/qpid/cpp/rubygen/cppgen.rb
@@ -0,0 +1,452 @@
+#!/usr/bin/ruby
+#
+# General purpose C++ code generation.
+#
+require 'amqpgen'
+require 'set'
+
+Copyright=<<EOS
+/*
+ *
+ * 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.
+ *
+ */
+
+///
+/// This file was automatically generated from the AMQP specification.
+/// Do not edit.
+///
+
+
+EOS
+
+CppKeywords = Set.new(["and", "and_eq", "asm", "auto", "bitand",
+ "bitor", "bool", "break", "case", "catch", "char",
+ "class", "compl", "const", "const_cast", "continue",
+ "default", "delete", "do", "DomainInfo", "double",
+ "dynamic_cast", "else", "enum", "explicit", "extern",
+ "false", "float", "for", "friend", "goto", "if",
+ "inline", "int", "long", "mutable", "namespace", "new",
+ "not", "not_eq", "operator", "or", "or_eq", "private",
+ "protected", "public", "register", "reinterpret_cast",
+ "return", "short", "signed", "sizeof", "static",
+ "static_cast", "struct", "switch", "template", "this",
+ "throw", "true", "try", "typedef", "typeid",
+ "typename", "union", "unsigned", "using", "virtual",
+ "void", "volatile", "wchar_t", "while", "xor",
+ "xor_eq"])
+# Names that need a trailing "_" to avoid clashes.
+CppMangle = CppKeywords+Set.new(["std::string"])
+
+class String
+ def cppsafe() CppMangle.include?(self) ? self+"_" : self; end
+
+ def amqp2cpp()
+ path=split(".")
+ name=path.pop
+ return name.typename if path.empty?
+ path.map! { |n| n.nsname }
+ return (path << name.caps.cppsafe).join("::")
+ end
+
+ def typename() caps.cppsafe; end
+ def nsname() bars.cppsafe; end
+ def constname() shout.cppsafe; end
+ def funcname() lcaps.cppsafe; end
+ def varname() lcaps.cppsafe; end
+end
+
+# preview: Hold information about a C++ type.
+#
+# new mapping does not use CppType,
+# Each amqp type corresponds exactly by dotted name
+# to a type, domain or struct, which in turns
+# corresponds by name to a C++ type or typedef.
+# (see String.amqp2cpp)
+#
+class CppType
+ def initialize(name) @name=@param=@ret=name; end
+ attr_reader :name, :param, :ret, :code
+
+ def retref() @ret="#{name}&"; self; end
+ def retcref() @ret="const #{name}&"; self; end
+ def passcref() @param="const #{name}&"; self; end
+ def code(str) @code=str; self; end
+ def defval(str) @defval=str; self; end
+ def encoded() @code end
+ def ret_by_val() @name; end
+
+ def encode(value, buffer)
+ @code ? "#{buffer}.put#{@code}(#{value});" : "#{value}.encode(#{buffer});"
+ end
+
+ def decode(value,buffer)
+ if @code
+ if /&$/===param then
+ "#{buffer}.get#{@code}(#{value});"
+ else
+ "#{value} = #{buffer}.get#{@code}();"
+ end
+ else
+ "#{value}.decode(#{buffer});"
+ end
+ end
+
+ def default_value()
+ return @defval ||= "#{name}()"
+ end
+
+ def to_s() name; end;
+end
+
+class AmqpRoot
+ # preview; map 0-10 types to preview code generator types
+ @@typemap = {
+ "bit"=> CppType.new("bool").code("Octet").defval("false"),
+ "boolean"=> CppType.new("bool").code("Octet").defval("false"),
+ "uint8"=>CppType.new("uint8_t").code("Octet").defval("0"),
+ "uint16"=>CppType.new("uint16_t").code("Short").defval("0"),
+ "uint32"=>CppType.new("uint32_t").code("Long").defval("0"),
+ "uint64"=>CppType.new("uint64_t").code("LongLong").defval("0"),
+ "datetime"=>CppType.new("uint64_t").code("LongLong").defval("0"),
+ "str8"=>CppType.new("std::string").passcref.retcref.code("ShortString"),
+ "str16"=>CppType.new("std::string").passcref.retcref.code("MediumString"),
+ "str32"=>CppType.new("std::string").passcref.retcref.code("LongString"),
+ "vbin8"=>CppType.new("std::string").passcref.retcref.code("ShortString"),
+ "vbin16"=>CppType.new("std::string").passcref.retcref.code("MediumString"),
+ "vbin32"=>CppType.new("std::string").passcref.retcref.code("LongString"),
+ "map"=>CppType.new("FieldTable").passcref.retcref,
+ "array"=>CppType.new("Array").passcref.retcref,
+ "sequence-no"=>CppType.new("SequenceNumber").passcref,
+ "sequence-set"=>CppType.new("SequenceSet").passcref.retcref,
+ "struct32"=>CppType.new("std::string").passcref.retcref.code("LongString"),
+ "uuid"=>CppType.new("Uuid").passcref.retcref,
+ "byte-ranges"=>CppType.new("ByteRanges").passcref.retcref
+ }
+
+ # preview: map amqp types to preview cpp types.
+ def lookup_cpptype(t) t = @@typemap[t] and return t end
+end
+
+
+class AmqpElement
+ # convert my amqp type_ attribute to a C++ type.
+ def amqp2cpp()
+ return "ArrayDomain<#{array_type(name).amqp2cpp}> " if type_=="array"
+ return type_.amqp2cpp
+ end
+
+ # Does this object have a type-like child named name?
+ def typechild(name)
+ child = domain(name) if respond_to? :domain
+ child = struct(name) if not child and respond_to? :struct
+ child = type_(name) if not child and respond_to? :type_
+ child
+ end
+
+ # dotted name to a type object
+ def dotted_typechild(name)
+ names=name.split('.')
+ context = self
+ while context and names.size > 1
+ context = context.child_named(names.shift)
+ end
+ return context.typechild(names[0]) if context
+ end
+
+ # preview mapping - type_ attribute to C++ type
+ def lookup_cpptype(name)
+ if t = root.lookup_cpptype(name) then return t
+ elsif c = containing_class.typechild(name) then return c.cpptype
+ elsif c= root.dotted_typechild(name) then return c.cpptype
+ else raise "Cannot resolve type-name #{name} from #{self}"
+ end
+ end
+
+ def containing_class()
+ return self if is_a? AmqpClass
+ return parent && parent.containing_class
+ end
+end
+
+
+class AmqpField
+ def struct?()
+ c=containing_class
+ c.struct(type_)
+ end
+ def cpptype() lookup_cpptype(type_) or raise "no cpptype #{type_} for field #{self}" end
+ def cppname() name.lcaps.cppsafe; end
+ def bit?() type_ == "bit"; end
+ def signature() cpptype.param+" "+cppname; end
+
+ def fqtypename()
+ unless type_.index(".")
+ c=containing_class
+ return c.domain(type_).fqtypename if c.domain(type_)
+ return c.struct(type_).fqclassname if c.struct(type_)
+ end
+ return amqp2cpp
+ end
+ def paramtype()
+ /^(int|uint|char|boolean|bit)/ === type_ ? fqtypename : "const #{fqtypename}&"
+ end
+ def param_default() "=#{fqtypename}()" end
+
+ # Default value is normally the C++ default but over-ridden in specific cases
+ def default_value()
+ defval = cpptype.default_value;
+ if name == "accept-mode" and parent.name == "transfer" then defval = "1"; end
+ return defval
+ end
+end
+
+class AmqpMethod
+ def cppname() name.lcaps.cppsafe; end
+ def param_names() fields.map { |f| f.cppname }; end
+ def signature() fields.map { |f| f.signature }; end
+ def classname() parent.name; end
+ def body_name()
+ classname().caps+name.caps+"Body"
+ end
+ def cpp_pack_type() root.lookup_cpptype("uint16") end
+end
+
+module AmqpHasFields
+ def parameters(with_default=nil)
+ fields.map { |f|
+ "#{f.paramtype} #{f.cppname}_#{f.param_default if with_default}"
+ }
+ end
+ def unused_parameters() fields.map { |f| "#{f.paramtype} /*#{f.cppname}_*/"} end
+ def arguments() fields.map { |f| "#{f.cppname}_"} end
+ def values() fields.map { |f| "#{f.cppname}"} end
+ def initializers() fields.map { |f| "#{f.cppname}(#{f.cppname}_)"} end
+end
+
+class AmqpAction
+ def classname() name.typename; end
+ def funcname() parent.name.funcname + name.caps; end
+ def fqclassname() parent.name+"::"+classname; end
+ def full_code() (containing_class.code.hex << 8)+code.hex; end
+ include AmqpHasFields
+end
+
+class AmqpType
+ def cpptype() root.lookup_cpptype(name) end # preview
+ def typename() name.typename; end # new mapping
+ def fixed?() fixed_width; end
+end
+
+class AmqpCommand < AmqpAction
+ def base() "Command"; end
+end
+
+class AmqpControl < AmqpAction
+ def base() "Control"; end
+end
+
+class AmqpClass
+ def cppname() name.caps; end # preview
+ def nsname() name.nsname; end
+end
+
+class AmqpDomain
+ # preview
+ def cpptype() lookup_cpptype(type_) end
+ def cppname() name.caps; end
+
+ # new mapping
+ def fqtypename()
+ return containing_class.nsname+"::"+name.typename if containing_class
+ name.typename
+ end
+end
+
+class AmqpResult
+ # preview
+ def cpptype()
+ if type_ then lookup_cpptype(type_)
+ else CppType.new(parent.parent.name.caps+parent.name.caps+"Result").passcref
+ end
+ end
+end
+
+class AmqpStruct
+ include AmqpHasFields
+
+ @@pack_types={ "1"=>"uint8", "2"=>"uint16", "4"=>"uint32"}
+ def cpp_pack_type() # preview
+ root.lookup_cpptype(@@pack_types[pack])
+ end
+ def cpptype() CppType.new(cppname).passcref.retcref end
+ #def cppname() containing_class.cppname+name.caps; end
+ def cppname()
+ if parent.kind_of? AmqpResult
+ parent.parent.parent.name.caps+parent.parent.name.caps+"Result"
+ else
+ name.caps
+ end
+ end
+ def fqclassname() containing_class.nsname+"::"+name.typename; end
+ def classname() name.typename; end
+ def full_code() (containing_class.code.hex << 8)+code.hex; end
+end
+
+class CppGen < Generator
+ def initialize(outdir, *specs)
+ super(outdir,*specs)
+ # need to sort classes for dependencies
+ @actions=[] # Stack of end-scope actions
+ end
+
+ # Write a header file.
+ def h_file(path, &block)
+ path = (/\.h$/ === path ? path : path+".h")
+ guard=path.upcase.tr('./-','_')
+ file(path) {
+ gen "#ifndef #{guard}\n"
+ gen "#define #{guard}\n"
+ gen Copyright
+ yield
+ gen "#endif /*!#{guard}*/\n"
+ }
+ end
+
+ # Write a .cpp file.
+ def cpp_file(path, &block)
+ path = (/\.cpp$/ === path ? path : path+".cpp")
+ file(path) do
+ gen Copyright
+ yield
+ end
+ end
+
+ def include(header)
+ header+=".h" unless /(\.h|[">])$/===header
+ header="\"#{header}\"" unless /(^<.*>$)|(^".*"$)/===header
+ genl "#include #{header}"
+ end
+
+ def scope(open="{",close="}", &block)
+ genl open
+ indent &block
+ genl close
+ end
+
+ def namespace(name, &block)
+ genl
+ names = name.split("::")
+ names.each { |n| genl "namespace #{n} {" }
+ genl "namespace {" if (names.empty?)
+ genl
+ yield
+ genl
+ genl('}'*([names.size, 1].max)+" // namespace "+name)
+ genl
+ end
+
+ def struct_class(type, name, bases, &block)
+ gen "#{type} #{name}"
+ if (!bases.empty?)
+ genl ":"
+ indent { gen "#{bases.join(",\n")}" }
+ end
+ genl
+ scope("{","};", &block)
+ end
+
+ def struct(name, *bases, &block)
+ struct_class("struct", name, bases, &block);
+ end
+ def cpp_class(name, *bases, &block)
+ struct_class("class", name, bases, &block);
+ end
+ def cpp_extern_class(scope, name, *bases, &block)
+ struct_class("class "+scope, name, bases, &block);
+ end
+
+ def typedef(type, name) genl "typedef #{type} #{name};\n"; end
+
+ def variant(types) "boost::variant<#{types.join(", ")}>"; end
+ def variantl(types) "boost::variant<#{types.join(", \n")}>"; end
+ def blank_variant(types) variant(["boost::blank"]+types); end
+ def tuple(types) "boost::tuple<#{types.join(', ')}>"; end
+
+ def public() outdent { genl "public:" } end
+ def private() outdent { genl "private:" } end
+ def protected() outdent { genl "protected:" } end
+
+ # Returns [namespace, classname, filename]
+ def parse_classname(full_cname)
+ names=full_cname.split("::")
+ return names[0..-2].join('::'), names[-1], names.join("/")
+ end
+
+ def doxygen_comment(&block)
+ genl "/**"
+ prefix(" * ",&block)
+ genl " */"
+ end
+
+ # Generate code in namespace for each class
+ def each_class_ns()
+ @amqp.classes.each { |c| namespace(c.nsname) { yield c } }
+ end
+
+ def signature(ret_name, params, trailer="")
+ if params.size <= 1
+ genl ret_name+"(#{params})"+trailer
+ else
+ scope(ret_name+"(",")"+trailer) { genl params.join(",\n") }
+ end
+ end
+
+ def function_decl(ret_name, params=[], trailer="")
+ signature(ret_name, params, trailer+";")
+ end
+
+ def function_defn(ret_name, params=[], trailer="")
+ genl
+ signature(ret_name, params, trailer)
+ scope() { yield }
+ end
+
+ def ctor_decl(name, params=[]) function_decl(name, params); end
+
+ def ctor_defn(name, params=[], inits=[])
+ signature(name, params, inits.empty? ? "" : " :")
+ indent { gen inits.join(",\n") } if not inits.empty?
+ scope() { yield }
+ end
+
+ def function_call(name, params=[], trailer="")
+ gen name
+ list "(",params, ")"
+ gen trailer
+ end
+end
+
+# Fully-qualified class name
+class FqClass < Struct.new(:fqname,:namespace,:name,:file)
+ def initialize(fqclass)
+ names=fqclass.split "::"
+ super(fqclass, names[0..-2].join('::'), names[-1], names.join("/"))
+ end
+end
+
diff --git a/qpid/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb b/qpid/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb
new file mode 100755
index 0000000000..d784e589df
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/MethodBodyConstVisitor.rb
@@ -0,0 +1,45 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class MethodBodyConstVisitorGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @classname="MethodBodyConstVisitor"
+ @filename="qpid/framing/MethodBodyConstVisitor"
+ end
+
+ def generate()
+ h_file("#{@filename}") {
+ namespace(@namespace) {
+ @amqp.methods_.each { |m| genl "class #{m.body_name};" }
+ cpp_class("MethodBodyConstVisitor") {
+ genl "public:"
+ genl "virtual ~MethodBodyConstVisitor() {}"
+ @amqp.methods_.each { |m| genl "virtual void visit(const #{m.body_name}&) = 0;" }
+ }}}
+ end
+end
+
+MethodBodyConstVisitorGen.new($outdir, $amqp).generate();
+
diff --git a/qpid/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb b/qpid/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb
new file mode 100755
index 0000000000..4c58ff2bbb
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb
@@ -0,0 +1,54 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class MethodBodyDefaultVisitorGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace, @classname, @filename = parse_classname("qpid::framing::MethodBodyDefaultVisitor")
+ end
+
+ def generate()
+ h_file(@filename) {
+ include "qpid/framing/MethodBodyConstVisitor"
+ include "qpid/CommonImportExport.h"
+ namespace(@namespace) {
+ genl "class AMQMethodBody;"
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", @classname, "public MethodBodyConstVisitor") {
+ genl "public:"
+ genl "virtual void defaultVisit(const AMQMethodBody&) = 0;"
+ @amqp.methods_.each { |m|
+ genl "QPID_COMMON_EXTERN virtual void visit(const #{m.body_name}&);" }
+ }}}
+
+ cpp_file(@filename) {
+ include(@filename)
+ include("qpid/framing/all_method_bodies.h")
+ namespace(@namespace) {
+ @amqp.methods_.each { |m|
+ genl "void #{@classname}::visit(const #{m.body_name}& b) { defaultVisit(b); }"
+ }}}
+ end
+end
+
+MethodBodyDefaultVisitorGen.new($outdir, $amqp).generate();
+
diff --git a/qpid/cpp/rubygen/framing.0-10/MethodBodyFactory.rb b/qpid/cpp/rubygen/framing.0-10/MethodBodyFactory.rb
new file mode 100644
index 0000000000..28a5d94e32
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/MethodBodyFactory.rb
@@ -0,0 +1,59 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class MethodBodyFactoryGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @classname="MethodBodyFactory"
+ @filename="qpid/framing/MethodBodyFactory"
+ end
+
+ def generate()
+ cpp_file(@filename) {
+ include @filename
+ include "qpid/framing/BodyFactory"
+ @amqp.methods_.each { |m| include "qpid/framing/#{m.body_name}" }
+ include "qpid/Exception.h"
+ include "qpid/Msg.h"
+ genl
+ namespace(@namespace) {
+ scope("boost::intrusive_ptr<AMQMethodBody> #{@classname}::create(ClassId c, MethodId m) {") {
+ scope("switch (c) {") {
+ @amqp.classes.each { |c|
+ scope("case #{c.code}: switch(m) {") {
+ c.methods_.each { |m|
+ genl "case #{m.code}: return BodyFactory::create<#{m.body_name}>();"
+ }
+ genl "default: throw Exception(QPID_MSG(\"Invalid method id \" << int(m) << \" for class #{c.name} \"));"
+ }
+ genl "break;"
+ }
+ genl "default: throw Exception(QPID_MSG(\"Invalid class id \" << int(c)));"
+ }
+ }
+ }}
+ end
+end
+
+MethodBodyFactoryGen.new($outdir, $amqp).generate();
diff --git a/qpid/cpp/rubygen/framing.0-10/Operations.rb b/qpid/cpp/rubygen/framing.0-10/Operations.rb
new file mode 100755
index 0000000000..cd6a363c56
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/Operations.rb
@@ -0,0 +1,121 @@
+#!/usr/bin/env 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.
+#
+# Usage: output_directory xml_spec_file [xml_spec_file...]
+#
+$: << '..'
+require 'cppgen'
+require 'fileutils'
+require 'etc'
+require 'pathname'
+
+class OperationsGen < CppGen
+
+ def initialize(chassis, outdir, amqp)
+ super(outdir, amqp)
+ @chassis=chassis
+ @classname="AMQP_#{@chassis.caps}Operations"
+ end
+
+ def handler_method (m)
+ return_type = m.result ? m.result.cpptype.ret_by_val : "void"
+ gen "\nvirtual #{return_type} #{m.cppname}("
+ gen m.signature.join(",\n")
+ gen ") = 0;\n"
+ end
+
+ def handler_classname(c) c.name.caps+"Handler"; end
+
+ def methods_on(parent, chassis)
+ chassis == "all" ? parent.methods_ : parent.methods_on(chassis)
+ end
+
+ def handler_class(c)
+ m = methods_on(c,@chassis)
+ if (not m.empty?)
+ handlerclass=handler_classname c
+ gen <<EOS
+// ==================== class #{handlerclass} ====================
+class #{handlerclass} {
+ // Constructors and destructors
+ public:
+ class Invoker; // Declared in #{@chassis.caps}Invoker
+
+ #{handlerclass}(){};
+ virtual ~#{handlerclass}() {}
+ // Protocol methods
+EOS
+ m.each { |m| handler_method(m) if !m.content() }
+ gen <<EOS
+}; // class #{handlerclass}
+
+
+EOS
+ end
+ end
+
+ def handler_get(c)
+ m = methods_on(c,@chassis)
+ if (not m.empty?)
+ handlerclass=handler_classname c
+ gen "virtual #{handlerclass}* get#{handlerclass}() = 0;\n"
+ end
+ end
+
+ def generate()
+ h_file("qpid/framing/#{@classname}.h") {
+ gen <<EOS
+#include <sstream>
+#include "qpid/framing/ProtocolVersion.h"
+#include "qpid/framing/amqp_structs.h"
+
+namespace qpid {
+namespace framing {
+
+class AMQMethodBody;
+
+class #{@classname} {
+ public:
+ class Invoker; // Declared in #{@chassis.caps}Invoker
+
+ virtual ~#{@classname}() {}
+
+ virtual ProtocolVersion getVersion() const = 0;
+
+ // Inner classes
+EOS
+ indent { @amqp.classes.each { |c| handler_class(c) } }
+ gen <<EOS
+
+ // Method handler get methods
+
+EOS
+ indent { @amqp.classes.each { |c| handler_get(c) } }
+ gen <<EOS
+}; /* class #{@classname} */
+}}
+EOS
+}
+ end
+end
+
+OperationsGen.new("client",$outdir, $amqp).generate()
+OperationsGen.new("server",$outdir, $amqp).generate()
+OperationsGen.new("all",$outdir, $amqp).generate()
+
diff --git a/qpid/cpp/rubygen/framing.0-10/OperationsInvoker.rb b/qpid/cpp/rubygen/framing.0-10/OperationsInvoker.rb
new file mode 100755
index 0000000000..f9b5ce58d8
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/OperationsInvoker.rb
@@ -0,0 +1,117 @@
+#!/usr/bin/env 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.
+#
+# Usage: output_directory xml_spec_file [xml_spec_file...]
+#
+$: << '..'
+require 'cppgen'
+
+class OperationsInvokerGen < CppGen
+ def initialize(chassis, outdir, amqp)
+ super(outdir, amqp)
+ @chassis=chassis
+ @ops="AMQP_#{@chassis.caps}Operations"
+ @classname="#{@ops}::Invoker"
+ @filename="qpid/framing/#{@chassis.caps}Invoker"
+ end
+
+ def methods_on(parent, chassis)
+ chassis == "all" ? parent.methods_ : parent.methods_on(chassis)
+ end
+
+ def handler(c) "#{@ops}::#{c.cppname}Handler"; end
+ def getter(c) "get#{c.cppname}Handler"; end
+ def invoker(c) "#{handler(c)}::Invoker"; end
+ def visit_methods(c) methods_on(c, @chassis).select { |m| !m.content } end
+
+
+ def handler_visits_cpp(c)
+ visit_methods(c).each { |m|
+ scope("void #{invoker(c)}::visit(const #{m.body_name}& body) {") {
+ if (m.result)
+ genl "this->encode(body.invoke(target), result.result);"
+ else
+ genl "body.invoke(target);"
+ end
+ genl "result.handled=true;"
+ }
+ }
+ end
+
+ def ops_visits_cpp()
+ @amqp.classes.each { |c|
+ visit_methods(c).each { |m|
+ scope("void #{@classname}::visit(const #{m.body_name}& body) {") {
+ genl "#{handler(c)}::Invoker invoker(*target.#{getter(c)}());"
+ genl "body.accept(invoker);"
+ genl "result=invoker.getResult();"
+ }
+ }
+ }
+ end
+
+ def invoker_h(invoker, target, methods)
+ return if methods.empty?
+ genl
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", invoker, "public qpid::framing::Invoker") {
+ genl "#{target}& target;"
+ public
+ genl("Invoker(#{target}& target_) : target(target_) {}")
+ genl "using MethodBodyDefaultVisitor::visit;"
+ methods.each { |m| genl "QPID_COMMON_EXTERN void visit(const #{m.body_name}& body);" }
+ }
+ end
+
+ def generate()
+ h_file(@filename) {
+ include "qpid/framing/#{@ops}"
+ include "qpid/framing/Invoker.h"
+ include "qpid/CommonImportExport.h"
+ namespace("qpid::framing") {
+ # AMQP_*Operations invoker.
+ methods=@amqp.classes.map { |c| visit_methods(c).to_a }.flatten
+ invoker_h(@classname, @ops, methods)
+
+ # AMQP_*Operations::*Handler invokers.
+ @amqp.classes.each { |c|
+ invoker_h(invoker(c), handler(c), visit_methods(c))
+ }
+ }
+ }
+
+ cpp_file(@filename) {
+ include @filename
+ @amqp.classes.each { |c|
+ visit_methods(c).each { |m|
+ include "qpid/framing/#{m.body_name}"
+ }}
+ namespace("qpid::framing") {
+ ops_visits_cpp
+ @amqp.classes.each { |c|
+ next if visit_methods(c).empty?
+ handler_visits_cpp(c)
+ }
+ }
+ }
+ end
+end
+
+OperationsInvokerGen.new("client",$outdir, $amqp).generate()
+OperationsInvokerGen.new("server",$outdir, $amqp).generate()
+OperationsInvokerGen.new("all",$outdir, $amqp).generate()
diff --git a/qpid/cpp/rubygen/framing.0-10/Proxy.rb b/qpid/cpp/rubygen/framing.0-10/Proxy.rb
new file mode 100755
index 0000000000..3325616754
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/Proxy.rb
@@ -0,0 +1,109 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class ProxyGen < CppGen
+
+ def initialize(chassis, outdir, amqp)
+ super(outdir, amqp)
+ @chassis=chassis
+ @classname="AMQP_#{@chassis.caps}Proxy"
+ @filename="qpid/framing/#{@classname}"
+ end
+
+ def methods_on(parent, chassis)
+ chassis == "all" ? parent.methods_ : parent.methods_on(chassis)
+ end
+
+ def proxy_member(c) c.name.lcaps+"Proxy"; end
+
+ def inner_class_decl(c)
+ cname=c.name.caps
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", cname, "public Proxy") {
+ gen <<EOS
+public:
+#{cname}(FrameHandler& f) : Proxy(f) {}
+static #{cname}& get(#{@classname}& proxy) { return proxy.get#{cname}(); }
+EOS
+ methods_on(c, @chassis).each { |m|
+ genl "QPID_COMMON_EXTERN virtual void #{m.cppname}(#{m.signature.join(",\n ")});"
+ genl
+ }}
+ end
+
+ def inner_class_defn(c)
+ cname=c.cppname
+ methods_on(c, @chassis).each { |m|
+ genl "void #{@classname}::#{cname}::#{m.cppname}(#{m.signature.join(", ")})"
+ scope {
+ params=(["getVersion()"]+m.param_names).join(", ")
+ genl "send(#{m.body_name}(#{params}));"
+ }}
+ end
+
+ def generate
+ # .h file
+ h_file(@filename) {
+ include "qpid/framing/Proxy.h"
+ include "qpid/framing/Array.h"
+ include "qpid/framing/amqp_types.h"
+ include "qpid/framing/amqp_structs.h"
+ include "qpid/CommonImportExport.h"
+
+ namespace("qpid::framing") {
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", @classname, "public Proxy") {
+ public
+ genl "QPID_COMMON_EXTERN #{@classname}(FrameHandler& out);"
+ genl
+ @amqp.classes.each { |c|
+ inner_class_decl(c)
+ genl
+ genl "#{c.cppname}& get#{c.cppname}() { return #{proxy_member(c)}; }"
+ genl
+ }
+ private
+ @amqp.classes.each{ |c| gen c.cppname+" "+proxy_member(c)+";\n" }
+ }}}
+
+ # .cpp file
+ cpp_file(@filename) {
+ include "<sstream>"
+ include "qpid/framing/#{@classname}.h"
+ include "qpid/framing/amqp_types_full.h"
+ methods_on(@amqp, @chassis).each {
+ |m| include "qpid/framing/"+m.body_name
+ }
+ genl
+ namespace("qpid::framing") {
+ genl "#{@classname}::#{@classname}(FrameHandler& f) :"
+ gen " Proxy(f)"
+ @amqp.classes.each { |c| gen ",\n "+proxy_member(c)+"(f)" }
+ genl "{}\n"
+ @amqp.classes.each { |c| inner_class_defn(c) }
+ }}
+ end
+end
+
+
+ProxyGen.new("client", $outdir, $amqp).generate;
+ProxyGen.new("server", $outdir, $amqp).generate;
+ProxyGen.new("all", $outdir, $amqp).generate;
+
diff --git a/qpid/cpp/rubygen/framing.0-10/Session.rb b/qpid/cpp/rubygen/framing.0-10/Session.rb
new file mode 100755
index 0000000000..e800df9b2e
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/Session.rb
@@ -0,0 +1,417 @@
+#!/usr/bin/env 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.
+#
+# Usage: output_directory xml_spec_file [xml_spec_file...]
+#
+$: << '..'
+require 'cppgen'
+
+class CppGen
+ def session_methods(sync_default)
+ excludes = ["connection", "session", "file", "stream"]
+ gen_methods=@amqp.methods_on(@chassis).reject { |m|
+ excludes.include? m.parent.name or m.body_name.include?("010")
+ }
+ gen_methods.each { |m| m.set_sync_default(sync_default) }
+ end
+
+
+ # Generates a doxygen comment for AmqpMethod m.
+ def doxygen(m)
+ doxygen_comment {
+ genl m.doc
+ genl
+ m.fields_c.each { |f|
+ genl "@param #{f.cppname}"
+ genl f.doc if f.doc
+ genl
+ }
+ }
+ end
+end
+
+# Sync vs. async APIs
+module SyncAsync
+ def sync_prefix() @async ? "Async" : "" end
+ def sync_adjective() @async ? "asynchronous" : "synchronous" end
+ def sync_convert() @async ? "async" : "sync" end
+
+
+ def decl_ctor_opeq()
+ genl
+ genl "QPID_CLIENT_EXTERN #{@classname}();"
+ genl "QPID_CLIENT_INLINE_EXTERN #{@classname}(const #{@version_base}& other);"
+ genl "QPID_CLIENT_INLINE_EXTERN #{@classname}& operator=(const #{@version_base}& other);"
+ end
+
+ def defn_ctor_opeq(inline="")
+ genl
+ genl "#{inline} #{@classname}::#{@classname}() {}"
+ scope("#{inline} #{@classname}::#{@classname}(const #{@version_base}& other) {") {
+ genl "*this = other;"
+ }
+ scope("#{inline} #{@classname}& #{@classname}::operator=(const #{@version_base}& other) {") {
+ genl "impl = static_cast<const #{@classname}&>(other).impl;"
+ genl "return *this;"
+ }
+ end
+
+ def sync_default() !@async end
+end
+
+class ContentField # For extra content parameters
+ def cppname() "content" end
+ def signature() "const Message& content" end
+ def sig_default() signature+"="+"Message(std::string())" end
+ def unpack() "p[arg::content|Message(std::string())]"; end
+ def doc() "Message content"; end
+end
+
+class SyncField # For extra sync parameters
+ def initialize(default_value) @default_value=default_value ? "true" : "false" end
+ def cppname() "sync" end
+ def signature() "bool sync" end
+ def sig_default() signature+"="+@default_value end
+ def unpack() "p[arg::sync|#{@default_value}]"; end
+ def doc() "If true the broker will respond with completion status as soon as possible."; end
+end
+
+class AmqpField
+ def unpack() "p[arg::#{cppname}|#{default_value}]"; end
+ def sig_default() signature+"="+default_value; end
+end
+
+class AmqpMethod
+ def set_sync_default(sync_default) @sync_default=sync_default end
+ def fields_c() result = fields + (content ? [ContentField.new] : []) + [SyncField.new(@sync_default)] end
+ def param_names_c() fields_c.map { |f| f.cppname} end
+ def signature_c() fields_c.map { |f| f.signature }; end
+ def sig_c_default() fields_c.map { |f| f.sig_default }; end
+ def argpack_name() "#{parent.cppname}#{name.caps}Parameters"; end
+ def argpack_type()
+ "boost::parameter::parameters<" +
+ fields_c.map { |f| "arg::keyword_tags::"+f.cppname }.join(',') +
+ ">"
+ end
+
+ def return_type(async)
+ if (async)
+ return "TypedResult<qpid::framing::#{result.cpptype.ret_by_val}>" if (result)
+ return "Completion"
+ else
+ return "qpid::framing::#{result.cpptype.ret_by_val}" if (result)
+ return "void"
+ end
+ end
+
+ def session_function() "#{parent.name.lcaps}#{name.caps}"; end
+end
+
+class SessionNoKeywordGen < CppGen
+ include SyncAsync
+
+ def initialize(outdir, amqp, async)
+ super(outdir, amqp)
+ @async=async
+ @chassis="server"
+ @namespace,@classname,@file=
+ parse_classname "qpid::client::no_keyword::#{sync_prefix}Session_#{@amqp.version.bars}"
+ @version_base="SessionBase_#{@amqp.major}_#{@amqp.minor}"
+ end
+
+ def generate()
+ public_api("#{@file}.h")
+ h_file(@file) {
+ include "qpid/client/#{@version_base}.h"
+ include "qpid/client/ClientImportExport.h"
+ namespace(@namespace) {
+ doxygen_comment {
+ genl "AMQP #{@amqp.version} #{sync_adjective} session API."
+ genl @amqp.class_("session").doc
+ # FIXME aconway 2008-05-23: additional doc on sync/async use.
+ }
+ cpp_class(@classname, "public #{@version_base}") {
+ public
+ decl_ctor_opeq()
+ session_methods(sync_default).each { |m|
+ genl
+ doxygen(m)
+ args=m.sig_c_default.join(", ")
+ genl "QPID_CLIENT_EXTERN #{m.return_type(@async)} #{m.session_function}(#{args});"
+ }
+ }
+ }}
+
+ cpp_file(@file) {
+ include "qpid/client/#{@classname}"
+ include "qpid/framing/all_method_bodies.h"
+ include "qpid/client/SessionImpl.h"
+ include "qpid/client/MessageImpl.h"
+ include "qpid/client/PrivateImplRef.h"
+ include "qpid/client/CompletionImpl.h"
+ include "<boost/intrusive_ptr.hpp>"
+ namespace(@namespace) {
+ genl "using namespace framing;"
+ session_methods(sync_default).each { |m|
+ genl
+ sig=m.signature_c.join(", ")
+ func="#{@classname}::#{m.session_function}"
+ scope("#{m.return_type(@async)} #{func}(#{sig}) {") {
+ args=(["ProtocolVersion(#{@amqp.major},#{@amqp.minor})"]+m.param_names).join(", ")
+ genl "#{m.body_name} body(#{args});";
+ genl "body.setSync(sync);"
+ sendargs="body"
+ sendargs << ", *MessageImpl::get(content)" if m.content
+ async_retval="#{m.return_type(true)}(new CompletionImpl(impl->send(#{sendargs}), impl))"
+ if @async then
+ genl "return #{async_retval};"
+ else
+ if m.result
+ genl "return #{async_retval}.get();"
+ else
+ genl "#{async_retval}.wait();"
+ end
+ end
+ }}
+ defn_ctor_opeq()
+ }}
+ end
+end
+
+class SessionGen < CppGen
+ include SyncAsync
+
+ def initialize(outdir, amqp, async)
+ super(outdir, amqp)
+ @async=async
+ @chassis="server"
+ session="#{sync_prefix}Session_#{@amqp.version.bars}"
+ @base="no_keyword::#{session}"
+ @fqclass=FqClass.new "qpid::client::#{session}"
+ @classname=@fqclass.name
+ @fqbase=FqClass.new("qpid::client::#{@base}")
+ @version_base="SessionBase_#{@amqp.major}_#{@amqp.minor}"
+ end
+
+ def gen_keyword_decl(m)
+ return if m.fields_c.empty? # Inherited function will do.
+ scope("BOOST_PARAMETER_MEMFUN(#{m.return_type(@async)}, #{m.session_function}, 0, #{m.fields_c.size}, #{m.argpack_name}) {") {
+ scope("return #{@base}::#{m.session_function}(",");") {
+ gen m.fields_c.map { |f| f.unpack() }.join(",\n")
+ }
+ }
+ genl
+ end
+
+ def generate()
+ keyword_methods=session_methods(sync_default).reject { |m| m.fields_c.empty? }
+ max_arity = keyword_methods.map{ |m| m.fields_c.size }.max
+
+ public_api("qpid/client/arg.h")
+ h_file("qpid/client/arg.h") {
+ # Generate keyword tag declarations.
+ genl "#define BOOST_PARAMETER_MAX_ARITY #{max_arity}"
+ include "<boost/parameter.hpp>"
+ namespace("qpid::client::arg") {
+ keyword_methods.map{ |m| m.param_names_c }.flatten.uniq.each { |k|
+ genl "BOOST_PARAMETER_KEYWORD(keyword_tags, #{k})"
+ }}
+ }
+ public_api("#{@fqclass.file}.h")
+ h_file(@fqclass.file) {
+ include @fqbase.file
+ include "qpid/client/arg"
+ include "qpid/client/ClientImportExport"
+ namespace("qpid::client") {
+ # Doxygen comment.
+ doxygen_comment {
+ genl "AMQP #{@amqp.version} session API with keyword arguments."
+ genl <<EOS
+This class provides the same set of functions as #{@base}, but also
+allows parameters be passed using keywords. The keyword is the
+parameter name in the namespace "arg".
+
+For example given the normal function "foo(int x=0, int y=0, int z=0)"
+you could call it in either of the following ways:
+
+@code
+session.foo(1,2,3); // Normal no keywords
+session.foo(arg::z=3, arg::x=1); // Keywords and a default
+@endcode
+
+The keyword functions are easy to use but their declarations are hard
+to read. You may find it easier to read the documentation for #{@base}
+which provides the same set of functions using normal non-keyword
+declarations.
+
+\\ingroup clientapi
+
+
+\\details
+
+<h2>Publishing Messages</h2>
+<ul>
+<li><p>messageTransfer()</p>
+<pre>session.messageTransfer(arg::content=message, arg::destination="amq.topic");</pre></li>
+<li><p>messageTransfer() &mdash; asynchronous</p>
+<pre>#include &lt;qpid/client/AsyncSession.h>
+
+for (int i=0; i&lt;10; i++) {
+ message.setData(message_data.str());
+ async(session).messageTransfer(arg::content=message, arg::destination="amq.direct");
+}
+
+session.sync();
+</pre>
+</li>
+</ul>
+
+<h2>Exchanges</h2>
+<ul>
+<li><p>exchangeBind()</p>
+<pre>session.exchangeBind(arg::exchange="amq.topic", arg::queue=queue, arg::bindingKey=routing_key);</pre>
+</li>
+<li><p>exchangeUnbind()</p>
+<pre>session.exchangeUnBind(arg::exchange="amq.topic", arg::queue=queue, arg::bindingKey=routing_key);</pre></li>
+<li><p>exchangeBound()</p>
+<pre>if (session.exchangeBound(arg::exchange="amq.topic", arg::queue=queue, arg::bindingKey=rk)){...}</pre>
+<pre>if (session.exchangeBound(arg::exchange="amq.topic", arg::queue=queue)){...}</pre>
+</li>
+<li><p>exchangeDeclare()</p>
+<pre>session.exchangeDeclare(arg::exchange="my.topic", arg::type="topic");</pre>
+<pre>session.exchangeDeclare(arg::exchange="xml", arg::type="xml");</pre>
+</li>
+<li><p>exchangeDelete()</p>
+<pre>session.exchangeDeclare(arg::exchange="my.topic");</pre>
+<pre>session.exchangeDeclare(arg::exchange="xml", arg::ifUnused=true);</pre>
+</li>
+<li><p>exchangeQuery()</p>
+<pre>ExchangeQueryResult eqr = session.exchangeQuery(arg::exchange="my.topic");</pre></li>
+</ul>
+
+
+<h2>Configuring exchanges in session.exchangeDeclare</h2>
+
+<pre>arg::durable=true</pre>
+<p>Default: false.</p>
+<p>If durable=true, an exchange remains active even if the server is restarted. If durable=false, an exchange is purged when a server restarts.</p>
+
+<pre>arg::autoDelete=true</pre>
+<p>Default: false.</p>
+<p>If autoDelete=true, deleting the last binding for an exchange also deletes the exchange.</p>
+
+<pre>arg::alternatExchange="my.exchange"</pre>
+<p>Default: none.</p>
+<p>If an alternate exchange is specified, messages that can not be delivered to any queue are sent to the alternate exchange.</p>
+
+<h2>Queues</h2>
+<ul>
+<li><p>queueDeclare()</p>
+<pre>session.queueDeclare(arg::queue="message_queue");</pre>
+</li>
+<li><p>queueDelete()</p>
+<pre>session.queueDelete(arg::queue="message_queue");</pre></li>
+<li><p>queuePurge()</p>
+<pre>session.queuePurge(arg::queue="message_queue");</pre></li>
+<li><p>queueQuery()</p>
+<pre>QueueQueryResult qqr = session.queueQuery(arg::queue="message_queue");</pre></li>
+</ul>
+
+
+<h2>Configuring queues with session.queueDeclare</h2>
+<pre>arg::durable=true</pre>
+<p>Default: false.</p>
+<p>If durable=true, a queue remains active if the server is restarted. If durable=false, a queue and its contents are lost when a server restarts.</p>
+<br/>
+
+<pre>arg::autoDelete=true</pre>
+<p>Default: false.</p>
+<p>If autoDelete=true, the queue is deleted when the last active Subscription to the Queue is canceled.</p>
+<br/>
+
+<pre>arg::exclusive=true</pre>
+<p>Default: false.</p>
+<p>If exclusive=true, only the Session that created a queue can access it.</p>
+<br/>
+
+<pre>arg::alternateExchange="my.exchange"</pre>
+<p>Default: none. </p>
+<p>If an alternate exchange is specified, messages are routed to it if (1) they are rejected by a client, or (2) they remain on the queue when it is deleted.</p>
+<br/>
+
+
+<h2>Accepting, Acquiring, Rejecting, or Releasing Messages</h2>
+<ul>
+<li><p>messageAccept() &mdash; acknowledges messages</p>
+<pre>SequenceSet tobeAccepted;
+toAccepted.add(msg.getId());
+session.messageAccept(toBeAccepted);</pre>
+</li>
+<li><p>messageAcquire()</p>
+<pre>SequenceSet tobeAcquired;
+toBeAcquired.add(msg.getId());
+session.messageAcquire(toBeAcquired);</pre>
+</li>
+<li><p>messageReject()</p>
+<pre>SequenceSet tobeRejected;
+toRejected.add(msg.getId());
+session.messageReject(toBeRejected);</pre>
+</li>
+<li><p>messageRelease()</p>
+<pre>SequenceSet tobeReleased;
+toReleased.add(msg.getId());
+session.messageRelease(toBeReleased);</pre></li>
+</ul>
+
+<h2>Transactions</h2>
+<ul>
+<li><p>txSelect()</p>
+<pre>session.txSelect();</pre>
+</li>
+<li><p>txCommit()</p>
+<pre>session.txSelect();</pre></li>
+<li><p>txRollback()</p>
+<pre>session.txRollback();</pre></li>
+</ul>
+
+
+EOS
+ }
+ # Session class.
+ cpp_class(@classname,"public #{@base}") {
+ public
+ decl_ctor_opeq()
+ private
+ keyword_methods.each { |m| typedef m.argpack_type, m.argpack_name }
+ genl "friend class Connection;"
+ public
+ keyword_methods.each { |m| gen_keyword_decl(m) }
+ }
+ genl "/** Conversion to #{@classname} from another session type */"
+ genl "inline #{@classname} #{sync_convert}(const #{@version_base}& other) { return #{@clasname}(other); }"
+ defn_ctor_opeq("inline")
+ }}
+ end
+end
+
+SessionNoKeywordGen.new($outdir, $amqp, true).generate()
+SessionNoKeywordGen.new($outdir, $amqp, false).generate()
+SessionGen.new($outdir, $amqp, true).generate()
+SessionGen.new($outdir, $amqp, false).generate()
+
diff --git a/qpid/cpp/rubygen/framing.0-10/all_method_bodies.rb b/qpid/cpp/rubygen/framing.0-10/all_method_bodies.rb
new file mode 100755
index 0000000000..4c7fccfff5
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/all_method_bodies.rb
@@ -0,0 +1,39 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class AllMethodBodiesGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @filename="qpid/framing/all_method_bodies"
+ end
+
+ def generate()
+ h_file(@filename) {
+ @amqp.methods_.each { |m| include "qpid/framing/"+m.body_name }
+ }
+ end
+end
+
+AllMethodBodiesGen.new($outdir, $amqp).generate();
+
diff --git a/qpid/cpp/rubygen/framing.0-10/constants.rb b/qpid/cpp/rubygen/framing.0-10/constants.rb
new file mode 100755
index 0000000000..85bfb96ac0
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/constants.rb
@@ -0,0 +1,208 @@
+#!/usr/bin/env 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class ConstantsGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ @namespace="qpid::framing"
+ @dir="qpid/framing"
+ end
+
+ def constants_h()
+ public_api("#{@dir}/constants.h")
+ h_file("#{@dir}/constants.h") {
+ namespace(@namespace) {
+ # Constants for class/method names.
+ scope("enum AmqpConstant {","};") {
+ l=[]
+ l.concat @amqp.constants.map { |c| "#{c.name.shout}=#{c.value}" }
+ @amqp.classes.each { |c|
+ l << "#{c.name.shout}_CLASS_ID=#{c.code}"
+ l.concat c.methods_.map { |m|
+ "#{c.name.shout}_#{m.name.shout}_METHOD_ID=#{m.code}" }
+ }
+ genl l.join(",\n")
+ }
+ }
+ }
+ end
+
+ def typecode_enum(t) "TYPE_CODE_#{t.name.shout}" end
+
+ def typecode_h_cpp
+ path="#{@dir}/TypeCode"
+ public_api(path+".h")
+ h_file(path) {
+ include("<iosfwd>")
+ include("\"qpid/sys/IntegerTypes.h\"")
+ namespace(@namespace) {
+ scope("enum TypeCode {", "};") {
+ genl @amqp.types.map { |t| "#{typecode_enum t} = #{t.code}" if t.code }.compact.join(",\n")
+ }
+ genl <<EOS
+
+/** True if t is a valid TypeCode value */
+bool isTypeCode(uint8_t t);
+
+/** Throw exception if not a valid TypeCode */
+TypeCode typeCode(uint8_t);
+
+/**@return 0 if t is not a valid enum TypeCode value. */
+const char* typeName(TypeCode t);
+
+std::ostream& operator<<(std::ostream&, TypeCode);
+EOS
+ }
+ }
+
+ cpp_file(path) {
+ include(path);
+ include("qpid/Exception.h")
+ include("qpid/Msg.h")
+ include("<ostream>")
+ namespace(@namespace) {
+ scope("const char* typeName(TypeCode t) {") {
+ scope("switch (t) {") {
+ @amqp.types.each { |t| genl "case #{typecode_enum t}: return \"#{t.name}\";" if t.code }
+ genl "default: break;"
+ }
+ genl "return 0;";
+ }
+ genl <<EOS
+
+bool isTypeCode(uint8_t t) { return typeName(TypeCode(t)); }
+
+TypeCode typeCode(uint8_t t) {
+ if (!isTypeCode(t)) throw Exception(QPID_MSG("Invalid TypeCode " << t));
+ return TypeCode(t);
+}
+
+std::ostream& operator<<(std::ostream& o, TypeCode t) {
+ if (isTypeCode(t)) return o << typeName(t);
+ else return o << "Invalid TypeCode " << t;
+}
+EOS
+ }
+ }
+ end
+
+ def enum_h()
+ public_api("#{@dir}/enum.h")
+ h_file("#{@dir}/enum.h") {
+ # Constants for enum domains.
+ namespace(@namespace) {
+ @amqp.domains.each { |d| declare_enum(d.enum) if d.enum }
+ @amqp.classes.each { |c|
+ enums=c.collect_all(AmqpEnum)
+ if !enums.empty? then
+ namespace(c.nsname) { enums.each { |e| declare_enum(e) } }
+ end
+ }
+ }
+ }
+ end
+
+ def declare_enum(enum)
+ # Generated like this: enum containing_class::Foo { FOO_X, FOO_Y; }
+ name="#{enum.parent.name.caps}"
+ prefix=enum.parent.name.shout+"_"
+ scope("enum #{name} {","};") {
+ genl enum.choices.collect { |c| "#{prefix}#{c.name.shout}=#{c.value}" }.join(",\n")
+ }
+ end
+
+ def declare_exception(c, base, package, enum)
+ name=c.name.caps+"Exception"
+ value="#{package}::#{enum.parent.name.shout}_#{c.name.shout}"
+ genl
+ doxygen_comment { genl c.doc }
+ struct(c.name.caps+"Exception", base) {
+ genl "std::string getPrefix() const { return \"#{c.name}\"; }"
+ genl "#{c.name.caps}Exception(const std::string& msg=std::string()) : #{base}(#{value}, \"\"+msg) {}"
+ }
+ end
+
+ def declare_exceptions(class_name, domain_name, base)
+ enum = @amqp.class_(class_name).domain(domain_name).enum
+ enum.choices.each { |c| declare_exception(c, base, class_name, enum) unless c.name == "normal" }
+ genl
+ genl "QPID_COMMON_EXTERN sys::ExceptionHolder create#{base}(int code, const std::string& text);"
+ end
+
+ def create_exception(class_name, domain_name, base, invalid)
+ scope("sys::ExceptionHolder create#{base}(int code, const std::string& text) {") {
+ genl "sys::ExceptionHolder holder;"
+ scope("switch (code) {") {
+ enum = @amqp.class_(class_name).domain(domain_name).enum
+ enum.choices.each { |c|
+ assign = "holder = new #{c.name.caps}Exception(text); " unless c.name == "normal"
+ genl "case #{c.value}: #{assign}break;"
+ }
+ genl "default: holder = new #{invalid}(QPID_MSG(\"Bad #{enum.parent.name}: \" << code << \": \" << text));"
+ }
+ genl "return holder;"
+ }
+ end
+
+ def reply_exceptions_h()
+ public_api("#{@dir}/reply_exceptions.h")
+ h_file("#{@dir}/reply_exceptions.h") {
+ include "qpid/Exception"
+ include "qpid/sys/ExceptionHolder"
+ include "qpid/framing/enum"
+ include "qpid/CommonImportExport.h"
+ namespace(@namespace) {
+ declare_exceptions("execution", "error-code", "SessionException")
+ declare_exceptions("connection", "close-code", "ConnectionException")
+ declare_exceptions("session", "detach-code", "ChannelException")
+ }
+ }
+ end
+
+ def reply_exceptions_cpp()
+ cpp_file("#{@dir}/reply_exceptions") {
+ include "#{@dir}/reply_exceptions"
+ include "qpid/Msg.h"
+ include "<sstream>"
+ include "<assert.h>"
+ namespace("qpid::framing") {
+ create_exception("execution", "error-code", "SessionException", "InvalidArgumentException")
+ # FIXME aconway 2008-10-07: there are no good exception codes in 0-10 for an invalid code.
+ # The following choices are arbitrary.
+ create_exception("connection", "close-code", "ConnectionException", "FramingErrorException")
+ create_exception("session", "detach-code", "ChannelException", "NotAttachedException")
+ }
+ }
+ end
+
+ def generate()
+ constants_h
+ enum_h
+ reply_exceptions_h
+ reply_exceptions_cpp
+ typecode_h_cpp
+ end
+end
+
+ConstantsGen.new($outdir, $amqp).generate();
+
diff --git a/qpid/cpp/rubygen/framing.0-10/frame_body_lists.rb b/qpid/cpp/rubygen/framing.0-10/frame_body_lists.rb
new file mode 100644
index 0000000000..4f1b976032
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/frame_body_lists.rb
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+$: << ".." # Include .. in load path
+require 'cppgen'
+
+class FrameBodyListsGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp);
+ end
+
+ def generate
+ h_file("qpid/framing/frame_body_lists.h") {
+ gen <<EOS
+/**@file
+ * Macro lists of frame body classes, used to generate Visitors
+ */
+EOS
+ gen "#define METHOD_BODIES() "
+ @amqp.methods_.each { |m| gen "\\\n (#{m.body_name}) " }
+ gen <<EOS
+
+
+#define OTHER_BODIES() (AMQContentBody)(AMQHeaderBody)(AMQHeartbeatBody))
+
+EOS
+ }
+ end
+end
+
+FrameBodyListsGen.new($outdir, $amqp).generate;
+
+
diff --git a/qpid/cpp/rubygen/framing.0-10/structs.rb b/qpid/cpp/rubygen/framing.0-10/structs.rb
new file mode 100755
index 0000000000..62b33ce773
--- /dev/null
+++ b/qpid/cpp/rubygen/framing.0-10/structs.rb
@@ -0,0 +1,615 @@
+#!/usr/bin/env 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.
+#
+# Usage: output_directory xml_spec_file [xml_spec_file...]
+#
+$: << '..'
+require 'cppgen'
+
+class StructGen < CppGen
+
+ def initialize(outdir, amqp)
+ super(outdir, amqp)
+ end
+
+ SizeMap={
+ "Octet"=>1,
+ "Short"=>2,
+ "Long"=>4,
+ "LongLong"=>8,
+ "int8"=>1,
+ "int16"=>2,
+ "int32"=>4,
+ "int64"=>8,
+ "uint8"=>1,
+ "uint16"=>2,
+ "uint32"=>4,
+ "uint64"=>8,
+ "timestamp"=>8
+ }
+
+ StringSizeMap={
+ "LongString"=>4,
+ "MediumString"=>2,
+ "ShortString"=>1
+ }
+
+ SizeType={
+ 1=>"Octet",
+ 2=>"Short",
+ 4=>"Long",
+ 8=>"LongLong"
+ }
+
+ ValueTypes=["uint8_t", "uint16_t", "uint32_t", "uint64_t"]
+
+ def is_packed(s) s.pack and s.pack != "0" end
+
+ def execution_header?(s)
+ s.is_a? AmqpMethod and not s.parent.control?
+ # s.kind_of? AmqpMethod and s.parent.name.include?("010") and not s.parent.control?
+ end
+
+ def has_bitfields_only(s)
+ s.fields.select {|f| f.type_ != "bit"}.empty?
+ end
+
+ def default_initialisation(s)
+ params = s.fields.select {|f| ValueTypes.include?(f.cpptype.name) || (!is_packed(s) && f.type_ == "bit")}
+ strings = params.collect {|f| "#{f.cppname}(#{f.default_value})"}
+ strings << "flags(0)" if (is_packed(s))
+ if strings.empty?
+ return ""
+ else
+ return " : " + strings.join(", ")
+ end
+ end
+
+ def printable_form(f)
+ if (f.cpptype.name == "uint8_t")
+ return "(int) " + f.cppname
+ elsif (f.type_ == "bit")
+ return "get#{f.name.caps}()"
+ else
+ return f.cppname
+ end
+ end
+
+ def flag_mask(s, i)
+ pos = s.pack.to_i*8 - 8 - (i/8)*8 + (i % 8)
+ return "(1 << #{pos})"
+ end
+
+ def encode_packed_struct(s)
+ genl s.cpp_pack_type.encode('flags', 'buffer')
+ process_packed_fields(s) { |f, i| encode_packed_field(s, f, i) unless f.type_ == "bit" }
+ end
+
+ def decode_packed_struct(s)
+ genl "#{s.cpp_pack_type.decode('flags', 'buffer')}"
+ process_packed_fields(s) { |f, i| decode_packed_field(s, f, i) unless f.type_ == "bit" }
+ end
+
+ def size_packed_struct(s)
+ genl "total += #{s.pack};"
+ process_packed_fields(s) { |f, i| size_packed_field(s, f, i) unless f.type_ == "bit" }
+ end
+
+ def print_packed_struct(s)
+ process_packed_fields(s) { |f, i| print_packed_field(s, f, i) }
+ end
+
+ def encode_packed_field(s, f, i)
+ genl "if (flags & #{flag_mask(s, i)})"
+ indent { genl f.cpptype.encode(f.cppname,"buffer") }
+ end
+
+ def decode_packed_field(s, f, i)
+ genl "if (flags & #{flag_mask(s, i)})"
+ indent { genl f.cpptype.decode(f.cppname,"buffer") }
+ end
+
+ def size_packed_field(s, f, i)
+ genl "if (flags & #{flag_mask(s, i)})"
+ indent { generate_size(f, []) }
+ end
+
+ def print_packed_field(s, f, i)
+ classname = s.cppname
+ if (s.kind_of? AmqpMethod)
+ classname = s.body_name
+ end
+ genl "if (flags & #{flag_mask(s, i)})"
+ indent {
+ unless (classname == "ConnectionStartOkBody" && f.name == "response")
+ genl "out << \"#{f.name}=\" << #{printable_form(f)} << \"; \";"
+ else
+ genl "out << \"response=\" << \"xxxxxx\" << \"; \";"
+ end
+ }
+ end
+
+ def generate_encode(f, combined)
+ if (f.type_ == "bit")
+ genl "uint8_t #{f.cppname}_bits = #{f.cppname};"
+ count = 0
+ combined.each { |c| genl "#{f.cppname}_bits |= #{c.cppname} << #{count += 1};" }
+ genl "buffer.putOctet(#{f.cppname}_bits);"
+ else
+ genl f.cpptype.encode(f.cppname,"buffer")
+ end
+ end
+
+ def generate_decode(f, combined)
+ if (f.type_ == "bit")
+ genl "uint8_t #{f.cppname}_bits = buffer.getOctet();"
+ genl "#{f.cppname} = 1 & #{f.cppname}_bits;"
+ count = 0
+ combined.each { |c| genl "#{c.cppname} = (1 << #{count += 1}) & #{f.cppname}_bits;" }
+ else
+ genl f.cpptype.decode(f.cppname,"buffer")
+ end
+ end
+
+ def generate_size(f, combined)
+ if (f.type_ == "bit")
+ names = ([f] + combined).collect {|g| g.cppname}
+ genl "total += 1;//#{names.join(", ")}"
+ else
+ size = SizeMap[f.cpptype.encoded]
+ if (size)
+ genl "total += #{size};//#{f.cppname}"
+ elsif (f.cpptype.name == "SequenceNumberSet")
+ genl "total += #{f.cppname}.encodedSize();"
+ elsif (size = StringSizeMap[f.cpptype.encoded])
+ genl "total += #{size} + #{f.cppname}.size();"
+ else
+ genl "total += #{f.cppname}.encodedSize();"
+ end
+ end
+ end
+
+ def process_packed_fields(s)
+ s.fields.each { |f| yield f, s.fields.index(f) }
+ end
+
+ def process_fields(s)
+ last = nil
+ count = 0
+ bits = []
+ s.fields.each {
+ |f| if (last and last.bit? and f.bit? and count < 7)
+ count += 1
+ bits << f
+ else
+ if (last and last.bit?)
+ yield last, bits
+ count = 0
+ bits = []
+ end
+ if (not f.bit?)
+ yield f
+ end
+ last = f
+ end
+ }
+ if (last and last.bit?)
+ yield last, bits
+ end
+ end
+
+ def all_fields_via_accessors(s)
+ s.fields.collect { |f| "get#{f.name.caps}()" }.join(", ")
+ end
+
+ def methodbody_extra_defs(s)
+ if (s.parent.control?)
+ genl "virtual uint8_t type() const { return 0;/*control segment*/ }"
+ end
+
+
+ gen <<EOS
+ typedef #{s.result ? s.result.cpptype.name : 'void'} ResultType;
+
+ template <class T> ResultType invoke(T& invocable) const {
+ return invocable.#{s.cppname}(#{all_fields_via_accessors(s)});
+ }
+
+ using AMQMethodBody::accept;
+ void accept(MethodBodyConstVisitor& v) const { v.visit(*this); }
+ boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
+
+ ClassId amqpClassId() const { return CLASS_ID; }
+ MethodId amqpMethodId() const { return METHOD_ID; }
+ bool isContentBearing() const { return #{s.content ? "true" : "false" }; }
+ bool resultExpected() const { return #{s.result ? "true" : "false"}; }
+ bool responseExpected() const { return #{s.responses().empty? ? "false" : "true"}; }
+EOS
+ end
+
+ def define_constructor(name, s)
+ if (s.fields.size > 0)
+ genl "#{name}("
+ if (s.kind_of? AmqpMethod)
+ indent {gen "ProtocolVersion, "}
+ end
+ indent { gen s.fields.collect { |f| "#{f.cpptype.param} _#{f.cppname}" }.join(",\n") }
+ genl ") : "
+ if (is_packed(s))
+ initialisers = s.fields.select { |f| f.type_ != "bit"}.collect { |f| "#{f.cppname}(_#{f.cppname})"}
+
+ initialisers << "flags(0)"
+ indent { gen initialisers.join(",\n") }
+ genl "{"
+ indent {
+ process_packed_fields(s) { |f, i| genl "set#{f.name.caps}(_#{f.cppname});" if f.type_ == "bit"}
+ process_packed_fields(s) { |f, i| genl "flags |= #{flag_mask(s, i)};" unless f.type_ == "bit"}
+ }
+ genl "}"
+ else
+ indent { gen s.fields.collect { |f| " #{f.cppname}(_#{f.cppname})" }.join(",\n") }
+ genl "{}"
+ end
+ end
+ #default constructors:
+ if (s.kind_of? AmqpMethod)
+ genl "#{name}(ProtocolVersion=ProtocolVersion()) #{default_initialisation(s)} {}"
+ end
+ if (s.kind_of? AmqpStruct)
+ genl "#{name}() #{default_initialisation(s)} {}"
+ end
+ end
+
+ def define_packed_field_accessors(s, f, i)
+ if (s.kind_of? AmqpMethod)
+ define_packed_field_accessors_for_method(s, f, i)
+ else
+ define_packed_field_accessors_for_struct(s, f, i)
+ end
+ end
+
+ def define_packed_field_accessors_for_struct(s, f, i)
+ if (f.type_ == "bit")
+ genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};"
+ genl "else flags &= ~#{flag_mask(s, i)};"
+ }
+ genl "}"
+ genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }"
+ else
+ genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "#{f.cppname} = _#{f.cppname};"
+ genl "flags |= #{flag_mask(s, i)};"
+ }
+ genl "}"
+ genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return #{f.cppname}; }"
+ if (f.cpptype.name == "FieldTable")
+ genl "#{f.cpptype.name}& #{s.cppname}::get#{f.name.caps}() {"
+ indent {
+ genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set'
+ genl "return #{f.cppname};"
+ }
+ genl "}"
+ end
+ genl "bool #{s.cppname}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }"
+ genl "void #{s.cppname}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }"
+ end
+ genl ""
+ end
+
+ def define_packed_field_accessors_for_method(s, f, i)
+ if (f.type_ == "bit")
+ genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};"
+ genl "else flags &= ~#{flag_mask(s, i)};"
+ }
+ genl "}"
+ genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }"
+ else
+ genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "#{f.cppname} = _#{f.cppname};"
+ genl "flags |= #{flag_mask(s, i)};"
+ }
+ genl "}"
+ genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return #{f.cppname}; }"
+ if (f.cpptype.name == "FieldTable")
+ genl "#{f.cpptype.name}& #{s.body_name}::get#{f.name.caps}() {"
+ indent {
+ genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set'
+ genl "return #{f.cppname};"
+ }
+ genl "}"
+ end
+ genl "bool #{s.body_name}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }"
+ genl "void #{s.body_name}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }"
+ end
+ genl ""
+ end
+
+ def define_packed_accessors(s)
+ process_packed_fields(s) { |f, i| define_packed_field_accessors(s, f, i) }
+ end
+
+ def declare_packed_accessors(f)
+ genl "QPID_COMMON_EXTERN void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname});";
+ genl "QPID_COMMON_EXTERN #{f.cpptype.ret} get#{f.name.caps}() const;"
+ if (f.cpptype.name == "FieldTable")
+ genl "QPID_COMMON_EXTERN #{f.cpptype.name}& get#{f.name.caps}();"
+ end
+ if (f.type_ != "bit")
+ #extra 'accessors' for packed fields:
+ genl "QPID_COMMON_EXTERN bool has#{f.name.caps}() const;"
+ genl "QPID_COMMON_EXTERN void clear#{f.name.caps}Flag();"
+ end
+ end
+
+ def define_accessors(f)
+ genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) { #{f.cppname} = _#{f.cppname}; }"
+ genl "#{f.cpptype.ret} get#{f.name.caps}() const { return #{f.cppname}; }"
+ if (f.cpptype.name == "FieldTable")
+ genl "#{f.cpptype.name}& get#{f.name.caps}() { return #{f.cppname}; }"
+ end
+ end
+
+ def define_struct(s)
+ classname = s.cppname
+ inheritance = ""
+ if (s.kind_of? AmqpMethod)
+ classname = s.body_name
+ if (execution_header?(s))
+ inheritance = ": public ModelMethod"
+ else
+ inheritance = ": public AMQMethodBody"
+ end
+ else
+ public_api("qpid/framing/#{classname}.h") # Non-method structs are public
+ end
+
+ h_file("qpid/framing/#{classname}.h") {
+ if (s.kind_of? AmqpMethod)
+ gen <<EOS
+#include "qpid/framing/AMQMethodBody.h"
+#include "qpid/framing/AMQP_ServerOperations.h"
+#include "qpid/framing/MethodBodyConstVisitor.h"
+EOS
+ end
+ include "qpid/framing/ModelMethod.h" if (execution_header?(s))
+
+ s.fields.each { |f| include "qpid/framing/#{f.cpptype.name}" if f.struct?}
+
+ gen <<EOS
+
+#include <ostream>
+#include "qpid/framing/amqp_types_full.h"
+#include "qpid/CommonImportExport.h"
+
+namespace qpid {
+namespace framing {
+
+class QPID_COMMON_CLASS_EXTERN #{classname} #{inheritance} {
+EOS
+ if (is_packed(s))
+ indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" unless f.type_ == "bit"} }
+ indent {
+ genl "#{s.cpp_pack_type.name} flags;"
+ }
+ else
+ indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" } }
+ end
+ genl "public:"
+ if (s.kind_of? AmqpMethod)
+ indent { genl "static const ClassId CLASS_ID = #{s.parent.code};" }
+ indent { genl "static const MethodId METHOD_ID = #{s.code};" }
+ end
+
+ if (s.kind_of? AmqpStruct)
+ if (s.code)
+ indent { genl "static const uint16_t TYPE = #{s.full_code};" }
+ end
+ end
+
+ indent {
+ define_constructor(classname, s)
+ genl ""
+ if (is_packed(s))
+ s.fields.each { |f| declare_packed_accessors(f) }
+ else
+ s.fields.each { |f| define_accessors(f) }
+ end
+ }
+ if (s.kind_of? AmqpMethod)
+ methodbody_extra_defs(s)
+ end
+ if (s.kind_of? AmqpStruct)
+ indent {genl "QPID_COMMON_EXTERN friend std::ostream& operator<<(std::ostream&, const #{classname}&);" }
+ end
+
+ gen <<EOS
+ QPID_COMMON_EXTERN void encode(Buffer&) const;
+ QPID_COMMON_EXTERN void decode(Buffer&, uint32_t=0);
+ QPID_COMMON_EXTERN void encodeStructBody(Buffer&) const;
+ QPID_COMMON_EXTERN void decodeStructBody(Buffer&, uint32_t=0);
+ QPID_COMMON_EXTERN uint32_t encodedSize() const;
+ QPID_COMMON_EXTERN uint32_t bodySize() const;
+ QPID_COMMON_EXTERN void print(std::ostream& out) const;
+}; /* class #{classname} */
+
+}}
+EOS
+ }
+ cpp_file("qpid/framing/#{classname}.cpp") {
+ if (is_packed(s) || s.fields.size > 0 || execution_header?(s))
+ buffer = "buffer"
+ else
+ buffer = "/*buffer*/"
+ end
+ gen <<EOS
+#include "qpid/framing/#{classname}.h"
+#include "qpid/framing/reply_exceptions.h"
+
+using namespace qpid::framing;
+
+EOS
+
+ if (is_packed(s))
+ define_packed_accessors(s)
+ end
+ gen <<EOS
+void #{classname}::encodeStructBody(Buffer& #{buffer}) const
+{
+EOS
+ if (execution_header?(s))
+ genl "encodeHeader(buffer);"
+ end
+
+ if (is_packed(s))
+ indent {encode_packed_struct(s)}
+ else
+ indent { process_fields(s) { |f, combined| generate_encode(f, combined) } }
+ end
+ gen <<EOS
+}
+
+void #{classname}::encode(Buffer& buffer) const
+{
+EOS
+ indent {
+ if (s.kind_of? AmqpStruct)
+ if (s.code)
+ genl "buffer.put#{SizeType[s.size.to_i]}(bodySize() + 2/*typecode*/);" if s.size and s.size.to_i != 0
+ genl "buffer.putShort(TYPE);"
+ else
+ genl "buffer.put#{SizeType[s.size.to_i]}(bodySize());" if s.size and s.size.to_i != 0
+ end
+ end
+ genl "encodeStructBody(buffer);"
+ }
+ gen <<EOS
+}
+
+void #{classname}::decodeStructBody(Buffer& #{buffer}, uint32_t /*size*/)
+{
+EOS
+ if (execution_header?(s))
+ genl "decodeHeader(buffer);"
+ end
+
+ if (is_packed(s))
+ indent {decode_packed_struct(s)}
+ else
+ indent { process_fields(s) { |f, combined| generate_decode(f, combined) } }
+ end
+ gen <<EOS
+}
+
+void #{classname}::decode(Buffer& buffer, uint32_t /*size*/)
+{
+EOS
+ indent {
+ if (s.kind_of? AmqpStruct)
+ genl "buffer.get#{SizeType[s.size.to_i]}();" if s.size and s.size.to_i != 0
+ genl "if (TYPE != buffer.getShort()) throw FramingErrorException(\"Bad type code for struct\");" if s.code
+ end
+ genl "decodeStructBody(buffer);"
+ }
+ gen <<EOS
+}
+
+uint32_t #{classname}::bodySize() const
+{
+ uint32_t total = 0;
+EOS
+ if (execution_header?(s))
+ genl "total += headerSize();"
+ end
+
+ if (is_packed(s))
+ indent {size_packed_struct(s)}
+ else
+ indent { process_fields(s) { |f, combined| generate_size(f, combined) } }
+ end
+ gen <<EOS
+ return total;
+}
+
+uint32_t #{classname}::encodedSize() const {
+ uint32_t total = bodySize();
+EOS
+ if (s.kind_of? AmqpStruct)
+ genl "total += #{s.size}/*size field*/;" if s.size
+ genl "total += 2/*typecode*/;" if s.code
+ end
+ gen <<EOS
+ return total;
+}
+
+void #{classname}::print(std::ostream& out) const
+{
+ out << "{#{classname}: ";
+EOS
+ if (is_packed(s))
+ indent {print_packed_struct(s)}
+ else
+ copy = Array.new(s.fields)
+ f = copy.shift
+
+ indent {
+ genl "out << \"#{f.name}=\" << #{printable_form(f)};" if f
+ copy.each { |f| genl "out << \"; #{f.name}=\" << #{printable_form(f)};" }
+ }
+ end
+ gen <<EOS
+ out << "}";
+}
+EOS
+
+ if (s.kind_of? AmqpStruct)
+ gen <<EOS
+namespace qpid{
+namespace framing{
+
+ std::ostream& operator<<(std::ostream& out, const #{classname}& s)
+ {
+ s.print(out);
+ return out;
+ }
+
+}
+}
+EOS
+ end
+}
+ end
+
+ def generate()
+ structs = @amqp.collect_all(AmqpStruct).select { |s| not ["command-fragment"].include?(s.name) }
+ structs.each { |s| define_struct(s) }
+ @amqp.methods_.each { |m| define_struct(m) }
+ #generate a single include file containing the list of structs for convenience
+ public_api("qpid/framing/amqp_structs.h")
+ h_file("qpid/framing/amqp_structs.h") { structs.each { |s| genl "#include \"qpid/framing/#{s.cppname}.h\"" } }
+ end
+end
+
+StructGen.new($outdir, $amqp).generate()
+
diff --git a/qpid/cpp/rubygen/generate b/qpid/cpp/rubygen/generate
new file mode 100755
index 0000000000..89b9b99520
--- /dev/null
+++ b/qpid/cpp/rubygen/generate
@@ -0,0 +1,160 @@
+#!/usr/bin/env 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 'pathname'
+require 'amqpgen'
+
+#
+# Run a set of code generation templates.
+#
+if ARGV.size < 3
+ puts <<EOS
+Usage: #{ARGV[0]} SRCDIR APIDIR SPEC.xml [ ... ] TEMPLATE.rb [ ... ]
+or: #{ARGV[0]} SRCDIR APIDIR SPEC.xml [ ... ] all [ makefragment.mk | makefragment.cmake ]
+
+Parse all SPEC.xml files to create an AMQP model, run each TEMPLATE
+putting the resulting files under SRCDIR, public API files in APIdir.
+Prints a list of files generated to standard output.
+
+If SRCDIR and APIDIR are '-' then just prints file list without generating files.
+EOS
+ exit 1
+end
+
+# Create array of specs by version
+def parse_specs(files)
+ lists=Hash.new { |h,k| h[k]=[] }
+ files.each { |f|
+ spec=AmqpRoot.new(File.new(f))
+ lists[spec.version] << spec
+ }
+ specs={}
+ lists.each_pair { |k,l|
+ specs[k] = l.size==1 ? l.first : AmqpRoot.new(*l.map { |s| s.xml})
+ }
+ return specs
+end
+
+gendir=File.dirname(__FILE__)
+
+# Run selected templates
+if ARGV.any? { |arg| arg=="all" }
+ templates=Dir["#{gendir}/*/*.rb"]
+else
+templates=ARGV.grep(/\.rb$/)
+ ARGV.each { |arg|
+ d=File.join gendir,arg
+ templates += Dir["#{d}/*.rb"] if File.directory? d
+ }
+end
+
+$outdir=[ ARGV[0], ARGV[1] ]
+$models=parse_specs(ARGV.grep(/\.xml$/))
+
+templates.each { |t|
+ ver=Pathname.new(t).dirname.basename.to_s.split('.')[-1]
+ $amqp=$models[ver]
+ if $amqp
+ load t
+ else
+ puts "Warning: skipping #{t}, no spec file for version #{ver}."
+ end
+}
+
+def cmake_continue(lines) lines.join(" \n "); end
+def make_continue(lines) lines.join(" \\\n "); end
+
+# Generate makefile
+makefile=ARGV.grep(/.mk$/)[0]
+cmakefile=ARGV.grep(/.cmake$/)[0]
+if cmakefile || makefile
+ srcdir,apidir=$outdir
+ dir=Dir.getwd
+ Dir.chdir File.dirname(__FILE__)
+ generator_files=Dir["**/*.rb"] << File.basename(__FILE__)
+ Dir.chdir dir
+ rgen_generator=generator_files.map{ |f| "$(rgen_dir)/#{f}" }
+ cmake_rgen_generator=generator_files.map{ |f| "${rgen_dir}/#{f}" }
+ rgen_srcs=GenFiles.get.map{ |f| "#{GenFiles.public_api?(f) ? apidir : srcdir}/#{f}" }
+ rgen_subdirs={}
+ rgen_srcs.each { |src|
+ if src.match(%r{(#{srcdir}|#{apidir})/qpid/([^/]+)/})
+ subdir=$2
+ rgen_subdirs[subdir] ||= []
+ rgen_subdirs[subdir] << src
+ end
+ }
+ if (makefile)
+ File.open(makefile, 'w') { |out|
+ out << <<EOS
+# Generated makefile fragment.
+# Including makefile defines $(rgen_dir) $(rgen_cmd) and $(specs).
+
+rgen_generator=#{make_continue rgen_generator}
+EOS
+ rgen_subdirs.each_key { |subdir|
+ out << "\nrgen_#{subdir}_srcs = #{make_continue(rgen_subdirs[subdir])}\n"
+ }
+ out << <<EOS
+rgen_srcs=#{make_continue rgen_srcs}
+
+# Header file install rules.
+EOS
+ ["amqp_0_10", "framing", "client/no_keyword","client", "broker"].each { |ns|
+ dir="qpid/#{ns}"
+ dir_ = dir.tr("/", "_")
+ regex=%r|#{dir}/[^/]+\.h$|
+ out << <<EOS
+#{dir_}dir = $(includedir)/#{dir}
+dist_#{dir_}_HEADERS = #{make_continue rgen_srcs.grep(regex)}
+
+EOS
+ } # each
+ } # File makefile
+ end # if (makefile)
+
+ if (cmakefile)
+ File.open(cmakefile, 'w') { |out|
+ out << <<EOS
+# Generated makefile fragment.
+# Including makefile defines ${rgen_dir} ${rgen_cmd} and ${specs}.
+
+set(rgen_generator #{cmake_continue cmake_rgen_generator})
+EOS
+ rgen_subdirs.each_key { |subdir|
+ out << "\nset(rgen_#{subdir}_srcs #{cmake_continue(rgen_subdirs[subdir])})\n"
+ }
+ out << <<EOS
+set(rgen_srcs #{cmake_continue rgen_srcs})
+
+# Header file install rules.
+EOS
+ ["amqp_0_10", "framing", "client/no_keyword","client", "broker"].each { |ns|
+ dir="qpid/#{ns}"
+ dir_ = dir.tr("/", "_")
+ regex=%r|#{dir}/[^/]+\.h$|
+ out << <<EOS
+set(#{dir_}dir \${includedir}/#{dir})
+set(dist_#{dir_}_HEADERS #{cmake_continue rgen_srcs.grep(regex)})
+
+EOS
+ } # each
+ } # File makefile
+ end # if (makefile)
+end