PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/apigen/Apigen.cs

https://bitbucket.org/essiene/rabbitmq-dotnet-client
C# | 1072 lines | 869 code | 96 blank | 107 comment | 151 complexity | aee4d4944439ba5e7e0640e92b33c4d5 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, MPL-2.0-no-copyleft-exception
  1. // This source code is dual-licensed under the Apache License, version
  2. // 2.0, and the Mozilla Public License, version 1.1.
  3. //
  4. // The APL v2.0:
  5. //
  6. //---------------------------------------------------------------------------
  7. // Copyright (C) 2007-2009 LShift Ltd., Cohesive Financial
  8. // Technologies LLC., and Rabbit Technologies Ltd.
  9. //
  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
  13. //
  14. // http://www.apache.org/licenses/LICENSE-2.0
  15. //
  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. //---------------------------------------------------------------------------
  22. //
  23. // The MPL v1.1:
  24. //
  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
  30. //
  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
  34. // under the License.
  35. //
  36. // The Original Code is The RabbitMQ .NET Client.
  37. //
  38. // The Initial Developers of the Original Code are LShift Ltd,
  39. // Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd.
  40. //
  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.
  45. //
  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.
  51. //
  52. // All Rights Reserved.
  53. //
  54. // Contributor(s): ______________________________________.
  55. //
  56. //---------------------------------------------------------------------------
  57. using System;
  58. using System.Collections;
  59. using System.IO;
  60. using System.Reflection;
  61. using System.Text;
  62. using System.Xml;
  63. using RabbitMQ.Client.Apigen.Attributes;
  64. namespace RabbitMQ.Client.Apigen {
  65. public class Apigen {
  66. ///////////////////////////////////////////////////////////////////////////
  67. // Entry point
  68. public static void Main(string[] args) {
  69. Apigen instance = new Apigen(new ArrayList(args));
  70. instance.Generate();
  71. }
  72. ///////////////////////////////////////////////////////////////////////////
  73. // XML utilities
  74. public static XmlNodeList GetNodes(XmlNode n0, string path) {
  75. return n0.SelectNodes(path);
  76. }
  77. public static string GetString(XmlNode n0, string path, string d) {
  78. XmlNode n = n0.SelectSingleNode(path);
  79. return (n == null) ? d : n.InnerText;
  80. }
  81. public static string GetString(XmlNode n0, string path) {
  82. string s = GetString(n0, path, null);
  83. if (s == null) {
  84. throw new Exception("Missing spec XML node: " + path);
  85. }
  86. return s;
  87. }
  88. public static int GetInt(XmlNode n0, string path, int d) {
  89. string s = GetString(n0, path, null);
  90. return (s == null) ? d : int.Parse(s);
  91. }
  92. public static int GetInt(XmlNode n0, string path) {
  93. return int.Parse(GetString(n0, path));
  94. }
  95. ///////////////////////////////////////////////////////////////////////////
  96. // Name manipulation and mangling for C#
  97. public static string MangleConstant(string name) {
  98. // Previously, we used C_STYLE_CONSTANT_NAMES:
  99. /*
  100. return name
  101. .Replace(" ", "_")
  102. .Replace("-", "_").
  103. ToUpper();
  104. */
  105. // ... but TheseKindsOfNames are more in line with .NET style guidelines.
  106. return MangleClass(name);
  107. }
  108. public static ArrayList IdentifierParts(string name) {
  109. ArrayList result = new ArrayList();
  110. foreach (String s1 in name.Split(new Char[] { '-' })) {
  111. foreach (String s2 in s1.Split(new Char[] { ' ' })) {
  112. result.Add(s2);
  113. }
  114. }
  115. return result;
  116. }
  117. public static string MangleClass(string name) {
  118. StringBuilder sb = new StringBuilder();
  119. foreach (String s in IdentifierParts(name)) {
  120. sb.Append(Char.ToUpper(s[0]) + s.Substring(1).ToLower());
  121. }
  122. return sb.ToString();
  123. }
  124. public static string MangleMethod(string name) {
  125. StringBuilder sb = new StringBuilder();
  126. bool useUpper = false;
  127. foreach (String s in IdentifierParts(name)) {
  128. if (useUpper) {
  129. sb.Append(Char.ToUpper(s[0]) + s.Substring(1).ToLower());
  130. } else {
  131. sb.Append(s.ToLower());
  132. useUpper = true;
  133. }
  134. }
  135. return sb.ToString();
  136. }
  137. public static string MangleMethodClass(AmqpClass c, AmqpMethod m) {
  138. return MangleClass(c.Name) + MangleClass(m.Name);
  139. }
  140. ///////////////////////////////////////////////////////////////////////////
  141. public string m_framingSubnamespace = null;
  142. public string m_inputXmlFilename;
  143. public string m_outputFilename;
  144. public XmlDocument m_spec = null;
  145. public TextWriter m_outputFile = null;
  146. public bool m_versionOverridden = false;
  147. public int m_majorVersion;
  148. public int m_minorVersion;
  149. public string m_apiName;
  150. public bool m_emitComments = false;
  151. public Type m_modelType = typeof(RabbitMQ.Client.Impl.IFullModel);
  152. public ArrayList m_modelTypes = new ArrayList();
  153. public ArrayList m_constants = new ArrayList();
  154. public ArrayList m_classes = new ArrayList();
  155. public Hashtable m_domains = new Hashtable();
  156. public static Hashtable m_primitiveTypeMap;
  157. public static Hashtable m_primitiveTypeFlagMap;
  158. static Apigen() {
  159. m_primitiveTypeMap = new Hashtable();
  160. m_primitiveTypeFlagMap = new Hashtable();
  161. InitPrimitiveType("octet", "byte", false);
  162. InitPrimitiveType("shortstr", "string", true);
  163. InitPrimitiveType("longstr", "byte[]", true);
  164. InitPrimitiveType("short", "ushort", false);
  165. InitPrimitiveType("long", "uint", false);
  166. InitPrimitiveType("longlong", "ulong", false);
  167. InitPrimitiveType("bit", "bool", false);
  168. InitPrimitiveType("table", "System.Collections.IDictionary", true);
  169. InitPrimitiveType("timestamp", "AmqpTimestamp", false);
  170. InitPrimitiveType("content", "byte[]", true);
  171. }
  172. public static void InitPrimitiveType(string amqpType, string dotnetType, bool isReference)
  173. {
  174. m_primitiveTypeMap[amqpType] = dotnetType;
  175. m_primitiveTypeFlagMap[amqpType] = isReference;
  176. }
  177. public void HandleOption(string opt) {
  178. if (opt.StartsWith("/n:")) {
  179. m_framingSubnamespace = opt.Substring(3);
  180. } else if (opt.StartsWith("/apiName:")) {
  181. m_apiName = opt.Substring(9);
  182. } else if (opt.StartsWith("/v:")) {
  183. string[] parts = opt.Substring(3).Split(new char[] { '-' });
  184. m_versionOverridden = true;
  185. m_majorVersion = int.Parse(parts[0]);
  186. m_minorVersion = int.Parse(parts[1]);
  187. } else if (opt == "/c") {
  188. m_emitComments = true;
  189. } else {
  190. Console.Error.WriteLine("Unsupported command-line option: " + opt);
  191. Usage();
  192. }
  193. }
  194. public void Usage() {
  195. Console.Error.WriteLine("Usage: Apigen.exe [options ...] <input-spec-xml> <output-csharp-file>");
  196. Console.Error.WriteLine(" Options include:");
  197. Console.Error.WriteLine(" /apiName:<identifier>");
  198. Console.Error.WriteLine(" /n:<name.space.prefix>");
  199. Console.Error.WriteLine(" /v:<majorversion>-<minorversion>");
  200. Console.Error.WriteLine(" The apiName option is required.");
  201. Environment.Exit(1);
  202. }
  203. public Apigen(ArrayList args) {
  204. while (args.Count > 0 && ((string) args[0]).StartsWith("/")) {
  205. HandleOption((string) args[0]);
  206. args.RemoveAt(0);
  207. }
  208. if ((args.Count < 2)
  209. || (m_apiName == null))
  210. {
  211. Usage();
  212. }
  213. m_inputXmlFilename = (string) args[0];
  214. m_outputFilename = (string) args[1];
  215. }
  216. ///////////////////////////////////////////////////////////////////////////
  217. public string FramingSubnamespace {
  218. get {
  219. if (m_framingSubnamespace == null) {
  220. return VersionToken();
  221. } else {
  222. return m_framingSubnamespace;
  223. }
  224. }
  225. }
  226. public string ApiNamespaceBase {
  227. get {
  228. return "RabbitMQ.Client.Framing."+FramingSubnamespace;
  229. }
  230. }
  231. public string ImplNamespaceBase {
  232. get {
  233. return "RabbitMQ.Client.Framing.Impl."+FramingSubnamespace;
  234. }
  235. }
  236. public void Generate() {
  237. LoadSpec();
  238. ParseSpec();
  239. ReflectModel();
  240. GenerateOutput();
  241. }
  242. public void LoadSpec() {
  243. Console.WriteLine("* Loading spec from '" + m_inputXmlFilename + "'");
  244. m_spec = new XmlDocument();
  245. m_spec.Load(m_inputXmlFilename);
  246. }
  247. public void ParseSpec() {
  248. Console.WriteLine("* Parsing spec");
  249. if (!m_versionOverridden) {
  250. m_majorVersion = GetInt(m_spec, "/amqp/@major");
  251. m_minorVersion = GetInt(m_spec, "/amqp/@minor");
  252. }
  253. foreach (XmlNode n in m_spec.SelectNodes("/amqp/constant")) {
  254. m_constants.Add(new DictionaryEntry(GetString(n, "@name"), GetInt(n, "@value")));
  255. }
  256. foreach (XmlNode n in m_spec.SelectNodes("/amqp/class")) {
  257. m_classes.Add(new AmqpClass(n));
  258. }
  259. foreach (XmlNode n in m_spec.SelectNodes("/amqp/domain")) {
  260. m_domains[GetString(n, "@name")] = GetString(n, "@type");
  261. }
  262. }
  263. public void ReflectModel() {
  264. m_modelTypes.Add(m_modelType);
  265. for (int i = 0; i < m_modelTypes.Count; i++)
  266. {
  267. foreach (Type intf in ((Type) m_modelTypes[i]).GetInterfaces())
  268. {
  269. m_modelTypes.Add(intf);
  270. }
  271. }
  272. }
  273. public string ResolveDomain(string d) {
  274. while (m_domains[d] != null) {
  275. string newD = (string) m_domains[d];
  276. if (d.Equals(newD))
  277. break;
  278. d = newD;
  279. }
  280. return d;
  281. }
  282. public string MapDomain(string d) {
  283. return (string) m_primitiveTypeMap[ResolveDomain(d)];
  284. }
  285. public string VersionToken() {
  286. return "v" + m_majorVersion + "_" + m_minorVersion;
  287. }
  288. public void GenerateOutput() {
  289. Console.WriteLine("* Generating code into '" + m_outputFilename + "'");
  290. m_outputFile = new StreamWriter(m_outputFilename);
  291. EmitPrelude();
  292. EmitPublic();
  293. EmitPrivate();
  294. m_outputFile.Close();
  295. }
  296. public void Emit(object o) {
  297. m_outputFile.Write(o);
  298. }
  299. public void EmitLine(object o) {
  300. m_outputFile.WriteLine(o);
  301. }
  302. public void EmitSpecComment(object o) {
  303. if (m_emitComments)
  304. EmitLine(o);
  305. }
  306. public void EmitPrelude() {
  307. EmitLine("// Autogenerated code. Do not edit.");
  308. EmitLine("");
  309. EmitLine("using RabbitMQ.Client;");
  310. EmitLine("using RabbitMQ.Client.Exceptions;");
  311. EmitLine("");
  312. }
  313. public void EmitPublic() {
  314. EmitLine("namespace "+ApiNamespaceBase+" {");
  315. EmitLine(" public class Protocol: "+ImplNamespaceBase+".ProtocolBase {");
  316. EmitLine(" ///<summary>Protocol major version (= "+m_majorVersion+")</summary>");
  317. EmitLine(" public override int MajorVersion { get { return " + m_majorVersion + "; } }");
  318. EmitLine(" ///<summary>Protocol minor version (= "+m_minorVersion+")</summary>");
  319. EmitLine(" public override int MinorVersion { get { return " + m_minorVersion + "; } }");
  320. EmitLine(" ///<summary>Protocol API name (= "+m_apiName+")</summary>");
  321. EmitLine(" public override string ApiName { get { return \"" + m_apiName + "\"; } }");
  322. int port = GetInt(m_spec, "/amqp/@port");
  323. EmitLine(" ///<summary>Default TCP port (= "+port+")</summary>");
  324. EmitLine(" public override int DefaultPort { get { return " + port + "; } }");
  325. EmitLine("");
  326. EmitMethodArgumentReader();
  327. EmitLine("");
  328. EmitContentHeaderReader();
  329. EmitLine(" }");
  330. EmitLine(" public class Constants {");
  331. foreach (DictionaryEntry de in m_constants) {
  332. EmitLine(" ///<summary>(= "+de.Value+")</summary>");
  333. EmitLine(" public const int "+MangleConstant((string) de.Key)+" = "+de.Value+";");
  334. }
  335. EmitLine(" }");
  336. foreach (AmqpClass c in m_classes) {
  337. EmitClassMethods(c);
  338. }
  339. foreach (AmqpClass c in m_classes) {
  340. if (c.NeedsProperties) {
  341. EmitClassProperties(c);
  342. }
  343. }
  344. EmitLine("}");
  345. }
  346. public void EmitAutogeneratedSummary(string prefixSpaces, string extra) {
  347. EmitLine(prefixSpaces+"/// <summary>Autogenerated type. "+extra+"</summary>");
  348. }
  349. public void EmitClassMethods(AmqpClass c) {
  350. foreach (AmqpMethod m in c.m_Methods) {
  351. EmitAutogeneratedSummary(" ",
  352. "AMQP specification method \""+c.Name+"."+m.Name+"\".");
  353. EmitSpecComment(m.DocumentationCommentVariant(" ", "remarks"));
  354. EmitLine(" public interface I"+MangleMethodClass(c, m)+": IMethod {");
  355. foreach (AmqpField f in m.m_Fields) {
  356. EmitSpecComment(f.DocumentationComment(" "));
  357. EmitLine(" "+MapDomain(f.Domain)+" "+MangleClass(f.Name)+" { get; }");
  358. }
  359. EmitLine(" }");
  360. }
  361. }
  362. public bool HasFactoryMethod(AmqpClass c) {
  363. foreach (Type t in m_modelTypes) {
  364. foreach (MethodInfo method in t.GetMethods()) {
  365. AmqpContentHeaderFactoryAttribute f = (AmqpContentHeaderFactoryAttribute)
  366. Attribute(method, typeof(AmqpContentHeaderFactoryAttribute));
  367. if (f != null && MangleClass(f.m_contentClass) == MangleClass(c.Name)) {
  368. return true;
  369. }
  370. }
  371. }
  372. return false;
  373. }
  374. public bool IsBoolean(AmqpField f) {
  375. return ResolveDomain(f.Domain) == "bit";
  376. }
  377. public bool IsReferenceType(AmqpField f) {
  378. return (bool) m_primitiveTypeFlagMap[ResolveDomain(f.Domain)];
  379. }
  380. public bool IsAmqpClass(Type t)
  381. {
  382. foreach (AmqpClass c in m_classes)
  383. {
  384. if (c.Name == t.Name)
  385. return true;
  386. }
  387. return false;
  388. }
  389. public void EmitClassProperties(AmqpClass c) {
  390. bool hasCommonApi = HasFactoryMethod(c);
  391. string propertiesBaseClass =
  392. hasCommonApi
  393. ? "RabbitMQ.Client.Impl."+MangleClass(c.Name)+"Properties"
  394. : "RabbitMQ.Client.Impl.ContentHeaderBase";
  395. string maybeOverride = hasCommonApi ? "override " : "";
  396. EmitAutogeneratedSummary(" ",
  397. "AMQP specification content header properties for "+
  398. "content class \""+c.Name+"\"");
  399. EmitSpecComment(c.DocumentationCommentVariant(" ", "remarks"));
  400. EmitLine(" public class "+MangleClass(c.Name)
  401. +"Properties: "+propertiesBaseClass+" {");
  402. foreach (AmqpField f in c.m_Fields) {
  403. EmitLine(" private "+MapDomain(f.Domain)+" m_"+MangleMethod(f.Name)+";");
  404. }
  405. EmitLine("");
  406. foreach (AmqpField f in c.m_Fields)
  407. {
  408. if (!IsBoolean(f)) {
  409. EmitLine(" private bool m_"+MangleMethod(f.Name)+"_present = false;");
  410. }
  411. }
  412. EmitLine("");
  413. foreach (AmqpField f in c.m_Fields)
  414. {
  415. EmitSpecComment(f.DocumentationComment(" ", "@label"));
  416. EmitLine(" public "+maybeOverride+MapDomain(f.Domain)+" "+MangleClass(f.Name)+" {");
  417. EmitLine(" get {");
  418. EmitLine(" return m_"+MangleMethod(f.Name)+";");
  419. EmitLine(" }");
  420. EmitLine(" set {");
  421. if (!IsBoolean(f)) {
  422. EmitLine(" m_"+MangleMethod(f.Name)+"_present = true;");
  423. }
  424. EmitLine(" m_"+MangleMethod(f.Name)+" = value;");
  425. EmitLine(" }");
  426. EmitLine(" }");
  427. }
  428. EmitLine("");
  429. foreach (AmqpField f in c.m_Fields)
  430. {
  431. if (!IsBoolean(f)) {
  432. EmitLine(" public "+maybeOverride+"void Clear"+MangleClass(f.Name)+"() { m_"+MangleMethod(f.Name)+"_present = false; }");
  433. }
  434. }
  435. EmitLine("");
  436. EmitLine(" public "+MangleClass(c.Name)+"Properties() {}");
  437. EmitLine(" public override int ProtocolClassId { get { return "+c.Index+"; } }");
  438. EmitLine(" public override string ProtocolClassName { get { return \""+c.Name+"\"; } }");
  439. EmitLine("");
  440. EmitLine(" public override void ReadPropertiesFrom(RabbitMQ.Client.Impl.ContentHeaderPropertyReader reader) {");
  441. foreach (AmqpField f in c.m_Fields)
  442. {
  443. if (IsBoolean(f)) {
  444. EmitLine(" m_"+MangleMethod(f.Name)+" = reader.ReadBit();");
  445. } else {
  446. EmitLine(" m_"+MangleMethod(f.Name)+"_present = reader.ReadPresence();");
  447. }
  448. }
  449. EmitLine(" reader.FinishPresence();");
  450. foreach (AmqpField f in c.m_Fields)
  451. {
  452. if (!IsBoolean(f)) {
  453. EmitLine(" if (m_"+MangleMethod(f.Name)+"_present) { m_"+MangleMethod(f.Name)+" = reader.Read"+MangleClass(ResolveDomain(f.Domain))+"(); }");
  454. }
  455. }
  456. EmitLine(" }");
  457. EmitLine("");
  458. EmitLine(" public override void WritePropertiesTo(RabbitMQ.Client.Impl.ContentHeaderPropertyWriter writer) {");
  459. foreach (AmqpField f in c.m_Fields)
  460. {
  461. if (IsBoolean(f)) {
  462. EmitLine(" writer.WriteBit(m_"+MangleMethod(f.Name)+");");
  463. } else {
  464. EmitLine(" writer.WritePresence(m_"+MangleMethod(f.Name)+"_present);");
  465. }
  466. }
  467. EmitLine(" writer.FinishPresence();");
  468. foreach (AmqpField f in c.m_Fields)
  469. {
  470. if (!IsBoolean(f)) {
  471. EmitLine(" if (m_"+MangleMethod(f.Name)+"_present) { writer.Write"+MangleClass(ResolveDomain(f.Domain))+"(m_"+MangleMethod(f.Name)+"); }");
  472. }
  473. }
  474. EmitLine(" }");
  475. EmitLine("");
  476. EmitLine(" public override void AppendPropertyDebugStringTo(System.Text.StringBuilder sb) {");
  477. EmitLine(" sb.Append(\"(\");");
  478. {
  479. int remaining = c.m_Fields.Count;
  480. foreach (AmqpField f in c.m_Fields)
  481. {
  482. Emit(" sb.Append(\""+f.Name+"=\");");
  483. if (IsBoolean(f)) {
  484. Emit(" sb.Append(m_"+MangleMethod(f.Name)+");");
  485. } else {
  486. string x = MangleMethod(f.Name);
  487. if (IsReferenceType(f)) {
  488. Emit(" sb.Append(m_"+x+"_present ? (m_"+x+" == null ? \"(null)\" : m_"+x+".ToString()) : \"_\");");
  489. } else {
  490. Emit(" sb.Append(m_"+x+"_present ? m_"+x+".ToString() : \"_\");");
  491. }
  492. }
  493. remaining--;
  494. if (remaining > 0) {
  495. EmitLine(" sb.Append(\", \");");
  496. } else {
  497. EmitLine("");
  498. }
  499. }
  500. }
  501. EmitLine(" sb.Append(\")\");");
  502. EmitLine(" }");
  503. EmitLine(" }");
  504. }
  505. public void EmitPrivate() {
  506. EmitLine("namespace "+ImplNamespaceBase+" {");
  507. EmitLine(" using "+ApiNamespaceBase+";");
  508. EmitLine(" public enum ClassId {");
  509. foreach (AmqpClass c in m_classes) {
  510. EmitLine(" "+MangleConstant(c.Name)+" = "+c.Index+",");
  511. }
  512. EmitLine(" Invalid = -1");
  513. EmitLine(" }");
  514. foreach (AmqpClass c in m_classes) {
  515. EmitClassMethodImplementations(c);
  516. }
  517. EmitLine("");
  518. EmitModelImplementation();
  519. EmitLine("}");
  520. }
  521. public void EmitClassMethodImplementations(AmqpClass c) {
  522. foreach (AmqpMethod m in c.m_Methods)
  523. {
  524. EmitAutogeneratedSummary(" ",
  525. "Private implementation class - do not use directly.");
  526. EmitLine(" public class "+MangleMethodClass(c,m)
  527. +": RabbitMQ.Client.Impl.MethodBase, I"+MangleMethodClass(c,m)+" {");
  528. EmitLine(" public const int ClassId = "+c.Index+";");
  529. EmitLine(" public const int MethodId = "+m.Index+";");
  530. EmitLine("");
  531. foreach (AmqpField f in m.m_Fields)
  532. {
  533. EmitLine(" public "+MapDomain(f.Domain)+" m_"+MangleMethod(f.Name)+";");
  534. }
  535. EmitLine("");
  536. foreach (AmqpField f in m.m_Fields)
  537. {
  538. EmitLine(" "+MapDomain(f.Domain)+" I"+MangleMethodClass(c,m)+
  539. "."+MangleClass(f.Name)+" { get {"
  540. + " return m_" + MangleMethod(f.Name) + "; } }");
  541. }
  542. EmitLine("");
  543. if (m.m_Fields.Count > 0)
  544. {
  545. EmitLine(" public "+MangleMethodClass(c,m)+"() {}");
  546. }
  547. EmitLine(" public "+MangleMethodClass(c,m)+"(");
  548. {
  549. int remaining = m.m_Fields.Count;
  550. foreach (AmqpField f in m.m_Fields)
  551. {
  552. Emit(" "+MapDomain(f.Domain)+" init"+MangleClass(f.Name));
  553. remaining--;
  554. if (remaining > 0) {
  555. EmitLine(",");
  556. }
  557. }
  558. }
  559. EmitLine(")");
  560. EmitLine(" {");
  561. foreach (AmqpField f in m.m_Fields)
  562. {
  563. EmitLine(" m_" + MangleMethod(f.Name) + " = init" + MangleClass(f.Name) + ";");
  564. }
  565. EmitLine(" }");
  566. EmitLine("");
  567. EmitLine(" public override int ProtocolClassId { get { return "+c.Index+"; } }");
  568. EmitLine(" public override int ProtocolMethodId { get { return "+m.Index+"; } }");
  569. EmitLine(" public override string ProtocolMethodName { get { return \""+c.Name+"."+m.Name+"\"; } }");
  570. EmitLine(" public override bool HasContent { get { return "
  571. +(m.HasContent ? "true" : "false")+"; } }");
  572. EmitLine("");
  573. EmitLine(" public override void ReadArgumentsFrom(RabbitMQ.Client.Impl.MethodArgumentReader reader) {");
  574. foreach (AmqpField f in m.m_Fields)
  575. {
  576. EmitLine(" m_" + MangleMethod(f.Name) + " = reader.Read" + MangleClass(ResolveDomain(f.Domain)) + "();");
  577. }
  578. EmitLine(" }");
  579. EmitLine("");
  580. EmitLine(" public override void WriteArgumentsTo(RabbitMQ.Client.Impl.MethodArgumentWriter writer) {");
  581. foreach (AmqpField f in m.m_Fields)
  582. {
  583. EmitLine(" writer.Write"+MangleClass(ResolveDomain(f.Domain))
  584. + "(m_" + MangleMethod(f.Name) + ");");
  585. }
  586. EmitLine(" }");
  587. EmitLine("");
  588. EmitLine(" public override void AppendArgumentDebugStringTo(System.Text.StringBuilder sb) {");
  589. EmitLine(" sb.Append(\"(\");");
  590. {
  591. int remaining = m.m_Fields.Count;
  592. foreach (AmqpField f in m.m_Fields)
  593. {
  594. Emit(" sb.Append(m_" + MangleMethod(f.Name) + ");");
  595. remaining--;
  596. if (remaining > 0) {
  597. EmitLine(" sb.Append(\",\");");
  598. } else {
  599. EmitLine("");
  600. }
  601. }
  602. }
  603. EmitLine(" sb.Append(\")\");");
  604. EmitLine(" }");
  605. EmitLine(" }");
  606. }
  607. }
  608. public void EmitMethodArgumentReader() {
  609. EmitLine(" public override RabbitMQ.Client.Impl.MethodBase DecodeMethodFrom(RabbitMQ.Util.NetworkBinaryReader reader) {");
  610. EmitLine(" ushort classId = reader.ReadUInt16();");
  611. EmitLine(" ushort methodId = reader.ReadUInt16();");
  612. EmitLine("");
  613. EmitLine(" switch (classId) {");
  614. foreach (AmqpClass c in m_classes) {
  615. EmitLine(" case "+c.Index+": {");
  616. EmitLine(" switch (methodId) {");
  617. foreach (AmqpMethod m in c.m_Methods)
  618. {
  619. EmitLine(" case "+m.Index+": {");
  620. EmitLine(" "+ImplNamespaceBase+"."+MangleMethodClass(c,m)+" result = new "+ImplNamespaceBase+"."+MangleMethodClass(c,m)+"();");
  621. EmitLine(" result.ReadArgumentsFrom(new RabbitMQ.Client.Impl.MethodArgumentReader(reader));");
  622. EmitLine(" return result;");
  623. EmitLine(" }");
  624. }
  625. EmitLine(" default: break;");
  626. EmitLine(" }");
  627. EmitLine(" break;");
  628. EmitLine(" }");
  629. }
  630. EmitLine(" default: break;");
  631. EmitLine(" }");
  632. EmitLine(" throw new RabbitMQ.Client.Impl.UnknownClassOrMethodException(classId, methodId);");
  633. EmitLine(" }");
  634. }
  635. public void EmitContentHeaderReader() {
  636. EmitLine(" public override RabbitMQ.Client.Impl.ContentHeaderBase DecodeContentHeaderFrom(RabbitMQ.Util.NetworkBinaryReader reader) {");
  637. EmitLine(" ushort classId = reader.ReadUInt16();");
  638. EmitLine("");
  639. EmitLine(" switch (classId) {");
  640. foreach (AmqpClass c in m_classes) {
  641. if (c.NeedsProperties) {
  642. EmitLine(" case "+c.Index+": return new "
  643. +MangleClass(c.Name)+"Properties();");
  644. }
  645. }
  646. EmitLine(" default: break;");
  647. EmitLine(" }");
  648. EmitLine(" throw new RabbitMQ.Client.Impl.UnknownClassOrMethodException(classId, 0);");
  649. EmitLine(" }");
  650. }
  651. public Attribute Attribute(MemberInfo mi, Type t) {
  652. return Attribute(mi.GetCustomAttributes(t, false), t);
  653. }
  654. public Attribute Attribute(ParameterInfo pi, Type t) {
  655. return Attribute(pi.GetCustomAttributes(t, false), t);
  656. }
  657. public Attribute Attribute(ICustomAttributeProvider p, Type t) {
  658. return Attribute(p.GetCustomAttributes(t, false), t);
  659. }
  660. public Attribute Attribute(IEnumerable attributes, Type t) {
  661. if (t.IsSubclassOf(typeof(AmqpApigenAttribute))) {
  662. AmqpApigenAttribute result = null;
  663. foreach (AmqpApigenAttribute candidate in attributes) {
  664. if (candidate.m_namespaceName == null && result == null) {
  665. result = candidate;
  666. }
  667. if (candidate.m_namespaceName == ApiNamespaceBase) {
  668. result = candidate;
  669. }
  670. }
  671. return result;
  672. } else {
  673. foreach (Attribute attribute in attributes) {
  674. return attribute;
  675. }
  676. return null;
  677. }
  678. }
  679. public void EmitModelImplementation() {
  680. EmitLine(" public class Model: RabbitMQ.Client.Impl.ModelBase {");
  681. EmitLine(" public Model(RabbitMQ.Client.Impl.ISession session): base(session) {}");
  682. ArrayList asynchronousHandlers = new ArrayList();
  683. foreach (Type t in m_modelTypes) {
  684. foreach (MethodInfo method in t.GetMethods()) {
  685. if (method.DeclaringType.Namespace != null &&
  686. method.DeclaringType.Namespace.StartsWith("RabbitMQ.Client")) {
  687. if (method.Name.StartsWith("Handle") ||
  688. (Attribute(method, typeof(AmqpAsynchronousHandlerAttribute)) != null))
  689. {
  690. asynchronousHandlers.Add(method);
  691. } else {
  692. MaybeEmitModelMethod(method);
  693. }
  694. }
  695. }
  696. }
  697. EmitAsynchronousHandlers(asynchronousHandlers);
  698. EmitLine(" }");
  699. }
  700. public void EmitContentHeaderFactory(MethodInfo method) {
  701. AmqpContentHeaderFactoryAttribute factoryAnnotation = (AmqpContentHeaderFactoryAttribute)
  702. Attribute(method, typeof(AmqpContentHeaderFactoryAttribute));
  703. string contentClass = factoryAnnotation.m_contentClass;
  704. EmitModelMethodPreamble(method);
  705. EmitLine(" {");
  706. EmitLine(" return new "+MangleClass(contentClass)+"Properties();");
  707. EmitLine(" }");
  708. }
  709. public void MaybeEmitModelMethod(MethodInfo method) {
  710. if (method.IsSpecialName) {
  711. // It's some kind of event- or property-related method.
  712. // It shouldn't be autogenerated.
  713. } else if (Attribute(method, typeof(AmqpMethodDoNotImplementAttribute)) != null) {
  714. // Skip this method, by request (AmqpMethodDoNotImplement)
  715. } else if (Attribute(method, typeof(AmqpContentHeaderFactoryAttribute)) != null) {
  716. EmitContentHeaderFactory(method);
  717. } else if (Attribute(method, typeof(AmqpUnsupportedAttribute)) != null) {
  718. EmitModelMethodPreamble(method);
  719. EmitLine(" {");
  720. EmitLine(" throw new UnsupportedMethodException(\""+method.Name+"\");");
  721. EmitLine(" }");
  722. } else {
  723. EmitModelMethod(method);
  724. }
  725. }
  726. public string SanitisedFullName(Type t) {
  727. if (t == typeof(void)) {
  728. return "void";
  729. } else {
  730. return t.FullName;
  731. }
  732. }
  733. public void EmitModelMethodPreamble(MethodInfo method) {
  734. Emit(" public override "+SanitisedFullName(method.ReturnType)+" "+method.Name);
  735. ParameterInfo[] parameters = method.GetParameters();
  736. int remaining = parameters.Length;
  737. if (remaining == 0) {
  738. EmitLine("()");
  739. } else {
  740. EmitLine("(");
  741. foreach (ParameterInfo pi in parameters) {
  742. Emit(" "+SanitisedFullName(pi.ParameterType)+" @"+pi.Name);
  743. remaining--;
  744. if (remaining > 0) {
  745. EmitLine(",");
  746. } else {
  747. EmitLine(")");
  748. }
  749. }
  750. }
  751. }
  752. public void LookupAmqpMethod(MethodInfo method,
  753. string methodName,
  754. out AmqpClass amqpClass,
  755. out AmqpMethod amqpMethod)
  756. {
  757. amqpClass = null;
  758. amqpMethod = null;
  759. // First, try autodetecting the class/method via the
  760. // IModel method name.
  761. foreach (AmqpClass c in m_classes) {
  762. foreach (AmqpMethod m in c.m_Methods)
  763. {
  764. if (methodName.Equals(MangleMethodClass(c,m))) {
  765. amqpClass = c;
  766. amqpMethod = m;
  767. goto stopSearching; // wheee
  768. }
  769. }
  770. }
  771. stopSearching:
  772. // If an explicit mapping was provided as an attribute,
  773. // then use that instead, whether the autodetect worked or
  774. // not.
  775. {
  776. AmqpMethodMappingAttribute methodMapping =
  777. Attribute(method, typeof(AmqpMethodMappingAttribute)) as AmqpMethodMappingAttribute;
  778. if (methodMapping != null) {
  779. amqpClass = null;
  780. foreach (AmqpClass c in m_classes) {
  781. if (c.Name == methodMapping.m_className) {
  782. amqpClass = c;
  783. break;
  784. }
  785. }
  786. amqpMethod = amqpClass.MethodNamed(methodMapping.m_methodName);
  787. }
  788. }
  789. // At this point, if can't find either the class or the
  790. // method, we can't proceed. Complain.
  791. if (amqpClass == null || amqpMethod == null) {
  792. throw new Exception("Could not find AMQP class or method for IModel method " + method.Name);
  793. }
  794. }
  795. public void EmitModelMethod(MethodInfo method) {
  796. ParameterInfo[] parameters = method.GetParameters();
  797. AmqpClass amqpClass = null;
  798. AmqpMethod amqpMethod = null;
  799. LookupAmqpMethod(method, method.Name, out amqpClass, out amqpMethod);
  800. string requestImplClass = MangleMethodClass(amqpClass, amqpMethod);
  801. // At this point, we know which request method to
  802. // send. Now compute whether it's an RPC or not.
  803. AmqpMethod amqpReplyMethod = null;
  804. AmqpMethodMappingAttribute replyMapping =
  805. Attribute(method.ReturnTypeCustomAttributes, typeof(AmqpMethodMappingAttribute))
  806. as AmqpMethodMappingAttribute;
  807. if (Attribute(method, typeof(AmqpForceOneWayAttribute)) == null &&
  808. (amqpMethod.IsSimpleRpcRequest || replyMapping != null))
  809. {
  810. // We're not forcing oneway, and either are a simple
  811. // RPC request, or have an explicit replyMapping
  812. amqpReplyMethod = amqpClass.MethodNamed(replyMapping == null
  813. ? (string) amqpMethod.m_ResponseMethods[0]
  814. : replyMapping.m_methodName);
  815. if (amqpReplyMethod == null) {
  816. throw new Exception("Could not find AMQP reply method for IModel method " + method.Name);
  817. }
  818. }
  819. // If amqpReplyMethod is null at this point, it's a
  820. // one-way operation, and no continuation needs to be
  821. // consed up. Otherwise, we should expect a reply of kind
  822. // identified by amqpReplyMethod - unless there's a nowait
  823. // parameter thrown into the equation!
  824. //
  825. // Examine the parameters to discover which might be
  826. // nowait, content header or content body.
  827. ParameterInfo nowaitParameter = null;
  828. string nowaitExpression = "null";
  829. ParameterInfo contentHeaderParameter = null;
  830. ParameterInfo contentBodyParameter = null;
  831. foreach (ParameterInfo pi in parameters) {
  832. AmqpNowaitArgumentAttribute nwAttr =
  833. Attribute(pi, typeof(AmqpNowaitArgumentAttribute)) as AmqpNowaitArgumentAttribute;
  834. if (nwAttr != null) {
  835. nowaitParameter = pi;
  836. if (nwAttr.m_replacementExpression != null) {
  837. nowaitExpression = nwAttr.m_replacementExpression;
  838. }
  839. }
  840. if (Attribute(pi, typeof(AmqpContentHeaderMappingAttribute)) != null) {
  841. contentHeaderParameter = pi;
  842. }
  843. if (Attribute(pi, typeof(AmqpContentBodyMappingAttribute)) != null) {
  844. contentBodyParameter = pi;
  845. }
  846. }
  847. // Compute expression text for the content header and body.
  848. string contentHeaderExpr =
  849. contentHeaderParameter == null
  850. ? "null"
  851. : " ("+MangleClass(amqpClass.Name)+"Properties) "+contentHeaderParameter.Name;
  852. string contentBodyExpr =
  853. contentBodyParameter == null ? "null" : contentBodyParameter.Name;
  854. // Emit the method declaration and preamble.
  855. EmitModelMethodPreamble(method);
  856. EmitLine(" {");
  857. // Emit the code to build the request.
  858. EmitLine(" "+requestImplClass+" __req = new "+requestImplClass+"();");
  859. foreach (ParameterInfo pi in parameters) {
  860. if (pi != contentHeaderParameter &&
  861. pi != contentBodyParameter)
  862. {
  863. if (Attribute(pi, typeof(AmqpUnsupportedAttribute)) != null) {
  864. EmitLine(" if (@"+pi.Name+" != null) {");
  865. EmitLine(" throw new UnsupportedMethodFieldException(\""+method.Name+"\",\""+pi.Name+"\");");
  866. EmitLine(" }");
  867. } else {
  868. AmqpFieldMappingAttribute fieldMapping =
  869. Attribute(pi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
  870. if (fieldMapping != null) {
  871. EmitLine(" __req.m_"+fieldMapping.m_fieldName+" = @" + pi.Name + ";");
  872. } else {
  873. EmitLine(" __req.m_"+pi.Name+" = @" + pi.Name + ";");
  874. }
  875. }
  876. }
  877. }
  878. // If we have a nowait parameter, sometimes that can turn
  879. // a ModelRpc call into a ModelSend call.
  880. if (nowaitParameter != null) {
  881. EmitLine(" if ("+nowaitParameter.Name+") {");
  882. EmitLine(" ModelSend(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
  883. if (method.ReturnType != typeof(void)) {
  884. EmitLine(" return "+nowaitExpression+";");
  885. }
  886. EmitLine(" }");
  887. }
  888. // At this point, perform either a ModelRpc or a
  889. // ModelSend.
  890. if (amqpReplyMethod == null) {
  891. EmitLine(" ModelSend(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
  892. } else {
  893. string replyImplClass = MangleMethodClass(amqpClass, amqpReplyMethod);
  894. EmitLine(" RabbitMQ.Client.Impl.MethodBase __repBase = ModelRpc(__req,"+contentHeaderExpr+","+contentBodyExpr+");");
  895. EmitLine(" "+replyImplClass+" __rep = __repBase as "+replyImplClass+";");
  896. EmitLine(" if (__rep == null) throw new UnexpectedMethodException(__repBase);");
  897. if (method.ReturnType == typeof(void)) {
  898. // No need to further examine the reply.
  899. } else {
  900. // At this point, we have the reply method. Extract values from it.
  901. AmqpFieldMappingAttribute returnMapping =
  902. Attribute(method.ReturnTypeCustomAttributes, typeof(AmqpFieldMappingAttribute))
  903. as AmqpFieldMappingAttribute;
  904. if (returnMapping == null) {
  905. string fieldPrefix = IsAmqpClass(method.ReturnType) ? "m_" : "";
  906. // No field mapping --> it's assumed to be a struct to fill in.
  907. EmitLine(" "+method.ReturnType+" __result = new "+method.ReturnType+"();");
  908. foreach (FieldInfo fi in method.ReturnType.GetFields()) {
  909. AmqpFieldMappingAttribute returnFieldMapping =
  910. Attribute(fi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
  911. if (returnFieldMapping != null) {
  912. EmitLine(" __result." + fi.Name + " = __rep." + fieldPrefix + returnFieldMapping.m_fieldName + ";");
  913. } else {
  914. EmitLine(" __result." + fi.Name + " = __rep." + fieldPrefix + fi.Name + ";");
  915. }
  916. }
  917. EmitLine(" return __result;");
  918. } else {
  919. // Field mapping --> return just the field we're interested in.
  920. EmitLine(" return __rep.m_"+returnMapping.m_fieldName+";");
  921. }
  922. }
  923. }
  924. // All the IO and result-extraction has been done. Emit
  925. // the method postamble.
  926. EmitLine(" }");
  927. }
  928. public void EmitAsynchronousHandlers(ArrayList asynchronousHandlers) {
  929. EmitLine(" public override bool DispatchAsynchronous(RabbitMQ.Client.Impl.Command cmd) {");
  930. EmitLine(" RabbitMQ.Client.Impl.MethodBase __method = (RabbitMQ.Client.Impl.MethodBase) cmd.Method;");
  931. EmitLine(" switch ((__method.ProtocolClassId << 16) | __method.ProtocolMethodId) {");
  932. foreach (MethodInfo method in asynchronousHandlers) {
  933. string methodName = method.Name;
  934. if (methodName.StartsWith("Handle")) {
  935. methodName = methodName.Substring(6);
  936. }
  937. AmqpClass amqpClass = null;
  938. AmqpMethod amqpMethod = null;
  939. LookupAmqpMethod(method, methodName, out amqpClass, out amqpMethod);
  940. string implClass = MangleMethodClass(amqpClass, amqpMethod);
  941. EmitLine(" case "+((amqpClass.Index << 16) | amqpMethod.Index)+": {");
  942. ParameterInfo[] parameters = method.GetParameters();
  943. if (parameters.Length > 0) {
  944. EmitLine(" "+implClass+" __impl = ("+implClass+") __method;");
  945. EmitLine(" "+method.Name+"(");
  946. int remaining = parameters.Length;
  947. foreach (ParameterInfo pi in parameters) {
  948. if (Attribute(pi, typeof(AmqpContentHeaderMappingAttribute)) != null) {
  949. Emit(" ("+pi.ParameterType+") cmd.Header");
  950. } else if (Attribute(pi, typeof(AmqpContentBodyMappingAttribute)) != null) {
  951. Emit(" cmd.Body");
  952. } else {
  953. AmqpFieldMappingAttribute fieldMapping =
  954. Attribute(pi, typeof(AmqpFieldMappingAttribute)) as AmqpFieldMappingAttribute;
  955. Emit(" __impl.m_"+(fieldMapping == null
  956. ? pi.Name
  957. : fieldMapping.m_fieldName));
  958. }
  959. remaining--;
  960. if (remaining > 0) {
  961. EmitLine(",");
  962. }
  963. }
  964. EmitLine(");");
  965. } else {
  966. EmitLine(" "+method.Name+"();");
  967. }
  968. EmitLine(" return true;");
  969. EmitLine(" }");
  970. }
  971. EmitLine(" default: return false;");
  972. EmitLine(" }");
  973. EmitLine(" }");
  974. }
  975. }
  976. }