summaryrefslogtreecommitdiff
path: root/qpid/cpp/rubygen/framing.0-10
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/rubygen/framing.0-10')
-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
11 files changed, 1833 insertions, 0 deletions
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()
+