1 // This source code is dual-licensed under the Apache License, version
2 // 2.0, and the Mozilla Public License, version 1.1.
6 //---------------------------------------------------------------------------
7 // Copyright (C) 2007-2010 LShift Ltd., Cohesive Financial
8 // Technologies LLC., and Rabbit Technologies Ltd.
10 // Licensed under the Apache License, Version 2.0 (the "License");
11 // you may not use this file except in compliance with the License.
12 // You may obtain a copy of the License at
14 // http://www.apache.org/licenses/LICENSE-2.0
16 // Unless required by applicable law or agreed to in writing, software
17 // distributed under the License is distributed on an "AS IS" BASIS,
18 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 // See the License for the specific language governing permissions and
20 // limitations under the License.
21 //---------------------------------------------------------------------------
25 //---------------------------------------------------------------------------
26 // The contents of this file are subject to the Mozilla Public License
27 // Version 1.1 (the "License"); you may not use this file except in
28 // compliance with the License. You may obtain a copy of the License at
29 // http://www.rabbitmq.com/mpl.html
31 // Software distributed under the License is distributed on an "AS IS"
32 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
33 // License for the specific language governing rights and limitations
36 // The Original Code is The RabbitMQ .NET Client.
38 // The Initial Developers of the Original Code are LShift Ltd,
39 // Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd.
41 // Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd,
42 // Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd
43 // are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial
44 // Technologies LLC, and Rabbit Technologies Ltd.
46 // Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift
47 // Ltd. Portions created by Cohesive Financial Technologies LLC are
48 // Copyright (C) 2007-2010 Cohesive Financial Technologies
49 // LLC. Portions created by Rabbit Technologies Ltd are Copyright
50 // (C) 2007-2010 Rabbit Technologies Ltd.
52 // All Rights Reserved.
54 // Contributor(s): ______________________________________.
56 //---------------------------------------------------------------------------
58 using System.Collections;
60 using System.Reflection;
64 using RabbitMQ.Client.Apigen.Attributes;
66 namespace RabbitMQ.Client.Apigen {
68 ///////////////////////////////////////////////////////////////////////////
71 public static void Main(string[] args) {
72 Apigen instance = new Apigen(new ArrayList(args));
76 ///////////////////////////////////////////////////////////////////////////
79 public static XmlNodeList GetNodes(XmlNode n0, string path) {
80 return n0.SelectNodes(path);
83 public static string GetString(XmlNode n0, string path, string d) {
84 XmlNode n = n0.SelectSingleNode(path);
85 return (n == null) ? d : n.InnerText;
88 public static string GetString(XmlNode n0, string path) {
89 string s = GetString(n0, path, null);
91 throw new Exception("Missing spec XML node: " + path);
96 public static int GetInt(XmlNode n0, string path, int d) {
97 string s = GetString(n0, path, null);
98 return (s == null) ? d : int.Parse(s);
101 public static int GetInt(XmlNode n0, string path) {
102 return int.Parse(GetString(n0, path));
105 ///////////////////////////////////////////////////////////////////////////
106 // Name manipulation and mangling for C#
108 public static string MangleConstant(string name) {
109 // Previously, we used C_STYLE_CONSTANT_NAMES:
116 // ... but TheseKindsOfNames are more in line with .NET style guidelines.
117 return MangleClass(name);
120 public static ArrayList IdentifierParts(string name) {
121 ArrayList result = new ArrayList();
122 foreach (String s1 in name.Split(new Char[] { '-' })) {
123 foreach (String s2 in s1.Split(new Char[] { ' ' })) {
130 public static string MangleClass(string name) {
131 StringBuilder sb = new StringBuilder();
132 foreach (String s in IdentifierParts(name)) {
133 sb.Append(Char.ToUpper(s[0]) + s.Substring(1).ToLower());
135 return sb.ToString();
138 public static string MangleMethod(string name) {
139 StringBuilder sb = new StringBuilder();
140 bool useUpper = false;
141 foreach (String s in IdentifierParts(name)) {
143 sb.Append(Char.ToUpper(s[0]) + s.Substring(1).ToLower());
145 sb.Append(s.ToLower());
149 return sb.ToString();
152 public static string MangleMethodClass(AmqpClass c, AmqpMethod m) {
153 return MangleClass(c.Name) + MangleClass(m.Name);
156 ///////////////////////////////////////////////////////////////////////////
158 public string m_framingSubnamespace = null;
159 public string m_inputXmlFilename;
160 public string m_outputFilename;
162 public XmlDocument m_spec = null;
163 public TextWriter m_outputFile = null;
165 public bool m_versionOverridden = false;
166 public int m_majorVersion;
167 public int m_minorVersion;
168 public string m_apiName;
169 public bool m_emitComments = false;
171 public Type m_modelType = typeof(RabbitMQ.Client.Impl.IFullModel);
172 public ArrayList m_modelTypes = new ArrayList();
173 public ArrayList m_constants = new ArrayList();
174 public ArrayList m_classes = new ArrayList();
175 public Hashtable m_domains = new Hashtable();
177 public static Hashtable m_primitiveTypeMap;
178 public static Hashtable m_primitiveTypeFlagMap;
180 m_primitiveTypeMap = new Hashtable();
181 m_primitiveTypeFlagMap = new Hashtable();
182 InitPrimitiveType("octet", "byte", false);
183 InitPrimitiveType("shortstr", "string", true);
184 InitPrimitiveType("longstr", "byte[]", true);
185 InitPrimitiveType("short", "ushort", false);
186 InitPrimitiveType("long", "uint", false);
187 InitPrimitiveType("longlong", "ulong", false);
188 InitPrimitiveType("bit", "bool", false);
189 InitPrimitiveType("table", "System.Collections.IDictionary", true);
190 InitPrimitiveType("timestamp", "AmqpTimestamp", false);
191 InitPrimitiveType("content", "byte[]", true);
194 public static void InitPrimitiveType(string amqpType, string dotnetType, bool isReference)
196 m_primitiveTypeMap[amqpType] = dotnetType;
197 m_primitiveTypeFlagMap[amqpType] = isReference;
200 public void HandleOption(string opt) {
201 if (opt.StartsWith("/n:")) {
202 m_framingSubnamespace = opt.Substring(3);
203 } else if (opt.StartsWith("/apiName:")) {
204 m_apiName = opt.Substring(9);
205 } else if (opt.StartsWith("/v:")) {
206 string[] parts = opt.Substring(3).Split(new char[] { '-' });
207 m_versionOverridden = true;
208 m_majorVersion = int.Parse(parts[0]);
209 m_minorVersion = int.Parse(parts[1]);
210 } else if (opt == "/c") {
211 m_emitComments = true;
213 Console.Error.WriteLine("Unsupported command-line option: " + opt);
218 public void Usage() {
219 Console.Error.WriteLine("Usage: Apigen.exe [options ...] <input-spec-xml> <output-csharp-file>");
220 Console.Error.WriteLine(" Options include:");
221 Console.Error.WriteLine(" /apiName:<identifier>");
222 Console.Error.WriteLine(" /n:<name.space.prefix>");
223 Console.Error.WriteLine(" /v:<majorversion>-<minorversion>");
224 Console.Error.WriteLine(" The apiName option is required.");
228 public Apigen(ArrayList args) {
229 while (args.Count > 0 && ((string) args[0]).StartsWith("/")) {
230 HandleOption((string) args[0]);
234 || (m_apiName == null))
238 m_inputXmlFilename = (string) args[0];
239 m_outputFilename = (string) args[1];
242 ///////////////////////////////////////////////////////////////////////////
244 public string FramingSubnamespace {
246 if (m_framingSubnamespace == null) {
247 return VersionToken();
249 return m_framingSubnamespace;
254 public string ApiNamespaceBase {
256 return "RabbitMQ.Client.Framing."+FramingSubnamespace;
260 public string ImplNamespaceBase {
262 return "RabbitMQ.Client.Framing.Impl."+FramingSubnamespace;
266 public void Generate() {
273 public void LoadSpec() {
274 Console.WriteLine("* Loading spec from '" + m_inputXmlFilename + "'");
275 m_spec = new XmlDocument();
276 m_spec.Load(m_inputXmlFilename);
279 public void ParseSpec() {
280 Console.WriteLine("* Parsing spec");
281 if (!m_versionOverridden) {
282 m_majorVersion = GetInt(m_spec, "/amqp/@major");
283 m_minorVersion = GetInt(m_spec, "/amqp/@minor");
285 foreach (XmlNode n in m_spec.SelectNodes("/amqp/constant")) {
286 m_constants.Add(new DictionaryEntry(GetString(n, "@name"), GetInt(n, "@value")));
288 foreach (XmlNode n in m_spec.SelectNodes("/amqp/class")) {
289 m_classes.Add(new AmqpClass(n));
291 foreach (XmlNode n in m_spec.SelectNodes("/amqp/domain")) {
292 m_domains[GetString(n, "@name")] = GetString(n, "@type");
296 public void ReflectModel() {
297 m_modelTypes.Add(m_modelType);
298 for (int i = 0; i < m_modelTypes.Count; i++)
300 foreach (Type intf in ((Type) m_modelTypes[i]).GetInterfaces())
302 m_modelTypes.Add(intf);
307 public string ResolveDomain(string d) {
308 while (m_domains[d] != null) {
309 string newD = (string) m_domains[d];
317 public string MapDomain(string d) {
318 return (string) m_primitiveTypeMap[ResolveDomain(d)];
321 public string VersionToken() {
322 return "v" + m_majorVersion + "_" + m_minorVersion;
325 public void GenerateOutput() {
326 Console.WriteLine("* Generating code into '" + m_outputFilename + "'");
327 m_outputFile = new StreamWriter(m_outputFilename);
331 m_outputFile.Close();
334 public void Emit(object o) {
335 m_outputFile.Write(o);
338 public void EmitLine(object o) {
339 m_outputFile.WriteLine(o);
342 public void EmitSpecComment(object o) {
347 public void EmitPrelude() {
348 EmitLine("// Autogenerated code. Do not edit.");
350 EmitLine("// This source code is dual-licensed under the Apache License, version");
351 EmitLine("// 2.0, and the Mozilla Public License, version 1.1.");
353 EmitLine("// The APL v2.0:");
355 EmitLine("//---------------------------------------------------------------------------");
356 EmitLine("// Copyright (C) 2007-2010 LShift Ltd., Cohesive Financial");
357 EmitLine("// Technologies LLC., and Rabbit Technologies Ltd.");
359 EmitLine("// Licensed under the Apache License, Version 2.0 (the \"License\");");
360 EmitLine("// you may not use this file except in compliance with the License.");
361 EmitLine("// You may obtain a copy of the License at");
363 EmitLine("// http://www.apache.org/licenses/LICENSE-2.0");
365 EmitLine("// Unless required by applicable law or agreed to in writing, software");
366 EmitLine("// distributed under the License is distributed on an \"AS IS\" BASIS,");
367 EmitLine("// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.");
368 EmitLine("// See the License for the specific language governing permissions and");
369 EmitLine("// limitations under the License.");
370 EmitLine("//---------------------------------------------------------------------------");
372 EmitLine("// The MPL v1.1:");
374 EmitLine("//---------------------------------------------------------------------------");
375 EmitLine("// The contents of this file are subject to the Mozilla Public License");
376 EmitLine("// Version 1.1 (the \"License\"); you may not use this file except in");
377 EmitLine("// compliance with the License. You may obtain a copy of the License at");
378 EmitLine("// http://www.rabbitmq.com/mpl.html");
380 EmitLine("// Software distributed under the License is distributed on an \"AS IS\"");
381 EmitLine("// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the");
382 EmitLine("// License for the specific language governing rights and limitations");
383 EmitLine("// under the License.");
385 EmitLine("// The Original Code is The RabbitMQ .NET Client.");
387 EmitLine("// The Initial Developers of the Original Code are LShift Ltd,");
388 EmitLine("// Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd.");
390 EmitLine("// Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd,");
391 EmitLine("// Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd");
392 EmitLine("// are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial");
393 EmitLine("// Technologies LLC, and Rabbit Technologies Ltd.");
395 EmitLine("// Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift");
396 EmitLine("// Ltd. Portions created by Cohesive Financial Technologies LLC are");
397 EmitLine("// Copyright (C) 2007-2010 Cohesive Financial Technologies");
398 EmitLine("// LLC. Portions created by Rabbit Technologies Ltd are Copyright");
399 EmitLine("// (C) 2007-2010 Rabbit Technologies Ltd.");
401 EmitLine("// All Rights Reserved.");
403 EmitLine("// Contributor(s): ______________________________________.");
405 EmitLine("//---------------------------------------------------------------------------");
407 EmitLine("using RabbitMQ.Client;");
408 EmitLine("using RabbitMQ.Client.Exceptions;");
412 public void EmitPublic() {
413 EmitLine("namespace "+ApiNamespaceBase+" {");
414 EmitLine(" public class Protocol: "+ImplNamespaceBase+".ProtocolBase {");
415 EmitLine(" ///<summary>Protocol major version (= "+m_majorVersion+")</summary>");
416 EmitLine(" public override int MajorVersion { get { return " + m_majorVersion + "; } }");
417 EmitLine(" ///<summary>Protocol minor version (= "+m_minorVersion+")</summary>");
418 EmitLine(" public override int MinorVersion { get { return " + m_minorVersion + "; } }");
419 EmitLine(" ///<summary>Protocol API name (= "+m_apiName+")</summary>");
420 EmitLine(" public override string ApiName { get { return \"" + m_apiName + "\"; } }");
421 int port = GetInt(m_spec, "/amqp/@port");
422 EmitLine(" ///<summary>Default TCP port (= "+port+")</summary>");
423 EmitLine(" public override int DefaultPort { get { return " + port + "; } }");
425 EmitMethodArgumentReader();
427 EmitContentHeaderReader();
429 EmitLine(" public class Constants {");
430 foreach (DictionaryEntry de in m_constants) {
431 EmitLine(" ///<summary>(= "+de.Value+")</summary>");
432 EmitLine(" public const int "+MangleConstant((string) de.Key)+" = "+de.Value+";");
435 foreach (AmqpClass c in m_classes) {
438 foreach (AmqpClass c in m_classes) {
439 if (c.NeedsProperties) {
440 EmitClassProperties(c);
446 public void EmitAutogeneratedSummary(string prefixSpaces, string extra) {
447 EmitLine(prefixSpaces+"/// <summary>Autogenerated type. "+extra+"</summary>");
450 public void EmitClassMethods(AmqpClass c) {
451 foreach (AmqpMethod m in c.m_Methods) {
452 EmitAutogeneratedSummary(" ",
453 "AMQP specification method \""+c.Name+"."+m.Name+"\".");
454 EmitSpecComment(m.DocumentationCommentVariant(" ", "remarks"));
455 EmitLine(" public interface I"+MangleMethodClass(c, m)+": IMethod {");
456 foreach (AmqpField f in m.m_Fields) {
457 EmitSpecComment(f.DocumentationComment(" "));
458 EmitLine(" "+MapDomain(f.Domain)+" "+MangleClass(f.Name)+" { get; }");
464 public bool HasFactoryMethod(AmqpClass c) {
465 foreach (Type t in m_modelTypes) {
466 foreach (MethodInfo method in t.GetMethods()) {
467 AmqpContentHeaderFactoryAttribute f = (AmqpContentHeaderFactoryAttribute)
468 Attribute(method, typeof(AmqpContentHeaderFactoryAttribute));
469 if (f != null && MangleClass(f.m_contentClass) == MangleClass(c.Name)) {
477 public bool IsBoolean(AmqpField f) {
478 return ResolveDomain(f.Domain) == "bit";
481 public bool IsReferenceType(AmqpField f) {
482 return (bool) m_primitiveTypeFlagMap[ResolveDomain(f.Domain)];
485 public bool IsAmqpClass(Type t)
487 foreach (AmqpClass c in m_classes)
489 if (c.Name == t.Name)
495 public void EmitClassProperties(AmqpClass c) {
496 bool hasCommonApi = HasFactoryMethod(c);
497 string propertiesBaseClass =
499 ? "RabbitMQ.Client.Impl."+MangleClass(c.Name)+"Properties"
500 : "RabbitMQ.Client.Impl.ContentHeaderBase";
501 string maybeOverride = hasCommonApi ? "override " : "";
503 EmitAutogeneratedSummary(" ",
504 "AMQP specification content header properties for "+
505 "content class \""+c.Name+"\"");
506 EmitSpecComment(c.DocumentationCommentVariant(" ", "remarks"));
507 EmitLine(" public class "+MangleClass(c.Name)
508 +"Properties: "+propertiesBaseClass+" {");
509 foreach (AmqpField f in c.m_Fields) {
510 EmitLine(" private "+MapDomain(f.Domain)+" m_"+MangleMethod(f.Name)+";");
513 foreach (AmqpField f in c.m_Fields)
516 EmitLine(" private bool m_"+MangleMethod(f.Name)+"_present = false;");
520 foreach (AmqpField f in c.m_Fields)
522 EmitSpecComment(f.DocumentationComment(" ", "@label"));
523 EmitLine(" public "+maybeOverride+MapDomain(f.Domain)+" "+MangleClass(f.Name)+" {");
525 EmitLine(" return m_"+MangleMethod(f.Name)+";");
529 EmitLine(" m_"+MangleMethod(f.Name)+"_present = true;");
531 EmitLine(" m_"+MangleMethod(f.Name)+" = value;");
536 foreach (AmqpField f in c.m_Fields)
539 EmitLine(" public "+maybeOverride+"void Clear"+MangleClass(f.Name)+"() { m_"+MangleMethod(f.Name)+"_present = false; }");
544 foreach (AmqpField f in c.m_Fields)
547 EmitLine(" public " + maybeOverride + "bool Is" + MangleClass(f.Name) + "Present() { return m_" + MangleMethod(f.Name) + "_present; }");
551 EmitLine(" public "+MangleClass(c.Name)+"Properties() {}");
552 EmitLine(" public override int ProtocolClassId { get { return "+c.Index+"; } }");
553 EmitLine(" public override string ProtocolClassName { get { return \""+c.Name+"\"; } }");
555 EmitLine(" public override void ReadPropertiesFrom(RabbitMQ.Client.Impl.ContentHeaderPropertyReader reader) {");
556 foreach (AmqpField f in c.m_Fields)
559 EmitLine(" m_"+MangleMethod(f.Name)+" = reader.ReadBit();");
561 EmitLine(" m_"+MangleMethod(f.Name)+"_present = reader.ReadPresence();");
564 EmitLine(" reader.FinishPresence();");
565 foreach (AmqpField f in c.m_Fields)
568 EmitLine(" if (m_"+MangleMethod(f.Name)+"_present) { m_"+MangleMethod(f.Name)+" = reader.Read"+MangleClass(ResolveDomain(f.Domain))+"(); }");
573 EmitLine(" public override void WritePropertiesTo(RabbitMQ.Client.Impl.ContentHeaderPropertyWriter writer) {");
574 foreach (AmqpField f in c.m_Fields)
577 EmitLine(" writer.WriteBit(m_"+MangleMethod(f.Name)+");");
579 EmitLine(" writer.WritePresence(m_"+MangleMethod(f.Name)+"_present);");
582 EmitLine(" writer.FinishPresence();");
583 foreach (AmqpField f in c.m_Fields)
586 EmitLine(" if (m_"+MangleMethod(f.Name)+"_present) { writer.Write"+MangleClass(ResolveDomain(f.Domain))+"(m_"+MangleMethod(f.Name)+"); }");
591 EmitLine(" public override void AppendPropertyDebugStringTo(System.Text.StringBuilder sb) {");
592 EmitLine(" sb.Append(\"(\");");
594 int remaining = c.m_Fields.Count;
595 foreach (AmqpField f in c.m_Fields)
597 Emit(" sb.Append(\""+f.Name+"=\");");
599 Emit(" sb.Append(m_"+MangleMethod(f.Name)+");");
601 string x = MangleMethod(f.Name);
602 if (IsReferenceType(f)) {
603 Emit(" sb.Append(m_"+x+"_present ? (m_"+x+" == null ? \"(null)\" : m_"+x+".ToString()) : \"_\");");
605 Emit(" sb.Append(m_"+x+"_present ? m_"+x+".ToString() : \"_\");");
610 EmitLine(" sb.Append(\", \");");
616 EmitLine(" sb.Append(\")\");");
621 public void EmitPrivate() {
622 EmitLine("namespace "+ImplNamespaceBase+" {");
623 EmitLine(" using "+ApiNamespaceBase+";");
624 EmitLine(" public enum ClassId {");
625 foreach (AmqpClass c in m_classes) {
626 EmitLine(" "+MangleConstant(c.Name)+" = "+c.Index+",");
628 EmitLine(" Invalid = -1");
630 foreach (AmqpClass c in m_classes) {
631 EmitClassMethodImplementations(c);
634 EmitModelImplementation();
638 public void EmitClassMethodImplementations(AmqpClass c) {
639 foreach (AmqpMethod m in c.m_Methods)
641 EmitAutogeneratedSummary(" ",
642 "Private implementation class - do not use directly.");
643 EmitLine(" public class "+MangleMethodClass(c,m)
644 +": RabbitMQ.Client.Impl.MethodBase, I"+MangleMethodClass(c,m)+" {");
645 EmitLine(" public const int ClassId = "+c.Index+";");
646 EmitLine(" public const int MethodId = "+m.Index+";");
648 foreach (AmqpField f in m.m_Fields)
650 EmitLine(" public "+MapDomain(f.Domain)+" m_"+MangleMethod(f.Name)+";");
653 foreach (AmqpField f in m.m_Fields)
655 EmitLine(" "+MapDomain(f.Domain)+" I"+MangleMethodClass(c,m)+
656 "."+MangleClass(f.Name)+" { get {"
657 + " return m_" + MangleMethod(f.Name) + "; } }");
660 if (m.m_Fields.Count > 0)
662 EmitLine(" public "+MangleMethodClass(c,m)+"() {}");
664 EmitLine(" public "+MangleMethodClass(c,m)+"(");
666 int remaining = m.m_Fields.Count;
667 foreach (AmqpField f in m.m_Fields)
669 Emit(" "+MapDomain(f.Domain)+" init"+MangleClass(f.Name));
678 foreach (AmqpField f in m.m_Fields)
680 EmitLine(" m_" + MangleMethod(f.Name) + " = init" + MangleClass(f.Name) + ";");
684 EmitLine(" public override int ProtocolClassId { get { return "+c.Index+"; } }");
685 EmitLine(" public override int ProtocolMethodId { get { return "+m.Index+"; } }");
686 EmitLine(" public override string ProtocolMethodName { get { return \""+c.Name+"."+m.Name+"\"; } }");
687 EmitLine(" public override bool HasContent { get { return "
688 +(m.HasContent ? "true" : "false")+"; } }");
690 EmitLine(" public override void ReadArgumentsFrom(RabbitMQ.Client.Impl.MethodArgumentReader reader) {");
691 foreach (AmqpField f in m.m_Fields)
693 EmitLine(" m_" + MangleMethod(f.Name) + " = reader.Read" + MangleClass(ResolveDomain(f.Domain)) + "();");
697 EmitLine(" public override void WriteArgumentsTo(RabbitMQ.Client.Impl.MethodArgumentWriter writer) {");
698 foreach (AmqpField f in m.m_Fields)
700 EmitLine(" writer.Write"+MangleClass(ResolveDomain(f.Domain))
701 + "(m_" + MangleMethod(f.Name) + ");");
705 EmitLine(" public override void AppendArgumentDebugStringTo(System.Text.StringBuilder sb) {");
706 EmitLine(" sb.Append(\"(\");");
708 int remaining = m.m_Fields.Count;
709 foreach (AmqpField f in m.m_Fields)
711 Emit(" sb.Append(m_" + MangleMethod(f.Name) + ");");
714 EmitLine(" sb.Append(\",\");");
720 EmitLine(" sb.Append(\")\");");
726 public void EmitMethodArgumentReader() {
727 EmitLine(" public override RabbitMQ.Client.Impl.MethodBase DecodeMethodFrom(RabbitMQ.Util.NetworkBinaryReader reader) {");
728 EmitLine(" ushort classId = reader.ReadUInt16();");
729 EmitLine(" ushort methodId = reader.ReadUInt16();");
731 EmitLine(" switch (classId) {");
732 foreach (AmqpClass c in m_classes) {
733 EmitLine(" case "+c.Index+": {");
734 EmitLine(" switch (methodId) {");
735 foreach (AmqpMethod m in c.m_Methods)
737 EmitLine(" case "+m.Index+": {");
738 EmitLine(" "+ImplNamespaceBase+"."+MangleMethodClass(c,m)+" result = new "+ImplNamespaceBase+"."+MangleMethodClass(c,m)+"();");
739 EmitLine(" result.ReadArgumentsFrom(new RabbitMQ.Client.Impl.MethodArgumentReader(reader));");
740 EmitLine(" return result;");
743 EmitLine(" default: break;");
748 EmitLine(" default: break;");
750 EmitLine(" throw new RabbitMQ.Client.Impl.UnknownClassOrMethodException(classId, methodId);");
754 public void EmitContentHeaderReader() {
755 EmitLine(" public override RabbitMQ.Client.Impl.ContentHeaderBase DecodeContentHeaderFrom(RabbitMQ.Util.NetworkBinaryReader reader) {");
756 EmitLine(" ushort classId = reader.ReadUInt16();");
758 EmitLine(" switch (classId) {");
759 foreach (AmqpClass c in m_classes) {
760 if (c.NeedsProperties) {
761 EmitLine(" case "+c.Index+": return new "
762 +MangleClass(c.Name)+"Properties();");
765 EmitLine(" default: break;");
767 EmitLine(" throw new RabbitMQ.Client.Impl.UnknownClassOrMethodException(classId, 0);");
771 public Attribute Attribute(MemberInfo mi, Type t) {
772 return Attribute(mi.GetCustomAttributes(t, false), t);
775 public Attribute Attribute(ParameterInfo pi, Type t) {
776 return Attribute(pi.GetCustomAttributes(t, false), t);
779 public Attribute Attribute(ICustomAttributeProvider p, Type t) {
780 return Attribute(p.GetCustomAttributes(t, false), t);
783 public Attribute Attribute(IEnumerable attributes, Type t) {
784 if (t.IsSubclassOf(typeof(AmqpApigenAttribute))) {
785 AmqpApigenAttribute result = null;
786 foreach (AmqpApigenAttribute candidate in attributes) {
787 if (candidate.m_namespaceName == null && result == null) {
790 if (candidate.m_namespaceName == ApiNamespaceBase) {
796 foreach (Attribute attribute in attributes) {
803 public void EmitModelImplementation() {
804 EmitLine(" public class Model: RabbitMQ.Client.Impl.ModelBase {");
805 EmitLine(" public Model(RabbitMQ.Client.Impl.ISession session): base(session) {}");
806 ArrayList asynchronousHandlers = new ArrayList();
807 foreach (Type t in m_modelTypes) {
808 foreach (MethodInfo method in t.GetMethods()) {
809 if (method.DeclaringType.Namespace != null &&
810 method.DeclaringType.Namespace.StartsWith("RabbitMQ.Client")) {
811 if (method.Name.StartsWith("Handle") ||
812 (Attribute(method, typeof(AmqpAsynchronousHandlerAttribute)) != null))
814 asynchronousHandlers.Add(method);
816 MaybeEmitModelMethod(method);
821 EmitAsynchronousHandlers(asynchronousHandlers);
825 public void EmitContentHeaderFactory(MethodInfo method) {
826 AmqpContentHeaderFactoryAttribute factoryAnnotation = (AmqpContentHeaderFactoryAttribute)
827 Attribute(method, typeof(AmqpContentHeaderFactoryAttribute));
828 string contentClass = factoryAnnotation.m_contentClass;
829 EmitModelMethodPreamble(method);
831 EmitLine(" return new "+MangleClass(contentClass)+"Properties();");
835 public void MaybeEmitModelMethod(MethodInfo method) {
836 if (method.IsSpecialName) {
837 // It's some kind of event- or property-related method.
838 // It shouldn't be autogenerated.
839 } else if (Attribute(method, typeof(AmqpMethodDoNotImplementAttribute)) != null) {
840 // Skip this method, by request (AmqpMethodDoNotImplement)
841 } else if (Attribute(method, typeof(AmqpContentHeaderFactoryAttribute)) != null) {
842 EmitContentHeaderFactory(method);
843 } else if (Attribute(method, typeof(AmqpUnsupportedAttribute)) != null) {
844 EmitModelMethodPreamble(method);
846 EmitLine(" throw new UnsupportedMethodException(\""+method.Name+"\");");
849 EmitModelMethod(method);
853 public string SanitisedFullName(Type t) {
854 if (t == typeof(void)) {
861 public void EmitModelMethodPreamble(MethodInfo method) {
862 Emit(" public override "+SanitisedFullName(method.ReturnType)+" "+method.Name);
863 ParameterInfo[] parameters = method.GetParameters();
864 int remaining = parameters.Length;
865 if (remaining == 0) {
869 foreach (ParameterInfo pi in parameters) {
870 Emit(" "+SanitisedFullName(pi.ParameterType)+" @"+pi.Name);
881 public void LookupAmqpMethod(MethodInfo method,
883 out AmqpClass amqpClass,
884 out AmqpMethod amqpMethod)
889 // First, try autodetecting the class/method via the
890 // IModel method name.
892 foreach (AmqpClass c in m_classes) {
893 foreach (AmqpMethod m in c.m_Methods)
895 if (methodName.Equals(MangleMethodClass(c,m))) {
898 goto stopSearching; // wheee
904 // If an explicit mapping was provided as an attribute,
905 // then use that instead, whether the autodetect worked or
909 AmqpMethodMappingAttribute methodMapping =
910 Attribute(method, typeof(AmqpMethodMappingAttribute)) as AmqpMethodMappingAttribute;
911 if (methodMapping != null) {
913 foreach (AmqpClass c in m_classes) {
914 if (c.Name == methodMapping.m_className) {
919 amqpMethod = amqpClass.MethodNamed(methodMapping.m_methodName);
923 // At this point, if can't find either the class or the
924 // method, we can't proceed. Complain.
926 if (amqpClass == null || amqpMethod == null) {
927 throw new Exception("Could not find AMQP class or method for IModel method " + method.Name);
931 public void EmitModelMethod(MethodInfo method) {
932 ParameterInfo[] parameters = method.GetParameters();
934 AmqpClass amqpClass = null;
935 AmqpMethod amqpMethod = null;
936 LookupAmqpMethod(method, method.Name, out amqpClass, out amqpMethod);
938 string requestImplClass = MangleMethodClass(amqpClass, amqpMethod);
940 // At this point, we know which request method to
941 // send. Now compute whether it's an RPC or not.
943 AmqpMethod amqpReplyMethod = null;
944 AmqpMethodMappingAttribute replyMapping =
945 Attribute(method.ReturnTypeCustomAttributes, typeof(AmqpMethodMappingAttribute))
946 as AmqpMethodMappingAttribute;
947 if (Attribute(method, typeof(AmqpForceOneWayAttribute)) == null &&
948 (amqpMethod.IsSimpleRpcRequest || replyMapping != null))
950 // We're not forcing oneway, and either are a simple
951 // RPC request, or have an explicit replyMapping
952 amqpReplyMethod = amqpClass.MethodNamed(replyMapping == null
953 ? (string) amqpMethod.m_ResponseMethods[0]
954 : replyMapping.m_methodName);
955 if (amqpReplyMethod == null) {
956 throw new Exception("Could not find AMQP reply method for IModel method " + method.Name);
960 // If amqpReplyMethod is null at this point, it's a
961 // one-way operation, and no continuation needs to be
962 // consed up. Otherwise, we should expect a reply of kind
963 // identified by amqpReplyMethod - unless there's a nowait
964 // parameter thrown into the equation!
966 // Examine the parameters to discover which might be
967 // nowait, content header or content body.
969 ParameterInfo nowaitParameter = null;
970 string nowaitExpression = "null";
971 ParameterInfo contentHeaderParameter = null;
972 ParameterInfo contentBodyParameter = null;
973 foreach (ParameterInfo pi in parameters) {
974 AmqpNowaitArgumentAttribute nwAttr =
975 Attribute(pi, typeof(AmqpNowaitArgumentAttribute)) as AmqpNowaitArgumentAttribute;
976 if (nwAttr != null) {
977 nowaitParameter = pi;
978 if (nwAttr.m_replacementExpression != null) {
979 nowaitExpression = nwAttr.m_replacementExpression;
982 if (Attribute(pi, typeof(AmqpContentHeaderMappingAttribute)) != null) {
983 contentHeaderParameter = pi;
985 if (Attribute(pi, typeof(AmqpContentBodyMappingAttribute)) != null) {
986 contentBodyParameter = pi;
990 // Compute expression text for the content header and body.
992 string contentHeaderExpr =
993 contentHeaderParameter == null
995 : " ("+MangleClass(amqpClass.Name)+"Properties) "+contentHeaderParameter.Name;
996 string contentBodyExpr =
997 contentBodyParameter == null ? "null" : contentBodyParameter.Name;
999 // Emit the method declaration and preamble.
1001 EmitModelMethodPreamble(method);
1004 // Emit the code to build the request.
1006 EmitLine(" "+requestImplClass+" __req = new "+requestImplClass+"();");
1007 foreach (ParameterInfo pi in parameters) {
1008 if (pi != contentHeaderParameter &&
1009 pi != contentBodyParameter)
1011 if (Attribute(pi, typeof(AmqpUnsupportedAttribute)) != null) {
1012 EmitLine(" if (@"+pi.Name+" != null) {");
1013 EmitLine(" throw new UnsupportedMethodFieldException(\""+method.Name+"\",\""+pi.Name+"\");");
1016 AmqpFieldMappingAttribute fieldMapping =
1017 Attribute(pi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
1018 if (fieldMapping != null) {
1019 EmitLine(" __req.m_"+fieldMapping.m_fieldName+" = @" + pi.Name + ";");
1021 EmitLine(" __req.m_"+pi.Name+" = @" + pi.Name + ";");
1027 // If we have a nowait parameter, sometimes that can turn
1028 // a ModelRpc call into a ModelSend call.
1030 if (nowaitParameter != null) {
1031 EmitLine(" if ("+nowaitParameter.Name+") {");
1032 EmitLine(" ModelSend(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
1033 if (method.ReturnType != typeof(void)) {
1034 EmitLine(" return "+nowaitExpression+";");
1039 // At this point, perform either a ModelRpc or a
1042 if (amqpReplyMethod == null) {
1043 EmitLine(" ModelSend(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
1045 string replyImplClass = MangleMethodClass(amqpClass, amqpReplyMethod);
1047 EmitLine(" RabbitMQ.Client.Impl.MethodBase __repBase = ModelRpc(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
1048 EmitLine(" "+replyImplClass+" __rep = __repBase as "+replyImplClass+";");
1049 EmitLine(" if (__rep == null) throw new UnexpectedMethodException(__repBase);");
1051 if (method.ReturnType == typeof(void)) {
1052 // No need to further examine the reply.
1054 // At this point, we have the reply method. Extract values from it.
1055 AmqpFieldMappingAttribute returnMapping =
1056 Attribute(method.ReturnTypeCustomAttributes, typeof(AmqpFieldMappingAttribute))
1057 as AmqpFieldMappingAttribute;
1058 if (returnMapping == null) {
1059 string fieldPrefix = IsAmqpClass(method.ReturnType) ? "m_" : "";
1061 // No field mapping --> it's assumed to be a struct to fill in.
1062 EmitLine(" "+method.ReturnType+" __result = new "+method.ReturnType+"();");
1063 foreach (FieldInfo fi in method.ReturnType.GetFields()) {
1064 AmqpFieldMappingAttribute returnFieldMapping =
1065 Attribute(fi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
1066 if (returnFieldMapping != null) {
1067 EmitLine(" __result." + fi.Name + " = __rep." + fieldPrefix + returnFieldMapping.m_fieldName + ";");
1069 EmitLine(" __result." + fi.Name + " = __rep." + fieldPrefix + fi.Name + ";");
1072 EmitLine(" return __result;");
1074 // Field mapping --> return just the field we're interested in.
1075 EmitLine(" return __rep.m_"+returnMapping.m_fieldName+";");
1080 // All the IO and result-extraction has been done. Emit
1081 // the method postamble.
1086 public void EmitAsynchronousHandlers(ArrayList asynchronousHandlers) {
1087 EmitLine(" public override bool DispatchAsynchronous(RabbitMQ.Client.Impl.Command cmd) {");
1088 EmitLine(" RabbitMQ.Client.Impl.MethodBase __method = (RabbitMQ.Client.Impl.MethodBase) cmd.Method;");
1089 EmitLine(" switch ((__method.ProtocolClassId << 16) | __method.ProtocolMethodId) {");
1090 foreach (MethodInfo method in asynchronousHandlers) {
1091 string methodName = method.Name;
1092 if (methodName.StartsWith("Handle")) {
1093 methodName = methodName.Substring(6);
1096 AmqpClass amqpClass = null;
1097 AmqpMethod amqpMethod = null;
1098 LookupAmqpMethod(method, methodName, out amqpClass, out amqpMethod);
1100 string implClass = MangleMethodClass(amqpClass, amqpMethod);
1102 EmitLine(" case "+((amqpClass.Index << 16) | amqpMethod.Index)+": {");
1103 ParameterInfo[] parameters = method.GetParameters();
1104 if (parameters.Length > 0) {
1105 EmitLine(" "+implClass+" __impl = ("+implClass+") __method;");
1106 EmitLine(" "+method.Name+"(");
1107 int remaining = parameters.Length;
1108 foreach (ParameterInfo pi in parameters) {
1109 if (Attribute(pi, typeof(AmqpContentHeaderMappingAttribute)) != null) {
1110 Emit(" ("+pi.ParameterType+") cmd.Header");
1111 } else if (Attribute(pi, typeof(AmqpContentBodyMappingAttribute)) != null) {
1114 AmqpFieldMappingAttribute fieldMapping =
1115 Attribute(pi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
1116 Emit(" __impl.m_"+(fieldMapping == null
1118 : fieldMapping.m_fieldName));
1121 if (remaining > 0) {
1127 EmitLine(" "+method.Name+"();");
1129 EmitLine(" return true;");
1132 EmitLine(" default: return false;");