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-2009 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-2009 LShift
47 // Ltd. Portions created by Cohesive Financial Technologies LLC are
48 // Copyright (C) 2007-2009 Cohesive Financial Technologies
49 // LLC. Portions created by Rabbit Technologies Ltd are Copyright
50 // (C) 2007-2009 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 framingSubnamespace = null;
159 public string inputXmlFilename;
160 public string outputFilename;
162 public XmlDocument spec = null;
163 public TextWriter outputFile = null;
165 public bool versionOverridden = false;
166 public int majorVersion;
167 public int minorVersion;
168 public string apiName;
170 public Type modelType = typeof(RabbitMQ.Client.Impl.IFullModel);
171 public ArrayList modelTypes = new ArrayList();
172 public ArrayList constants = new ArrayList();
173 public ArrayList classes = new ArrayList();
174 public Hashtable domains = new Hashtable();
176 public static Hashtable primitiveTypeMap;
177 public static Hashtable primitiveTypeFlagMap;
179 primitiveTypeMap = new Hashtable();
180 primitiveTypeFlagMap = new Hashtable();
181 InitPrimitiveType("octet", "byte", false);
182 InitPrimitiveType("shortstr", "string", true);
183 InitPrimitiveType("longstr", "byte[]", true);
184 InitPrimitiveType("short", "ushort", false);
185 InitPrimitiveType("long", "uint", false);
186 InitPrimitiveType("longlong", "ulong", false);
187 InitPrimitiveType("bit", "bool", false);
188 InitPrimitiveType("table", "System.Collections.IDictionary", true);
189 InitPrimitiveType("timestamp", "AmqpTimestamp", false);
190 InitPrimitiveType("content", "byte[]", true);
193 public static void InitPrimitiveType(string amqpType, string dotnetType, bool isReference)
195 primitiveTypeMap[amqpType] = dotnetType;
196 primitiveTypeFlagMap[amqpType] = isReference;
199 public void HandleOption(string opt) {
200 if (opt.StartsWith("/n:")) {
201 framingSubnamespace = opt.Substring(3);
202 } else if (opt.StartsWith("/apiName:")) {
203 apiName = opt.Substring(9);
204 } else if (opt.StartsWith("/v:")) {
205 string[] parts = opt.Substring(3).Split(new char[] { '-' });
206 versionOverridden = true;
207 majorVersion = int.Parse(parts[0]);
208 minorVersion = int.Parse(parts[1]);
210 Console.Error.WriteLine("Unsupported command-line option: " + opt);
215 public void Usage() {
216 Console.Error.WriteLine("Usage: Apigen.exe [options ...] <input-spec-xml> <output-csharp-file>");
217 Console.Error.WriteLine(" Options include:");
218 Console.Error.WriteLine(" /apiName:<identifier>");
219 Console.Error.WriteLine(" /n:<name.space.prefix>");
220 Console.Error.WriteLine(" /v:<majorversion>-<minorversion>");
221 Console.Error.WriteLine(" The apiName option is required.");
225 public Apigen(ArrayList args) {
226 while (args.Count > 0 && ((string) args[0]).StartsWith("/")) {
227 HandleOption((string) args[0]);
231 || (apiName == null))
235 this.inputXmlFilename = (string) args[0];
236 this.outputFilename = (string) args[1];
239 ///////////////////////////////////////////////////////////////////////////
241 public string FramingSubnamespace {
243 if (framingSubnamespace == null) {
244 return VersionToken();
246 return framingSubnamespace;
251 public string ApiNamespaceBase {
253 return "RabbitMQ.Client.Framing."+FramingSubnamespace;
257 public string ImplNamespaceBase {
259 return "RabbitMQ.Client.Framing.Impl."+FramingSubnamespace;
263 public void Generate() {
270 public void LoadSpec() {
271 Console.WriteLine("* Loading spec from '" + this.inputXmlFilename + "'");
272 this.spec = new XmlDocument();
273 this.spec.Load(this.inputXmlFilename);
276 public void ParseSpec() {
277 Console.WriteLine("* Parsing spec");
278 if (!versionOverridden) {
279 majorVersion = GetInt(spec, "/amqp/@major");
280 minorVersion = GetInt(spec, "/amqp/@minor");
282 foreach (XmlNode n in spec.SelectNodes("/amqp/constant")) {
283 constants.Add(new DictionaryEntry(GetString(n, "@name"), GetInt(n, "@value")));
285 foreach (XmlNode n in spec.SelectNodes("/amqp/class")) {
286 classes.Add(new AmqpClass(n));
288 foreach (XmlNode n in spec.SelectNodes("/amqp/domain")) {
289 domains[GetString(n, "@name")] = GetString(n, "@type");
293 public void ReflectModel() {
294 modelTypes.Add(modelType);
295 for (int i = 0; i < modelTypes.Count; i++) {
296 foreach (Type intf in ((Type) modelTypes[i]).GetInterfaces()) {
297 modelTypes.Add(intf);
302 public string ResolveDomain(string d) {
303 while (domains[d] != null) {
304 string newD = (string) domains[d];
312 public string MapDomain(string d) {
313 return (string) primitiveTypeMap[ResolveDomain(d)];
316 public string VersionToken() {
317 return "v" + majorVersion + "_" + minorVersion;
320 public void GenerateOutput() {
321 Console.WriteLine("* Generating code into '" + this.outputFilename + "'");
322 this.outputFile = new StreamWriter(this.outputFilename);
326 this.outputFile.Close();
329 public void Emit(object o) {
330 this.outputFile.Write(o);
333 public void EmitLine(object o) {
334 this.outputFile.WriteLine(o);
337 public void EmitPrelude() {
338 EmitLine("// Autogenerated code. Do not edit.");
340 EmitLine("using RabbitMQ.Client;");
341 EmitLine("using RabbitMQ.Client.Exceptions;");
345 public void EmitPublic() {
346 EmitLine("namespace "+ApiNamespaceBase+" {");
347 EmitLine(" public class Protocol: "+ImplNamespaceBase+".ProtocolBase {");
348 EmitLine(" ///<summary>Protocol major version (= "+majorVersion+")</summary>");
349 EmitLine(" public override int MajorVersion { get { return " + majorVersion + "; } }");
350 EmitLine(" ///<summary>Protocol minor version (= "+minorVersion+")</summary>");
351 EmitLine(" public override int MinorVersion { get { return " + minorVersion + "; } }");
352 EmitLine(" ///<summary>Protocol API name (= "+apiName+")</summary>");
353 EmitLine(" public override string ApiName { get { return \"" + apiName + "\"; } }");
354 int port = GetInt(spec, "/amqp/@port");
355 EmitLine(" ///<summary>Default TCP port (= "+port+")</summary>");
356 EmitLine(" public override int DefaultPort { get { return " + port + "; } }");
358 EmitMethodArgumentReader();
360 EmitContentHeaderReader();
362 EmitLine(" public class Constants {");
363 foreach (DictionaryEntry de in constants) {
364 EmitLine(" ///<summary>(= "+de.Value+")</summary>");
365 EmitLine(" public const int "+MangleConstant((string) de.Key)+" = "+de.Value+";");
368 foreach (AmqpClass c in classes) {
371 foreach (AmqpClass c in classes) {
372 if (c.NeedsProperties) {
373 EmitClassProperties(c);
379 public void EmitAutogeneratedSummary(string prefixSpaces, string extra) {
380 EmitLine(prefixSpaces+"/// <summary>Autogenerated type. "+extra+"</summary>");
383 public void EmitClassMethods(AmqpClass c) {
384 foreach (AmqpMethod m in c.Methods) {
385 EmitAutogeneratedSummary(" ",
386 "AMQP specification method \""+c.Name+"."+m.Name+"\".");
387 EmitLine(m.DocumentationCommentVariant(" ", "remarks"));
388 EmitLine(" public interface I"+MangleMethodClass(c, m)+": IMethod {");
389 foreach (AmqpField f in m.Fields) {
390 EmitLine(f.DocumentationComment(" "));
391 EmitLine(" "+MapDomain(f.Domain)+" "+MangleClass(f.Name)+" { get; }");
397 public bool HasFactoryMethod(AmqpClass c) {
398 foreach (Type t in modelTypes) {
399 foreach (MethodInfo method in t.GetMethods()) {
400 AmqpContentHeaderFactoryAttribute f = (AmqpContentHeaderFactoryAttribute)
401 Attribute(method, typeof(AmqpContentHeaderFactoryAttribute));
402 if (f != null && MangleClass(f.m_contentClass) == MangleClass(c.Name)) {
410 public bool IsBoolean(AmqpField f) {
411 return ResolveDomain(f.Domain) == "bit";
414 public bool IsReferenceType(AmqpField f) {
415 return (bool) primitiveTypeFlagMap[ResolveDomain(f.Domain)];
418 public void EmitClassProperties(AmqpClass c) {
419 bool hasCommonApi = HasFactoryMethod(c);
420 string propertiesBaseClass =
422 ? "RabbitMQ.Client.Impl."+MangleClass(c.Name)+"Properties"
423 : "RabbitMQ.Client.Impl.ContentHeaderBase";
424 string maybeOverride = hasCommonApi ? "override " : "";
426 EmitAutogeneratedSummary(" ",
427 "AMQP specification content header properties for "+
428 "content class \""+c.Name+"\"");
429 EmitLine(c.DocumentationCommentVariant(" ", "remarks"));
430 EmitLine(" public class "+MangleClass(c.Name)
431 +"Properties: "+propertiesBaseClass+" {");
432 foreach (AmqpField f in c.Fields) {
433 EmitLine(" private "+MapDomain(f.Domain)+" m_"+MangleMethod(f.Name)+";");
436 foreach (AmqpField f in c.Fields) {
438 EmitLine(" private bool "+MangleMethod(f.Name)+"_present = false;");
442 foreach (AmqpField f in c.Fields) {
443 EmitLine(f.DocumentationComment(" ", "@label"));
444 EmitLine(" public "+maybeOverride+MapDomain(f.Domain)+" "+MangleClass(f.Name)+" {");
446 EmitLine(" return m_"+MangleMethod(f.Name)+";");
450 EmitLine(" "+MangleMethod(f.Name)+"_present = true;");
452 EmitLine(" m_"+MangleMethod(f.Name)+" = value;");
457 foreach (AmqpField f in c.Fields) {
459 EmitLine(" public "+maybeOverride+"void Clear"+MangleClass(f.Name)+"() { "+MangleMethod(f.Name)+"_present = false; }");
463 EmitLine(" public "+MangleClass(c.Name)+"Properties() {}");
464 EmitLine(" public override int ProtocolClassId { get { return "+c.Index+"; } }");
465 EmitLine(" public override string ProtocolClassName { get { return \""+c.Name+"\"; } }");
467 EmitLine(" public override void ReadPropertiesFrom(RabbitMQ.Client.Impl.ContentHeaderPropertyReader reader) {");
468 foreach (AmqpField f in c.Fields) {
470 EmitLine(" m_"+MangleMethod(f.Name)+" = reader.ReadBit();");
472 EmitLine(" "+MangleMethod(f.Name)+"_present = reader.ReadPresence();");
475 EmitLine(" reader.FinishPresence();");
476 foreach (AmqpField f in c.Fields) {
478 EmitLine(" if ("+MangleMethod(f.Name)+"_present) { m_"+MangleMethod(f.Name)+" = reader.Read"+MangleClass(ResolveDomain(f.Domain))+"(); }");
483 EmitLine(" public override void WritePropertiesTo(RabbitMQ.Client.Impl.ContentHeaderPropertyWriter writer) {");
484 foreach (AmqpField f in c.Fields) {
486 EmitLine(" writer.WriteBit(m_"+MangleMethod(f.Name)+");");
488 EmitLine(" writer.WritePresence("+MangleMethod(f.Name)+"_present);");
491 EmitLine(" writer.FinishPresence();");
492 foreach (AmqpField f in c.Fields) {
494 EmitLine(" if ("+MangleMethod(f.Name)+"_present) { writer.Write"+MangleClass(ResolveDomain(f.Domain))+"(m_"+MangleMethod(f.Name)+"); }");
499 EmitLine(" public override void AppendPropertyDebugStringTo(System.Text.StringBuilder sb) {");
500 EmitLine(" sb.Append(\"(\");");
502 int remaining = c.Fields.Count;
503 foreach (AmqpField f in c.Fields) {
504 Emit(" sb.Append(\""+f.Name+"=\");");
506 Emit(" sb.Append(m_"+MangleMethod(f.Name)+");");
508 string x = MangleMethod(f.Name);
509 if (IsReferenceType(f)) {
510 Emit(" sb.Append("+x+"_present ? (m_"+x+" == null ? \"(null)\" : m_"+x+".ToString()) : \"_\");");
512 Emit(" sb.Append("+x+"_present ? m_"+x+".ToString() : \"_\");");
517 EmitLine(" sb.Append(\", \");");
523 EmitLine(" sb.Append(\")\");");
528 public void EmitPrivate() {
529 EmitLine("namespace "+ImplNamespaceBase+" {");
530 EmitLine(" using "+ApiNamespaceBase+";");
531 EmitLine(" public enum ClassId {");
532 foreach (AmqpClass c in classes) {
533 EmitLine(" "+MangleConstant(c.Name)+" = "+c.Index+",");
535 EmitLine(" Invalid = -1");
537 foreach (AmqpClass c in classes) {
538 EmitClassMethodImplementations(c);
541 EmitModelImplementation();
545 public void EmitClassMethodImplementations(AmqpClass c) {
546 foreach (AmqpMethod m in c.Methods) {
547 EmitAutogeneratedSummary(" ",
548 "Private implementation class - do not use directly.");
549 EmitLine(" public class "+MangleMethodClass(c,m)
550 +": RabbitMQ.Client.Impl.MethodBase, I"+MangleMethodClass(c,m)+" {");
551 EmitLine(" public const int ClassId = "+c.Index+";");
552 EmitLine(" public const int MethodId = "+m.Index+";");
554 foreach (AmqpField f in m.Fields) {
555 EmitLine(" public "+MapDomain(f.Domain)+" m_"+MangleMethod(f.Name)+";");
558 foreach (AmqpField f in m.Fields) {
559 EmitLine(" "+MapDomain(f.Domain)+" I"+MangleMethodClass(c,m)+
560 "."+MangleClass(f.Name)+" { get {"
561 +" return m_"+MangleMethod(f.Name)+"; } }");
564 if (m.Fields.Count > 0) {
565 EmitLine(" public "+MangleMethodClass(c,m)+"() {}");
567 EmitLine(" public "+MangleMethodClass(c,m)+"(");
569 int remaining = m.Fields.Count;
570 foreach (AmqpField f in m.Fields) {
571 Emit(" "+MapDomain(f.Domain)+" init"+MangleClass(f.Name));
580 foreach (AmqpField f in m.Fields) {
581 EmitLine(" m_"+MangleMethod(f.Name)+" = init"+MangleClass(f.Name)+";");
585 EmitLine(" public override int ProtocolClassId { get { return "+c.Index+"; } }");
586 EmitLine(" public override int ProtocolMethodId { get { return "+m.Index+"; } }");
587 EmitLine(" public override string ProtocolMethodName { get { return \""+c.Name+"."+m.Name+"\"; } }");
588 EmitLine(" public override bool HasContent { get { return "
589 +(m.HasContent ? "true" : "false")+"; } }");
591 EmitLine(" public override void ReadArgumentsFrom(RabbitMQ.Client.Impl.MethodArgumentReader reader) {");
592 foreach (AmqpField f in m.Fields) {
593 EmitLine(" m_"+MangleMethod(f.Name)+" = reader.Read"+MangleClass(ResolveDomain(f.Domain))+"();");
597 EmitLine(" public override void WriteArgumentsTo(RabbitMQ.Client.Impl.MethodArgumentWriter writer) {");
598 foreach (AmqpField f in m.Fields) {
599 EmitLine(" writer.Write"+MangleClass(ResolveDomain(f.Domain))
600 +"(m_"+MangleMethod(f.Name)+");");
604 EmitLine(" public override void AppendArgumentDebugStringTo(System.Text.StringBuilder sb) {");
605 EmitLine(" sb.Append(\"(\");");
607 int remaining = m.Fields.Count;
608 foreach (AmqpField f in m.Fields) {
609 Emit(" sb.Append(m_"+MangleMethod(f.Name)+");");
612 EmitLine(" sb.Append(\",\");");
618 EmitLine(" sb.Append(\")\");");
624 public void EmitMethodArgumentReader() {
625 EmitLine(" public override RabbitMQ.Client.Impl.MethodBase DecodeMethodFrom(RabbitMQ.Util.NetworkBinaryReader reader) {");
626 EmitLine(" ushort classId = reader.ReadUInt16();");
627 EmitLine(" ushort methodId = reader.ReadUInt16();");
629 EmitLine(" switch (classId) {");
630 foreach (AmqpClass c in classes) {
631 EmitLine(" case "+c.Index+": {");
632 EmitLine(" switch (methodId) {");
633 foreach (AmqpMethod m in c.Methods) {
634 EmitLine(" case "+m.Index+": {");
635 EmitLine(" "+ImplNamespaceBase+"."+MangleMethodClass(c,m)+" result = new "+ImplNamespaceBase+"."+MangleMethodClass(c,m)+"();");
636 EmitLine(" result.ReadArgumentsFrom(new RabbitMQ.Client.Impl.MethodArgumentReader(reader));");
637 EmitLine(" return result;");
640 EmitLine(" default: break;");
645 EmitLine(" default: break;");
647 EmitLine(" throw new RabbitMQ.Client.Impl.UnknownClassOrMethodException(classId, methodId);");
651 public void EmitContentHeaderReader() {
652 EmitLine(" public override RabbitMQ.Client.Impl.ContentHeaderBase DecodeContentHeaderFrom(RabbitMQ.Util.NetworkBinaryReader reader) {");
653 EmitLine(" ushort classId = reader.ReadUInt16();");
655 EmitLine(" switch (classId) {");
656 foreach (AmqpClass c in classes) {
657 if (c.NeedsProperties) {
658 EmitLine(" case "+c.Index+": return new "
659 +MangleClass(c.Name)+"Properties();");
662 EmitLine(" default: break;");
664 EmitLine(" throw new RabbitMQ.Client.Impl.UnknownClassOrMethodException(classId, 0);");
668 public Attribute Attribute(MemberInfo mi, Type t) {
669 return Attribute(mi.GetCustomAttributes(t, false), t);
672 public Attribute Attribute(ParameterInfo pi, Type t) {
673 return Attribute(pi.GetCustomAttributes(t, false), t);
676 public Attribute Attribute(ICustomAttributeProvider p, Type t) {
677 return Attribute(p.GetCustomAttributes(t, false), t);
680 public Attribute Attribute(IEnumerable attributes, Type t) {
681 if (t.IsSubclassOf(typeof(AmqpApigenAttribute))) {
682 AmqpApigenAttribute result = null;
683 foreach (AmqpApigenAttribute candidate in attributes) {
684 if (candidate.m_namespaceName == null && result == null) {
687 if (candidate.m_namespaceName == ApiNamespaceBase) {
693 foreach (Attribute attribute in attributes) {
700 public void EmitModelImplementation() {
701 EmitLine(" public class Model: RabbitMQ.Client.Impl.ModelBase {");
702 EmitLine(" public Model(RabbitMQ.Client.Impl.ISession session): base(session) {}");
703 ArrayList asynchronousHandlers = new ArrayList();
704 foreach (Type t in modelTypes) {
705 foreach (MethodInfo method in t.GetMethods()) {
706 if (method.DeclaringType.Namespace != null &&
707 method.DeclaringType.Namespace.StartsWith("RabbitMQ.Client")) {
708 if (method.Name.StartsWith("Handle") ||
709 (Attribute(method, typeof(AmqpAsynchronousHandlerAttribute)) != null))
711 asynchronousHandlers.Add(method);
713 MaybeEmitModelMethod(method);
718 EmitAsynchronousHandlers(asynchronousHandlers);
722 public void EmitContentHeaderFactory(MethodInfo method) {
723 AmqpContentHeaderFactoryAttribute factoryAnnotation = (AmqpContentHeaderFactoryAttribute)
724 Attribute(method, typeof(AmqpContentHeaderFactoryAttribute));
725 string contentClass = factoryAnnotation.m_contentClass;
726 EmitModelMethodPreamble(method);
728 EmitLine(" return new "+MangleClass(contentClass)+"Properties();");
732 public void MaybeEmitModelMethod(MethodInfo method) {
733 if (method.IsSpecialName) {
734 // It's some kind of event- or property-related method.
735 // It shouldn't be autogenerated.
736 } else if (Attribute(method, typeof(AmqpMethodDoNotImplementAttribute)) != null) {
737 // Skip this method, by request (AmqpMethodDoNotImplement)
738 } else if (Attribute(method, typeof(AmqpContentHeaderFactoryAttribute)) != null) {
739 EmitContentHeaderFactory(method);
740 } else if (Attribute(method, typeof(AmqpUnsupportedAttribute)) != null) {
741 EmitModelMethodPreamble(method);
743 EmitLine(" throw new UnsupportedMethodException(\""+method.Name+"\");");
746 EmitModelMethod(method);
750 public string SanitisedFullName(Type t) {
751 if (t == typeof(void)) {
758 public void EmitModelMethodPreamble(MethodInfo method) {
759 Emit(" public override "+SanitisedFullName(method.ReturnType)+" "+method.Name);
760 ParameterInfo[] parameters = method.GetParameters();
761 int remaining = parameters.Length;
762 if (remaining == 0) {
766 foreach (ParameterInfo pi in parameters) {
767 Emit(" "+SanitisedFullName(pi.ParameterType)+" @"+pi.Name);
778 public void LookupAmqpMethod(MethodInfo method,
780 out AmqpClass amqpClass,
781 out AmqpMethod amqpMethod)
786 // First, try autodetecting the class/method via the
787 // IModel method name.
789 foreach (AmqpClass c in classes) {
790 foreach (AmqpMethod m in c.Methods) {
791 if (methodName.Equals(MangleMethodClass(c,m))) {
794 goto stopSearching; // wheee
800 // If an explicit mapping was provided as an attribute,
801 // then use that instead, whether the autodetect worked or
805 AmqpMethodMappingAttribute methodMapping =
806 Attribute(method, typeof(AmqpMethodMappingAttribute)) as AmqpMethodMappingAttribute;
807 if (methodMapping != null) {
809 foreach (AmqpClass c in classes) {
810 if (c.Name == methodMapping.m_className) {
815 amqpMethod = amqpClass.MethodNamed(methodMapping.m_methodName);
819 // At this point, if can't find either the class or the
820 // method, we can't proceed. Complain.
822 if (amqpClass == null || amqpMethod == null) {
823 throw new Exception("Could not find AMQP class or method for IModel method " + method.Name);
827 public void EmitModelMethod(MethodInfo method) {
828 ParameterInfo[] parameters = method.GetParameters();
830 AmqpClass amqpClass = null;
831 AmqpMethod amqpMethod = null;
832 LookupAmqpMethod(method, method.Name, out amqpClass, out amqpMethod);
834 string requestImplClass = MangleMethodClass(amqpClass, amqpMethod);
836 // At this point, we know which request method to
837 // send. Now compute whether it's an RPC or not.
839 AmqpMethod amqpReplyMethod = null;
840 AmqpMethodMappingAttribute replyMapping =
841 Attribute(method.ReturnTypeCustomAttributes, typeof(AmqpMethodMappingAttribute))
842 as AmqpMethodMappingAttribute;
843 if (Attribute(method, typeof(AmqpForceOneWayAttribute)) == null &&
844 (amqpMethod.IsSimpleRpcRequest || replyMapping != null))
846 // We're not forcing oneway, and either are a simple
847 // RPC request, or have an explicit replyMapping
848 amqpReplyMethod = amqpClass.MethodNamed(replyMapping == null
849 ? (string) amqpMethod.ResponseMethods[0]
850 : replyMapping.m_methodName);
851 if (amqpReplyMethod == null) {
852 throw new Exception("Could not find AMQP reply method for IModel method " + method.Name);
856 // If amqpReplyMethod is null at this point, it's a
857 // one-way operation, and no continuation needs to be
858 // consed up. Otherwise, we should expect a reply of kind
859 // identified by amqpReplyMethod - unless there's a nowait
860 // parameter thrown into the equation!
862 // Examine the parameters to discover which might be
863 // nowait, content header or content body.
865 ParameterInfo nowaitParameter = null;
866 string nowaitExpression = "null";
867 ParameterInfo contentHeaderParameter = null;
868 ParameterInfo contentBodyParameter = null;
869 foreach (ParameterInfo pi in parameters) {
870 AmqpNowaitArgumentAttribute nwAttr =
871 Attribute(pi, typeof(AmqpNowaitArgumentAttribute)) as AmqpNowaitArgumentAttribute;
872 if (nwAttr != null) {
873 nowaitParameter = pi;
874 if (nwAttr.m_replacementExpression != null) {
875 nowaitExpression = nwAttr.m_replacementExpression;
878 if (Attribute(pi, typeof(AmqpContentHeaderMappingAttribute)) != null) {
879 contentHeaderParameter = pi;
881 if (Attribute(pi, typeof(AmqpContentBodyMappingAttribute)) != null) {
882 contentBodyParameter = pi;
886 // Compute expression text for the content header and body.
888 string contentHeaderExpr =
889 contentHeaderParameter == null
891 : " ("+MangleClass(amqpClass.Name)+"Properties) "+contentHeaderParameter.Name;
892 string contentBodyExpr =
893 contentBodyParameter == null ? "null" : contentBodyParameter.Name;
895 // Emit the method declaration and preamble.
897 EmitModelMethodPreamble(method);
900 // Emit the code to build the request.
902 EmitLine(" "+requestImplClass+" __req = new "+requestImplClass+"();");
903 foreach (ParameterInfo pi in parameters) {
904 if (pi != contentHeaderParameter &&
905 pi != contentBodyParameter)
907 if (Attribute(pi, typeof(AmqpUnsupportedAttribute)) != null) {
908 EmitLine(" if (@"+pi.Name+" != null) {");
909 EmitLine(" throw new UnsupportedMethodFieldException(\""+method.Name+"\",\""+pi.Name+"\");");
912 AmqpFieldMappingAttribute fieldMapping =
913 Attribute(pi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
914 if (fieldMapping != null) {
915 EmitLine(" __req.m_"+fieldMapping.m_fieldName+" = @" + pi.Name + ";");
917 EmitLine(" __req.m_"+pi.Name+" = @" + pi.Name + ";");
923 // If we have a nowait parameter, sometimes that can turn
924 // a ModelRpc call into a ModelSend call.
926 if (nowaitParameter != null) {
927 EmitLine(" if ("+nowaitParameter.Name+") {");
928 EmitLine(" ModelSend(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
929 if (method.ReturnType != typeof(void)) {
930 EmitLine(" return "+nowaitExpression+";");
935 // At this point, perform either a ModelRpc or a
938 if (amqpReplyMethod == null) {
939 EmitLine(" ModelSend(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
941 string replyImplClass = MangleMethodClass(amqpClass, amqpReplyMethod);
943 EmitLine(" RabbitMQ.Client.Impl.MethodBase __repBase = ModelRpc(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
944 EmitLine(" "+replyImplClass+" __rep = __repBase as "+replyImplClass+";");
945 EmitLine(" if (__rep == null) throw new UnexpectedMethodException(__repBase);");
947 if (method.ReturnType == typeof(void)) {
948 // No need to further examine the reply.
950 // At this point, we have the reply method. Extract values from it.
952 AmqpFieldMappingAttribute returnMapping =
953 Attribute(method.ReturnTypeCustomAttributes, typeof(AmqpFieldMappingAttribute))
954 as AmqpFieldMappingAttribute;
955 if (returnMapping == null) {
956 // No field mapping --> it's assumed to be a struct to fill in.
957 EmitLine(" "+method.ReturnType+" __result = new "+method.ReturnType+"();");
958 foreach (FieldInfo fi in method.ReturnType.GetFields()) {
959 AmqpFieldMappingAttribute returnFieldMapping =
960 Attribute(fi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
961 if (returnFieldMapping != null) {
962 EmitLine(" __result."+fi.Name+" = __rep.m_"+returnFieldMapping.m_fieldName+";");
964 EmitLine(" __result."+fi.Name+" = __rep.m_"+fi.Name+";");
967 EmitLine(" return __result;");
969 // Field mapping --> return just the field we're interested in.
970 EmitLine(" return __rep.m_"+returnMapping.m_fieldName+";");
975 // All the IO and result-extraction has been done. Emit
976 // the method postamble.
981 public void EmitAsynchronousHandlers(ArrayList asynchronousHandlers) {
982 EmitLine(" public override bool DispatchAsynchronous(RabbitMQ.Client.Impl.Command cmd) {");
983 EmitLine(" RabbitMQ.Client.Impl.MethodBase __method = (RabbitMQ.Client.Impl.MethodBase) cmd.Method;");
984 EmitLine(" switch ((__method.ProtocolClassId << 16) | __method.ProtocolMethodId) {");
985 foreach (MethodInfo method in asynchronousHandlers) {
986 string methodName = method.Name;
987 if (methodName.StartsWith("Handle")) {
988 methodName = methodName.Substring(6);
991 AmqpClass amqpClass = null;
992 AmqpMethod amqpMethod = null;
993 LookupAmqpMethod(method, methodName, out amqpClass, out amqpMethod);
995 string implClass = MangleMethodClass(amqpClass, amqpMethod);
997 EmitLine(" case "+((amqpClass.Index << 16) | amqpMethod.Index)+": {");
998 ParameterInfo[] parameters = method.GetParameters();
999 if (parameters.Length > 0) {
1000 EmitLine(" "+implClass+" __impl = ("+implClass+") __method;");
1001 EmitLine(" "+method.Name+"(");
1002 int remaining = parameters.Length;
1003 foreach (ParameterInfo pi in parameters) {
1004 if (Attribute(pi, typeof(AmqpContentHeaderMappingAttribute)) != null) {
1005 Emit(" ("+pi.ParameterType+") cmd.Header");
1006 } else if (Attribute(pi, typeof(AmqpContentBodyMappingAttribute)) != null) {
1009 AmqpFieldMappingAttribute fieldMapping =
1010 Attribute(pi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
1011 Emit(" __impl.m_"+(fieldMapping == null
1013 : fieldMapping.m_fieldName));
1016 if (remaining > 0) {
1022 EmitLine(" "+method.Name+"();");
1024 EmitLine(" return true;");
1027 EmitLine(" default: return false;");